decode static method
Future<Image?>
decode(
- UPdfDocument document,
- UPdfStream stream, {
- UPdfDict? resources,
- Color fillColor = const Color(0xFF000000),
Implementation
static Future<ui.Image?> decode(UPdfDocument document, UPdfStream stream, {UPdfDict? resources, Color fillColor = const Color(0xFF000000)}) async {
try {
final UPdfDict dict = stream.dict;
final int width = ((await document.resolve(dict["Width"] ?? dict["W"])) as num?)?.toInt() ?? 0;
final int height = ((await document.resolve(dict["Height"] ?? dict["H"])) as num?)?.toInt() ?? 0;
if (width <= 0 || height <= 0 || width * height > 80000000) return null;
final bool isMask = (await document.resolve(dict["ImageMask"] ?? dict["IM"])) as bool? ?? false;
final int bits = isMask ? 1 : (((await document.resolve(dict["BitsPerComponent"] ?? dict["BPC"])) as num?)?.toInt() ?? 8);
final UPdfDecodedStream decoded = await document.decodeStream(stream);
final List<double> decodeArray = <double>[];
final Object? decodeObject = await document.resolve(dict["Decode"] ?? dict["D"]);
if (decodeObject is List<Object?>) {
for (final Object? entry in decodeObject) {
final Object? value = await document.resolve(entry);
if (value is num) decodeArray.add(value.toDouble());
}
}
Uint8List samples = decoded.bytes;
UPdfColorSpace space = isMask ? UPdfColorSpace.deviceGray : await UPdfColorSpace.load(document, dict["ColorSpace"] ?? dict["CS"], resources: resources);
final String? filter = decoded.imageFilter;
if (filter == "DCTDecode" || filter == "DCT" || filter == "JPXDecode") {
final ui.Image? direct = await fromEncoded(samples);
if (direct != null) return await _applyMask(document, direct, dict, resources);
if (filter == "JPXDecode") return null;
return null;
}
if (filter == "JBIG2Decode") {
Uint8List? globals;
final UPdfDict? parms = decoded.imageParms;
final Object? globalsObject = await document.resolve(parms?["JBIG2Globals"]);
if (globalsObject is UPdfStream) globals = (await document.decodeStream(globalsObject)).bytes;
final Uint8List? packed = UJbig2.decode(samples, width: width, height: height, globals: globals);
if (packed == null) return null;
samples = packed;
space = UPdfColorSpace.deviceGray;
}
if (filter == "CCITTFaxDecode" || filter == "CCF") {
final UPdfDict? parms = decoded.imageParms;
final int k = ((await document.resolve(parms?["K"])) as num?)?.toInt() ?? 0;
final int columns = ((await document.resolve(parms?["Columns"])) as num?)?.toInt() ?? 1728;
final int rowCount = ((await document.resolve(parms?["Rows"])) as num?)?.toInt() ?? height;
final bool blackIs1 = (await document.resolve(parms?["BlackIs1"])) as bool? ?? false;
final bool align = (await document.resolve(parms?["EncodedByteAlign"])) as bool? ?? false;
samples = UPdfCcittDecoder(columns: columns, rows: rowCount, k: k, blackIs1: blackIs1, byteAlign: align).decode(samples);
space = UPdfColorSpace.deviceGray;
}
final int componentCount = isMask ? 1 : space.components;
final int maxValue = (1 << bits) - 1;
final int rowBits = width * componentCount * bits;
final int rowBytes = (rowBits + 7) >> 3;
final Uint8List rgba = Uint8List(width * height * 4);
final bool indexed = space.family == "Indexed";
final List<double> values = List<double>.filled(componentCount, 0);
final Map<int, int> cache = <int, int>{};
for (int y = 0; y < height; y++) {
final int rowStart = y * rowBytes;
if (rowStart >= samples.length) break;
for (int x = 0; x < width; x++) {
int key = 0;
for (int c = 0; c < componentCount; c++) {
final int bitOffset = (x * componentCount + c) * bits;
int raw = 0;
if (bits == 8) {
final int index = rowStart + (bitOffset >> 3);
raw = index < samples.length ? samples[index] : 0;
} else if (bits == 16) {
final int index = rowStart + (bitOffset >> 3);
raw = index + 1 < samples.length ? samples[index] : 0;
} else {
for (int b = 0; b < bits; b++) {
final int bit = bitOffset + b;
final int index = rowStart + (bit >> 3);
final int value = index < samples.length ? (samples[index] >> (7 - (bit & 7))) & 1 : 0;
raw = (raw << 1) | value;
}
}
if (bits == 16) raw = raw & 0xFF;
double normalized = indexed ? raw.toDouble() : raw / (bits == 16 ? 255 : maxValue);
if (decodeArray.length >= (c + 1) * 2) {
final double low = decodeArray[c * 2];
final double high = decodeArray[c * 2 + 1];
normalized = indexed ? low + raw * (high - low) / maxValue : low + normalized * (high - low);
}
values[c] = normalized;
key = (key << 8) | (raw & 0xFF);
}
final int offset = (y * width + x) * 4;
if (isMask) {
final bool paint = values[0] < 0.5;
rgba[offset] = (fillColor.r * 255).round();
rgba[offset + 1] = (fillColor.g * 255).round();
rgba[offset + 2] = (fillColor.b * 255).round();
rgba[offset + 3] = paint ? 255 : 0;
continue;
}
final int? cached = componentCount <= 2 ? cache[key] : null;
final int argb = cached ?? space.color(values).toARGB32();
if (componentCount <= 2 && cached == null) cache[key] = argb;
rgba[offset] = (argb >> 16) & 0xFF;
rgba[offset + 1] = (argb >> 8) & 0xFF;
rgba[offset + 2] = argb & 0xFF;
rgba[offset + 3] = 255;
}
}
await _applyAlpha(document, rgba, width, height, dict, resources);
return await fromPixels(rgba, width, height);
} on Object {
return null;
}
}