WinJS/WinRT 更改图像的分辨率 (dpi)

WinJS/WinRT Change resolution (dpi) of image

前段时间,我使用 WinRT API.

编写了一些代码来修改现代 UI WinJS 应用程序中的图像大小

现在有人要求我也将图像分辨率更改为最大边的 2.7 倍(不要问)。

问题是我找不到使用 WinRT 设置图像分辨率的正确方法(如果存在的话)。

这是到目前为止的代码。请注意,它考虑了多帧图像(如 GIF)。

function resizePictureAsync(file) {
    const MAX_HEIGHT = 1600,
        MAX_WIDTH = 1600,
        RATIO_FOR_TWO_COLUMN_WORD_FIT = 2.7;

    var inStream = null, outStream = null;

    return file.openAsync(Windows.Storage.FileAccessMode.read).then(function (stream) {
        inStream = stream;
        return Windows.Graphics.Imaging.BitmapDecoder.createAsync(stream);
    }).then(function (decoder) {
        return Windows.Storage.ApplicationData.current.temporaryFolder.createFileAsync("temp-picture-resize", Windows.Storage.CreationCollisionOption.replaceExisting).then(function (resizedFile) {
            return resizedFile.openAsync(Windows.Storage.FileAccessMode.readWrite).then(function (stream) {
                outStream = stream;
                return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(outStream, decoder);
            }).then(function (encoder) {
                var framesMasterPromise = WinJS.Promise.wrap(null);

                for (var i = 0; i < decoder.frameCount; i++) {
                    (function (i) {
                        framesMasterPromise = framesMasterPromise.then(function () {
                            return decoder.getFrameAsync(i).then(function (bitmapFrame) {
                                return bitmapFrame.getPixelDataAsync().then(function (pixelDataContainer) {
                                    var pixelData = pixelDataContainer.detachPixelData();

                                    var newWidth = bitmapFrame.orientedPixelWidth,
                                        newHeight = bitmapFrame.orientedPixelHeight;

                                    if (bitmapFrame.orientedPixelWidth > MAX_WIDTH || bitmapFrame.orientedPixelHeight > MAX_HEIGHT) {
                                        if (bitmapFrame.orientedPixelWidth > bitmapFrame.orientedPixelHeight) {
                                            newWidth = MAX_WIDTH;
                                            newHeight = Math.round(MAX_HEIGHT * bitmapFrame.orientedPixelHeight / bitmapFrame.orientedPixelWidth);
                                        } else {
                                            newWidth = Math.round(MAX_WIDTH * bitmapFrame.orientedPixelWidth / bitmapFrame.orientedPixelHeight);
                                            newHeight = MAX_HEIGHT;
                                        }
                                    }

                                    var biggestSide = Math.max(newWidth, newHeight);
                                    var dpiForBothSides = biggestSide / RATIO_FOR_TWO_COLUMN_WORD_FIT;

                                    encoder.setPixelData(
                                        bitmapFrame.bitmapPixelFormat,
                                        bitmapFrame.bitmapAlphaMode,
                                        bitmapFrame.orientedPixelWidth,
                                        bitmapFrame.orientedPixelHeight,
                                        dpiForBothSides/*bitmapFrame.dpiX*/,
                                        dpiForBothSides/*bitmapFrame.dpiY*/,
                                        pixelData
                                    );

                                    encoder.bitmapTransform.scaledWidth = newWidth;
                                    encoder.bitmapTransform.scaledHeight = newHeight;

                                    if (i >= decoder.frameCount - 1)
                                        return encoder.flushAsync();

                                    return encoder.goToNextFrameAsync();
                                });
                            });
                        });
                    })(i);
                }

                return framesMasterPromise;
            }).then(function () {
                if (inStream) inStream.close();
                if (outStream) outStream.close();

                return resizedFile;
            });
        });
    });
}

setPixelData() 似乎是唯一接受分辨率的方法,但它对生成的图像没有影响。实际上,我可以删除整个 encoder.setPixelData(...) 部分,完全看不到任何变化。

getPixelDataAsync() 可以接受额外的参数,但这似乎没有帮助。

感谢您提供的任何帮助。

知道了。找到了一些解决方案 here.

我在本应使用 BitmapEncoder.CreateAsync 的地方使用了 BitmapEncoder.CreateForTranscodingAsync,因为第一个用于简单转换,不允许您更改图像分辨率(值将被忽略,因为我注意到了)。

这是完整的解决方案,并进行了一些重构:

