|
|
/* |
|
|
* Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved. |
|
|
* |
|
|
* Redistribution and use in source and binary forms, with or without |
|
|
* modification, are permitted provided that the following conditions are met: |
|
|
* |
|
|
* -Redistributions of source code must retain the above copyright notice, this |
|
|
* list of conditions and the following disclaimer. |
|
|
* |
|
|
* -Redistribution in binary form must reproduct the above copyright notice, |
|
|
* this list of conditions and the following disclaimer in the documentation |
|
|
* and/or other materials provided with the distribution. |
|
|
* |
|
|
* Neither the name of Sun Microsystems, Inc. or the names of contributors may |
|
|
* be used to endorse or promote products derived from this software without |
|
|
* specific prior written permission. |
|
|
* |
|
|
* This software is provided "AS IS," without a warranty of any kind. ALL |
|
|
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY |
|
|
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR |
|
|
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE |
|
|
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING |
|
|
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS |
|
|
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, |
|
|
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER |
|
|
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF |
|
|
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE |
|
|
* POSSIBILITY OF SUCH DAMAGES. |
|
|
* |
|
|
* You acknowledge that Software is not designed,licensed or intended for use in |
|
|
* the design, construction, operation or maintenance of any nuclear facility. |
|
|
*/ |
|
|
package com.fr.third.JAI; |
|
|
import java.awt.Point; |
|
|
import java.awt.Transparency; |
|
|
import java.awt.color.ColorSpace; |
|
|
import java.awt.image.ColorModel; |
|
|
import java.awt.image.ComponentColorModel; |
|
|
import java.awt.image.DataBuffer; |
|
|
import java.awt.image.DataBufferByte; |
|
|
import java.awt.image.Raster; |
|
|
import java.awt.image.SampleModel; |
|
|
import java.awt.image.WritableRaster; |
|
|
import java.io.ByteArrayInputStream; |
|
|
import java.io.InputStream; |
|
|
import java.io.IOException; |
|
|
import java.util.Enumeration; |
|
|
import java.util.Hashtable; |
|
|
|
|
|
import com.sun.image.codec.jpeg.JPEGCodec; |
|
|
import com.sun.image.codec.jpeg.JPEGDecodeParam; |
|
|
import com.sun.image.codec.jpeg.JPEGEncodeParam; |
|
|
import com.sun.image.codec.jpeg.JPEGImageDecoder; |
|
|
|
|
|
public class FPXImage extends SimpleRenderedImage { |
|
|
|
|
|
private static final int SUBIMAGE_COLOR_SPACE_COLORLESS = 0; |
|
|
private static final int SUBIMAGE_COLOR_SPACE_MONOCHROME = 0; |
|
|
private static final int SUBIMAGE_COLOR_SPACE_PHOTOYCC = 0; |
|
|
private static final int SUBIMAGE_COLOR_SPACE_NIFRGB = 0; |
|
|
private static final String[] COLORSPACE_NAME = { |
|
|
"Colorless", "Monochrome", "PhotoYCC", "NIF RGB" |
|
|
}; |
|
|
|
|
|
StructuredStorage storage; |
|
|
|
|
|
int numResolutions; |
|
|
int highestResWidth; |
|
|
int highestResHeight; |
|
|
float defaultDisplayHeight; |
|
|
float defaultDisplayWidth; |
|
|
int displayHeightWidthUnits; |
|
|
|
|
|
boolean[] subimageValid; |
|
|
int[] subimageWidth; |
|
|
int[] subimageHeight; |
|
|
|
|
|
int[][] subimageColor; |
|
|
|
|
|
// subimageNumericalFormat |
|
|
int[] decimationMethod; |
|
|
float[] decimationPrefilterWidth; |
|
|
// subimage ICC profile |
|
|
|
|
|
int highestResolution = -1; |
|
|
|
|
|
int maxJPEGTableIndex; |
|
|
byte[][] JPEGTable; |
|
|
|
|
|
// Values from "Subimage 0000 Header" stream |
|
|
int numChannels; |
|
|
int tileHeaderTableOffset; |
|
|
int tileHeaderEntryLength; |
|
|
|
|
|
// int[] tileOffset; |
|
|
// int[] tileSize; |
|
|
// int[] compressionType; |
|
|
// int[] compressionSubtype; |
|
|
|
|
|
// The "Subimage 0000 Header" stream |
|
|
SeekableStream subimageHeaderStream; |
|
|
|
|
|
// The "Subimage 0000 Data" stream |
|
|
SeekableStream subimageDataStream; |
|
|
|
|
|
int resolution; |
|
|
|
|
|
// Derived values |
|
|
int tilesAcross; |
|
|
|
|
|
int[] bandOffsets = { 0, 1, 2 }; |
|
|
|
|
|
private static final int[] RGBBits8 = { 8, 8, 8 }; |
|
|
private static final ComponentColorModel colorModelRGB8 = |
|
|
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), |
|
|
RGBBits8, false, false, |
|
|
Transparency.OPAQUE, |
|
|
DataBuffer.TYPE_BYTE); |
|
|
|
|
|
public FPXImage(SeekableStream stream, FPXDecodeParam param) |
|
|
throws IOException { |
|
|
|
|
|
this.storage = new StructuredStorage(stream); |
|
|
|
|
|
readImageContents(); |
|
|
if (param == null) { |
|
|
param = new FPXDecodeParam(); |
|
|
} |
|
|
this.resolution = param.getResolution(); |
|
|
readResolution(); |
|
|
|
|
|
bandOffsets = new int[numChannels]; |
|
|
for (int i = 0; i < numChannels; i++) { |
|
|
bandOffsets[i] = i; |
|
|
} |
|
|
|
|
|
this.minX = 0; |
|
|
this.minY = 0; |
|
|
|
|
|
this.sampleModel = |
|
|
RasterFactory.createPixelInterleavedSampleModel( |
|
|
DataBuffer.TYPE_BYTE, |
|
|
tileWidth, |
|
|
tileHeight, |
|
|
numChannels, |
|
|
numChannels*tileWidth, |
|
|
bandOffsets); |
|
|
this.colorModel = ImageCodec.createComponentColorModel(sampleModel); |
|
|
} |
|
|
|
|
|
private void readImageContents() throws IOException { |
|
|
storage.changeDirectoryToRoot(); |
|
|
storage.changeDirectory("Data Object Store 000001"); |
|
|
SeekableStream imageContents = |
|
|
storage.getStream("Image Contents"); |
|
|
|
|
|
PropertySet icps = new PropertySet(imageContents); |
|
|
this.numResolutions = (int)icps.getUI4(0x01000000); |
|
|
this.highestResWidth = (int)icps.getUI4(0x01000002); |
|
|
this.highestResHeight = (int)icps.getUI4(0x01000003); |
|
|
// this.defaultDisplayHeight = icps.getR4(0x01000004); |
|
|
// this.defaultDisplayWidth = icps.getR4(0x01000005); |
|
|
|
|
|
this.displayHeightWidthUnits = (int)icps.getUI4(0x01000006, 0L); |
|
|
|
|
|
/* |
|
|
System.out.println("\nImage Contents Property Set:\n"); |
|
|
System.out.println(" numResolutions = " + numResolutions); |
|
|
System.out.println(" highestResWidth = " + highestResWidth); |
|
|
System.out.println(" highestResHeight = " + highestResHeight); |
|
|
System.out.println(); |
|
|
*/ |
|
|
|
|
|
this.subimageValid = new boolean[numResolutions]; |
|
|
this.subimageWidth = new int[numResolutions]; |
|
|
this.subimageHeight = new int[numResolutions]; |
|
|
this.subimageColor = new int[numResolutions][]; |
|
|
// subimageNumericalFormat |
|
|
this.decimationMethod = new int[numResolutions]; |
|
|
this.decimationPrefilterWidth = new float[numResolutions]; |
|
|
// subimage ICC profile |
|
|
|
|
|
for (int i = 0; i < numResolutions; i++) { |
|
|
int index = i << 16; |
|
|
if (!icps.hasProperty(0x02000000 | index)) { |
|
|
break; |
|
|
} |
|
|
|
|
|
this.highestResolution = i; |
|
|
subimageValid[i] = true; |
|
|
subimageWidth[i] = (int)icps.getUI4(0x02000000 | index); |
|
|
subimageHeight[i] = (int)icps.getUI4(0x02000001 | index); |
|
|
byte[] subimageColorBlob = icps.getBlob(0x02000002 | index); |
|
|
decimationMethod[i] = icps.getI4(0x02000004 | index); |
|
|
// decimationPrefilterWidth[i] = icps.getR4(0x02000005 | index); |
|
|
|
|
|
int numSubImages = FPXUtils.getIntLE(subimageColorBlob, 0); |
|
|
int numChannels = FPXUtils.getIntLE(subimageColorBlob, 4); |
|
|
|
|
|
// System.out.println(" subimageWidth[" + i + "] = " + |
|
|
// subimageWidth[i]); |
|
|
// System.out.println(" subimageHeight[" + i + "] = " + |
|
|
// subimageHeight[i]); |
|
|
// System.out.println(" subimageColor[" + i + "] = "); |
|
|
// System.out.println(" numSubimages = " + numSubImages); |
|
|
|
|
|
subimageColor[i] = new int[numChannels]; |
|
|
for (int c = 0; c < numChannels; c++) { |
|
|
int color = FPXUtils.getIntLE(subimageColorBlob, 8 + 4*c); |
|
|
subimageColor[i][c] = color; |
|
|
// System.out.println(" channel " + c + " color space " + |
|
|
// (color >> 16) + |
|
|
// " (" + COLORSPACE_NAME[color >> 16] +")"); |
|
|
|
|
|
// System.out.println(" channel " + c + " color type " + |
|
|
// (color & 0x7fff)); |
|
|
// if ((color & 0x8000) != 0) { |
|
|
// System.out.println(" channel " + c + " has premultiplied opacity"); |
|
|
// } |
|
|
} |
|
|
|
|
|
// System.out.println(" decimationMethod[" + i + "] = " + |
|
|
// decimationMethod[i]); |
|
|
// System.out.println(); |
|
|
} |
|
|
|
|
|
this.maxJPEGTableIndex = (int)icps.getUI4(0x03000002, -1L); |
|
|
// System.out.println("maxJPEGTableIndex = " + maxJPEGTableIndex); |
|
|
this.JPEGTable = new byte[maxJPEGTableIndex + 1][]; |
|
|
for (int i = 0; i <= maxJPEGTableIndex; i++) { |
|
|
int index = i << 16; |
|
|
if (icps.hasProperty(0x03000001 | index)) { |
|
|
// System.out.println("Found a table at index " + i); |
|
|
this.JPEGTable[i] = icps.getBlob(0x03000001 | index); |
|
|
} else { |
|
|
// System.out.println("No table at index " + i); |
|
|
this.JPEGTable[i] = null; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
private void readResolution() throws IOException { |
|
|
if (resolution == -1) { |
|
|
resolution = this.highestResolution; |
|
|
} |
|
|
|
|
|
// System.out.println("Reading resolution " + resolution); |
|
|
|
|
|
storage.changeDirectoryToRoot(); |
|
|
storage.changeDirectory("Data Object Store 000001"); |
|
|
storage.changeDirectory("Resolution 000" + resolution); // FIX |
|
|
|
|
|
this.subimageHeaderStream = storage.getStream("Subimage 0000 Header"); |
|
|
subimageHeaderStream.skip(28); |
|
|
int headerLength = subimageHeaderStream.readIntLE(); |
|
|
this.width = subimageHeaderStream.readIntLE(); |
|
|
this.height = subimageHeaderStream.readIntLE(); |
|
|
int numTiles = subimageHeaderStream.readIntLE(); |
|
|
this.tileWidth = subimageHeaderStream.readIntLE(); |
|
|
this.tileHeight = subimageHeaderStream.readIntLE(); |
|
|
this.numChannels = subimageHeaderStream.readIntLE(); |
|
|
this.tileHeaderTableOffset = subimageHeaderStream.readIntLE() + 28; |
|
|
this.tileHeaderEntryLength = subimageHeaderStream.readIntLE(); |
|
|
|
|
|
// System.out.println("\nResolution 000" + resolution + "\n"); |
|
|
// System.out.println("Subimage 0000 Header:\n"); |
|
|
// System.out.println(" headerLength = " + headerLength); |
|
|
// System.out.println(" width = " + width); |
|
|
// System.out.println(" height = " + height); |
|
|
// System.out.println(" numTiles = " + numTiles); |
|
|
// System.out.println(" tileWidth = " + tileWidth); |
|
|
// System.out.println(" tileHeight = " + tileHeight); |
|
|
// System.out.println(" numChannels = " + numChannels); |
|
|
// System.out.println(" tileHeaderTableOffset = " + |
|
|
// tileHeaderTableOffset); |
|
|
// System.out.println(" tileHeaderEntryLength = " + |
|
|
// tileHeaderEntryLength); |
|
|
|
|
|
this.subimageDataStream = storage.getStream("Subimage 0000 Data"); |
|
|
|
|
|
// Compute derived values |
|
|
tilesAcross = (width + tileWidth - 1)/tileWidth; |
|
|
} |
|
|
|
|
|
private int getTileOffset(int tileIndex) throws IOException { |
|
|
// return tileOffset[tileIndex]; |
|
|
|
|
|
subimageHeaderStream.seek(tileHeaderTableOffset + |
|
|
16*tileIndex); |
|
|
return subimageHeaderStream.readIntLE() + 28; |
|
|
} |
|
|
|
|
|
private int getTileSize(int tileIndex) throws IOException { |
|
|
// return tileSize[tileIndex]; |
|
|
|
|
|
subimageHeaderStream.seek(tileHeaderTableOffset + |
|
|
16*tileIndex + 4); |
|
|
return subimageHeaderStream.readIntLE(); |
|
|
} |
|
|
|
|
|
private int getCompressionType(int tileIndex) throws IOException { |
|
|
// return compressionType[tileIndex]; |
|
|
|
|
|
subimageHeaderStream.seek(tileHeaderTableOffset + |
|
|
16*tileIndex + 8); |
|
|
return subimageHeaderStream.readIntLE(); |
|
|
} |
|
|
|
|
|
private int getCompressionSubtype(int tileIndex) throws IOException { |
|
|
// return compressionSubtype[tileIndex]; |
|
|
|
|
|
subimageHeaderStream.seek(tileHeaderTableOffset + |
|
|
16*tileIndex + 12); |
|
|
return subimageHeaderStream.readIntLE(); |
|
|
} |
|
|
|
|
|
private static final byte[] PhotoYCCToRGBLUT = { |
|
|
(byte)0, (byte)1, (byte)1, (byte)2, (byte)2, (byte)3, (byte)4, |
|
|
(byte)5, (byte)6, (byte)7, (byte)8, (byte)9, (byte)10, (byte)11, |
|
|
(byte)12, (byte)13, (byte)14, (byte)15, (byte)16, (byte)17, (byte)18, |
|
|
(byte)19, (byte)20, (byte)22, (byte)23, (byte)24, (byte)25, (byte)26, |
|
|
(byte)28, (byte)29, (byte)30, (byte)31, |
|
|
|
|
|
(byte)33, (byte)34, (byte)35, (byte)36, (byte)38, (byte)39, |
|
|
(byte)40, (byte)41, (byte)43, (byte)44, (byte)45, (byte)47, (byte)48, |
|
|
(byte)49, (byte)51, (byte)52, (byte)53, (byte)55, (byte)56, (byte)57, |
|
|
(byte)59, (byte)60, (byte)61, (byte)63, (byte)64, (byte)65, (byte)67, |
|
|
(byte)68, (byte)70, (byte)71, (byte)72, (byte)74, |
|
|
|
|
|
(byte)75, (byte)76, (byte)78, (byte)79, (byte)81, (byte)82, (byte)83, |
|
|
(byte)85, (byte)86, (byte)88, (byte)89, (byte)91, (byte)92, (byte)93, |
|
|
(byte)95, (byte)96, (byte)98, (byte)99, (byte)101, (byte)102, |
|
|
(byte)103, (byte)105, (byte)106, (byte)108, (byte)109, (byte)111, |
|
|
(byte)112, (byte)113, (byte)115, (byte)116, (byte)118, (byte)119, |
|
|
|
|
|
(byte)121, (byte)122, (byte)123, (byte)125, (byte)126, (byte)128, |
|
|
(byte)129, (byte)130, (byte)132, (byte)133, (byte)134, (byte)136, |
|
|
(byte)137, (byte)138, (byte)140, (byte)141, (byte)142, (byte)144, |
|
|
(byte)145, (byte)146, (byte)148, (byte)149, (byte)150, (byte)152, |
|
|
(byte)153, (byte)154, (byte)155, (byte)157, (byte)158, (byte)159, |
|
|
(byte)160, (byte)162, |
|
|
|
|
|
(byte)163, (byte)164, (byte)165, (byte)166, (byte)168, (byte)169, |
|
|
(byte)170, (byte)171, (byte)172, (byte)174, (byte)175, (byte)176, |
|
|
(byte)177, (byte)178, (byte)179, (byte)180, (byte)182, (byte)183, |
|
|
(byte)184, (byte)185, (byte)186, (byte)187, (byte)188, (byte)189, |
|
|
(byte)190, (byte)191, (byte)192, (byte)194, (byte)195, (byte)196, |
|
|
(byte)197, (byte)198, |
|
|
|
|
|
(byte)199, (byte)200, (byte)201, (byte)202, (byte)203, (byte)204, |
|
|
(byte)204, (byte)205, (byte)206, (byte)207, (byte)208, (byte)209, |
|
|
(byte)210, (byte)211, (byte)212, (byte)213, (byte)213, (byte)214, |
|
|
(byte)215, (byte)216, (byte)217, (byte)217, (byte)218, (byte)219, |
|
|
(byte)220, (byte)221, (byte)221, (byte)222, (byte)223, (byte)223, |
|
|
(byte)224, (byte)225, |
|
|
|
|
|
(byte)225, (byte)226, (byte)227, (byte)227, (byte)228, (byte)229, |
|
|
(byte)229, (byte)230, (byte)230, (byte)231, (byte)231, (byte)232, |
|
|
(byte)233, (byte)233, (byte)234, (byte)234, (byte)235, (byte)235, |
|
|
(byte)236, (byte)236, (byte)236, (byte)237, (byte)237, (byte)238, |
|
|
(byte)238, (byte)238, (byte)239, (byte)239, (byte)240, (byte)240, |
|
|
(byte)240, (byte)241, |
|
|
|
|
|
(byte)241, (byte)241, (byte)242, (byte)242, (byte)242, (byte)242, |
|
|
(byte)243, (byte)243, (byte)243, (byte)244, (byte)244, (byte)244, |
|
|
(byte)244, (byte)245, (byte)245, (byte)245, (byte)245, (byte)245, |
|
|
(byte)246, (byte)246, (byte)246, (byte)246, (byte)246, (byte)247, |
|
|
(byte)247, (byte)247, (byte)247, (byte)247, (byte)247, (byte)248, |
|
|
(byte)248, (byte)248, |
|
|
|
|
|
(byte)248, (byte)248, (byte)248, (byte)249, (byte)249, (byte)249, |
|
|
(byte)249, (byte)249, (byte)249, (byte)249, (byte)249, (byte)249, |
|
|
(byte)250, (byte)250, (byte)250, (byte)250, (byte)250, (byte)250, |
|
|
(byte)250, (byte)250, (byte)250, (byte)250, (byte)251, (byte)251, |
|
|
(byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, |
|
|
(byte)251, (byte)251, |
|
|
|
|
|
(byte)251, (byte)251, (byte)251, (byte)251, (byte)252, (byte)252, |
|
|
(byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, |
|
|
(byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, |
|
|
(byte)252, (byte)252, (byte)252, (byte)253, (byte)253, (byte)253, |
|
|
(byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, |
|
|
(byte)253, (byte)253, |
|
|
|
|
|
(byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, |
|
|
(byte)253, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, |
|
|
(byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, |
|
|
(byte)254, (byte)254, (byte)254, (byte)254, (byte)255, (byte)255, |
|
|
(byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, |
|
|
(byte)255, (byte)255, |
|
|
|
|
|
(byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, |
|
|
(byte)255, (byte)255, (byte)255 |
|
|
}; |
|
|
|
|
|
private final byte PhotoYCCToNIFRed(float scaledY, float Cb, float Cr) { |
|
|
float red = scaledY + 1.8215F*Cr - 249.55F; |
|
|
if (red < 0.0F) { |
|
|
return (byte)0; |
|
|
} else if (red > 360.0F) { |
|
|
return (byte)255; |
|
|
} else { |
|
|
byte r = PhotoYCCToRGBLUT[(int)red]; |
|
|
return r; |
|
|
} |
|
|
} |
|
|
|
|
|
private final byte PhotoYCCToNIFGreen(float scaledY, float Cb, float Cr) { |
|
|
float green = scaledY - .43031F*Cb - .9271F*Cr + 194.14F; |
|
|
if (green < 0.0F) { |
|
|
return (byte)0; |
|
|
} else if (green > 360.0F) { |
|
|
return (byte)255; |
|
|
} else { |
|
|
byte g = PhotoYCCToRGBLUT[(int)green]; |
|
|
return g; |
|
|
} |
|
|
} |
|
|
|
|
|
private final byte PhotoYCCToNIFBlue(float scaledY, float Cb, float Cr) { |
|
|
float blue = scaledY + 2.2179F*Cb - 345.99F; |
|
|
if (blue < 0.0F) { |
|
|
return (byte)0; |
|
|
} else if (blue > 360.0F) { |
|
|
return (byte)255; |
|
|
} else { |
|
|
byte b = PhotoYCCToRGBLUT[(int)blue]; |
|
|
return b; |
|
|
} |
|
|
} |
|
|
|
|
|
private final byte YCCToNIFRed(float Y, float Cb, float Cr) { |
|
|
float red = Y + 1.402F*Cr - (255.0F*.701F); |
|
|
if (red < 0.0F) { |
|
|
return (byte)0; |
|
|
} else if (red > 255.0F) { |
|
|
return (byte)255; |
|
|
} else { |
|
|
return (byte)red; |
|
|
} |
|
|
} |
|
|
|
|
|
private final byte YCCToNIFGreen(float Y, float Cb, float Cr) { |
|
|
float green = Y - .34414F*Cb - .71414F*Cr + (255.0F*.52914F); |
|
|
if (green < 0.0F) { |
|
|
return (byte)0; |
|
|
} else if (green > 255.0F) { |
|
|
return (byte)255; |
|
|
} else { |
|
|
return (byte)green; |
|
|
} |
|
|
} |
|
|
|
|
|
private final byte YCCToNIFBlue(float Y, float Cb, float Cr) { |
|
|
float blue = Y + 1.772F*Cb - (255.0F*.886F); |
|
|
if (blue < 0.0F) { |
|
|
return (byte)0; |
|
|
} else if (blue > 255.0F) { |
|
|
return (byte)255; |
|
|
} else { |
|
|
return (byte)blue; |
|
|
} |
|
|
} |
|
|
|
|
|
private Raster getUncompressedTile(int tileX, int tileY) |
|
|
throws IOException { |
|
|
int tx = tileXToX(tileX); |
|
|
int ty = tileYToY(tileY); |
|
|
Raster ras = |
|
|
RasterFactory.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
|
tileWidth, |
|
|
tileHeight, |
|
|
numChannels*tileWidth, |
|
|
numChannels, |
|
|
bandOffsets, |
|
|
new Point(tx, ty)); |
|
|
// System.out.println("Uncompressed tile."); |
|
|
|
|
|
DataBufferByte dataBuffer = (DataBufferByte)ras.getDataBuffer(); |
|
|
byte[] data = dataBuffer.getData(); |
|
|
|
|
|
int tileIndex = tileY*tilesAcross + tileX; |
|
|
subimageDataStream.seek(getTileOffset(tileIndex)); |
|
|
subimageDataStream.readFully(data, 0, |
|
|
numChannels*tileWidth*tileHeight); |
|
|
|
|
|
// Color convert if subimage is in PhotoYCC format |
|
|
if (subimageColor[resolution][0] >> 16 == 2) { |
|
|
int size = tileWidth*tileHeight; |
|
|
for (int i = 0; i < size; i++) { |
|
|
float Y = data[3*i] & 0xff; |
|
|
float Cb = data[3*i + 1] & 0xff; |
|
|
float Cr = data[3*i + 2] & 0xff; |
|
|
|
|
|
float scaledY = Y*1.3584F; |
|
|
byte red = PhotoYCCToNIFRed(scaledY, Cb, Cr); |
|
|
byte green = PhotoYCCToNIFGreen(scaledY, Cb, Cr); |
|
|
byte blue = PhotoYCCToNIFBlue(scaledY, Cb, Cr); |
|
|
|
|
|
data[3*i] = red; |
|
|
data[3*i + 1] = green; |
|
|
data[3*i + 2] = blue; |
|
|
} |
|
|
} |
|
|
|
|
|
return ras; |
|
|
} |
|
|
|
|
|
private Raster getSingleColorCompressedTile(int tileX, int tileY) |
|
|
throws IOException { |
|
|
// System.out.println("Single color compressed tile."); |
|
|
|
|
|
int tx = tileXToX(tileX); |
|
|
int ty = tileYToY(tileY); |
|
|
Raster ras = |
|
|
RasterFactory.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
|
tileWidth, |
|
|
tileHeight, |
|
|
numChannels*tileWidth, |
|
|
numChannels, |
|
|
bandOffsets, |
|
|
new Point(tx, ty)); |
|
|
|
|
|
int subimageColorType = subimageColor[resolution][0] >> 16; |
|
|
|
|
|
DataBufferByte dataBuffer = (DataBufferByte)ras.getDataBuffer(); |
|
|
byte[] data = dataBuffer.getData(); |
|
|
|
|
|
int tileIndex = tileY*tilesAcross + tileX; |
|
|
int color = getCompressionSubtype(tileIndex); |
|
|
byte c0 = (byte)((color >> 0) & 0xff); |
|
|
byte c1 = (byte)((color >> 8) & 0xff); |
|
|
byte c2 = (byte)((color >> 16) & 0xff); |
|
|
byte alpha = (byte)((color >> 24) & 0xff); |
|
|
|
|
|
byte red, green, blue; |
|
|
|
|
|
// Color convert if subimage is in PhotoYCC format |
|
|
if (subimageColor[resolution][0] >> 16 == 2) { |
|
|
float Y = c0 & 0xff; |
|
|
float Cb = c1 & 0xff; |
|
|
float Cr = c2 & 0xff; |
|
|
|
|
|
float scaledY = Y*1.3584F; |
|
|
red = PhotoYCCToNIFRed(scaledY, Cb, Cr); |
|
|
green = PhotoYCCToNIFGreen(scaledY, Cb, Cr); |
|
|
blue = PhotoYCCToNIFBlue(scaledY, Cb, Cr); |
|
|
} else { |
|
|
red = c0; |
|
|
green = c1; |
|
|
blue = c2; |
|
|
} |
|
|
|
|
|
int index = 0; |
|
|
int pixels = tileWidth*tileHeight; |
|
|
|
|
|
if (numChannels == 1) { |
|
|
} else if (numChannels == 2) { |
|
|
} else if (numChannels == 3) { |
|
|
for (int i = 0; i < pixels; i++) { |
|
|
data[index + 0] = red; |
|
|
data[index + 1] = green; |
|
|
data[index + 2] = blue; |
|
|
|
|
|
index += 3; |
|
|
} |
|
|
} else if (numChannels == 4) { |
|
|
for (int i = 0; i < pixels; i++) { |
|
|
data[index + 0] = red; |
|
|
data[index + 1] = green; |
|
|
data[index + 2] = blue; |
|
|
data[index + 3] = alpha; |
|
|
|
|
|
index += 4; |
|
|
} |
|
|
} |
|
|
|
|
|
return ras; |
|
|
} |
|
|
|
|
|
private Raster getJPEGCompressedTile(int tileX, int tileY) |
|
|
throws IOException { |
|
|
// System.out.println("JPEG compressed tile."); |
|
|
|
|
|
int tileIndex = tileY*tilesAcross + tileX; |
|
|
|
|
|
int tx = tileXToX(tileX); |
|
|
int ty = tileYToY(tileY); |
|
|
|
|
|
int subtype = getCompressionSubtype(tileIndex); |
|
|
int interleave = (subtype >> 0) & 0xff; |
|
|
int chroma = (subtype >> 8) & 0xff; |
|
|
int conversion = (subtype >> 16) & 0xff; |
|
|
int table = (subtype >> 24) & 0xff; |
|
|
|
|
|
JPEGImageDecoder dec; |
|
|
JPEGDecodeParam param = null; |
|
|
|
|
|
if (table != 0) { |
|
|
InputStream tableStream = |
|
|
new ByteArrayInputStream(JPEGTable[table]); |
|
|
dec = JPEGCodec.createJPEGDecoder(tableStream); |
|
|
Raster junk = dec.decodeAsRaster(); |
|
|
param = dec.getJPEGDecodeParam(); |
|
|
} |
|
|
|
|
|
subimageDataStream.seek(getTileOffset(tileIndex)); |
|
|
if (param != null) { |
|
|
dec = JPEGCodec.createJPEGDecoder(subimageDataStream, param); |
|
|
} else { |
|
|
dec = JPEGCodec.createJPEGDecoder(subimageDataStream); |
|
|
} |
|
|
Raster ras = dec.decodeAsRaster().createTranslatedChild(tx, ty); |
|
|
|
|
|
DataBufferByte dataBuffer = (DataBufferByte)ras.getDataBuffer(); |
|
|
byte[] data = dataBuffer.getData(); |
|
|
|
|
|
int subimageColorType = subimageColor[resolution][0] >> 16; |
|
|
|
|
|
int size = tileWidth*tileHeight; |
|
|
if ((conversion == 0) && (subimageColorType == 2)) { |
|
|
// System.out.println("Converting PhotoYCC to NIFRGB"); |
|
|
int offset = 0; |
|
|
for (int i = 0; i < size; i++) { |
|
|
float Y = data[offset] & 0xff; |
|
|
float Cb = data[offset + 1] & 0xff; |
|
|
float Cr = data[offset + 2] & 0xff; |
|
|
|
|
|
float scaledY = Y*1.3584F; |
|
|
byte red = PhotoYCCToNIFRed(scaledY, Cb, Cr); |
|
|
byte green = PhotoYCCToNIFGreen(scaledY, Cb, Cr); |
|
|
byte blue = PhotoYCCToNIFBlue(scaledY, Cb, Cr); |
|
|
|
|
|
data[offset] = red; |
|
|
data[offset + 1] = green; |
|
|
data[offset + 2] = blue; |
|
|
|
|
|
offset += numChannels; |
|
|
} |
|
|
} else if ((conversion == 1) && (subimageColorType == 3)) { |
|
|
// System.out.println("Converting YCC to NIFRGB"); |
|
|
int offset = 0; |
|
|
for (int i = 0; i < size; i++) { |
|
|
float Y = data[offset] & 0xff; |
|
|
float Cb = data[offset + 1] & 0xff; |
|
|
float Cr = data[offset + 2] & 0xff; |
|
|
|
|
|
byte red = YCCToNIFRed(Y, Cb, Cr); |
|
|
byte green = YCCToNIFGreen(Y, Cb, Cr); |
|
|
byte blue = YCCToNIFBlue(Y, Cb, Cr); |
|
|
|
|
|
data[offset] = red; |
|
|
data[offset + 1] = green; |
|
|
data[offset + 2] = blue; |
|
|
|
|
|
offset += numChannels; |
|
|
} |
|
|
} |
|
|
|
|
|
// Perform special inversion step when output space is |
|
|
// NIF RGB (subimageColorType == 3) with premultiplied opacity |
|
|
// (numChannels == 4). |
|
|
if ((conversion == 1) && |
|
|
(subimageColorType == 3) && (numChannels == 4)) { |
|
|
// System.out.println("Flipping NIFRGB"); |
|
|
|
|
|
int offset = 0; |
|
|
for (int i = 0; i < size; i++) { |
|
|
data[offset + 0] = (byte)(255 - data[offset + 0]); |
|
|
data[offset + 1] = (byte)(255 - data[offset + 1]); |
|
|
data[offset + 2] = (byte)(255 - data[offset + 2]); |
|
|
|
|
|
offset += 4; |
|
|
} |
|
|
} |
|
|
|
|
|
return ras; |
|
|
} |
|
|
|
|
|
public synchronized Raster getTile(int tileX, int tileY) { |
|
|
int tileIndex = tileY*tilesAcross + tileX; |
|
|
|
|
|
try { |
|
|
int ctype = getCompressionType(tileIndex); |
|
|
if (ctype == 0) { |
|
|
return getUncompressedTile(tileX, tileY); |
|
|
} else if (ctype == 1) { |
|
|
return getSingleColorCompressedTile(tileX, tileY); |
|
|
} else if (ctype == 2) { |
|
|
return getJPEGCompressedTile(tileX, tileY); |
|
|
} |
|
|
return null; |
|
|
} catch (IOException e) { |
|
|
e.printStackTrace(); |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
Hashtable properties = null; |
|
|
|
|
|
private void addLPSTRProperty(String name, PropertySet ps, int id) { |
|
|
String s = ps.getLPSTR(id); |
|
|
if (s != null) { |
|
|
properties.put(name.toLowerCase(), s); |
|
|
} |
|
|
} |
|
|
|
|
|
private void addLPWSTRProperty(String name, PropertySet ps, int id) { |
|
|
String s = ps.getLPWSTR(id); |
|
|
if (s != null) { |
|
|
properties.put(name.toLowerCase(), s); |
|
|
} |
|
|
} |
|
|
|
|
|
private void addUI4Property(String name, PropertySet ps, int id) { |
|
|
if (ps.hasProperty(id)) { |
|
|
long i = ps.getUI4(id); |
|
|
properties.put(name.toLowerCase(), new Integer((int)i)); |
|
|
} |
|
|
} |
|
|
|
|
|
private void getSummaryInformation() { |
|
|
SeekableStream summaryInformation = null; |
|
|
PropertySet sips = null; |
|
|
try { |
|
|
storage.changeDirectoryToRoot(); |
|
|
summaryInformation = storage.getStream("SummaryInformation"); |
|
|
sips = new PropertySet(summaryInformation); |
|
|
} catch (IOException e) { |
|
|
e.printStackTrace(); |
|
|
return; |
|
|
} |
|
|
|
|
|
addLPSTRProperty("title", sips, 0x000000002); |
|
|
addLPSTRProperty("subject", sips, 0x000000003); |
|
|
addLPSTRProperty("author", sips, 0x000000004); |
|
|
addLPSTRProperty("keywords", sips, 0x000000005); |
|
|
addLPSTRProperty("comments", sips, 0x000000006); |
|
|
addLPSTRProperty("template", sips, 0x000000007); |
|
|
addLPSTRProperty("last saved by", sips, 0x000000008); |
|
|
addLPSTRProperty("revision number", sips, 0x000000009); |
|
|
} |
|
|
|
|
|
private void getImageInfo() { |
|
|
SeekableStream imageInfo = null; |
|
|
PropertySet iips = null; |
|
|
try { |
|
|
storage.changeDirectoryToRoot(); |
|
|
imageInfo = storage.getStream("Image Info"); |
|
|
if (imageInfo == null) { |
|
|
return; |
|
|
} |
|
|
iips = new PropertySet(imageInfo); |
|
|
} catch (IOException e) { |
|
|
e.printStackTrace(); |
|
|
return; |
|
|
} |
|
|
|
|
|
addUI4Property("file source", iips, 0x21000000); |
|
|
addUI4Property("scene type", iips, 0x21000001); |
|
|
// creation path vector |
|
|
addLPWSTRProperty("software name/manufacturer/release", |
|
|
iips, 0x21000003); |
|
|
addLPWSTRProperty("user defined id", |
|
|
iips, 0x21000004); |
|
|
|
|
|
addLPWSTRProperty("copyright message", |
|
|
iips, 0x22000000); |
|
|
addLPWSTRProperty("legal broker for the original image", |
|
|
iips, 0x22000001); |
|
|
addLPWSTRProperty("legal broker for the digital image", |
|
|
iips, 0x22000002); |
|
|
addLPWSTRProperty("authorship", iips, 0x22000003); |
|
|
addLPWSTRProperty("intellectual property notes", iips, 0x22000004); |
|
|
} |
|
|
|
|
|
private synchronized void getProperties() { |
|
|
if (properties != null) { |
|
|
return; |
|
|
} |
|
|
properties = new Hashtable(); |
|
|
|
|
|
getSummaryInformation(); |
|
|
getImageInfo(); |
|
|
|
|
|
// Ad hoc properties |
|
|
properties.put("max_resolution", new Integer(highestResolution)); |
|
|
} |
|
|
|
|
|
public String[] getPropertyNames() { |
|
|
getProperties(); |
|
|
|
|
|
int len = properties.size(); |
|
|
String[] names = new String[len]; |
|
|
Enumeration enum1 = properties.keys(); |
|
|
|
|
|
int count = 0; |
|
|
while (enum1.hasMoreElements()) { |
|
|
names[count++] = (String)enum1.nextElement(); |
|
|
} |
|
|
|
|
|
return names; |
|
|
} |
|
|
|
|
|
public Object getProperty(String name) { |
|
|
getProperties(); |
|
|
return properties.get(name.toLowerCase()); |
|
|
} |
|
|
}
|
|
|
|