locked
Sharing an in-memory .PNG file

    Question

  • Hi,

    I'd like to share a PNG file created in memory from a WriteableBitmap. This is my code:

    // The 'image' variable is my WriteableBitmap
    
    IRandomAccessStream stream = new InMemoryRandomAccessStream(); 
     
    BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
    Stream pixelStream = image.PixelBuffer.AsStream(); 
    byte[] pixels = new byte[pixelStream.Length]; 
    await pixelStream.ReadAsync(pixels, 0, pixels.Length); 
    encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)image.PixelWidth, (uint)image.PixelHeight, 96.0, 96.0, pixels);
    
    request.Data.SetBitmap(RandomAccessStreamReference.CreateFromStream(stream)); 
    await encoder.FlushAsync();
    

    When I run the above code in DataTransferManager.DataRequested a BMP file is shared rather than PNG.

    Is there a way to share the PNG file?

    Thanks,

    Leszek


    Wiki: wbswiki.com
    Website: www.wisenheimerbrainstorm.com

    Thursday, April 17, 2014 1:21 PM

All replies

  • you can maybe use the request.Data.SetStorageItems method.... you can create the "abstract" file using the StorageFile.CreateStreamedFileAsync function.... sample usage for this function:

    var resultantFile = await StorageFile.CreateStreamedFileAsync("MyFile.png", async (fileStream) =>
        {
            await fileStream.WriteAsync(resultingBuffer.AsBuffer());
            fileStream.FlushAsync();
            fileStream.Dispose();
        }, null);
    

    I inserted null as the last parameter, but you can as well create a stream for your a thumbnail image.

    hope it helps.


    Can Bilgin
    Blog Samples CompuSight

    Thursday, April 17, 2014 1:59 PM
  • Thanks Can for your response. 

    I'm aware of the StorageFile.CreateStreamedFileAsync function and I have never made it work right. In your code snippet:

    - what should I provide for the fileStream parameter (it is of type StreamedFileDataRequest)?

    - what should I provide for the resultingBuffer?

    I have tried to match my code that creates a PNG file to yours with no luck.

    Thanks,

    Leszek


    Wiki: wbswiki.com
    Website: www.wisenheimerbrainstorm.com

    Thursday, April 17, 2014 2:17 PM
  • oh in the code example you just need to set the resultingBuffer as the byte array from your in-memory png... the lambda is correct as it is...

    Here is the updated code for reading the bytes from your stream and writing them out to the streamed storage file:

    var resultingBuffer = new byte[stream.Size];
    await stream.ReadAsync(resultingBuffer.AsBuffer(), (uint)bytes.Length, InputStreamOptions.None);
    
    var resultantFile = await StorageFile.CreateStreamedFileAsync("MyFile.png", async (fileStream) =>
        {
            await fileStream.WriteAsync(resultingBuffer.AsBuffer());
            fileStream.FlushAsync();
            fileStream.Dispose();
        }, null);
    I am updating my Visual Studio right now so I tried my best not make mistake writing the code sample :) sorry about any typos


    Can Bilgin
    Blog Samples CompuSight


    • Edited by Can BilginMVP Thursday, April 17, 2014 2:31 PM added code
    Thursday, April 17, 2014 2:22 PM
  • Thanks Can.

    I have incorporated your code to my code (the 'image' variable is the WriteableBitmap to share):

    IRandomAccessStream stream = new InMemoryRandomAccessStream(); BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream); Stream pixelStream = image.PixelBuffer.AsStream(); byte[] pixels = new byte[pixelStream.Length]; await pixelStream.ReadAsync(pixels, 0, pixels.Length); encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)image.PixelWidth, (uint)image.PixelHeight, 96.0, 96.0, pixels); //request.Data.SetBitmap(RandomAccessStreamReference.CreateFromStream(stream)); await encoder.FlushAsync();

    // ... here goes your code with the 'bytes' array replaced by the 'pixels' array byte[] resultingBuffer = new byte[stream.Size]; await stream.ReadAsync(resultingBuffer.AsBuffer(), (uint)pixels.Length, InputStreamOptions.None); var resultantFile = await StorageFile.CreateStreamedFileAsync("MyFile.png", async fileStream => { await fileStream.WriteAsync(resultingBuffer.AsBuffer()); await fileStream.FlushAsync(); fileStream.Dispose(); }, null);


    It still does not work. When I try to share the bitmap I get a message "There was a problem with data". 

    Leszek


    Wiki: wbswiki.com
    Website: www.wisenheimerbrainstorm.com

    Thursday, April 17, 2014 3:42 PM
  • hey sorry I couldn't reply earlier... I see two problems with the code posted:

    1) in the line : await stream.ReadAsync; we are supposed to use the resultingBuffer size (it's showing pixels array). Pixels are the un-encoded array as I see. (but this should not give you the there was a problem with data error).

    2) The operation is in an async call, so we need to use a deferral (while handling the operation)

    try
    {
        var deferral = request.GetDeferral();
    
        // The block that prepares the storage file for share
    }
    catch(Exception ex)
    {
        // Report the problem
    }
    finally
    {
        deferral.Complete();
    }


    Can Bilgin
    Blog Samples CompuSight

    • Marked as answer by ata6502 Wednesday, April 23, 2014 1:34 PM
    • Unmarked as answer by ata6502 Thursday, April 24, 2014 12:46 PM
    Friday, April 18, 2014 2:29 PM
  • Thanks Can for your response. The code I attached is a snippet from the actual event handler which is wrapped into a deferral. Below there is the complete event handler together with the change you suggested (using resultingBuffer.Length rather then pixels.Length):

    private async void OnDataTransferDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
    {
        DataRequest request = args.Request;
    
        request.Data.Properties.Title = "My Title";
    
        DataRequestDeferral deferral = request.GetDeferral();
    
        try
        {
            WriteableBitmap image = GetBitmap(); // a method to obtain the bitmap
    
            IRandomAccessStream stream = new InMemoryRandomAccessStream();
            BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
            Stream pixelStream = image.PixelBuffer.AsStream();
            byte[] pixels = new byte[pixelStream.Length];
            await pixelStream.ReadAsync(pixels, 0, pixels.Length);
            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)image.PixelWidth, (uint)image.PixelHeight, 96.0, 96.0, pixels);
    
            await encoder.FlushAsync();
    
            byte[] resultingBuffer = new byte[stream.Size];
            await stream.ReadAsync(resultingBuffer.AsBuffer(), (uint)resultingBuffer.Length, InputStreamOptions.None);
    
            var resultantFile = await StorageFile.CreateStreamedFileAsync("MyFile.png", async fileStream =>
            {
                await fileStream.WriteAsync(resultingBuffer.AsBuffer());
                await fileStream.FlushAsync();
                fileStream.Dispose();
            }, null);
        }
        catch (Exception exc)
        {
            // ... an exception handler goes here
        }
        finally
        {
            deferral.Complete();
        }
    }
    

    It still results in the same message "there was a problem with the data". Note that this message comes from Windows Runtime. It's not an exception I can catch.

    Any other suggestions?

    Thanks,

    Leszek


    Wiki: wbswiki.com
    Website: www.wisenheimerbrainstorm.com

    Wednesday, April 23, 2014 1:27 PM
  • oops! I missed a line (actually three lines) to add the file to storage items. This is the complete code and it works like a charm :)

    private async void OnDataTransferDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
    {
        DataRequest request = args.Request;
    
        request.Data.Properties.Title = "My Title";
    
        DataRequestDeferral deferral = request.GetDeferral();
    
        try
        {
            WriteableBitmap image = this.simulation.Bitmap;
    
            IRandomAccessStream stream = new InMemoryRandomAccessStream();
            BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
            Stream pixelStream = image.PixelBuffer.AsStream();
            byte[] pixels = new byte[pixelStream.Length];
            await pixelStream.ReadAsync(pixels, 0, pixels.Length);
            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)image.PixelWidth, (uint)image.PixelHeight, 96.0, 96.0, pixels);
    
            await encoder.FlushAsync();
    
            byte[] resultingBuffer = new byte[stream.Size];
            await stream.ReadAsync(resultingBuffer.AsBuffer(), (uint)resultingBuffer.Length, InputStreamOptions.None);
    
            StorageFile resultantFile = await StorageFile.CreateStreamedFileAsync("MyFile.png", async fileStream =>
            {
                await fileStream.WriteAsync(resultingBuffer.AsBuffer());
                await fileStream.FlushAsync();
                fileStream.Dispose();
            }, null);
    
            // These lines were missing.
            List<IStorageItem> storageItems = new List<IStorageItem>();
            storageItems.Add(resultantFile);
            request.Data.SetStorageItems(storageItems);
        }
        catch (Exception exc)
        {
            // ... an exception handler goes here
        }
        finally
        {
            deferral.Complete();
        }
    }
    

    Thanks Can! Problem solved.

    Leszek


    Wiki: wbswiki.com
    Website: www.wisenheimerbrainstorm.com

    Wednesday, April 23, 2014 1:33 PM
  • Thanks Can for your contribution so far. Unfortunately I had to unmark your post as the answer. The code still does not work on Windows 8 (although it works perfectly on Windows 8.1). On Windows 8 the shared image is PNG but it is corrupted. When I didn't used StorageFile.CreateStreamedFileAsync and just stuck to request.Data.SetBitmap, the image was not corrupted but it was BMP.

    This is the complete code with two methods of sharing (request.Data.SetBitmap  and  StorageFile.CreateStreamedFileAsync). Comment it in and out to replicate the issue on Windows 8.

    private async void OnDataTransferDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
    {
        DataRequest request = args.Request;
    
        request.Data.Properties.Title = "My Title";
    
        DataRequestDeferral deferral = request.GetDeferral();
    
        try
        {
            WriteableBitmap image = GetBitmap(); // a custom method to get a bitmap
    
            IRandomAccessStream stream = new InMemoryRandomAccessStream();
            BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
            Stream pixelStream = image.PixelBuffer.AsStream();
            byte[] pixels = new byte[pixelStream.Length];
            await pixelStream.ReadAsync(pixels, 0, pixels.Length);
            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)image.PixelWidth, (uint)image.PixelHeight, 96.0, 96.0, pixels);
            await encoder.FlushAsync();
    
            // Method #1: Share the BMP file. It works fine. Uncomment this line and comment method #2 to test it.
      //request.Data.SetBitmap(RandomAccessStreamReference.CreateFromStream(stream));
    
            // Method #2: Create an in-memory PNG storage file. It does not work under Windows 8.
            byte[] resultingBuffer = new byte[stream.Size];
            await stream.ReadAsync(resultingBuffer.AsBuffer(), (uint)resultingBuffer.Length, InputStreamOptions.None);
    
            StorageFile file = await StorageFile.CreateStreamedFileAsync("MyFile.png", async fileStream =>
            {
                await fileStream.WriteAsync(resultingBuffer.AsBuffer());
                await fileStream.FlushAsync();
                fileStream.Dispose();
            }, null);
    
            // Add the storage file to storage items.
            List<IStorageItem> storageItems = new List<IStorageItem>();
            storageItems.Add(file);
            request.Data.SetStorageItems(storageItems);
        }
        catch (Exception exc)
        {
            // ... an exception handler goes here
        }
        finally
        {
            deferral.Complete();
        }
    }

    Thanks,

    Leszek


    Wiki: wbswiki.com
    Website: www.wisenheimerbrainstorm.com

    Thursday, April 24, 2014 12:58 PM
  • Hi,

    I have a similar solution, but unfortunately the createStreamedFileAsync function is not supported on windows phone 8.1!

    Are there any other solution to share in-memory "file"?

    Thanks,
    Peter

    Wednesday, February 11, 2015 11:54 AM