forked from LupusNobilis/java-org.hwo
358 lines
7.5 KiB
Java
358 lines
7.5 KiB
Java
package org.hwo.image.tiff;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
import javax.imageio.ImageIO;
|
|
|
|
import org.hwo.Unsigned;
|
|
import org.hwo.image.UnsupportedFormatException;
|
|
|
|
class IFD
|
|
{
|
|
final TiffFile tiffFile;
|
|
|
|
boolean decoded;
|
|
|
|
Short numEntries;
|
|
HashMap<Short, IFDEntry> entries;
|
|
|
|
BufferedImage bufferedImage;
|
|
|
|
protected IFD(TiffFile tiffFile)
|
|
{
|
|
this.tiffFile = tiffFile;
|
|
decoded = false;
|
|
entries = new HashMap<Short, IFDEntry>();
|
|
bufferedImage = null;
|
|
|
|
numEntries = this.tiffFile.sourceBuffer.getShort();
|
|
System.err.println(String.format("readIFD(): reading %d entries",numEntries));
|
|
|
|
for (int i=0;i<numEntries;i++)
|
|
{
|
|
IFDEntry entry = IFDEntry.read(this);
|
|
if (entry != null)
|
|
entries.put(entry.tag, entry);
|
|
}
|
|
|
|
Integer nextIFD = this.tiffFile.sourceBuffer.getInt();
|
|
if (nextIFD != 0)
|
|
{
|
|
this.tiffFile.sourceBuffer.position(nextIFD);
|
|
this.tiffFile.ifds.add(0,new IFD(this.tiffFile));
|
|
}
|
|
}
|
|
protected IFD(TiffFile tiffFile, BufferedImage image)
|
|
{
|
|
this.tiffFile = tiffFile;
|
|
decoded = true;
|
|
bufferedImage = image;
|
|
numEntries = 0;
|
|
entries = new HashMap<Short, IFDEntry>();
|
|
}
|
|
|
|
public boolean isDecoded()
|
|
{
|
|
return decoded;
|
|
}
|
|
|
|
private BufferedImage decode()
|
|
{
|
|
System.err.println("TiffFile: Decoding...");
|
|
if (isDecoded())
|
|
return bufferedImage;
|
|
|
|
int width = getWidth(),
|
|
height = getHeight();
|
|
|
|
Short compression = getCompression();
|
|
|
|
short[] bitsPerSample = getBitsPerSample();
|
|
PHOTOMETRIC photometric = getPhotometric();
|
|
short samplesPerPixel = getSamplesPerPixel();
|
|
|
|
System.err.println(String.format("Size: %dx%d TYPE: %s COMP: %d BPS: %s",width,height,photometric.toString(),compression,Arrays.toString(bitsPerSample)));
|
|
|
|
BufferedImage image = new BufferedImage(width, height, getJImageType(photometric, bitsPerSample));
|
|
|
|
int rowsPerStrip = getRowsPerStrip();
|
|
int[] stripOffsets = getStripOffsets();
|
|
int[] stripByteCounts = getStripByteCounts();
|
|
|
|
if (stripOffsets.length != stripByteCounts.length)
|
|
throw new UnsupportedFormatException("StripOffset.length != StripbyteCount.length");
|
|
|
|
if ((stripOffsets.length * rowsPerStrip) < height)
|
|
throw new UnsupportedFormatException("stripped lines < scanlines");
|
|
|
|
ArrayList<Strip> strips = new ArrayList<Strip>();
|
|
|
|
for (int i=0;i<stripOffsets.length;i++)
|
|
strips.add(new Strip(this, stripOffsets[i], stripByteCounts[i], rowsPerStrip));
|
|
|
|
switch (photometric)
|
|
{
|
|
case BLACK:
|
|
case WHITE:
|
|
decodeGray(image,strips);
|
|
break;
|
|
case RGB:
|
|
decodeRGB(image,strips);
|
|
break;
|
|
default:
|
|
throw new UnsupportedFormatException("unsupported pixel format");
|
|
}
|
|
return image;
|
|
}
|
|
|
|
private void decodeGray(BufferedImage image,List<Strip> strips)
|
|
{
|
|
PHOTOMETRIC photometric = getPhotometric();
|
|
|
|
int width = getWidth(),
|
|
height = getHeight();
|
|
|
|
short[] bitsPerSample = getBitsPerSample();
|
|
short samplesPerPixel = getSamplesPerPixel();
|
|
|
|
|
|
|
|
switch (bitsPerSample[0])
|
|
{
|
|
case 1:
|
|
case 4:
|
|
case 8:
|
|
break;
|
|
default:
|
|
throw new UnsupportedFormatException();
|
|
}
|
|
|
|
int y = 0;
|
|
short predictor = getPredictor();
|
|
|
|
for (Strip strip:strips)
|
|
{
|
|
ByteBuffer buffer = strip.getBuffer();
|
|
int p = 0;
|
|
|
|
for (int row=0; (row < strip.rowcount) && (y + row < height); row++)
|
|
{
|
|
int[] lastPixel = new int[0];
|
|
|
|
for (int column = 0; column < width ; column++)
|
|
{
|
|
int[] pixel = new int[1];
|
|
|
|
pixel[0] = Unsigned.byte2short(buffer.get(p));
|
|
if (predictor == 2 && column > 0)
|
|
pixel[0] += lastPixel[0];
|
|
|
|
image.getRaster().setPixel(column, y + row, pixel);
|
|
|
|
p += samplesPerPixel;
|
|
lastPixel = pixel;
|
|
}
|
|
}
|
|
y += strip.rowcount;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
private void decodeRGB(BufferedImage image,List<Strip> strips)
|
|
{
|
|
int width = getWidth(),
|
|
height = getHeight();
|
|
|
|
short[] bitsPerSample = getBitsPerSample();
|
|
short samplesPerPixel = getSamplesPerPixel();
|
|
|
|
switch (samplesPerPixel)
|
|
{
|
|
case 3:
|
|
break;
|
|
case 4:
|
|
break;
|
|
default:
|
|
throw new UnsupportedFormatException();
|
|
}
|
|
|
|
for (int i=0;i<samplesPerPixel;i++)
|
|
if (bitsPerSample[i] != 8)
|
|
throw new UnsupportedFormatException();
|
|
|
|
int y = 0;
|
|
|
|
short predictor = getPredictor();
|
|
|
|
for (Strip strip:strips)
|
|
{
|
|
ByteBuffer buffer = strip.getBuffer();
|
|
int p = 0;
|
|
|
|
for (int row=0; (row < strip.rowcount) && (y + row < height); row++)
|
|
{
|
|
int[] lastPixel = new int[0];
|
|
|
|
for (int column = 0; column < width ; column++)
|
|
{
|
|
byte r,g,b,a;
|
|
|
|
int[] pixel = new int[samplesPerPixel];
|
|
|
|
for (int n=0;n<samplesPerPixel;n++)
|
|
{
|
|
pixel[n] = Unsigned.byte2short(buffer.get(p + n));
|
|
if (predictor == 2 && column > 0)
|
|
{
|
|
pixel[n] += lastPixel[n];
|
|
}
|
|
}
|
|
|
|
image.getRaster().setPixel(column, y + row, pixel);
|
|
|
|
p += samplesPerPixel;
|
|
lastPixel = pixel;
|
|
}
|
|
}
|
|
y += strip.rowcount;
|
|
}
|
|
}
|
|
|
|
public BufferedImage getBufferedImage()
|
|
{
|
|
if (bufferedImage == null)
|
|
bufferedImage = decode();
|
|
return bufferedImage;
|
|
}
|
|
|
|
int getJImageType(PHOTOMETRIC p,short[] bitsPerSample)
|
|
{
|
|
switch (p)
|
|
{
|
|
case WHITE:
|
|
case BLACK:
|
|
return BufferedImage.TYPE_BYTE_GRAY;
|
|
case RGB:
|
|
switch (bitsPerSample.length)
|
|
{
|
|
case 3:
|
|
return BufferedImage.TYPE_3BYTE_BGR;
|
|
case 4:
|
|
return BufferedImage.TYPE_4BYTE_ABGR;
|
|
}
|
|
break;
|
|
case PALETTE:
|
|
return BufferedImage.TYPE_3BYTE_BGR;
|
|
}
|
|
throw new UnsupportedFormatException();
|
|
}
|
|
|
|
IFDEntry getEntry(short tag)
|
|
{
|
|
if (entries.containsKey(tag))
|
|
return entries.get(tag);
|
|
return null;
|
|
}
|
|
|
|
int getWidth()
|
|
{
|
|
IFDEntry e = getEntry((short)256);
|
|
if (e == null)
|
|
throw new UnsupportedFormatException("missing width tag");
|
|
return ((NumericEntry)e).getInteger();
|
|
}
|
|
|
|
int getHeight()
|
|
{
|
|
IFDEntry e = getEntry((short)257);
|
|
if (e == null)
|
|
throw new UnsupportedFormatException("missing width tag");
|
|
return ((NumericEntry)e).getInteger();
|
|
}
|
|
|
|
short getPageNumber()
|
|
{
|
|
ShortEntry e = (ShortEntry)entries.get((short)297);
|
|
if (e == null)
|
|
return -1;
|
|
return e.getShort();
|
|
}
|
|
|
|
|
|
PHOTOMETRIC getPhotometric()
|
|
{
|
|
ShortEntry e = (ShortEntry)entries.get((short)262);
|
|
if (e == null)
|
|
return PHOTOMETRIC.WHITE;
|
|
return PHOTOMETRIC.fromCode(e.getShort());
|
|
}
|
|
|
|
short[] getBitsPerSample()
|
|
{
|
|
ShortEntry e = (ShortEntry)entries.get((short)258);
|
|
if (e == null)
|
|
return new short[]{1};
|
|
return e.getShortArray();
|
|
}
|
|
Integer getRowsPerStrip()
|
|
{
|
|
NumericEntry e = (NumericEntry)entries.get((short)278);
|
|
if (e == null)
|
|
return 0;
|
|
return e.getInteger();
|
|
}
|
|
short getSamplesPerPixel()
|
|
{
|
|
ShortEntry e = (ShortEntry)entries.get((short)277);
|
|
if (e == null)
|
|
return 1;
|
|
return e.getShort();
|
|
}
|
|
int[] getStripOffsets()
|
|
{
|
|
NumericEntry e = (NumericEntry)entries.get((short)273);
|
|
if (e == null)
|
|
return new int[0];
|
|
return e.getIntArray();
|
|
}
|
|
int[] getStripByteCounts()
|
|
{
|
|
NumericEntry e = (NumericEntry)entries.get((short)279);
|
|
if (e == null)
|
|
return new int[0];
|
|
return e.getIntArray();
|
|
}
|
|
short getCompression()
|
|
{
|
|
ShortEntry e = (ShortEntry)entries.get((short)259);
|
|
if (e == null)
|
|
return 1;
|
|
return e.getShort();
|
|
}
|
|
short getPredictor()
|
|
{
|
|
ShortEntry e = (ShortEntry)entries.get((short)317);
|
|
if (e == null)
|
|
return 1;
|
|
return e.getShort();
|
|
}
|
|
short getFillOrder()
|
|
{
|
|
ShortEntry e = (ShortEntry)entries.get((short)266);
|
|
if (e == null)
|
|
return 1;
|
|
return e.getShort();
|
|
}
|
|
|
|
|
|
} |