Flutter Raw Image Provider
Flutter 中的 Image Widget 內(nèi)置支持 file、network、memory三種形式的文件。
但這幾種都只支持常規(guī)的經(jīng)過壓縮后的圖片文件或二進制數(shù)據(jù),如jpg、png、webp文件等。并沒有支持原始的rgba 二進制數(shù)據(jù)。
這里說的原始二進制數(shù)據(jù)是指圖像的每個像素的色彩值所組成的字節(jié)數(shù)組。一張圖有寬x高個像素點,一個像素點的色彩值用32bit來存儲,分為4個通道,每個通道各占用8bit,分別為紅、綠、藍、透明度(RGBA),這個數(shù)組就是每個像素點色彩值的集合,dart 中一般用Uint8List。
一般情況下,考慮網(wǎng)絡(luò)傳輸效率,會采用算法來壓縮這個數(shù)據(jù),故而你會看到有各種各樣的圖像壓縮算法和文件格式。
你可能會問什么情況下會有需要直接去加載一張圖的原始rgba數(shù)據(jù)?
這里舉個簡單例子:分塊加載圖片。將圖片解碼后,分割成一個個矩形區(qū)域,每個矩形就有一個 raw rgba 數(shù)據(jù),將其交給Image渲染,這樣做可以降低一定的GPU 內(nèi)存壓力,減少出現(xiàn)GPU OOM 或黑屏的概率。
要支持 raw rgba ,其實很簡單,在 dart:ui包下有個方法decodeImageFromPixels可以直接使用,前提是需要有原始的二進制數(shù)據(jù)、寬、高。
import 'dart:ui';Future<Image> decodeRawRgba(ByteData bytes, int width, int height) {final Completer<Image> completer = Completer<Image>();decodeImageFromPixels(bytes.buffer.asUint8List(),width,height,PixelFormat.rgba8888,completer.complete,);return completer.future; }有了這個 Image(dart:ui)對象就可以交給 RawImage Widget 來加載了。但RawImage太過于底層了,能不能只用 Image Widget呢?因為需要復(fù)用 LoadingBuilder這些邏輯。
當然可以。查看一下 Image Widget 的構(gòu)造函數(shù)就知道,我們需要一個 ImageProvider,那么問題進一步簡化到如何寫一個ImageProvider 支持 raw rgba 數(shù)據(jù)。
實現(xiàn)一個 ImageProvider,我們需要實現(xiàn) load這個關(guān)鍵方法。以MemoryImage為例:
class MemoryImage extends ImageProvider<MemoryImage> {@overrideImageStreamCompleter load(MemoryImage key, DecoderCallback decode) {return MultiFrameImageStreamCompleter(codec: _loadAsync(key, decode),scale: key.scale,debugLabel: 'MemoryImage(${describeIdentity(key.bytes)})',);}Future<ui.Codec> _loadAsync(MemoryImage key, DecoderCallback decode) {return decode(bytes);} }很顯然,我們需要想一個方法構(gòu)造出raw rgba 數(shù)據(jù)的 Codec。
其實秘密就在 decodeImageFromPixels這個方法實現(xiàn)里:
void decodeImageFromPixels(Uint8List pixels,int width,int height,PixelFormat format,ImageDecoderCallback callback, {int? rowBytes,int? targetWidth,int? targetHeight,bool allowUpscaling = true, }) {if (targetWidth != null) {assert(allowUpscaling || targetWidth <= width);}if (targetHeight != null) {assert(allowUpscaling || targetHeight <= height);}ImmutableBuffer.fromUint8List(pixels).then((ImmutableBuffer buffer) {final ImageDescriptor descriptor = ImageDescriptor.raw(buffer,width: width,height: height,rowBytes: rowBytes,pixelFormat: format,);if (!allowUpscaling) {if (targetWidth != null && targetWidth! > descriptor.width) {targetWidth = descriptor.width;}if (targetHeight != null && targetHeight! > descriptor.height) {targetHeight = descriptor.height;}}descriptor.instantiateCodec(targetWidth: targetWidth,targetHeight: targetHeight,).then((Codec codec) => codec.getNextFrame()).then((FrameInfo frameInfo) => callback(frameInfo.image));}); }先從數(shù)據(jù)構(gòu)造出ImageDescriptor,再把descriptor.instantiateCodec()這一步抽出來就可以獲取 raw rgba 數(shù)據(jù)的 Codec,進而實現(xiàn)一個自己的RawImageProvider了。
如:
class RawImageProvider extends ImageProvider<_RawImageKey> {final RawImageData image;/// see [ui.decodeImageFromPixels]Future<ui.Codec> _loadAsync(_RawImageKey key) async {var buffer = await ui.ImmutableBuffer.fromUint8List(image.pixels);final descriptor = ui.ImageDescriptor.raw(buffer,width: image.width,height: image.height,pixelFormat: image.pixelFormat,);return descriptor.instantiateCodec(targetWidth: targetWidth, targetHeight: targetHeight);}如果你恰好也有這個需要,可以直接添加 pub 依賴
dependencies:raw_image_provider: ^0.1.0完。
總結(jié)
以上是生活随笔為你收集整理的Flutter Raw Image Provider的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: goj基础环境的配置
- 下一篇: Golang加密md5