Nikhil
Nikhil

Reputation: 151

manipulation of byte data slow in flutter

i am trying to fetch a series of pixel colors at different offsets using this function,

   _getColor(ByteData rgbaImageData, int x, int y) {
var byteOffset = x * 4 + y * imageWidth * 4;
var r = rgbaImageData.getUint8(byteOffset);
var g = rgbaImageData.getUint8(byteOffset + 1);
var b = rgbaImageData.getUint8(byteOffset + 2);
var a = rgbaImageData.getUint8(byteOffset + 3);
return Color.fromARGB(a, r, g, b);

}

but the it is taking long time to execute, any solution for this?

Upvotes: 3

Views: 717

Answers (1)

lrn
lrn

Reputation: 71693

It shouldn't be that slow, but you could reduce the number of reads:

Color getColor(ByteData rgbaData, int x, int y) {
  var byteOffset = (x + y * imageWidth) * 4;
  var rgba = rgbaImageData.getUint32(byteOffset);
  return Color(argb);
}

That only performs one (potentially unaligned) read per color, and doesn't have to recombine the bytes.

Alternatively, you can change the type of your typed data to Uint8List:

  var bytes = Uint8List.view(
    rgbaImageData.buffer, rgbaImageData.offsetInBytes, rgbaImageData.lengthInBytes);
...
  var color = _getColor(bytes, x, y);
...
Color _getColor(Uint8List bytes, int x, int y) {
  var byteOffset = (x + y * imageWith) * 4;
  var r = bytes[byteOffset];
  var g = bytes[byteOffset + 1]; 
  var b = bytes[byteOffset + 2];
  var a = bytes[byteOffset + 3];
  return Color.fromARGB(a, r, g, b);
}

This might be more efficient than the original, but likely not a lot.

You can even interpret the original buffer as a Uint32List, but that only works if offsetInBytes is a multiple of four, since a Uint32List requires data to be 32-bit aligned (which is also why it maybe be slightly more efficient than ByteData). If offsetInBytes is always zero anyway, this may work for you too. Example:

  var words = Uint32List.view(
    rgbaImageData.buffer, 
    rgbaImageData.offsetInBytes, 
    rgbaImageData.lengthInBytes ~/ Uint32List.bytesPerElement);
...
  var color = _getColor(words, x, y);
...
Color _getColor(Uint32List words, int x, int y) {
  var offset = x + y * imageWith;
  return Color(words[offset]);
}

Another reason this may be slow is that you are allocating a lots of Color objects if you traverse the entire image. If the image doesn't have that many different colors, you can choose to reuse existing color objects. If the image has a lot of different colors, caching them may just cause even more memory churn. Example:

Map<int, Color> _colorCache = {};
const int _maxCacheSize = 1024;
Color createColor(int argb) {
  var result = _colorCache[argb];
  if (result != null) return result;
  result = Color(argb);
  if (_colorCache.length == _maxCacheSize) {
    _colorCache.remove(_colorCache.keys.first);
  }
  _colorCache[argb] = result;
  return result;
}

then use createColor(argb) instead of Color(argb). Again, if your image has lots of different varying colors, and no large patches of the same color, then this is most likely just overhead.

Upvotes: 3

Related Questions