Answered Deep Copy Writeablebitmap in C# Metro

  • Tuesday, August 07, 2012 6:43 PM
     
     
    In my app I have a Writeablebitmap object that I want to copy over to another Writeablebitmap object so I can perform operations on the first object and still have a copy of the original. I was wondering how I can perform a deep copy of object one into object to inside a WinRT app? I can't seem to find any Win8/Metro support for  the MemberwiseClone method that I'm familiar with in WPF. Thanks.

All Replies

  • Tuesday, August 07, 2012 7:35 PM
     
     Proposed Answer Has Code

    The simplest approach for WriteableBitmap would be to copy the pixel buffer content from one WB to another, e.g.:

    using (var source = writeableBitmap1.PixelBuffer.AsStream())
    {
        using (var destination = writeableBitmap2.PixelBuffer.AsStream())
        {
            await source.CopyToAsync(destination);
        }
    }

    (Note you'll have to import System.Runtime.InteropServices.WindowsRuntime for the AsStream() extension method) 

    • Marked As Answer by GGCO Wednesday, August 08, 2012 5:43 PM
    • Unmarked As Answer by GGCO Wednesday, August 08, 2012 8:44 PM
    • Proposed As Answer by StuMan8424 Thursday, December 06, 2012 4:01 AM
    •  
  • Tuesday, August 07, 2012 7:41 PM
     
     
    Well, for one, MemberwiseClose() is not a "deep" copy in the sense that it doesn't create new instances of the objects it references.  Now, if you need to back up the data in a writeable bitmap, that isn't hard to do.  You easily have access to the pixel buffer via the PixelBuffer property.  You can then use

    System.Runtime.InteropServices.WindowsRuntime.

    WindowsRuntimeBufferExtensions.CopyTo() to copy the bytes of the IBuffer to a byte array.

  • Tuesday, August 07, 2012 9:55 PM
     
     

    That makes sense, but when I run it Visual Studio gives me this error:

    An exception of type 'System.NotSupportedException' occurred in mscorlib.dll but was not handled in user code
    Additional information: Unable to expand length of this stream beyond its capacity.
    The program '[12424] Pichur.exe: Managed (v4.0.30319)' has exited with code -1 (0xffffffff).

    Does the stream end with -1 and that's what is causing the program to crash? Even if it does I don't really know what that'd even be an issue. Thanks so much for your help.

  • Tuesday, August 07, 2012 10:50 PM
     
     
    The second stream isn't big enough to hold the bytes.  I think the better approach is to make a second stream (apart from the bitmap's stream) that holds the capacity of the first buffer, then copy all the bytes over, then push that into the pixel buffer.
  • Wednesday, August 08, 2012 4:33 AM
     
      Has Code

    Ok right I'll try that. It's strange though, because both the first and second writeablebitmaps are essentially the same thing and are passed in the same parameters (width, height) when they're constructed.

    Here's what I'm doing.

    MemoryStream originalStream = new MemoryStream;
    await (wb1.PixelBuffer.AsStream()).CopyToAsync(originalStream);
    await originalStream.CopyToAsync(wb2.PixelBuffer.AsStream());

    However, when I assign an image source to be wb2, nothing shows up inside the image. Also, the code you see there doesn't crash - I'm loading the data into originalStream and then trying to dump that into wb2's PixelBuffer.

    • Edited by GGCO Wednesday, August 08, 2012 5:11 AM
    • Edited by GGCO Wednesday, August 08, 2012 5:26 AM
    • Edited by GGCO Wednesday, August 08, 2012 5:27 AM
    •  
  • Wednesday, August 08, 2012 1:02 PM
     
     
    So I debugged the code a little more and found out that for some reason it doesn't seem to be copying the pixel buffer over into the originalStream memory stream. When I do wb1.PixelBuffer.AsStream().ReadByte() it gives me a value above 0, but when I do originalStream.ReadByte() it gives me -1. That is done after I copy wb1's pixel buffer into the originalStream object.
  • Wednesday, August 08, 2012 2:08 PM
     
      Has Code

    So when you copy everything over to the new stream, the stream's position is set to end-of-stream for you.  You need to reset the position of the stream back to zero:

    originalStream.Position = 0;
    await originalStream.CopyToAsync(wb2.PixelBuffer.AsStream());

    This should fix it.  Note that this is going to come up a bunch when you use streams, so keep this in mind whenever you work with them.
    • Marked As Answer by GGCO Wednesday, August 08, 2012 5:42 PM
    • Unmarked As Answer by GGCO Wednesday, August 08, 2012 6:47 PM
    •  
  • Wednesday, August 08, 2012 6:55 PM
     
      Has Code

    Ok so I thought I had this working earlier and marked it as an answer, but that's not the case. Most of my code (at least the part that's giving me issues) is below.

    private MemoryStream originalStream = new MemoryStream();
    WriteableBitmap wb1 = new WriteableBitmap((int)photoBox.Width, (int)photoBox.Height);
    WriteableBitmap wb2 = new WriteableBitmap((int)photoBox.Width, (int)photoBox.Height);
    
    ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg();
    var ps = new InMemoryRandomAccessStream();
    
    await mc.CapturePhotoToStreamAsync(imageProperties, ps);
    await ps.FlushAsync();
    
    ps.Seek(0);
    
    wb1.SetSource(ps);
    (wb1.PixelBuffer.AsStream()).CopyTo(originalStream); // this works
    
    originalStream.Position = 0;
    originalStream.CopyTo(wb2.PixelBuffer.AsStream()); // this line gives me the error described above ie "Unable to expand length of this stream beyond its capacity"
                
    Image img = new Image(); 
    img.Source = wb2; // my hope is to treat this as it's own entity and modify this image independently of wb1 or originalStream
                
    photoBox.Source = wb1;
    


  • Wednesday, August 08, 2012 7:38 PM
     
     
    I haven't tried to capture a photo yet, but what I would do at this point is to check that the length of the image capture stream isn't longer than the size of the pixel buffer stream.  I can't see any other obvious mistakes here.
  • Wednesday, August 08, 2012 8:44 PM
     
      Has Code

    Yeah this is really strange. Turns out the camera's stream is very small and can fit inside wb1 and wb2, but wb1 is much larger than wb2. In fact, originalStream's length is equal to the length of wb1. That is something I don't understand. I am also trying to set wb1's length but that's just giving me the same size error.

    wb1.PixelBuffer.AsStream().SetLength(originalStream.Length);

    Another thing I tried was to supply a buffer size in the CopyTo method.

    (wb1.PixelBuffer.AsStream()).CopyTo(wb2.PixelBuffer.AsStream(), wb2.PixelBuffer.AsStream().Length)

    I'm really stumped here. Thanks for any help.

  • Thursday, August 09, 2012 2:02 PM
     
     Answered

    Check ps.Capacity and wb1.PixelBuffer.Capacity before and after the call to SetSource.  When you call SetSource(), the implementation might just straight up swap the buffers underlying the image, and if the backing store of ps is larger than the original buffer, that might cause the problem.  In this case, the remedy might be to create a second memory stream, copy the image capture stream to the second stream, and then set the source of wb2 to the copied memory stream.