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;
});
}
前段时间,我使用 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;
});
}