23882

Saving a Canvas as an image in UWP

Question:

I found <a href="https://social.msdn.microsoft.com/Forums/en-US/cf8c17dc-68d4-4777-951f-bb7f0665bd06/saving-a-canvas-as-an-image-in-windows-8?forum=winappswithhtml5" rel="nofollow">this documentation</a> for exporting and saving a Canvas as a png. It gives the following code:

var Imaging = Windows.Graphics.Imaging; var picker = new Windows.Storage.Pickers.FileSavePicker(); picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary; picker.fileTypeChoices.insert("PNG file", [".png"]); var imgData, fileStream = null; picker.pickSaveFileAsync().then(function (file) { if (file) { return file.openAsync(Windows.Storage.FileAccessMode.readWrite); } else { return WinJS.Promise.wrapError("No file selected"); } }).then(function (stream) { fileStream = stream; var canvas = document.getElementById("canvas1"); var ctx = canvas.getContext("2d"); imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); return Imaging.BitmapEncoder.createAsync(Imaging.BitmapEncoder.pngEncoderId, stream); }).then(function (encoder) { //Set the pixel data--assume "encoding" object has options from elsewhere encoder.setPixelData(encoding.pixelFormat, encoding.alphaMode, encoding.width, encoding.height, encoding.dpiX, encoding.dpiY, new Uint8Array(imgData.data)); //Go do the encoding return encoder.flushAsync(); }).done(function () { //Make sure to do this at the end fileStream.close(); }, function () { //Empty error handler (do nothing if the user canceled the picker });

In the 'encoder.setPixelData' area, they use an object called 'encoding' to set all the values, but have no explanation of how this object is created in any of the prior steps.

How can I create this 'encoding' object to complete the example?

Answer1:

I found a different way to save the canvas, still didn't solve my initial problem

I can turn the canvas into a blob stream and save that to a png file

function saveCanvasAsImage(canvasElement) { var picker = new Windows.Storage.Pickers.FileSavePicker(); picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary; picker.fileTypeChoices.insert("PNG file", [".png"]); var input, output = null; var blob = canvasElement.msToBlob(); return picker.pickSaveFileAsync() .then(function (file) { if (file) { return file.openAsync(Windows.Storage.FileAccessMode.readWrite); } else { return WinJS.Promise.wrapError("No file selected"); } }) .then(function (op) { output = op; input = blob.msDetachStream(); return Windows.Storage.Streams.RandomAccessStream.copyAsync(input, output); }) .then(function() { return output.flushAsync(); }) .done(function() { input.close(); output.close(); }, function(e) { // handle error here }); }

Unfortunately it's saving with a transparent background, but maybe there's a way I can make my canvas have a white background.

[edit] ----

So I found a way to answer my original question more directly:

function saveCanvasAsImage(canvasElement) { var Imaging = Windows.Graphics.Imaging; var picker = new Windows.Storage.Pickers.FileSavePicker(); picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary; // picker.fileTypeChoices.insert("JPEG file", [".jpg"]); picker.fileTypeChoices.insert("PNG file", [".png"]); var fileStream, decoder, encoding, pixels = null; var encoderIds = { '.png': Imaging.BitmapEncoder.pngEncoderId, '.jpg': Imaging.BitmapEncoder.jpegEncoderId } var encoderId = encoderIds['.jpg']; var blob = canvasElement.msToBlob(); return Imaging.BitmapDecoder.createAsync(Imaging.BitmapDecoder.pngDecoderId, blob.msDetachStream()) .then(function (dc) { decoder = dc; return picker.pickSaveFileAsync(); }).then(function (file) { if (file) { encoderId = encoderIds[file.fileType]; return file.openAsync(Windows.Storage.FileAccessMode.readWrite); } else { return WinJS.Promise.wrapError("No file selected"); } }).then(function(stream) { fileStream = stream; var transform = new Windows.Graphics.Imaging.BitmapTransform(); return decoder.getPixelDataAsync( decoder.bitmapPixelFormat, decoder.bitmapAlphaMode, transform, Windows.Graphics.Imaging.ExifOrientationMode.respectExifOrientation, Windows.Graphics.Imaging.ColorManagementMode.colorManageToSRgb ); }).then(function (pixelProvider) { pixels = pixelProvider.detachPixelData(); return Imaging.BitmapEncoder.createAsync(encoderId, fileStream); }).then(function (encoder) { encoding = decoder; //Set the pixel data--assume "encoding" object has options from elsewhere encoder.setPixelData(encoding.bitmapPixelFormat, encoding.bitmapAlphaMode, encoding.pixelWidth, encoding.pixelHeight, encoding.dpiX, encoding.dpiY, pixels); //Go do the encoding return encoder.flushAsync(); }).done(function () { //Make sure to do this at the end fileStream.close(); }, function () { //Empty error handler (do nothing if the user canceled the picker }); }

I've commented out the ability to save as JPG because that's a new problem, but this works for pngs

Answer2:

Place your canvas within a Grid with background white and then render it using RenderTargetBitmap. It worked fine for me.

RenderTargetBitmap rtb = new RenderTargetBitmap(); await rtb.RenderAsync("Your Grid containing canvas goes here"); var pixelBuffer = await rtb.GetPixelsAsync(); var pixels = pixelBuffer.ToArray(); var displayInformation = DisplayInformation.GetForCurrentView(); FileSavePicker savePicker = new FileSavePicker(); savePicker.FileTypeChoices.Add("png", new List<string>() { ".png" }); savePicker.SuggestedFileName = "PNG File"; StorageFile file = await savePicker.PickSaveFileAsync();

Recommend

  • Pure JavaFX: convert Image to bytes Array (+ opposit operation). What's wrong?
  • When to use BitmapCreateOptions.PreservePixelFormat?
  • Two Different GCHandle's refer to same array in memory
  • System.Drawing and System.Drawing.Imaging alternatives for Windows Phone
  • Determine screen color depth in android
  • OpenCV + QML (grabbing frame from another thread)
  • Android : Place non-clickable view above all application windows but below notification bar
  • Convert SlimDX.Direct3D11 Texture2D to .Net Bitmap
  • BitmapSource.Create error - Buffer size is not large enough
  • iAd reducing FPS?
  • Problems calculating stride and offset in WritePixels
  • Silverlight 3 WriteableBitmap problem
  • C# Draw on image using unsafe pointers
  • What is PixelFormat.RGBX_888
  • Native Android Activity and with touchevents in Java required using Unity3D
  • Error : Object is currently in use elsewhere.
  • save jpg with lower bit depth (24 to 16 for instance) (C#)
  • detecting qr code then rotating document
  • mCamera.setpreview{@override onPreviewFrame() } not work
  • cordova is not defined - cordova.js has already been loaded :: Ionic
  • jQuery: add elements until a particular height is reached
  • Combining two different ActiveRecord collections into one
  • How to use carriage return with multiple line?
  • OpenGL ES texture problem, 4 duplicate columns and horizontal lines (Android)
  • Encrypt data by using a public key in c# and decrypt data by using a private key in php
  • Deleting and Updating values from a cusrsor adapter
  • Modifying destination and filename of gulp-svg-sprite
  • SSO with signing and signature validation doesn't work
  • Deserializing XML into class C#
  • 'TypeError' while using NSGA2 to solve Multi-objective prob. from pyopt-sparse in OpenMDAO
  • AT Commands to Send SMS not working in Windows 8.1
  • Rails 2: use form_for to build a form covering multiple objects of the same class
  • NSLayoutConstraint that would pin a view to the bottom edge of a superview
  • How do I configure my settings file to work with unit tests?
  • embed rChart in Markdown
  • Is it possible to post an object from jquery to bottle.py?
  • How to get NHibernate ISession to cache entity not retrieved by primary key
  • costura.fody for a dll that references another dll
  • How can I use `wmic` in a Windows PE script?
  • Unable to use reactive element in my shiny app