/* * 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()); } }