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 type = p.readUint16();
    final numValues = p.readUint32();
    final entry = TiffEntry(tag, type, numValues, p3);

    // 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 (entry.numValues * entry.typeSize > 4) {
      entry.valueOffset = p.readUint32();
    } else {
      entry.valueOffset = p.offset;
      p.offset += 4;
    }

    tags[entry.tag] = entry;

    if (entry.tag == TAG_IMAGE_WIDTH) {
      width = entry.readValue();
    } else if (entry.tag == TAG_IMAGE_LENGTH) {
      height = entry.readValue();
    } else if (entry.tag == TAG_PHOTOMETRIC_INTERPRETATION) {
      photometricType = entry.readValue();
    } else if (entry.tag == TAG_COMPRESSION) {
      compression = entry.readValue();
    } else if (entry.tag == TAG_BITS_PER_SAMPLE) {
      bitsPerSample = entry.readValue();
    } else if (entry.tag == TAG_SAMPLES_PER_PIXEL) {
      samplesPerPixel = entry.readValue();
    } else if (entry.tag == TAG_PREDICTOR) {
      predictor = entry.readValue();
    } else if (entry.tag == TAG_SAMPLE_FORMAT) {
      sampleFormat = entry.readValue();
    } else if (entry.tag == TAG_COLOR_MAP) {
      colorMap = entry.readValues();
      colorMapRed = 0;
      colorMapGreen = colorMap!.length ~/ 3;
      colorMapBlue = colorMapGreen * 2;
    }
  }

  if (width == 0 || height == 0) {
    return;
  }

  if (colorMap != null && bitsPerSample == 8) {
    for (var i = 0, len = colorMap!.length; i < len; ++i) {
      colorMap![i] >>= 8;
    }
  }

  if (photometricType == 0) {
    isWhiteZero = true;
  }

  if (hasTag(TAG_TILE_OFFSETS)) {
    tiled = true;
    // Image is in tiled format
    tileWidth = _readTag(TAG_TILE_WIDTH);
    tileHeight = _readTag(TAG_TILE_LENGTH);
    tileOffsets = _readTagList(TAG_TILE_OFFSETS);
    tileByteCounts = _readTagList(TAG_TILE_BYTE_COUNTS);
  } else {
    tiled = false;

    tileWidth = _readTag(TAG_TILE_WIDTH, width);
    if (!hasTag(TAG_ROWS_PER_STRIP)) {
      tileHeight = _readTag(TAG_TILE_LENGTH, height);
    } else {
      final l = _readTag(TAG_ROWS_PER_STRIP);
      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(TAG_STRIP_OFFSETS);
    tileByteCounts = _readTagList(TAG_STRIP_BYTE_COUNTS);
  }

  // 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(TAG_FILL_ORDER, 1);
  t4Options = _readTag(TAG_T4_OPTIONS);
  t6Options = _readTag(TAG_T6_OPTIONS);
  extraSamples = _readTag(TAG_EXTRA_SAMPLES);

  // Determine which kind of image we are dealing with.
  switch (photometricType) {
    case 0: // WhiteIsZero
    case 1: // BlackIsZero
      if (bitsPerSample == 1 && samplesPerPixel == 1) {
        imageType = TYPE_BILEVEL;
      } else if (bitsPerSample == 4 && samplesPerPixel == 1) {
        imageType = TYPE_GRAY_4BIT;
      } else if (bitsPerSample % 8 == 0) {
        if (samplesPerPixel == 1) {
          imageType = TYPE_GRAY;
        } else if (samplesPerPixel == 2) {
          imageType = TYPE_GRAY_ALPHA;
        } else {
          imageType = TYPE_GENERIC;
        }
      }
      break;
    case 2: // RGB
      if (bitsPerSample % 8 == 0) {
        if (samplesPerPixel == 3) {
          imageType = TYPE_RGB;
        } else if (samplesPerPixel == 4) {
          imageType = TYPE_RGB_ALPHA;
        } else {
          imageType = TYPE_GENERIC;
        }
      }
      break;
    case 3: // RGB Palette
      if (samplesPerPixel == 1 &&
          (bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16)) {
        imageType = TYPE_PALETTE;
      }
      break;
    case 4: // Transparency mask
      if (bitsPerSample == 1 && samplesPerPixel == 1) {
        imageType = TYPE_BILEVEL;
      }
      break;
    case 6: // YCbCr
      if (compression == COMPRESSION_JPEG &&
          bitsPerSample == 8 &&
          samplesPerPixel == 3) {
        imageType = TYPE_RGB;
      } else {
        if (hasTag(TAG_YCBCR_SUBSAMPLING)) {
          final v = tags[TAG_YCBCR_SUBSAMPLING]!.readValues();
          chromaSubH = v[0];
          chromaSubV = v[1];
        } else {
          chromaSubH = 2;
          chromaSubV = 2;
        }

        if (chromaSubH * chromaSubV == 1) {
          imageType = TYPE_GENERIC;
        } else if (bitsPerSample == 8 && samplesPerPixel == 3) {
          imageType = TYPE_YCBCR_SUB;
        }
      }
      break;
    default: // Other including CMYK, CIE L*a*b*, unknown.
      if (bitsPerSample % 8 == 0) {
        imageType = TYPE_GENERIC;
      }
      break;
  }
}