import 'dart:typed_data'; | |
import 'package:archive/archive.dart'; | |
import '../../color.dart'; | |
import '../../image.dart'; | |
import '../../image_exception.dart'; | |
import '../../formats/jpeg_decoder.dart'; | |
import '../../hdr/hdr_image.dart'; | |
import '../../internal/bit_operators.dart'; | |
import '../../util/input_buffer.dart'; | |
import 'tiff_bit_reader.dart'; | |
import 'tiff_entry.dart'; | |
import 'tiff_fax_decoder.dart'; | |
import 'tiff_lzw_decoder.dart'; | |
class TiffImage { | |
Map<int, TiffEntry> tags = {}; | |
int width; | |
int height; | |
int photometricType; | |
int compression = 1; | |
int bitsPerSample = 1; | |
int samplesPerPixel = 1; | |
int imageType = TYPE_UNSUPPORTED; | |
bool isWhiteZero = false; | |
int predictor = 1; | |
int chromaSubH; | |
int chromaSubV; | |
bool tiled = false; | |
int tileWidth; | |
int tileHeight; | |
List<int> tileOffsets; | |
List<int> tileByteCounts; | |
int tilesX; | |
int tilesY; | |
int tileSize; | |
int fillOrder = 1; | |
int t4Options = 0; | |
int t6Options = 0; | |
int extraSamples; | |
List<int> colorMap; | |
/// Starting index in the [colorMap] for the red channel. | |
int colorMapRed; | |
/// Starting index in the [colorMap] for the green channel. | |
int colorMapGreen; | |
/// Starting index in the [colorMap] for the blue channel. | |
int colorMapBlue; | |
Image image; | |
HdrImage hdrImage; | |
TiffImage(InputBuffer p) { | |
InputBuffer p3 = new InputBuffer.from(p); | |
int numDirEntries = p.readUint16(); | |
for (int i = 0; i < numDirEntries; ++i) { | |
int tag = p.readUint16(); | |
int type = p.readUint16(); | |
int numValues = p.readUint32(); | |
TiffEntry entry = new TiffEntry(tag, type, numValues); | |
// 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(p3); | |
} else if (entry.tag == TAG_IMAGE_LENGTH) { | |
height = entry.readValue(p3); | |
} else if (entry.tag == TAG_PHOTOMETRIC_INTERPRETATION) { | |
photometricType = entry.readValue(p3); | |
} else if (entry.tag == TAG_COMPRESSION) { | |
compression = entry.readValue(p3); | |
} else if (entry.tag == TAG_BITS_PER_SAMPLE) { | |
bitsPerSample = entry.readValue(p3); | |
} else if (entry.tag == TAG_SAMPLES_PER_PIXEL) { | |
samplesPerPixel = entry.readValue(p3); | |
} else if (entry.tag == TAG_PREDICTOR) { | |
predictor = entry.readValue(p3); | |
} else if (entry.tag == TAG_COLOR_MAP) { | |
colorMap = entry.readValues(p3); | |
colorMapRed = 0; | |
colorMapGreen = colorMap.length ~/ 3; | |
colorMapBlue = colorMapGreen * 2; | |
} | |
} | |
if (width == null || height == null || | |
bitsPerSample == null || compression == null) { | |
return; | |
} | |
if (colorMap != null && bitsPerSample == 8) { | |
for (int 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(p3, TAG_TILE_WIDTH); | |
tileHeight = _readTag(p3, TAG_TILE_LENGTH); | |
tileOffsets = _readTagList(p3, TAG_TILE_OFFSETS); | |
tileByteCounts = _readTagList(p3, TAG_TILE_BYTE_COUNTS); | |
} else { | |
tiled = false; | |
tileWidth = _readTag(p3, TAG_TILE_WIDTH, width); | |
if (!hasTag(TAG_ROWS_PER_STRIP)) { | |
tileHeight = _readTag(p3, TAG_TILE_LENGTH, height); | |
} else { | |
int l = _readTag(p3, TAG_ROWS_PER_STRIP); | |
int 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(p3, TAG_STRIP_OFFSETS); | |
tileByteCounts = _readTagList(p3, 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(p3, TAG_FILL_ORDER, 1); | |
t4Options = _readTag(p3, TAG_T4_OPTIONS, 0); | |
t6Options = _readTag(p3, TAG_T6_OPTIONS, 0); | |
extraSamples = _readTag(p3, TAG_EXTRA_SAMPLES, 0); | |
// 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)) { | |
List<int> v = tags[TAG_YCBCR_SUBSAMPLING].readValues(p3); | |
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; | |
} | |
} | |
bool get isValid => width != null && height != null && | |
samplesPerPixel != null && | |
bitsPerSample != null && | |
compression != null; | |
Image decode(InputBuffer p) { | |
image = new Image(width, height); | |
for (int tileY = 0, ti = 0; tileY < tilesY; ++tileY) { | |
for (int tileX = 0; tileX < tilesX; ++tileX, ++ti) { | |
_decodeTile(p, tileX, tileY); | |
} | |
} | |
return image; | |
} | |
HdrImage decodeHdr(InputBuffer p) { | |
hdrImage = new HdrImage.create(width, height, 4, HdrImage.HALF); | |
for (int tileY = 0, ti = 0; tileY < tilesY; ++tileY) { | |
for (int tileX = 0; tileX < tilesX; ++tileX, ++ti) { | |
_decodeTile(p, tileX, tileY); | |
} | |
} | |
return hdrImage; | |
} | |
bool hasTag(int tag) => tags.containsKey(tag); | |
void _decodeTile(InputBuffer p, int tileX, int tileY) { | |
// Read the data, uncompressing as needed. There are four cases: | |
// bilevel, palette-RGB, 4-bit grayscale, and everything else. | |
if (imageType == TYPE_BILEVEL) { | |
_decodeBilevelTile(p, tileX, tileY); | |
return; | |
} | |
int tileIndex = tileY * tilesX + tileX; | |
p.offset = tileOffsets[tileIndex]; | |
int outX = tileX * tileWidth; | |
int outY = tileY * tileHeight; | |
int byteCount = tileByteCounts[tileIndex]; | |
int bytesInThisTile = tileWidth * tileHeight * samplesPerPixel; | |
if (bitsPerSample == 16) { | |
bytesInThisTile *= 2; | |
} | |
InputBuffer bdata; | |
if (bitsPerSample == 8 || bitsPerSample == 16) { | |
if (compression == COMPRESSION_NONE) { | |
bdata = p; | |
} else if (compression == COMPRESSION_LZW) { | |
bdata = new InputBuffer(new Uint8List(bytesInThisTile)); | |
LzwDecoder decoder = new LzwDecoder(); | |
try { | |
decoder.decode(new InputBuffer.from(p, offset: 0, length: byteCount), | |
bdata.buffer); | |
} catch (e) { | |
} | |
// Horizontal Differencing Predictor | |
if (predictor == 2) { | |
int count; | |
for (int j = 0; j < tileHeight; j++) { | |
count = samplesPerPixel * (j * tileWidth + 1); | |
for (int i = samplesPerPixel, len = tileWidth * samplesPerPixel; | |
i < len; i++) { | |
bdata[count] += bdata[count - samplesPerPixel]; | |
count++; | |
} | |
} | |
} | |
} else if (compression == COMPRESSION_PACKBITS) { | |
bdata = new InputBuffer(new Uint8List(bytesInThisTile)); | |
_decodePackbits(p, bytesInThisTile, bdata.buffer); | |
} else if (compression == COMPRESSION_DEFLATE) { | |
List<int> data = p.toList(0, byteCount); | |
List<int> outData = new Inflate(data).getBytes(); | |
bdata = new InputBuffer(outData); | |
} else if (compression == COMPRESSION_ZIP) { | |
List<int> data = p.toList(0, byteCount); | |
List<int> outData = new ZLibDecoder().decodeBytes(data); | |
bdata = new InputBuffer(outData); | |
} else if (compression == COMPRESSION_OLD_JPEG) { | |
if (image == null) { | |
image = new Image(width, height); | |
} | |
List<int> data = p.toList(0, byteCount); | |
Image tile = new JpegDecoder().decodeImage(data); | |
_jpegToImage(tile, image, outX, outY, tileWidth, tileHeight); | |
if (hdrImage != null) { | |
hdrImage = new HdrImage.fromImage(image); | |
} | |
return; | |
} else { | |
throw new ImageException('Unsupported Compression Type: $compression'); | |
} | |
if (bdata == null) { | |
return; | |
} | |
int pi = 0; | |
for (int y = 0, py = outY; y < tileHeight && py < height; ++y, ++py) { | |
for (int x = 0, px = outX; x < tileWidth && px < width; ++x, ++px) { | |
if (samplesPerPixel == 1) { | |
int gray = bdata[pi++]; | |
int gray16 = gray; | |
// down-sample 16-bit to 8-bit.. | |
if (bitsPerSample == 16) { | |
if (!p.bigEndian) { | |
gray = bdata[pi++]; | |
gray16 = gray << 8 | gray16; | |
} else { | |
gray16 = gray16 << 8 | bdata[pi++]; | |
} | |
} | |
if (photometricType == 0) { | |
gray = 255 - gray; | |
gray16 = 0xffff - gray16; | |
} | |
if (hdrImage != null) { | |
double fg = gray16 / 0xffff; | |
hdrImage.setRed(px, py, fg); | |
hdrImage.setGreen(px, py, fg); | |
hdrImage.setBlue(px, py, fg); | |
hdrImage.setAlpha(px, py, 1.0); | |
} | |
if (image != null) { | |
int c; | |
if (photometricType == 3 && colorMap != null) { | |
c = getColor(colorMap[colorMapRed + gray], | |
colorMap[colorMapGreen + gray], | |
colorMap[colorMapBlue + gray]); | |
} else { | |
c = getColor(gray, gray, gray, 255); | |
} | |
image.setPixel(px, py, c); | |
} | |
} else if (samplesPerPixel == 2) { | |
int gray = bdata[pi++]; | |
int gray16 = gray; | |
if (bitsPerSample == 16) { | |
gray16 = gray16 << 8 | bdata[pi++]; | |
} | |
int alpha = bdata[pi++]; | |
int alpha16 = alpha; | |
if (bitsPerSample == 16) { | |
alpha16 = alpha16 << 8 | bdata[pi++]; | |
} | |
if (hdrImage != null) { | |
double fg = gray16 / 0xffff; | |
double fa = alpha16 / 0xffff; | |
hdrImage.setRed(px, py, fg); | |
hdrImage.setGreen(px, py, fg); | |
hdrImage.setBlue(px, py, fg); | |
hdrImage.setAlpha(px, py, fa); | |
} | |
if (image != null) { | |
int c = getColor(gray, gray, gray, alpha); | |
image.setPixel(px, py, c); | |
} | |
} else if (samplesPerPixel == 3) { | |
int r = bdata[pi++]; | |
int r16 = r; | |
if (bitsPerSample == 16) { | |
r16 = r16 << 8 | bdata[pi++]; | |
} | |
int g = bdata[pi++]; | |
int g16 = r; | |
if (bitsPerSample == 16) { | |
g16 = g16 << 8 | bdata[pi++]; | |
} | |
int b = bdata[pi++]; | |
int b16 = r; | |
if (bitsPerSample == 16) { | |
b16 = b16 << 8 | bdata[pi++]; | |
} | |
if (hdrImage != null) { | |
hdrImage.setRed(px, py, r16 / 0xffff); | |
hdrImage.setGreen(px, py, g16 / 0xffff); | |
hdrImage.setBlue(px, py, b16 / 0xffff); | |
hdrImage.setAlpha(px, py, 1.0); | |
} | |
if (image != null) { | |
int c = getColor(r, g, b, 255); | |
image.setPixel(px, py, c); | |
} | |
} else if (samplesPerPixel >= 4) { | |
int r = bdata[pi++]; | |
int r16 = r; | |
if (bitsPerSample == 16) { | |
r16 = r16 << 8 | bdata[pi++]; | |
} | |
int g = bdata[pi++]; | |
int g16 = g; | |
if (bitsPerSample == 16) { | |
g16 = g16 << 8 | bdata[pi++]; | |
} | |
int b = bdata[pi++]; | |
int b16 = b; | |
if (bitsPerSample == 16) { | |
b16 = b16 << 8 | bdata[pi++]; | |
} | |
int a = bdata[pi++]; | |
int a16 = a; | |
if (bitsPerSample == 16) { | |
a16 = a16 << 8 | bdata[pi++]; | |
} | |
if (hdrImage != null) { | |
hdrImage.setRed(px, py, r16 / 0xffff); | |
hdrImage.setGreen(px, py, g16 / 0xffff); | |
hdrImage.setBlue(px, py, b16 / 0xffff); | |
hdrImage.setAlpha(px, py, a16 / 0xffff); | |
} | |
if (image != null) { | |
int c = getColor(r, g, b, a); | |
image.setPixel(px, py, c); | |
} | |
} | |
} | |
} | |
} else { | |
throw new ImageException('Unsupported bitsPerSample: $bitsPerSample'); | |
} | |
} | |
void _jpegToImage(Image tile, Image image, int outX, int outY, | |
int tileWidth, int tileHeight) { | |
int width = tileWidth; | |
int height = tileHeight; | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
image.setPixel(x + outX, y + outY, tile.getPixel(x, y)); | |
} | |
} | |
/*Uint8List data = jpeg.getData(width, height); | |
List components = jpeg.components; | |
int i = 0; | |
int j = 0; | |
switch (components.length) { | |
case 1: // Luminance | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
int Y = data[i++]; | |
image.setPixel(x + outX, y + outY, getColor(Y, Y, Y, 255)); | |
} | |
} | |
break; | |
case 3: // RGB | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
int R = data[i++]; | |
int G = data[i++]; | |
int B = data[i++]; | |
int c = getColor(R, G, B, 255); | |
image.setPixel(x + outX, y + outY, c); | |
} | |
} | |
break; | |
case 4: // CMYK | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
int C = data[i++]; | |
int M = data[i++]; | |
int Y = data[i++]; | |
int K = data[i++]; | |
int R = 255 - _clamp(C * (1 - K ~/ 255) + K); | |
int G = 255 - _clamp(M * (1 - K ~/ 255) + K); | |
int B = 255 - _clamp(Y * (1 - K ~/ 255) + K); | |
image.setPixel(x + outX, y + outY, getColor(R, G, B, 255)); | |
} | |
} | |
break; | |
default: | |
throw 'Unsupported color mode'; | |
}*/ | |
} | |
/*int _clamp(int i) { | |
return i < 0 ? 0 : i > 255 ? 255 : i; | |
}*/ | |
void _decodeBilevelTile(InputBuffer p, int tileX, int tileY) { | |
int tileIndex = tileY * tilesX + tileX; | |
p.offset = tileOffsets[tileIndex]; | |
int outX = tileX * tileWidth; | |
int outY = tileY * tileHeight; | |
int byteCount = tileByteCounts[tileIndex]; | |
InputBuffer bdata; | |
if (compression == COMPRESSION_PACKBITS) { | |
// Since the decompressed data will still be packed | |
// 8 pixels into 1 byte, calculate bytesInThisTile | |
int bytesInThisTile; | |
if ((tileWidth % 8) == 0) { | |
bytesInThisTile = (tileWidth ~/ 8) * tileHeight; | |
} else { | |
bytesInThisTile = (tileWidth ~/ 8 + 1) * tileHeight; | |
} | |
bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight)); | |
_decodePackbits(p, bytesInThisTile, bdata.buffer); | |
} else if (compression == COMPRESSION_LZW) { | |
bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight)); | |
LzwDecoder decoder = new LzwDecoder(); | |
decoder.decode(new InputBuffer.from(p, length: byteCount), bdata.buffer); | |
// Horizontal Differencing Predictor | |
if (predictor == 2) { | |
int count; | |
for (int j = 0; j < height; j++) { | |
count = samplesPerPixel * (j * width + 1); | |
for (int i = samplesPerPixel; i < width * samplesPerPixel; i++) { | |
bdata[count] += bdata[count - samplesPerPixel]; | |
count++; | |
} | |
} | |
} | |
} else if (compression == COMPRESSION_CCITT_RLE) { | |
bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight)); | |
try { | |
new TiffFaxDecoder(fillOrder, tileWidth, tileHeight). | |
decode1D(bdata, p, 0, tileHeight); | |
} catch (_) { | |
} | |
} else if (compression == COMPRESSION_CCITT_FAX3) { | |
bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight)); | |
try { | |
new TiffFaxDecoder(fillOrder, tileWidth, tileHeight). | |
decode2D(bdata, p, 0, tileHeight, t4Options); | |
} catch (_) { | |
} | |
} else if (compression == COMPRESSION_CCITT_FAX4) { | |
bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight)); | |
try { | |
new TiffFaxDecoder(fillOrder, tileWidth, tileHeight). | |
decodeT6(bdata, p, 0, tileHeight, t6Options); | |
} catch (_) { | |
} | |
} else if (compression == COMPRESSION_ZIP) { | |
List<int> data = p.toList(0, byteCount); | |
List<int> outData = new ZLibDecoder().decodeBytes(data); | |
bdata = new InputBuffer(outData); | |
} else if (compression == COMPRESSION_DEFLATE) { | |
List<int> data = p.toList(0, byteCount); | |
List<int> outData = new Inflate(data).getBytes(); | |
bdata = new InputBuffer(outData); | |
} else if (compression == COMPRESSION_NONE) { | |
bdata = p; | |
} else { | |
throw new ImageException('Unsupported Compression Type: $compression'); | |
} | |
if (bdata == null) { | |
return; | |
} | |
TiffBitReader br = new TiffBitReader(bdata); | |
final int white = isWhiteZero ? 0xff000000 : 0xffffffff; | |
final int black = isWhiteZero ? 0xffffffff : 0xff000000; | |
for (int y = 0, py = outY; y < tileHeight; ++y, ++py) { | |
for (int x = 0, px = outX; x < tileWidth; ++x, ++px) { | |
if (br.readBits(1) == 0) { | |
image.setPixel(px, py, black); | |
} else { | |
image.setPixel(px, py, white); | |
} | |
} | |
br.flushByte(); | |
} | |
} | |
/** | |
* Uncompress packbits compressed image data. | |
*/ | |
void _decodePackbits(InputBuffer data, int arraySize, List<int> dst) { | |
int srcCount = 0; | |
int dstCount = 0; | |
while (dstCount < arraySize) { | |
int b = uint8ToInt8(data[srcCount++]); | |
if (b >= 0 && b <= 127) { | |
// literal run packet | |
for (int i = 0; i < (b + 1); ++i) { | |
dst[dstCount++] = data[srcCount++]; | |
} | |
} else if (b <= -1 && b >= -127) { | |
// 2 byte encoded run packet | |
int repeat = data[srcCount++]; | |
for (int i = 0; i < (-b + 1); ++i) { | |
dst[dstCount++] = repeat; | |
} | |
} else { | |
// no-op packet. Do nothing | |
srcCount++; | |
} | |
} | |
} | |
int _readTag(InputBuffer p, int type, [int defaultValue = 0]) { | |
if (!hasTag(type)) { | |
return defaultValue; | |
} | |
return tags[type].readValue(p); | |
} | |
List<int> _readTagList(InputBuffer p, int type) { | |
if (!hasTag(type)) { | |
return null; | |
} | |
return tags[type].readValues(p); | |
} | |
// Compression types | |
static const int COMPRESSION_NONE = 1; | |
static const int COMPRESSION_CCITT_RLE = 2; | |
static const int COMPRESSION_CCITT_FAX3 = 3; | |
static const int COMPRESSION_CCITT_FAX4 = 4; | |
static const int COMPRESSION_LZW = 5; | |
static const int COMPRESSION_OLD_JPEG = 6; | |
static const int COMPRESSION_JPEG = 7; | |
static const int COMPRESSION_NEXT = 32766; | |
static const int COMPRESSION_CCITT_RLEW = 32771; | |
static const int COMPRESSION_PACKBITS = 32773; | |
static const int COMPRESSION_THUNDERSCAN = 32809; | |
static const int COMPRESSION_IT8CTPAD = 32895; | |
static const int COMPRESSION_IT8LW = 32896; | |
static const int COMPRESSION_IT8MP = 32897; | |
static const int COMPRESSION_IT8BL = 32898; | |
static const int COMPRESSION_PIXARFILM = 32908; | |
static const int COMPRESSION_PIXARLOG = 32909; | |
static const int COMPRESSION_DEFLATE = 32946; | |
static const int COMPRESSION_ZIP = 8; | |
static const int COMPRESSION_DCS = 32947; | |
static const int COMPRESSION_JBIG = 34661; | |
static const int COMPRESSION_SGILOG = 34676; | |
static const int COMPRESSION_SGILOG24 = 34677; | |
static const int COMPRESSION_JP2000 = 34712; | |
// Image types | |
static const int TYPE_UNSUPPORTED = -1; | |
static const int TYPE_BILEVEL = 0; | |
static const int TYPE_GRAY_4BIT = 1; | |
static const int TYPE_GRAY = 2; | |
static const int TYPE_GRAY_ALPHA = 3; | |
static const int TYPE_PALETTE = 4; | |
static const int TYPE_RGB = 5; | |
static const int TYPE_RGB_ALPHA = 6; | |
static const int TYPE_YCBCR_SUB = 7; | |
static const int TYPE_GENERIC = 8; | |
// Tag types | |
static const int TAG_ARTIST = 315; | |
static const int TAG_BITS_PER_SAMPLE = 258; | |
static const int TAG_CELL_LENGTH = 265; | |
static const int TAG_CELL_WIDTH = 264; | |
static const int TAG_COLOR_MAP = 320; | |
static const int TAG_COMPRESSION = 259; | |
static const int TAG_DATE_TIME = 306; | |
static const int TAG_EXIF_IFD = 34665; | |
static const int TAG_EXTRA_SAMPLES = 338; | |
static const int TAG_FILL_ORDER = 266; | |
static const int TAG_FREE_BYTE_COUNTS = 289; | |
static const int TAG_FREE_OFFSETS = 288; | |
static const int TAG_GRAY_RESPONSE_CURVE = 291; | |
static const int TAG_GRAY_RESPONSE_UNIT = 290; | |
static const int TAG_HOST_COMPUTER = 316; | |
static const int TAG_ICC_PROFILE = 34675; | |
static const int TAG_IMAGE_DESCRIPTION = 270; | |
static const int TAG_IMAGE_LENGTH = 257; | |
static const int TAG_IMAGE_WIDTH = 256; | |
static const int TAG_IPTC = 33723; | |
static const int TAG_MAKE = 271; | |
static const int TAG_MAX_SAMPLE_VALUE = 281; | |
static const int TAG_MIN_SAMPLE_VALUE = 280; | |
static const int TAG_MODEL = 272; | |
static const int TAG_NEW_SUBFILE_TYPE = 254; | |
static const int TAG_ORIENTATION = 274; | |
static const int TAG_PHOTOMETRIC_INTERPRETATION = 262; | |
static const int TAG_PHOTOSHOP = 34377; | |
static const int TAG_PLANAR_CONFIGURATION = 284; | |
static const int TAG_PREDICTOR = 317; | |
static const int TAG_RESOLUTION_UNIT = 296; | |
static const int TAG_ROWS_PER_STRIP = 278; | |
static const int TAG_SAMPLES_PER_PIXEL = 277; | |
static const int TAG_SOFTWARE = 305; | |
static const int TAG_STRIP_BYTE_COUNTS = 279; | |
static const int TAG_STRIP_OFFSETS = 273; | |
static const int TAG_SUBFILE_TYPE = 255; | |
static const int TAG_T4_OPTIONS = 292; | |
static const int TAG_T6_OPTIONS = 293; | |
static const int TAG_THRESHOLDING = 263; | |
static const int TAG_TILE_WIDTH = 322; | |
static const int TAG_TILE_LENGTH = 323; | |
static const int TAG_TILE_OFFSETS = 324; | |
static const int TAG_TILE_BYTE_COUNTS = 325; | |
static const int TAG_XMP = 700; | |
static const int TAG_X_RESOLUTION = 282; | |
static const int TAG_Y_RESOLUTION = 283; | |
static const int TAG_YCBCR_COEFFICIENTS = 529; | |
static const int TAG_YCBCR_SUBSAMPLING = 530; | |
static const int TAG_YCBCR_POSITIONING = 531; | |
static const Map<int, String> TAG_NAME = const { | |
TAG_ARTIST: 'artist', | |
TAG_BITS_PER_SAMPLE: 'bitsPerSample', | |
TAG_CELL_LENGTH: 'cellLength', | |
TAG_CELL_WIDTH: 'cellWidth', | |
TAG_COLOR_MAP: 'colorMap', | |
TAG_COMPRESSION: 'compression', | |
TAG_DATE_TIME: 'dateTime', | |
TAG_EXIF_IFD: 'exifIFD', | |
TAG_EXTRA_SAMPLES: 'extraSamples', | |
TAG_FILL_ORDER: 'fillOrder', | |
TAG_FREE_BYTE_COUNTS: 'freeByteCounts', | |
TAG_FREE_OFFSETS: 'freeOffsets', | |
TAG_GRAY_RESPONSE_CURVE: 'grayResponseCurve', | |
TAG_GRAY_RESPONSE_UNIT: 'grayResponseUnit', | |
TAG_HOST_COMPUTER: 'hostComputer', | |
TAG_ICC_PROFILE: 'iccProfile', | |
TAG_IMAGE_DESCRIPTION: 'imageDescription', | |
TAG_IMAGE_LENGTH: 'imageLength', | |
TAG_IMAGE_WIDTH: 'imageWidth', | |
TAG_IPTC: 'iptc', | |
TAG_MAKE: 'make', | |
TAG_MAX_SAMPLE_VALUE: 'maxSampleValue', | |
TAG_MIN_SAMPLE_VALUE: 'minSampleValue', | |
TAG_MODEL: 'model', | |
TAG_NEW_SUBFILE_TYPE: 'newSubfileType', | |
TAG_ORIENTATION: 'orientation', | |
TAG_PHOTOMETRIC_INTERPRETATION: 'photometricInterpretation', | |
TAG_PHOTOSHOP: 'photoshop', | |
TAG_PLANAR_CONFIGURATION: 'planarConfiguration', | |
TAG_PREDICTOR: 'predictor', | |
TAG_RESOLUTION_UNIT: 'resolutionUnit', | |
TAG_ROWS_PER_STRIP: 'rowsPerStrip', | |
TAG_SAMPLES_PER_PIXEL: 'samplesPerPixel', | |
TAG_SOFTWARE: 'software', | |
TAG_STRIP_BYTE_COUNTS: 'stripByteCounts', | |
TAG_STRIP_OFFSETS: 'stropOffsets', | |
TAG_SUBFILE_TYPE: 'subfileType', | |
TAG_T4_OPTIONS: 't4Options', | |
TAG_T6_OPTIONS: 't6Options', | |
TAG_THRESHOLDING: 'thresholding', | |
TAG_TILE_WIDTH: 'tileWidth', | |
TAG_TILE_LENGTH: 'tileLength', | |
TAG_TILE_OFFSETS: 'tileOffsets', | |
TAG_TILE_BYTE_COUNTS: 'tileByteCounts', | |
TAG_XMP: 'xmp', | |
TAG_X_RESOLUTION: 'xResolution', | |
TAG_Y_RESOLUTION: 'yResolution', | |
TAG_YCBCR_COEFFICIENTS: 'yCbCrCoefficients', | |
TAG_YCBCR_SUBSAMPLING: 'yCbCrSubsampling', | |
TAG_YCBCR_POSITIONING: 'yCbCrPositioning' | |
}; | |
} |