decodeFrame method

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

Decode the frame (assuming startDecode has already been called).

Implementation

@override
Image? decodeFrame(int frame) {
  Uint8List imageData;

  int? width = _info.width;
  int? height = _info.height;

  if (!_info.isAnimated || frame == 0) {
    final dataBlocks = <Uint8List>[];
    var totalSize = 0;
    final len = _info.idat.length;
    for (var i = 0; i < len; ++i) {
      _input.offset = _info.idat[i];
      final chunkSize = _input.readUint32();
      final chunkType = _input.readString(4);
      final data = _input.readBytes(chunkSize).toUint8List();
      totalSize += data.length;
      dataBlocks.add(data);
      final crc = _input.readUint32();
      final computedCrc = _crc(chunkType, data);
      if (crc != computedCrc) {
        throw ImageException('Invalid $chunkType checksum');
      }
    }
    imageData = Uint8List(totalSize);
    var offset = 0;
    for (var data in dataBlocks) {
      imageData.setAll(offset, data);
      offset += data.length;
    }
  } else {
    if (frame < 0 || frame >= _info.frames.length) {
      throw ImageException('Invalid Frame Number: $frame');
    }

    final f = _info.frames[frame] as InternalPngFrame;
    width = f.width;
    height = f.height;
    var totalSize = 0;
    final dataBlocks = <Uint8List>[];
    for (var i = 0; i < f.fdat.length; ++i) {
      _input.offset = f.fdat[i];
      final chunkSize = _input.readUint32();
      _input
        ..readString(4) // fDat chunk header
        ..skip(4); // sequence number
      final data = _input.readBytes(chunkSize - 4).toUint8List();
      totalSize += data.length;
      dataBlocks.add(data);
    }
    imageData = Uint8List(totalSize);
    var offset = 0;
    for (var data in dataBlocks) {
      imageData.setAll(offset, data);
      offset += data.length;
    }
  }

  var numChannels = _info.colorType == PngColorType.indexed
      ? 1
      : _info.colorType == PngColorType.grayscale
          ? 1
          : _info.colorType == PngColorType.grayscaleAlpha
              ? 2
              : _info.colorType == PngColorType.rgba
                  ? 4
                  : 3;

  List<int> uncompressed;
  try {
    uncompressed = const ZLibDecoder().decodeBytes(imageData);
  } catch (error) {
    //print(error);
    return null;
  }

  // input is the decompressed data.
  final input = InputBuffer(uncompressed, bigEndian: true);
  _resetBits();

  PaletteUint8? palette;

  // Non-indexed PNGs may have a palette, but it only provides a suggested
  // set of colors to which an RGB color can be quantized if not displayed
  // directly. In this case, just ignore the palette.
  if (_info.colorType == PngColorType.indexed) {
    if (_info.palette != null) {
      final p = _info.palette!;
      final numColors = p.length ~/ 3;
      final t = _info.transparency;
      final tl = t != null ? t.length : 0;
      final nc = t != null ? 4 : 3;
      palette = PaletteUint8(numColors, nc);
      for (var i = 0, pi = 0; i < numColors; ++i, pi += 3) {
        var a = 255;
        if (nc == 4 && i < tl) {
          a = t![i];
        }
        palette.setRgba(i, p[pi]!, p[pi + 1]!, p[pi + 2]!, a);
      }
    }
  }

  // grayscale images with no palette but with transparency, get
  // converted to a indexed palette image.
  if (_info.colorType == PngColorType.grayscale &&
      _info.transparency != null &&
      palette == null &&
      _info.bits <= 8) {
    final t = _info.transparency!;
    final nt = t.length;
    final numColors = 1 << _info.bits;
    palette = PaletteUint8(numColors, 4);
    // palette color are 8-bit, so convert the grayscale bit value to the
    // 8-bit palette value.
    final to8bit = _info.bits == 1
        ? 255
        : _info.bits == 2
            ? 85
            : _info.bits == 4
                ? 17
                : 1;
    for (var i = 0; i < numColors; ++i) {
      final g = i * to8bit;
      palette.setRgba(i, g, g, g, 255);
    }
    for (var i = 0; i < nt; i += 2) {
      final ti = ((t[i] & 0xff) << 8) | (t[i + 1] & 0xff);
      if (ti < numColors) {
        palette.set(ti, 3, 0);
      }
    }
  }

  final format = _info.bits == 1
      ? Format.uint1
      : _info.bits == 2
          ? Format.uint2
          : _info.bits == 4
              ? Format.uint4
              : _info.bits == 16
                  ? Format.uint16
                  : Format.uint8;

  if (_info.colorType == PngColorType.grayscale &&
      _info.transparency != null &&
      _info.bits > 8) {
    numChannels = 4;
  }

  if (_info.colorType == PngColorType.rgb && _info.transparency != null) {
    numChannels = 4;
  }

  final image = Image(
      width: width,
      height: height,
      numChannels: numChannels,
      palette: palette,
      format: format);

  final origW = _info.width;
  final origH = _info.height;
  _info
    ..width = width
    ..height = height;

  final w = width;
  final h = height;
  _progressY = 0;
  if (_info.interlaceMethod != 0) {
    _processPass(input, image, 0, 0, 8, 8, (w + 7) >> 3, (h + 7) >> 3);
    _processPass(input, image, 4, 0, 8, 8, (w + 3) >> 3, (h + 7) >> 3);
    _processPass(input, image, 0, 4, 4, 8, (w + 3) >> 2, (h + 3) >> 3);
    _processPass(input, image, 2, 0, 4, 4, (w + 1) >> 2, (h + 3) >> 2);
    _processPass(input, image, 0, 2, 2, 4, (w + 1) >> 1, (h + 1) >> 2);
    _processPass(input, image, 1, 0, 2, 2, w >> 1, (h + 1) >> 1);
    _processPass(input, image, 0, 1, 1, 2, w, h >> 1);
  } else {
    _process(input, image);
  }

  _info
    ..width = origW
    ..height = origH;

  if (_info.iccpData != null) {
    image.iccProfile = IccProfile(
        _info.iccpName, IccProfileCompression.deflate, _info.iccpData!);
  }

  if (_info.textData.isNotEmpty) {
    image.addTextData(_info.textData);
  }

  return image;
}