views:

109

answers:

1

Hi,

In an app I need to serialize an image through a binarywriter, and to get it in an other app to display it.

Here is a part of my "serialization" code :

FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write);
BinaryWriter bin = new BinaryWriter(fs);                         

bin.Write((short)this.Animations.Count);

for (int i = 0; i < this.Animations.Count; i++)
{
    MemoryStream stream = new MemoryStream();
    BitmapEncoder encoder = new PngBitmapEncoder();

    encoder.Frames.Add(BitmapFrame.Create(Animations[i].Image));
    encoder.Save(stream);

    stream.Seek(0, SeekOrigin.Begin);

    bin.Write((int)stream.GetBuffer().Length);
    bin.Write(stream.GetBuffer());

    stream.Close();
}

bin.Close();

And here is the part of my deserialization that load the image :

FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader bin = new BinaryReader(fs);

int animCount = bin.ReadInt16();
int imageBytesLenght;
byte[] imageBytes;
BitmapSource img;

for (int i = 0; i < animCount; i++)
{    
    imageBytesLenght = bin.ReadInt32();
    imageBytes = bin.ReadBytes(imageBytesLenght);
    img = new BitmapImage();
    MemoryStream stream = new MemoryStream(imageBytes);
    BitmapDecoder dec = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    img = dec.Frames[0];
    stream.Close();
}

bin.Close();

When I use this method, I load the image (it seems to be stored in the "img" object) but it can't be displayed.

Has somedy an idea?

Thanks

KiTe

UPD :

I already do this : updating my binding, or even trying to affect it directly through the window code behing. None of these approaches work :s

However, when I add this :

private void CreateFile(byte[] bytes)
{
    FileStream fs = new FileStream(Environment.CurrentDirectory + "/" + "testeuh.png", FileMode.Create, FileAccess.Write);
    fs.Write(bytes, 0, bytes.Length);     
    fs.Close();
}

at the end of you function, it perfectly create the file, which can be read without any problems ... So I don't know where the problem can be.

UPD 2 :

A weird things happens.

Here is the binding I use :

<Image x:Name="imgSelectedAnim" Width="150" Height="150" Source="{Binding ElementName=lstAnims, Path=SelectedItem.Image}" Stretch="Uniform" />

(the list is itself binded on an observableCollection).

When I create manually the animation through the app, it works (the image is displayed).

But when I load it, it is not displayed (I look at my code, there are not any "new" which could break up my binding, since there are others properties binded the same way and they does not fail).

Moreover, I've put a breakpoint on the getter/setter of the properties Image of my animation.

When it is created, no problems, it has the good informations.

But when it is retreived through the getter, it return a good image the first time, and then and image with pixelWidth, pixelHeight, width, height of only 1 but without going through the setter anymore !

Any idea?

UPD3

tried what you said like this :

Added a property TEST in my viewModel :

  private BitmapSource test;
        public BitmapSource TEST { get { return test; } set { test = value; RaisePropertyChanged("TEST"); } }

then did it :

img = getBmpSrcFromBytes(bin.ReadBytes(imageBytesLenght));
TEST = img;

(in the code showed and modified before)

and my binding :

 <Image x:Name="imgSelectedAnim" Width="150" Height="150" Source="{Binding Path=TEST}" Stretch="Uniform" />

(datacontext is set to my ViewModel)

And it still doesn't work and does the weird image modification (pixW, pixH, W and H set to 1)

FINAL UPD:

It seems I finally solved the problem. Here is simply what I did :

byte[] bytes = bin.ReadBytes(imageBytesLenght);
MemoryStream mem = new MemoryStream(bytes);

img = new BitmapImage();
img.BeginInit();
img.StreamSource = mem;
img.EndInit();

the strange thing is that it didn't work the first time, maybe it is due to an architectural modification within my spriteanimation class, but I don't think it is.

Well, thank you a lot to Eugene Cheverda for his help

A: 

At the first, I think you should store your images in a list of BitmapSource and provide reverse encoding of image for using stream as BitmapImage.StreamSource:

FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
        BinaryReader bin = new BinaryReader(fs);

        int animCount = bin.ReadInt16();
        int imageBytesLenght;
        byte[] imageBytes;
        List<BitmapSource> bitmaps = new List<BitmapSource>();

        for (int i = 0; i < animCount; i++)
        {
            imageBytesLenght = bin.ReadInt32();
            imageBytes = bin.ReadBytes(imageBytesLenght);
            bitmaps.Add(getBmpSrcFromBytes(imageBytes));
        }

        bin.Close();

UPD In code above use func written below:

private BitmapSource getBmpSrcFromBytes(byte[] bytes)
        {
            using (var srcStream = new MemoryStream(bytes))
            {
                var dec = new PngBitmapDecoder(srcStream, BitmapCreateOptions.PreservePixelFormat,
                                                             BitmapCacheOption.Default);

                var encoder = new PngBitmapEncoder();
                encoder.Frames.Add(dec.Frames[0]);
                BitmapImage bitmapImage = null;
                bool isCreated;
                try
                {
                    using (var ms = new MemoryStream())
                    {
                        encoder.Save(ms);
                        bitmapImage = new BitmapImage();
                        bitmapImage.BeginInit();
                        bitmapImage.StreamSource = ms;
                        bitmapImage.EndInit();
                        isCreated = true;
                    }
                }
                catch
                {
                    isCreated = false;
                }
                return isCreated ? bitmapImage : null;
            }
        }

Hope this helps.

UPD #2

Your binding is incorrect. You may be should bind selected item to for example CurrentImageSource. And then this CurrentImageSource bind to Image control.

Code:

public class MyViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<BitmapSource> ImagesCollection { get; set; }
        private BitmapSource _currentImage;

        public BitmapSource CurrentImage
        {
            get { return _currentImage; }
            set
            {
                _currentImage = value;
                raiseOnPropertyChanged("CurrentImage");
            }
        }

        private void raiseOnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }        

        public event PropertyChangedEventHandler PropertyChanged;
    }

Then:

<ListBox ItemsSource="{Binding ImagesCollection}" SelectedItem="{Binding CurrentImage}"/>
<Image Source="{Binding CurrentImage}"/>

ADDED

Here is the sample project in which implemented technique as I described above. It creates animations collection and represent them in listbox, selected animation is displayed in Image control using its Image property.

What I want to say is that if you cannot obtain image as it should be, you need to search problems in serialization/deserialization.

Eugene Cheverda
it crashes at enc.save(stream)
kite
See update. Should be working
Eugene Cheverda
this return an image, but i can't display it :s
kite
Assign it to `Source` property of an `Image` control. If `Source` is binded to something, then assign obtained image to that property and call `PropertyChanged` of `INotifyPropertyChanged`
Eugene Cheverda
see UPD please.
kite
You have incorrect binding, see the technique in upd #2.
Eugene Cheverda
the thing is that "SelectedItem" then take the type of my object, SpriteAnimation, which has a property "Image".My images are not stored in a specific array, since i only have an ObservableCollection<SpriteAnimation>, and I access Image (and other properties) through there
kite
See update, I've created the sample solution for you. It simply creates Animation Collection and represent it in window. Use the same bindings, and it should work. If not search bug in serialization/deserialization.
Eugene Cheverda
thank you, your source helped me to see differently my problem. see my last update to see how i solved
kite
You're welcome! One note: use `IDisposable` with `using` construction.
Eugene Cheverda