javascriptuwpwinjs

Saving a Canvas as an image in UWP


I found this documentation 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?


Solution

  • 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