decodeFrame method
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;
}