function resizePictureAsync(file) {
    var inStream = null, outStream = null;

    return file.openAsync(Windows.Storage.FileAccessMode.read).then(function (stream) {
        inStream = stream;
        return Windows.Graphics.Imaging.BitmapDecoder.createAsync(stream);
    }).then(function (decoder) {
        return Windows.Storage.ApplicationData.current.temporaryFolder.createFileAsync("temp-picture-resize", Windows.Storage.CreationCollisionOption.replaceExisting).then(function (resizedFile) {
            return resizedFile.openAsync(Windows.Storage.FileAccessMode.readWrite).then(function (stream) {
                outStream = stream;
                outStream.size = 0;

                var encoderCodecId = getEncoderCodecIdFromDecoderCodecId(decoder.decoderInformation.codecId);
                return Windows.Graphics.Imaging.BitmapEncoder.createAsync(encoderCodecId, outStream);
            }).then(function (encoder) {
                return processAllBitmapFramesAsync(decoder, encoder);
            }).then(function () {
                return resizedFile;
            });
        });
    }).then(null, function (err) {
        var errorMessage = "Error transforming an image.\n" + err;
        console.error(errorMessage);

        return file;
    }).then(function(resultFile) {
        if (inStream) inStream.close();
        if (outStream) outStream.close();

        return resultFile;
    });
};

function getEncoderCodecIdFromDecoderCodecId(decoderCodecId) {
    var encoderCodecId;

    switch (decoderCodecId) {
        case Windows.Graphics.Imaging.BitmapDecoder.bmpDecoderId:
            encoderCodecId = Windows.Graphics.Imaging.BitmapEncoder.bmpEncoderId;
            break;
        case Windows.Graphics.Imaging.BitmapDecoder.jpegDecoderId:
            encoderCodecId = Windows.Graphics.Imaging.BitmapEncoder.jpegEncoderId;
            break;
        case Windows.Graphics.Imaging.BitmapDecoder.jpegXRDecoderId:
            encoderCodecId = Windows.Graphics.Imaging.BitmapEncoder.jpegXREncoderId;
            break;
        case Windows.Graphics.Imaging.BitmapDecoder.gifDecoderId:
            encoderCodecId = Windows.Graphics.Imaging.BitmapEncoder.gifEncoderId;
            break;
        case Windows.Graphics.Imaging.BitmapDecoder.pngDecoderId:
            encoderCodecId = Windows.Graphics.Imaging.BitmapEncoder.pngEncoderId;
            break;
        case Windows.Graphics.Imaging.BitmapDecoder.tiffDecoderId:
            encoderCodecId = Windows.Graphics.Imaging.BitmapEncoder.tiffEncoderId;
            break;
        default:
            throw new Error("Impossible de déterminer le codec de l'encoder à partir de celui du decoder");
            break;
    }

    return encoderCodecId;
}

function processAllBitmapFramesAsync(decoder, encoder) {
    var framesMasterPromise = WinJS.Promise.wrap(null);

    for (var i = 0; i < decoder.frameCount; i++) {
        (function (i) {
            framesMasterPromise = framesMasterPromise.then(function () {
                return decoder.getFrameAsync(i).then(function (bitmapFrame) {
                    return processBitmapFrameAsync(bitmapFrame, encoder);
                }).then(function () {
                    if (i >= decoder.frameCount - 1)
                        return encoder.flushAsync();

                    return encoder.goToNextFrameAsync();
                });
            });
        })(i);
    }

    return framesMasterPromise;
}

function processBitmapFrameAsync(bitmapFrame, encoder) {
    const MAX_HEIGHT = 1600,
        MAX_WIDTH = 1600,
        RATIO_FOR_TWO_COLUMN_WORD_FIT = 2.7;

    var newWidth = bitmapFrame.orientedPixelWidth,
        newHeight = bitmapFrame.orientedPixelHeight;

    if (bitmapFrame.orientedPixelWidth > MAX_WIDTH || bitmapFrame.orientedPixelHeight > MAX_HEIGHT) {
        if (bitmapFrame.orientedPixelWidth > bitmapFrame.orientedPixelHeight) {
            newWidth = MAX_WIDTH;
            newHeight = Math.round(MAX_HEIGHT * bitmapFrame.orientedPixelHeight / bitmapFrame.orientedPixelWidth);
        } else {
            newWidth = Math.round(MAX_WIDTH * bitmapFrame.orientedPixelWidth / bitmapFrame.orientedPixelHeight);
            newHeight = MAX_HEIGHT;
        }
    }

    var biggestSide = Math.max(newWidth, newHeight);
    var dpiForBothSides = biggestSide / RATIO_FOR_TWO_COLUMN_WORD_FIT;

    return bitmapFrame.getPixelDataAsync().then(function(pixelDataContainer) {
        var pixelData = pixelDataContainer.detachPixelData();

        encoder.setPixelData(
            bitmapFrame.bitmapPixelFormat,
            bitmapFrame.bitmapAlphaMode,
            bitmapFrame.orientedPixelWidth,
            bitmapFrame.orientedPixelHeight,
            dpiForBothSides /*bitmapFrame.dpiX*/,
            dpiForBothSides /*bitmapFrame.dpiY*/,
            pixelData
        );

        encoder.bitmapTransform.scaledWidth = newWidth;
        encoder.bitmapTransform.scaledHeight = newHeight;
    });
}