
org.monte.media.tiff.IFDEntry Maven / Gradle / Ivy
/*
* @(#)IFDEntry.java 2.0 2010-07-24
*
* Copyright (c) 2009-2010 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.tiff;
import java.io.IOException;
import java.nio.ByteOrder;
/**
* Represents a directory entry in a TIFF Image File Directory (IFD).
*
* Each 12-byte IFD entry has the following format:
*
* - Bytes 0-1 The Tag that identifies the field.
* - Bytes 2-3 The field Type.
* - Bytes 4-7 The number of values, Count of the indicated Type.
* - Bytes 8-11 The Value Offset, the file offset (in bytes) of the Value for the
* field. The Value is expected to begin on a word boundary; the corresponding
* Value Offset will thus be an even number. This file offset may point anywhere
* in the file, even after the image data.
*
* @author Werner Randelshofer
* @version 2.1 2010-09-07 Stores ifdOffset.
*
2.0 2010-07-24 Reworked.
*
1.0 2009-12-26 Created.
*/
public class IFDEntry {
/** The Tag number that identifies the field. */
private int tagNumber;
/** The field Type. */
private int typeNumber;
/** The number of values, Count of the indicated Type. */
private long count;
/** The Value Offset stores the value or the offset of the value depending
* on typeNumber and on count. */
private long valueOffset;
/** The Entry Offset stores the location of the entry in the file. */
private long entryOffset;
/** The IFD Offset stores the location of the IFD in the file. */
private long ifdOffset;
/* The entry data. */
private Object data;
public IFDEntry(int tagNumber, int typeNumber, long count, long valueOffset, long entryOffset) {
this.tagNumber = tagNumber;
this.typeNumber = typeNumber;
this.count = count;
this.valueOffset = valueOffset;
this.entryOffset = entryOffset;
}
public long getCount() {
return count;
}
public int getTagNumber() {
return tagNumber;
}
public int getTypeNumber() {
return typeNumber;
}
/** The value offset may either contain the data or point to the data
* depending on the type and the count.
*
* @return The value offset.
*/
public long getValueOffset() {
return valueOffset;
}
/** The offset to the data. */
public long getDataOffset() {
return isDataInValueOffset() ? entryOffset + 8 : valueOffset+ifdOffset;
}
public void setIFDOffset(long newValue) {
ifdOffset = newValue;
}
public long getEntryOffset() {
return entryOffset;
}
public long getIFDOffset() {
return ifdOffset;
}
public boolean isDataInValueOffset() {
switch (IFDDataType.valueOf(typeNumber)) {
case ASCII://8-bit byte that contains a 7-bit ASCII code; the last byte
//must be NUL (binary zero).
return false;
case BYTE://8-bit unsigned integer.
return count <= 4;
case SHORT://16-bit (2-byte) unsigned integer.
return count <= 2;
case LONG://32-bit (4-byte) unsigned integer.
return count <= 1;
case RATIONAL://Two LONGs: the first represents the numerator of a fraction; the second, the denominator.
return false;
case SBYTE: //An 8-bit signed (twos-complement) integer.
return count <= 4;
case UNDEFINED://An 8-bit byte that may contain anything, depending on the definition of the field.
return count <= 4;
case SSHORT://A 16-bit (2-byte) signed (twos-complement) integer.
return count <= 2;
case SLONG://A 32-bit (4-byte) signed (twos-complement) integer.
return count <= 1;
case SRATIONAL://Two SLONG’s: the first represents the numerator of a fraction, the second the denominator.
return false;
case FLOAT://Single precision (4-byte) IEEE prettyFormat.
return count <= 1;
case DOUBLE:// Double precision (8-byte) IEEE prettyFormat.
return false;
default:
return true;
}
}
public long getLength() {
switch (IFDDataType.valueOf(typeNumber)) {
case ASCII://8-bit byte that contains a 7-bit ASCII code; the last byte
return count;
case BYTE://8-bit unsigned integer.
return count;
case SHORT://16-bit (2-byte) unsigned integer.
return count * 2;
case LONG://32-bit (4-byte) unsigned integer.
return count * 4;
case RATIONAL://Two LONGs: the first represents the numerator of a fraction; the second, the denominator.
return count * 8;
case SBYTE: //An 8-bit signed (twos-complement) integer.
return count;
case UNDEFINED://An 8-bit byte that may contain anything, depending on the definition of the field.
return count;
case SSHORT://A 16-bit (2-byte) signed (twos-complement) integer.
return count * 2;
case SLONG://A 32-bit (4-byte) signed (twos-complement) integer.
return count * 4;
case SRATIONAL://Two SLONG’s: the first represents the numerator of a fraction, the second the denominator.
return count * 8;
case FLOAT://Single precision (4-byte) IEEE prettyFormat.
return count * 4;
case DOUBLE:// Double precision (8-byte) IEEE prettyFormat.
return count * 8;
default:
return 0;
}
}
/** Reads value data with ifdDataOffset=0*/
public Object readData(TIFFInputStream in) throws IOException {
return readData(in, ifdOffset);
}
/** Reads value data with the specified ifdDataOffset.*/
public Object readData(TIFFInputStream in, long ifdDataOffset) throws IOException {
Object d = null;
IFDDataType tt = IFDDataType.valueOf(typeNumber);
if (tt != null) {
switch (tt) {
case ASCII://8-bit byte that contains a 7-bit ASCII code; the last byte
//must be NUL (binary zero).
if (count <= 4) {
StringBuilder buf = new StringBuilder();
int data = (int) valueOffset;
if (in.getByteOrder() == ByteOrder.LITTLE_ENDIAN) {
for (int i = 0; i < count - 1; i++) {
buf.append((char) (data & 0xff));
data >>= 8;
}
} else {
for (int i = 0; i < count - 1; i++) {
buf.append((char) (data >>> 24));
data <<= 8;
}
}
return buf.toString();
} else {
return in.readASCII(valueOffset + ifdDataOffset, count);
}
case SHORT://16-bit (2-byte) unsigned integer.
if (count == 1) {
if (in.getByteOrder() == ByteOrder.LITTLE_ENDIAN) {
d = (int) (valueOffset & 0xffff);
} else {
d = (int) ((valueOffset >> 16) & 0xffff);
}
} else if (count == 2) {
d = new int[]{(int) (valueOffset & 0xffff), (int) ((valueOffset & 0xffff0000) >> 16)};
} else {
d = in.readSHORT(valueOffset + ifdDataOffset, count);
}
break;
case LONG://32-bit (4-byte) unsigned integer.
if (count == 1) {
d = valueOffset;
} else {
d = in.readLONG(valueOffset + ifdDataOffset, count);
}
break;
case RATIONAL://Two LONGs: the first represents the numerator of a fraction; the second, the denominator.
if (count == 1) {
d = in.readRATIONAL(valueOffset + ifdDataOffset);
} else {
d = in.readRATIONAL(valueOffset + ifdDataOffset, count);
}
break;
case BYTE://8-bit unsigned integer.
if (count == 1) {
d = (short) (valueOffset & 0xff);
} else if (count == 2) {
d = new short[]{(short) ((valueOffset & 0xff00) >> 8), (short) (valueOffset & 0xff)};
} else if (count == 3) {
d = new short[]{(short) ((valueOffset & 0xff0000) >> 16), (short) ((valueOffset & 0xff00) >> 8), (short) (valueOffset & 0xff)};
} else if (count == 4) {
d = new short[]{(short) ((valueOffset & 0xff000000) >> 24), (short) ((valueOffset & 0xff0000) >> 16), (short) ((valueOffset & 0xff00) >> 8), (short) (valueOffset & 0xff)};
} else {
byte[] b = new byte[(int) count];
in.read(valueOffset + ifdDataOffset, b, 0, b.length);
short[] s = new short[(int) count];
for (int i = 0; i < b.length; i++) {
s[i] = (short) (b[i] & 0xff);
}
d = s;
}
break;
case SBYTE: //An 8-bit signed (twos-complement) integer.
case UNDEFINED://An 8-bit byte that may contain anything, depending on the definition of the field.
if (count == 1) {
d = (byte) valueOffset;
} else if (count == 2) {
d = new byte[]{(byte) ((valueOffset & 0xff00) >> 8), (byte) (valueOffset & 0xff)};
} else if (count == 3) {
d = new byte[]{(byte) ((valueOffset & 0xff0000) >> 16), (byte) ((valueOffset & 0xff00) >> 8), (byte) (valueOffset & 0xff)};
} else if (count == 4) {
d = new byte[]{(byte) ((valueOffset & 0xff000000) >> 24), (byte) ((valueOffset & 0xff0000) >> 16), (byte) ((valueOffset & 0xff00) >> 8), (byte) (valueOffset & 0xff)};
} else {
byte[] b = new byte[(int) count];
in.read(valueOffset + ifdDataOffset, b, 0, b.length);
d = b;
}
break;
case SSHORT://A 16-bit (2-byte) signed (twos-complement) integer.
if (count == 1) {
if (in.getByteOrder() == ByteOrder.LITTLE_ENDIAN) {
d = (short) (valueOffset & 0xffff);
} else {
d = (short) ((valueOffset >> 16) & 0xffff);
}
} else if (count == 2) {
d = new int[]{(short) (valueOffset & 0xffff), (short) ((valueOffset & 0xffff0000) >> 16)};
} else {
d = in.readSSHORT(valueOffset + ifdDataOffset, count);
}
break;
case SLONG://A 32-bit (4-byte) signed (twos-complement) integer.
throw new IOException("Format " + typeNumber + " not implemented");
case SRATIONAL://Two SLONG’s: the first represents the numerator of a fraction, the second the denominator.
if (count == 1) {
d = in.readSRATIONAL(valueOffset + ifdDataOffset);
} else {
d = in.readSRATIONAL(valueOffset + ifdDataOffset, count);
}
break;
case FLOAT://Single precision (4-byte) IEEE prettyFormat.
case DOUBLE:// Double precision (8-byte) IEEE prettyFormat.
default:
throw new IOException("Format " + typeNumber + " not implemented");
}
}
return d;
}
public void loadData(TIFFInputStream in) throws IOException {
data = readData(in);
}
public Object getData() {
return data;
}
/** FIXME Output is used by EXIFView */
@Override
public String toString() {
return "IFD Entry: tag:0x" + Integer.toHexString(tagNumber) + " type:0x" + Integer.toHexString(typeNumber) + " count:0x" + Long.toHexString(count) + " valueOffset:0x" + Long.toHexString(valueOffset);
}
public String toString(Enum tagName) {
StringBuilder buf = new StringBuilder();
buf.append(
"Entry tag:" + tagName + "(" + Integer.toHexString(tagNumber) + "), type:" + IFDDataType.valueOf(typeNumber) + "(" + typeNumber + "), count:" + count + ", valueOffset:" + valueOffset);
if (data != null) {
buf.append(", data:");
if (data instanceof byte[]) {
byte[] d = (byte[]) data;
for (int i = 0; i < d.length; i++) {
if (i != 0) {
buf.append(',');
}
buf.append(d[i]);
}
} else if (data instanceof short[]) {
short[] d = (short[]) data;
for (int i = 0; i < d.length; i++) {
if (i != 0) {
buf.append(',');
}
buf.append(d[i]);
}
} else if (data instanceof int[]) {
int[] d = (int[]) data;
for (int i = 0; i < d.length; i++) {
if (i != 0) {
buf.append(',');
}
buf.append(d[i]);
}
} else if (data instanceof long[]) {
long[] d = (long[]) data;
for (int i = 0; i < d.length; i++) {
if (i != 0) {
buf.append(',');
}
buf.append(d[i]);
}
} else if (data instanceof Object[]) {
Object[] d = (Object[]) data;
for (int i = 0; i < d.length; i++) {
if (i != 0) {
buf.append(',');
}
buf.append(d[i]);
}
} else {
buf.append(data);
}
}
return buf.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy