TiffImage constructor
TiffImage()
Implementation
TiffImage(InputBuffer p) {
final p3 = InputBuffer.from(p);
final numDirEntries = p.readUint16();
for (var i = 0; i < numDirEntries; ++i) {
final tag = p.readUint16();
final ti = p.readUint16();
final type = IfdValueType.values[ti];
final typeSize = ifdValueTypeSize[ti];
final count = p.readUint32();
var valueOffset = 0;
// The value for the tag is either stored in another location,
// or within the tag itself (if the size fits in 4 bytes).
// We're not reading the data here, just storing offsets.
if (count * typeSize > 4) {
valueOffset = p.readUint32();
} else {
valueOffset = p.offset;
p.skip(4);
}
final entry = TiffEntry(tag, type, count, p3, valueOffset);
tags[entry.tag] = entry;
if (tag == exifTagNameToID['ImageWidth']) {
width = entry.read()?.toInt() ?? 0;
} else if (tag == exifTagNameToID['ImageLength']) {
height = entry.read()?.toInt() ?? 0;
} else if (tag == exifTagNameToID['PhotometricInterpretation']) {
final v = entry.read();
final pt = v?.toInt() ?? TiffPhotometricType.values.length;
if (pt < TiffPhotometricType.values.length) {
photometricType = TiffPhotometricType.values[pt];
} else {
photometricType = TiffPhotometricType.unknown;
}
} else if (tag == exifTagNameToID['Compression']) {
compression = entry.read()?.toInt() ?? 0;
} else if (tag == exifTagNameToID['BitsPerSample']) {
bitsPerSample = entry.read()?.toInt() ?? 0;
} else if (tag == exifTagNameToID['SamplesPerPixel']) {
samplesPerPixel = entry.read()?.toInt() ?? 0;
} else if (tag == exifTagNameToID['Predictor']) {
predictor = entry.read()?.toInt() ?? 0;
} else if (tag == exifTagNameToID['SampleFormat']) {
final v = entry.read()?.toInt() ?? 0;
sampleFormat = TiffFormat.values[v];
} else if (tag == exifTagNameToID['ColorMap']) {
final v = entry.read();
if (v != null) {
colorMap = v.toData().buffer.asUint16List();
colorMapRed = 0;
colorMapGreen = colorMap!.length ~/ 3;
colorMapBlue = colorMapGreen * 2;
}
}
}
if (colorMap != null && photometricType == TiffPhotometricType.palette) {
// Only support RGB palettes.
colorMapSamples = 3;
samplesPerPixel = 1;
}
if (width == 0 || height == 0) {
return;
}
if (colorMap != null && bitsPerSample == 8) {
final cm = colorMap!;
final len = cm.length;
for (var i = 0; i < len; ++i) {
cm[i] >>= 8;
}
}
if (photometricType == TiffPhotometricType.whiteIsZero) {
isWhiteZero = true;
}
if (hasTag(exifTagNameToID['TileOffsets']!)) {
tiled = true;
// Image is in tiled format
tileWidth = _readTag(exifTagNameToID['TileWidth']!);
tileHeight = _readTag(exifTagNameToID['TileLength']!);
tileOffsets = _readTagList(exifTagNameToID['TileOffsets']!);
tileByteCounts = _readTagList(exifTagNameToID['TileByteCounts']!);
} else {
tiled = false;
tileWidth = _readTag(exifTagNameToID['TileWidth']!, width);
if (!hasTag(exifTagNameToID['RowsPerStrip']!)) {
tileHeight = _readTag(exifTagNameToID['TileLength']!, height);
} else {
final l = _readTag(exifTagNameToID['RowsPerStrip']!);
var infinity = 1;
infinity = (infinity << 32) - 1;
if (l == infinity) {
// 2^32 - 1 (effectively infinity, entire image is 1 strip)
tileHeight = height;
} else {
tileHeight = l;
}
}
tileOffsets = _readTagList(exifTagNameToID['StripOffsets']!);
tileByteCounts = _readTagList(exifTagNameToID['StripByteCounts']!);
}
// Calculate number of tiles and the tileSize in bytes
tilesX = (width + tileWidth - 1) ~/ tileWidth;
tilesY = (height + tileHeight - 1) ~/ tileHeight;
tileSize = tileWidth * tileHeight * samplesPerPixel;
fillOrder = _readTag(exifTagNameToID['FillOrder']!, 1);
t4Options = _readTag(exifTagNameToID['T4Options']!);
t6Options = _readTag(exifTagNameToID['T6Options']!);
extraSamples = _readTag(exifTagNameToID['ExtraSamples']!);
// Determine which kind of image we are dealing with.
switch (photometricType) {
case TiffPhotometricType.whiteIsZero:
case TiffPhotometricType.blackIsZero:
if (bitsPerSample == 1 && samplesPerPixel == 1) {
imageType = TiffImageType.bilevel;
} else if (bitsPerSample == 4 && samplesPerPixel == 1) {
imageType = TiffImageType.gray4bit;
} else if (bitsPerSample % 8 == 0) {
if (samplesPerPixel == 1) {
imageType = TiffImageType.gray;
} else if (samplesPerPixel == 2) {
imageType = TiffImageType.grayAlpha;
} else {
imageType = TiffImageType.generic;
}
}
break;
case TiffPhotometricType.rgb:
if (bitsPerSample % 8 == 0) {
if (samplesPerPixel == 3) {
imageType = TiffImageType.rgb;
} else if (samplesPerPixel == 4) {
imageType = TiffImageType.rgba;
} else {
imageType = TiffImageType.generic;
}
}
break;
case TiffPhotometricType.palette:
if (samplesPerPixel == 1 &&
colorMap != null &&
(bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16)) {
imageType = TiffImageType.palette;
}
break;
case TiffPhotometricType.transparencyMask: // Transparency mask
if (bitsPerSample == 1 && samplesPerPixel == 1) {
imageType = TiffImageType.bilevel;
}
break;
case TiffPhotometricType.yCbCr:
if (compression == TiffCompression.jpeg &&
bitsPerSample == 8 &&
samplesPerPixel == 3) {
imageType = TiffImageType.rgb;
} else {
if (hasTag(exifTagNameToID['YCbCrSubSampling']!)) {
final v = tags[exifTagNameToID['YCbCrSubSampling']!]!.read()!;
chromaSubH = v.toInt();
chromaSubV = v.toInt(1);
} else {
chromaSubH = 2;
chromaSubV = 2;
}
if (chromaSubH * chromaSubV == 1) {
imageType = TiffImageType.generic;
} else if (bitsPerSample == 8 && samplesPerPixel == 3) {
imageType = TiffImageType.yCbCrSub;
}
}
break;
default: // Other including CMYK, CIE L*a*b*, unknown.
if (bitsPerSample % 8 == 0) {
imageType = TiffImageType.generic;
}
break;
}
}