TiffImage constructor

TiffImage(
  1. InputBuffer p
)

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