decodeFrame method

  1. @override
Image? decodeFrame(
  1. int frame
)
override

Decode a single frame from the data that was set with startDecode. If frame is out of the range of available frames, null is returned. Non animated image files will only have frame 0. An Image is returned, which provides the image, and top-left coordinates of the image, as animated frames may only occupy a subset of the canvas.

Implementation

@override
Image? decodeFrame(int frame) {
  if (_input == null || _icoInfo == null || frame >= _icoInfo!.numFrames) {
    return null;
  }

  final imageInfo = _icoInfo!.images[frame];
  final imageBuffer = _input!.buffer.sublist(
      _input!.start + imageInfo.bytesOffset,
      _input!.start + imageInfo.bytesOffset + imageInfo.bytesSize);

  final png = PngDecoder();
  if (png.isValidFile(imageBuffer as Uint8List)) {
    return png.decode(imageBuffer);
  }

  // should be bmp.
  final dummyBmpHeader = OutputBuffer(size: 14)
    ..writeUint16(BmpFileHeader.signature)
    ..writeUint32(imageInfo.bytesSize)
    ..writeUint32(0)
    ..writeUint32(0);

  final bmpInfo = IcoBmpInfo(InputBuffer(imageBuffer),
      fileHeader: BmpFileHeader(InputBuffer(dummyBmpHeader.getBytes())));

  if (bmpInfo.headerSize != 40 && bmpInfo.planes != 1) {
    // invalid header.
    return null;
  }

  int offset;
  if (bmpInfo.totalColors == 0 && bmpInfo.bitsPerPixel <= 8) {
    offset = /*14 +*/ 40 + 4 * (1 << bmpInfo.bitsPerPixel);
  } else {
    offset = /*14 +*/ 40 + 4 * bmpInfo.totalColors;
  }

  bmpInfo.header.imageOffset = offset;
  dummyBmpHeader
    ..length -= 4
    ..writeUint32(offset);
  final inp = InputBuffer(imageBuffer);
  final bmp = DibDecoder(inp, bmpInfo, forceRgba: true);

  final image = bmp.decodeFrame(0);

  if (bmpInfo.bitsPerPixel >= 32) {
    return image;
  }

  final padding = 32 - bmpInfo.width % 32;
  final rowLength =
      (padding == 32 ? bmpInfo.width : bmpInfo.width + padding) ~/ 8;

  // AND bitmask
  for (var y = 0; y < bmpInfo.height; y++) {
    final line = bmpInfo.readBottomUp ? y : image.height - 1 - y;
    final row = inp.readBytes(rowLength);
    final p = image.getPixel(0, line);
    for (var x = 0; x < bmpInfo.width;) {
      final b = row.readByte();
      for (var j = 7; j > -1 && x < bmpInfo.width; j--) {
        if (b & (1 << j) != 0) {
          // set the pixel to completely transparent.
          p.a = 0;
        }
        p.moveNext();
        x++;
      }
    }
  }

  return image;
}