3

Currently I have a Uint8List, formatted like [R,G,B,R,G,B,...] for all the pixels of the image. And of course I have its width and height.

I found decodeImageFromPixels while searching but it only takes RGBA/BGRA format. I converted my pixmap from RGB to RGBA and this function works fine.

However, my code now looks like this:

Uint8List rawPixel = raw.value.asTypedList(w * h * channel);
List<int> rgba = [];
for (int i = 0; i < rawPixel.length; i++) {
  rgba.add(rawPixel[i]);
    if ((i + 1) % 3 == 0) {
      rgba.add(0);
    }
}

Uint8List rgbaList = Uint8List.fromList(rgba);

Completer<Image> c = Completer<Image>();
decodeImageFromPixels(rgbaList, w, h, PixelFormat.rgba8888, (Image img) {
  c.complete(img);
});

I have to make a new list(waste in space) and iterate through the entire list(waste in time).

This is too inefficient in my opinion, is there any way to make this more elegant? Like add a new PixelFormat.rgb888?

Thanks in advance.

Richard Heap
  • 48,344
  • 9
  • 130
  • 112
itzMeerkat
  • 33
  • 5
  • Rather than calling `add` it might be faster to directly create a `Uint8List` of length `w * h * 4` and use your for loop to copy from `rawPixel` to `rgba` indexing into each array. `decodeImageFromPixels` ultimately ends up creating a `Codec` and that understands all sort of BMP formats (including RGB888). You could adapt this answer to prepend a BMP header and simply call `instantiateImageCodec`. The BMP parsing is presumably fast and optimized. https://stackoverflow.com/questions/51315442/use-ui-decodeimagefromlist-to-display-an-image-created-from-a-list-of-bytes/51316489#51316489 – Richard Heap Jan 11 '22 at 17:21
  • @RichardHeap Thanks! Now I'm going to live with padding RGB to RGBA for now. If the performance hurts I will try to switch to bmp as you mentioned. – itzMeerkat Jan 14 '22 at 08:38

1 Answers1

2

You may find that this loop is faster as it doesn't keep appending to the list and then copy it at the end.

  final rawPixel = raw.value.asTypedList(w * h * channel);
  final rgbaList = Uint8List(w * h * 4); // create the Uint8List directly as we know the width and height

  for (var i = 0; i < w * h; i++) {
    final rgbOffset = i * 3;
    final rgbaOffset = i * 4;
    rgbaList[rgbaOffset] = rawPixel[rgbOffset]; // red
    rgbaList[rgbaOffset + 1] = rawPixel[rgbOffset + 1]; // green
    rgbaList[rgbaOffset + 2] = rawPixel[rgbOffset + 2]; // blue
    rgbaList[rgbaOffset + 3] = 255; // a
  }

An alternative is to prepend the array with a BMP header by adapting this answer (though it would simpler as there would be no palette) and passing that bitmap to instantiateImageCodec as that code is presumably highly optimized for parsing bitmaps.

Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • 1
    I think there's a typo: Multiply by 4 instead of adding right?: `final rgbaList = Uint8List(w * h * 4);` Other than that, this is very neat and concise ;) – Cyrill Nov 05 '22 at 23:02