Web 上的原始图像使用正确的原点和颜色
摘要
#Web 上渲染原始图像的方式已得到纠正,现在与其他平台上的一致。这会破坏必须向 ui.ImageDescriptor.raw
或 ui.decodeImageFromPixels
提供不正确数据的旧版应用,导致生成的图像上下颠倒且颜色不正确(红色和蓝色通道已交换)。
上下文
#Flutter 在内部使用的“像素流”始终定义为相同的格式:对于每个像素,四个 8 位通道按 format
参数定义的顺序打包,然后按行分组,从左到右,然后按行从上到下。
然而,Flutter for Web,更确切地说是用于实现它的 HTML 渲染器,由于对 BMP 格式规范的理解错误,导致使用了错误的方式。结果,如果应用程序或库使用ui.ImageDescriptor.raw
或ui.decodeImageFromPixels
,则必须从下到上馈送像素并交换它们的红色和蓝色通道(例如,使用ui.PixelFormat.rgba8888
格式,数据的前 4 个字节被认为是第一个像素的蓝色、绿色、红色和 alpha 通道)。
此错误已由engine#29593修复,但应用程序和库必须更正其数据生成方式。
更改说明
#ui.ImageDescriptor.raw
或ui.decodeImageFromPixels
的pixels
参数现在使用format
描述的正确像素顺序,并且源自左上角。
通过直接调用这两个函数渲染的图像 直接调用这些函数的旧版代码可能会发现其图像上下颠倒且颜色不正确。
迁移指南
#如果应用程序使用最新版本的 Flutter 并遇到这种情况,最直接的解决方案是手动翻转图像,并使用备用像素格式。但是,这不太可能是最优化的解决方案,因为此类像素数据通常是从其他来源构建的,允许在构建过程中进行翻转。
迁移前代码
import 'dart:typed_data';
import 'dart:ui' as ui;
// Parse `image` as a displayable image.
//
// Each byte in `image` is a pixel channel, in the order of blue, green, red,
// and alpha, starting from the bottom left corner and going row first.
Future<ui.Image> parseMyImage(Uint8List image, int width, int height) async {
final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
await ui.ImmutableBuffer.fromUint8List(image),
width: width,
height: height,
pixelFormat: ui.PixelFormat.rgba8888,
);
return (await (await descriptor.instantiateCodec()).getNextFrame()).image;
}
迁移后代码
import 'dart:typed_data';
import 'dart:ui' as ui;
Uint8List verticallyFlipImage(Uint8List sourceBytes, int width, int height) {
final Uint32List source = Uint32List.sublistView(ByteData.sublistView(sourceBytes));
final Uint32List result = Uint32List(source.length);
int sourceOffset = 0;
int resultOffset = 0;
for (final int row = height - 1; row >= 0; row -= 1) {
sourceOffset = width * row;
for (final int col = 0; col < width; col += 1) {
result[resultOffset] = source[sourceOffset];
resultOffset += 1;
sourceOffset += 1;
}
}
return Uint8List.sublistView(ByteData.sublistView(sourceBytes))
}
Future<ui.Image> parseMyImage(Uint8List image, int width, int height) async {
final Uint8List correctedImage = verticallyFlipImage(image, width, height);
final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
await ui.ImmutableBuffer.fromUint8List(correctedImage),
width: width,
height: height,
pixelFormat: ui.PixelFormat.rgba8888,
);
return (await (await descriptor.instantiateCodec()).getNextFrame()).image;
}
更复杂的情况是,当您编写库时,希望此库在最新的 Flutter 和修补程序之前的版本上都能工作。在这种情况下,您可以通过首先让它解码单个像素来确定行为是否已更改。
迁移后代码
Uint8List verticallyFlipImage(Uint8List sourceBytes, int width, int height) {
// Same as the example above.
}
late Future<bool> imageRawUsesCorrectBehavior = (() async {
final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
await ui.ImmutableBuffer.fromUint8List(Uint8List.fromList(<int>[0xED, 0, 0, 0xFF])),
width: 1, height: 1, pixelFormat: ui.PixelFormat.rgba8888);
final ui.Image image = (await (await descriptor.instantiateCodec()).getNextFrame()).image;
final Uint8List resultPixels = Uint8List.sublistView(
(await image.toByteData(format: ui.ImageByteFormat.rawStraightRgba))!);
return resultPixels[0] == 0xED;
})();
Future<ui.Image> parseMyImage(Uint8List image, int width, int height) async {
final Uint8List correctedImage = (await imageRawUsesCorrectBehavior) ?
verticallyFlipImage(image, width, height) : image;
final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
await ui.ImmutableBuffer.fromUint8List(correctedImage), // Use the corrected image
width: width,
height: height,
pixelFormat: ui.PixelFormat.bgra8888, // Use the alternate format
);
return (await (await descriptor.instantiateCodec()).getNextFrame()).image;
}
时间线
#包含在版本中:2.9.0-0.0.pre
稳定版发布:2.10
参考
#API 文档
相关问题
相关 PR
除非另有说明,否则本网站上的文档反映了 Flutter 的最新稳定版本。页面上次更新于 2024-04-04。 查看源代码 或 报告问题.