WinJS/WinRT Изменение разрешения (dpi) изображения

Некоторое время назад я написал код для изменения размера изображения в современном приложении WinJS с пользовательским интерфейсом с помощью WinRT API.

Теперь меня также просят изменить разрешение изображения на 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() может принимать дополнительные параметры, но это не помогает.

Спасибо за любую помощь, которую вы могли бы предоставить.


person user1174017    schedule 18.02.2016    source источник


Ответы (1)


Понятно. Нашел немного решения здесь.

Я использовал BitmapEncoder.CreateForTranscodingAsync, где я должен был использовать BitmapEncoder.CreateAsync, потому что первый предназначен для простого преобразования и не позволяет изменять разрешение изображения (значения просто игнорируются, как я заметил).

Вот полное решение с небольшим рефакторингом:

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;
    });
}
person user1174017    schedule 19.02.2016