0001: /*
0002: * $RCSfile: J2KRenderedImageCodecLib.java,v $
0003: *
0004: *
0005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * - Redistribution of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: *
0014: * - Redistribution in binary form must reproduce the above copyright
0015: * notice, this list of conditions and the following disclaimer in
0016: * the documentation and/or other materials provided with the
0017: * distribution.
0018: *
0019: * Neither the name of Sun Microsystems, Inc. or the names of
0020: * contributors may be used to endorse or promote products derived
0021: * from this software without specific prior written permission.
0022: *
0023: * This software is provided "AS IS," without a warranty of any
0024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0035: * POSSIBILITY OF SUCH DAMAGES.
0036: *
0037: * You acknowledge that this software is not designed or intended for
0038: * use in the design, construction, operation or maintenance of any
0039: * nuclear facility.
0040: *
0041: * $Revision: 1.4 $
0042: * $Date: 2006/10/03 23:40:14 $
0043: * $State: Exp $
0044: */
0045: package com.sun.media.imageioimpl.plugins.jpeg2000;
0046:
0047: import javax.imageio.IIOException;
0048: import javax.imageio.ImageReadParam;
0049: import javax.imageio.stream.ImageInputStream;
0050:
0051: import java.awt.Point;
0052: import java.awt.Rectangle;
0053: import java.awt.Transparency;
0054: import java.awt.color.ColorSpace;
0055: import java.awt.color.ICC_ColorSpace;
0056: import java.awt.color.ICC_Profile;
0057: import java.awt.image.BufferedImage;
0058: import java.awt.image.ColorModel;
0059: import java.awt.image.ComponentColorModel;
0060: import java.awt.image.ComponentSampleModel;
0061: import java.awt.image.DataBuffer;
0062: import java.awt.image.DirectColorModel;
0063: import java.awt.image.IndexColorModel;
0064: import java.awt.image.MultiPixelPackedSampleModel;
0065: import java.awt.image.PixelInterleavedSampleModel;
0066: import java.awt.image.SinglePixelPackedSampleModel;
0067: import java.awt.image.Raster;
0068: import java.awt.image.RenderedImage;
0069: import java.awt.image.SampleModel;
0070: import java.awt.image.WritableRaster;
0071:
0072: import java.io.IOException;
0073:
0074: import java.lang.reflect.Constructor;
0075: import java.lang.reflect.Method;
0076: import java.lang.reflect.InvocationTargetException;
0077:
0078: import com.sun.medialib.codec.jp2k.CompParams;
0079: import com.sun.medialib.codec.jp2k.Constants;
0080: import com.sun.medialib.codec.jp2k.Decoder;
0081: import com.sun.medialib.codec.jp2k.Size;
0082: import com.sun.medialib.codec.jiio.mediaLibImage;
0083:
0084: import com.sun.media.imageio.plugins.jpeg2000.J2KImageReadParam;
0085: import com.sun.media.imageioimpl.common.SimpleRenderedImage;
0086: import com.sun.media.imageioimpl.common.ImageUtil;
0087:
0088: // XXX Overall documentation
0089:
0090: public class J2KRenderedImageCodecLib extends SimpleRenderedImage {
0091: /** The sample model for the original image. */
0092: private SampleModel originalSampleModel;
0093:
0094: private Raster currentTile;
0095: private Point currentTileGrid;
0096: private J2KMetadata metadata;
0097:
0098: /** The input stream we read from */
0099: private ImageInputStream iis = null;
0100:
0101: /** Caches the <code>J2KImageReader</code> which creates this object. This
0102: * variable is used to monitor the abortion.
0103: */
0104: private J2KImageReaderCodecLib reader;
0105:
0106: /** The <code>J2KImageReadParam</code> to create this
0107: * <code>renderedImage</code>.
0108: */
0109: private J2KImageReadParam param = null;
0110:
0111: /** Caches the medialib decoder. */
0112: private Decoder decoder;
0113: private Size size;
0114: private CompParams compParam;
0115: private int xStep, yStep; // JPEG 2000 internal subsampling parameters
0116:
0117: /** The destination bounds. */
0118: Rectangle destinationRegion;
0119: Rectangle originalRegion;
0120: Point sourceOrigin;
0121:
0122: /** The subsampling parameters. */
0123: private int scaleX, scaleY, xOffset, yOffset;
0124: private int[] destinationBands = null;
0125: private int[] sourceBands = null;
0126: private int nComp;
0127: private int[] channelMap;
0128:
0129: /** Coordinate transform is not needed from the source (image stream)
0130: * to the destination.
0131: */
0132: private boolean noTransform = true;
0133:
0134: /** The raster for medialib tiles to share. */
0135: private WritableRaster rasForATile;
0136:
0137: private BufferedImage destImage;
0138:
0139: public J2KRenderedImageCodecLib(ImageInputStream iis,
0140: J2KImageReaderCodecLib reader, ImageReadParam param)
0141: throws IOException {
0142: this .iis = iis;
0143: this .reader = reader;
0144:
0145: // Ensure the ImageReadParam is a J2KImageReadParam
0146: boolean allowZeroDestOffset = true;
0147: if (param == null) {
0148: // Use the default
0149: param = (J2KImageReadParam) reader.getDefaultReadParam();
0150: allowZeroDestOffset = false;
0151: } else if (!(param instanceof J2KImageReadParam)) {
0152: // Create a new one
0153: param = new J2KImageReadParamJava(param);
0154: allowZeroDestOffset = false;
0155: }
0156: this .param = (J2KImageReadParam) param;
0157:
0158: decoder = new Decoder(iis);
0159:
0160: decoder.setMode(Constants.JP2K_COMPOSITE_TILE);
0161:
0162: //set resolution before any calling of any calling for decode/decodeSize
0163: int resolution = ((J2KImageReadParam) param).getResolution();
0164: if (resolution != -1)
0165: decoder.setMaxLevels(resolution);
0166:
0167: size = decoder.decodeSize(null);
0168:
0169: compParam = new CompParams();
0170: for (int i = 0; i < size.csize; i++) {
0171: decoder.decodeCompParams(compParam, i);
0172: if (i == 0) {
0173: xStep = compParam.xstep;
0174: yStep = compParam.ystep;
0175: } else if (compParam.xstep != xStep
0176: || compParam.ystep != yStep) {
0177: // All components must have same subsampling along each axis.
0178: throw new IIOException(
0179: "All components must have the same subsampling factors!");
0180: }
0181: }
0182:
0183: // Set source sub-banding.
0184: sourceBands = param.getSourceBands();
0185: if (sourceBands == null) {
0186: nComp = size.csize;
0187: sourceBands = new int[nComp];
0188: for (int i = 0; i < nComp; i++)
0189: sourceBands[i] = i;
0190: } else {
0191: for (int i = 0; i < sourceBands.length; i++) {
0192: if (sourceBands[i] < 0 || sourceBands[i] >= size.csize) {
0193: throw new IIOException("Source band out of range!");
0194: }
0195: }
0196: }
0197:
0198: // Cache number of components.
0199: nComp = sourceBands.length;
0200:
0201: // Set destination sub-banding.
0202: destinationBands = param.getDestinationBands();
0203: if (destinationBands == null) {
0204: destinationBands = new int[nComp];
0205: for (int i = 0; i < nComp; i++)
0206: destinationBands[i] = i;
0207: } else {
0208: for (int i = 0; i < destinationBands.length; i++) {
0209: if (destinationBands[i] < 0
0210: || destinationBands[i] >= size.csize) {
0211: throw new IIOException(
0212: "Destination band out of range!");
0213: }
0214: }
0215: }
0216:
0217: // Check number of source and dest bands.
0218: if (destinationBands.length != sourceBands.length) {
0219: throw new IIOException(
0220: "Number of source and destination bands must be equal!");
0221: }
0222:
0223: this .width = (size.xosize + size.xsize + xStep - 1) / xStep;
0224: this .height = (size.yosize + size.ysize + yStep - 1) / yStep;
0225:
0226: Rectangle sourceRegion = new Rectangle(0, 0, this .width,
0227: this .height);
0228:
0229: originalRegion = (Rectangle) sourceRegion.clone();
0230:
0231: destinationRegion = (Rectangle) sourceRegion.clone();
0232:
0233: J2KImageReader.computeRegionsWrapper(param,
0234: allowZeroDestOffset, this .width, this .height, param
0235: .getDestination(), sourceRegion,
0236: destinationRegion);
0237: scaleX = param.getSourceXSubsampling();
0238: scaleY = param.getSourceYSubsampling();
0239: xOffset = param.getSubsamplingXOffset();
0240: yOffset = param.getSubsamplingYOffset();
0241:
0242: sourceOrigin = new Point(sourceRegion.x, sourceRegion.y);
0243: if (!destinationRegion.equals(originalRegion))
0244: noTransform = false;
0245:
0246: this .tileWidth = (size.xtsize + xStep - 1) / xStep;
0247: this .tileHeight = (size.ytsize + yStep - 1) / yStep;
0248: this .tileGridXOffset = (size.xtosize + xStep - 1) / xStep
0249: - (size.xosize + xStep - 1) / xStep;
0250: this .tileGridYOffset = (size.ytosize + yStep - 1) / yStep
0251: - (size.yosize + yStep - 1) / yStep;
0252:
0253: this .width = destinationRegion.width;
0254: this .height = destinationRegion.height;
0255: this .minX = destinationRegion.x;
0256: this .minY = destinationRegion.y;
0257:
0258: originalSampleModel = createOriginalSampleModel();
0259: sampleModel = createSampleModel();
0260: colorModel = createColorModel();
0261: tileGridXOffset += (XToTileX(minX) - XToTileX(tileGridXOffset))
0262: * tileWidth;
0263: tileGridYOffset += (YToTileY(minY) - YToTileY(tileGridYOffset))
0264: * tileHeight;
0265:
0266: // sets the resolution and decoding rate to the medialib decoder
0267: // Java decoding rate is in bit-per-pixel; the medialib rate is in
0268: // percentage; so convert first.
0269: double rate = ((J2KImageReadParam) param).getDecodingRate();
0270: if (rate != Double.MAX_VALUE) {
0271: // XXX Obtain bits per sample from elsewhere, e.g., ColorModel.
0272: rate /= ImageUtil.getElementSize(sampleModel);
0273: decoder.setRate(rate, 0);
0274: }
0275: }
0276:
0277: public synchronized Raster getTile(int tileX, int tileY) {
0278: if (currentTile != null && currentTileGrid.x == tileX
0279: && currentTileGrid.y == tileY)
0280: return currentTile;
0281:
0282: if (tileX < getMinTileX() || tileY < getMinTileY()
0283: || tileX > getMaxTileX() || tileY > getMaxTileY())
0284: throw new IllegalArgumentException(I18N
0285: .getString("J2KReadState1"));
0286:
0287: int x = tileXToX(tileX);
0288: int y = tileYToY(tileY);
0289: currentTile = Raster.createWritableRaster(sampleModel,
0290: new Point(x, y));
0291:
0292: try {
0293: readAsRaster((WritableRaster) currentTile);
0294: } catch (IOException ioe) {
0295: throw new RuntimeException(ioe);
0296: }
0297:
0298: if (currentTileGrid == null)
0299: currentTileGrid = new Point(tileX, tileY);
0300: else {
0301: currentTileGrid.x = tileX;
0302: currentTileGrid.y = tileY;
0303: }
0304:
0305: return currentTile;
0306: }
0307:
0308: synchronized WritableRaster readAsRaster(WritableRaster raster)
0309: throws IOException {
0310: int x = raster.getMinX();
0311: int y = raster.getMinY();
0312:
0313: try {
0314: if (noTransform) {
0315: int E2c = (size.xosize + xStep - 1) / xStep;
0316: int E1c = (size.yosize + yStep - 1) / yStep;
0317:
0318: int tXStart = ((x + E2c) * xStep - size.xtosize)
0319: / size.xtsize;
0320: int tXEnd = ((x + raster.getWidth() - 1 + E2c) * xStep - size.xtosize)
0321: / size.xtsize;
0322: int tYStart = ((y + E2c) * yStep - size.ytosize)
0323: / size.ytsize;
0324: int tYEnd = ((y + raster.getHeight() - 1 + E2c) * yStep - size.ytosize)
0325: / size.ytsize;
0326:
0327: int sourceFormatTag = MediaLibAccessor
0328: .findCompatibleTag(raster);
0329:
0330: if (tXStart == tXEnd && tYStart == tYEnd) {
0331: MediaLibAccessor accessor = new MediaLibAccessor(
0332: raster, raster.getBounds().intersection(
0333: originalRegion), sourceFormatTag,
0334: true);
0335:
0336: mediaLibImage[] mlImage = accessor
0337: .getMediaLibImages();
0338:
0339: //this image may be a subregion of the image in the stream
0340: // So use the original tile number.
0341: int tileNo = tXStart + tYStart * size.nxtiles;
0342: decoder.decode(mlImage, tileNo);
0343: accessor.copyDataToRaster(channelMap);
0344: } else {
0345: for (int ty = tYStart; ty <= tYEnd; ty++) {
0346: for (int tx = tXStart; tx <= tXEnd; tx++) {
0347: int sx = (size.xtosize + tx * size.xtsize
0348: + xStep - 1)
0349: / xStep - E2c;
0350: int sy = (size.ytosize + ty * size.ytsize
0351: + yStep - 1)
0352: / yStep - E1c;
0353: int ex = (size.xtosize + (tx + 1)
0354: * size.xtsize + xStep - 1)
0355: / xStep - E2c;
0356: int ey = (size.ytosize + (ty + 1)
0357: * size.ytsize + yStep - 1)
0358: / yStep - E1c;
0359: Rectangle subRect = new Rectangle(sx, sy,
0360: ex - sx, ey - sy);
0361: if (subRect.isEmpty()) {
0362: continue;
0363: }
0364: if (rasForATile == null) {
0365: rasForATile = Raster
0366: .createWritableRaster(
0367: originalSampleModel,
0368: null);
0369: }
0370: WritableRaster subRaster = rasForATile
0371: .createWritableChild(rasForATile
0372: .getMinX(), rasForATile
0373: .getMinY(), subRect.width,
0374: subRect.height, subRect.x,
0375: subRect.y, null);
0376: MediaLibAccessor accessor = new MediaLibAccessor(
0377: subRaster, subRect,
0378: sourceFormatTag, true);
0379:
0380: mediaLibImage[] mlImage = accessor
0381: .getMediaLibImages();
0382:
0383: int tileNo = tx + ty * size.nxtiles;
0384: decoder.decode(mlImage, tileNo);
0385: accessor.copyDataToRaster(channelMap);
0386:
0387: Rectangle rasBounds = raster.getBounds();
0388: Rectangle childRect = rasBounds
0389: .intersection(subRect);
0390: if (childRect.isEmpty()) {
0391: continue;
0392: }
0393:
0394: Raster childRaster = subRaster.createChild(
0395: childRect.x, childRect.y,
0396: childRect.width, childRect.height,
0397: childRect.x, childRect.y, null);
0398: ((WritableRaster) raster)
0399: .setRect(childRaster);
0400: }
0401: }
0402: }
0403: } else {
0404: readSubsampledRaster(raster);
0405: }
0406: } catch (IOException e) {
0407: throw new RuntimeException(e);
0408: }
0409:
0410: return raster;
0411: }
0412:
0413: private void readSubsampledRaster(WritableRaster raster)
0414: throws IOException {
0415:
0416: int numBands = sourceBands.length;
0417:
0418: Rectangle destRect = raster.getBounds().intersection(
0419: destinationRegion);
0420:
0421: int offx = destinationRegion.x;
0422: int offy = destinationRegion.y;
0423:
0424: int sourceSX = (destRect.x - offx) * scaleX + sourceOrigin.x;
0425: int sourceSY = (destRect.y - offy) * scaleY + sourceOrigin.y;
0426: int sourceEX = (destRect.width - 1) * scaleX + sourceSX;
0427: int sourceEY = (destRect.height - 1) * scaleY + sourceSY;
0428:
0429: int E2c = (size.xosize + xStep - 1) / xStep;
0430: int E1c = (size.yosize + yStep - 1) / yStep;
0431:
0432: int startXTile = ((sourceSX + E2c) * xStep - size.xtosize)
0433: / size.xtsize;
0434: int endXTile = ((sourceEX + E2c) * xStep - size.xtosize)
0435: / size.xtsize;
0436: int startYTile = ((sourceSY + E1c) * yStep - size.ytosize)
0437: / size.ytsize;
0438: int endYTile = ((sourceEY + E1c) * yStep - size.ytosize)
0439: / size.ytsize;
0440:
0441: startXTile = clip(startXTile, 0, size.nxtiles - 1);
0442: startYTile = clip(startYTile, 0, size.nytiles - 1);
0443: endXTile = clip(endXTile, 0, size.nxtiles - 1);
0444: endYTile = clip(endYTile, 0, size.nytiles - 1);
0445:
0446: int totalXTiles = endXTile - startXTile + 1;
0447: int totalYTiles = endYTile - startYTile + 1;
0448: int totalTiles = totalXTiles * totalYTiles;
0449:
0450: int[] pixbuf = null; // integer buffer for the decoded pixels.
0451:
0452: // Start the data delivery to the cached consumers tile by tile
0453: for (int y = startYTile; y <= endYTile; y++) {
0454: if (reader.getAbortRequest())
0455: break;
0456:
0457: // Loop on horizontal tiles
0458: for (int x = startXTile; x <= endXTile; x++) {
0459: if (reader.getAbortRequest())
0460: break;
0461:
0462: float percentage = // XXX Incorrect?
0463: (x - startXTile + 1.0F + y * totalXTiles) / totalTiles;
0464:
0465: int startX = (x * size.xtsize + size.xtosize + xStep - 1)
0466: / xStep - E2c;
0467: int startY = (y * size.ytsize + size.ytosize + yStep - 1)
0468: / yStep - E1c;
0469: int endX = ((x + 1) * size.xtsize + size.xtosize
0470: + xStep - 1)
0471: / xStep - E2c;
0472: int endY = ((y + 1) * size.ytsize + size.ytosize
0473: + yStep - 1)
0474: / yStep - E1c;
0475:
0476: if (rasForATile == null) {
0477: rasForATile = Raster.createWritableRaster(
0478: originalSampleModel, new Point(startX,
0479: startY));
0480: } else {
0481: rasForATile = rasForATile
0482: .createWritableTranslatedChild(startX,
0483: startY);
0484: }
0485:
0486: int tw = endX - startX;
0487: int th = endY - startY;
0488: WritableRaster targetRas;
0489: if (tw != tileWidth || th != tileHeight) {
0490: targetRas = rasForATile.createWritableChild(startX,
0491: startY, tw, th, startX, startY, null);
0492: } else {
0493: targetRas = rasForATile;
0494: }
0495:
0496: int sourceFormatTag = MediaLibAccessor
0497: .findCompatibleTag(targetRas);
0498:
0499: MediaLibAccessor accessor = new MediaLibAccessor(
0500: targetRas,
0501: // targetRas.getBounds(),
0502: targetRas.getBounds().intersection(
0503: originalRegion), sourceFormatTag, true);
0504:
0505: mediaLibImage[] mlImage = accessor.getMediaLibImages();
0506: decoder.decode(mlImage, x + y * size.nxtiles);
0507: accessor.copyDataToRaster(channelMap);
0508:
0509: int cTileHeight = th;
0510: int cTileWidth = tw;
0511:
0512: if (startY + cTileHeight >= originalRegion.height)
0513: cTileHeight = originalRegion.height - startY;
0514:
0515: if (startX + cTileWidth >= originalRegion.width)
0516: cTileWidth = originalRegion.width - startX;
0517:
0518: int tx = startX;
0519: int ty = startY;
0520:
0521: if (sourceSX > startX) {
0522: cTileWidth += startX - sourceSX;
0523: tx = sourceSX;
0524: startX = sourceSX;
0525: }
0526:
0527: if (sourceSY > startY) {
0528: cTileHeight += startY - sourceSY;
0529: ty = sourceSY;
0530: startY = sourceSY;
0531: }
0532:
0533: if (sourceEX < startX + cTileWidth - 1) {
0534: cTileWidth += sourceEX - startX - cTileWidth + 1;
0535: }
0536:
0537: if (sourceEY < startY + cTileHeight - 1) {
0538: cTileHeight += sourceEY - startY - cTileHeight + 1;
0539: }
0540:
0541: // The start X in the destination
0542: int x1 = (startX + scaleX - 1 - sourceOrigin.x)
0543: / scaleX;
0544: int x2 = (startX + scaleX - 1 + cTileWidth - sourceOrigin.x)
0545: / scaleX;
0546: int lineLength = x2 - x1;
0547: // Suppress further processing if lineLength is non-positive
0548: // XXX (which it should never be).
0549: if (lineLength <= 0)
0550: continue;
0551: x2 = (x2 - 1) * scaleX + sourceOrigin.x;
0552:
0553: int y1 = (startY + scaleY - 1 - sourceOrigin.y)
0554: / scaleY;
0555: startY = y1 * scaleY + sourceOrigin.y;
0556: startX = x1 * scaleX + sourceOrigin.x;
0557:
0558: x1 += offx;
0559: y1 += offy;
0560:
0561: if (pixbuf == null || pixbuf.length < lineLength)
0562: pixbuf = new int[lineLength]; // line buffer for pixel data
0563:
0564: // Deliver in lines to reduce memory usage
0565: for (int l = startY, m = y1; l < ty + cTileHeight; l += scaleY, m++) {
0566: if (reader.getAbortRequest())
0567: break;
0568:
0569: // Request line data
0570: for (int i = 0; i < numBands; i++) {
0571: for (int j = lineLength - 1, k1 = x2; j >= 0; j--, k1 -= scaleX) {
0572: pixbuf[j] = targetRas.getSample(k1, l, i);
0573: }
0574:
0575: // Send the line data to the BufferedImage
0576: raster.setSamples(x1, m, lineLength, 1,
0577: destinationBands[i], pixbuf);
0578: }
0579:
0580: if (destImage != null)
0581: reader.processImageUpdateWrapper(destImage, x1,
0582: m, cTileWidth, 1, 1, 1,
0583: destinationBands);
0584:
0585: reader.processImageProgressWrapper(percentage
0586: + (l - startY + 1.0F) / cTileHeight
0587: / totalTiles);
0588: }
0589: } // End loop on horizontal tiles
0590: } // End loop on vertical tiles
0591: }
0592:
0593: public void setDestImage(BufferedImage image) {
0594: destImage = image;
0595: }
0596:
0597: public void clearDestImage() {
0598: destImage = null;
0599: }
0600:
0601: private int getTileNum(int x, int y) {
0602: int num = (y - getMinTileY()) * getNumXTiles() + x
0603: - getMinTileX();
0604:
0605: if (num < 0 || num >= getNumXTiles() * getNumYTiles())
0606: throw new IllegalArgumentException(I18N
0607: .getString("J2KReadState1"));
0608:
0609: return num;
0610: }
0611:
0612: private int clip(int value, int min, int max) {
0613: if (value < min)
0614: value = min;
0615: if (value > max)
0616: value = max;
0617: return value;
0618: }
0619:
0620: private SampleModel createSampleModel() throws IOException {
0621: if (sampleModel != null)
0622: return sampleModel;
0623:
0624: if (metadata == null)
0625: readImageMetadata();
0626:
0627: HeaderBox header = (HeaderBox) metadata
0628: .getElement("JPEG2000HeaderBox");
0629: int maxDepth = 0;
0630: boolean isSigned = false;
0631: if (header != null) {
0632: maxDepth = header.getBitDepth();
0633: isSigned = (maxDepth & 0x80) > 0;
0634: maxDepth = (maxDepth & 0x7F) + 1;
0635: } else {
0636: CompParams compParam = new CompParams();
0637: for (int i = 0; i < size.csize; i++) {
0638: decoder.decodeCompParams(compParam, i);
0639: maxDepth = (compParam.depth & 0x7F) + 1;
0640: isSigned = (compParam.depth & 0x80) > 0 ? true : false;
0641: }
0642: }
0643:
0644: BitsPerComponentBox bits = (BitsPerComponentBox) metadata
0645: .getElement("JPEG2000BitsPerComponentBox");
0646:
0647: if (bits != null) {
0648: byte[] depths = bits.getBitDepth();
0649: maxDepth = (depths[0] & 0x7F) + 1;
0650: isSigned = (depths[0] & 0x80) > 0;
0651: for (int i = 1; i < nComp; i++)
0652: if (maxDepth > depths[sourceBands[i]])
0653: maxDepth = (depths[sourceBands[i]] & 0x7F) + 1;
0654: }
0655:
0656: int[] bandOffsets = new int[nComp];
0657: for (int i = 0; i < nComp; i++)
0658: bandOffsets[i] = i;
0659:
0660: ChannelDefinitionBox cdb = (ChannelDefinitionBox) metadata
0661: .getElement("JPEG2000ChannelDefinitionBox");
0662:
0663: if (cdb != null
0664: && metadata.getElement("JPEG2000PaletteBox") == null) {
0665: short[] assoc = cdb.getAssociation();
0666: short[] types = cdb.getTypes();
0667: short[] channels = cdb.getChannel();
0668:
0669: for (int i = 0; i < types.length; i++)
0670: if (types[i] == 0)
0671: bandOffsets[sourceBands[channels[i]]] = assoc[i] - 1;
0672: else if (types[i] == 1 || types[i] == 2)
0673: bandOffsets[sourceBands[channels[i]]] = channels[i];
0674: }
0675:
0676: return createSampleModel(nComp, maxDepth, bandOffsets,
0677: isSigned, tileWidth, tileHeight);
0678: }
0679:
0680: private SampleModel createOriginalSampleModel() throws IOException {
0681: if (metadata == null)
0682: readImageMetadata();
0683:
0684: HeaderBox header = (HeaderBox) metadata
0685: .getElement("JPEG2000HeaderBox");
0686: int maxDepth = 0;
0687: boolean isSigned = false;
0688: int nc = size.csize;
0689: if (header != null) {
0690: maxDepth = header.getBitDepth();
0691: isSigned = (maxDepth & 0x80) > 0;
0692: maxDepth = (maxDepth & 0x7F) + 1;
0693: } else {
0694: CompParams compParam = new CompParams();
0695: for (int i = 0; i < size.csize; i++) {
0696: decoder.decodeCompParams(compParam, i);
0697: maxDepth = (compParam.depth & 0x7F) + 1;
0698: isSigned = (compParam.depth & 0x80) > 0 ? true : false;
0699: }
0700: }
0701:
0702: BitsPerComponentBox bits = (BitsPerComponentBox) metadata
0703: .getElement("JPEG2000BitsPerComponentBox");
0704:
0705: if (bits != null) {
0706: byte[] depths = bits.getBitDepth();
0707: maxDepth = (depths[0] & 0x7F) + 1;
0708: isSigned = (depths[0] & 0x80) > 0;
0709: for (int i = 1; i < nc; i++)
0710: if (maxDepth > depths[i])
0711: maxDepth = (depths[i] & 0x7F) + 1;
0712: }
0713:
0714: int[] bandOffsets = new int[nc];
0715: for (int i = 0; i < nc; i++)
0716: bandOffsets[i] = i;
0717:
0718: ChannelDefinitionBox cdb = (ChannelDefinitionBox) metadata
0719: .getElement("JPEG2000ChannelDefinitionBox");
0720: if (cdb != null
0721: && metadata.getElement("JPEG2000PaletteBox") == null) {
0722: short[] assoc = cdb.getAssociation();
0723: short[] types = cdb.getTypes();
0724: short[] channels = cdb.getChannel();
0725:
0726: channelMap = new int[nc];
0727:
0728: for (int i = 0; i < types.length; i++)
0729: if (types[i] == 0) {
0730: bandOffsets[channels[i]] = assoc[i] - 1;
0731: channelMap[assoc[i] - 1] = channels[i];
0732: } else if (types[i] == 1 || types[i] == 2) {
0733: bandOffsets[channels[i]] = channels[i];
0734: channelMap[channels[i]] = channels[i];
0735: }
0736: }
0737:
0738: return createSampleModel(nc, maxDepth, bandOffsets, isSigned,
0739: tileWidth, tileHeight);
0740: }
0741:
0742: private SampleModel createSampleModel(int nc, int maxDepth,
0743: int[] bandOffsets, boolean isSigned, int tw, int th) {
0744: SampleModel sm = null;
0745: if (nc == 1
0746: && (maxDepth == 1 || maxDepth == 2 || maxDepth == 4))
0747: sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
0748: tw, th, maxDepth);
0749: else if (maxDepth <= 8)
0750: sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
0751: tw, th, nc, tw * nc, bandOffsets);
0752: else if (maxDepth <= 16)
0753: sm = new PixelInterleavedSampleModel(
0754: isSigned ? DataBuffer.TYPE_SHORT
0755: : DataBuffer.TYPE_USHORT, tw, th, nc, tw
0756: * nc, bandOffsets);
0757: else if (maxDepth <= 32)
0758: sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_INT,
0759: tw, th, nComp, tw * nComp, bandOffsets);
0760: else
0761: throw new IllegalArgumentException(I18N
0762: .getString("J2KReadState11")
0763: + " " + +maxDepth);
0764:
0765: return sm;
0766: }
0767:
0768: private ColorModel createColorModel() throws IOException {
0769: if (colorModel != null)
0770: return colorModel;
0771:
0772: PaletteBox pBox = (PaletteBox) metadata
0773: .getElement("JPEG2000PaletteBox");
0774: ChannelDefinitionBox cdef = (ChannelDefinitionBox) metadata
0775: .getElement("JPEG2000ChannelDefinitionBox");
0776:
0777: // Check 'nComp' instance variable here in case there is an
0778: // embedded palette such as in the pngsuite images pp0n2c16.png
0779: // and pp0n6a08.png.
0780: if (pBox != null && nComp == 1) {
0781: byte[][] lut = pBox.getLUT();
0782: int numComp = pBox.getNumComp();
0783:
0784: int[] mapping = new int[numComp];
0785:
0786: for (int i = 0; i < numComp; i++)
0787: mapping[i] = i;
0788:
0789: ComponentMappingBox cmap = (ComponentMappingBox) metadata
0790: .getElement("JPEG2000ComponentMappingBox");
0791:
0792: short[] comps = null;
0793: byte[] type = null;
0794: byte[] maps = null;
0795:
0796: if (cmap != null) {
0797: comps = cmap.getComponent();
0798: type = cmap.getComponentType();
0799: maps = cmap.getComponentAssociation();
0800: }
0801:
0802: if (comps != null)
0803: for (int i = 0; i < numComp; i++)
0804: if (type[i] == 1)
0805: mapping[i] = maps[i];
0806:
0807: if (numComp == 3)
0808: colorModel = new IndexColorModel(sampleModel
0809: .getSampleSize(0), lut[0].length,
0810: lut[mapping[0]], lut[mapping[1]],
0811: lut[mapping[2]]);
0812: else if (numComp == 4)
0813: colorModel = new IndexColorModel(sampleModel
0814: .getSampleSize(0), lut[0].length,
0815: lut[mapping[0]], lut[mapping[1]],
0816: lut[mapping[2]], lut[mapping[3]]);
0817: } else if (cdef != null) {
0818: HeaderBox header = (HeaderBox) metadata
0819: .getElement("JPEG2000HeaderBox");
0820: int numComp = header.getNumComponents();
0821: int bitDepth = header.getBitDepth();
0822:
0823: boolean hasAlpha = false;
0824: int alphaChannel = numComp - 1;
0825:
0826: short[] channels = cdef.getChannel();
0827: short[] cType = cdef.getTypes();
0828: short[] associations = cdef.getAssociation();
0829:
0830: for (int i = 0; i < channels.length; i++) {
0831: if (cType[i] == 1 && channels[i] == alphaChannel)
0832: hasAlpha = true;
0833: }
0834:
0835: boolean[] isPremultiplied = new boolean[] { false };
0836:
0837: if (hasAlpha) {
0838: isPremultiplied = new boolean[alphaChannel];
0839:
0840: for (int i = 0; i < alphaChannel; i++)
0841: isPremultiplied[i] = false;
0842:
0843: for (int i = 0; i < channels.length; i++) {
0844: if (cType[i] == 2)
0845: isPremultiplied[associations[i] - 1] = true;
0846: }
0847:
0848: for (int i = 1; i < alphaChannel; i++)
0849: isPremultiplied[0] &= isPremultiplied[i];
0850: }
0851:
0852: ColorSpecificationBox cBox = (ColorSpecificationBox) metadata
0853: .getElement("JPEG2000ColorSpecificationBox");
0854: ICC_Profile profile = null;
0855: int colorSpaceType = 0;
0856:
0857: if (cBox != null) {
0858: profile = cBox.getICCProfile();
0859: colorSpaceType = cBox.getEnumeratedColorSpace();
0860: }
0861:
0862: ColorSpace cs = null;
0863: if (profile != null)
0864: cs = new ICC_ColorSpace(profile);
0865: else if (colorSpaceType == ColorSpecificationBox.ECS_sRGB)
0866: cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
0867: else if (colorSpaceType == ColorSpecificationBox.ECS_GRAY)
0868: cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
0869: else if (colorSpaceType == ColorSpecificationBox.ECS_YCC)
0870: cs = ColorSpace.getInstance(ColorSpace.CS_PYCC);
0871:
0872: byte[] bitDepths = null;
0873: boolean isSigned = ((bitDepth & 0x80) == 0x80) ? true
0874: : false;
0875:
0876: BitsPerComponentBox bitBox = (BitsPerComponentBox) metadata
0877: .getElement("JPEG2000BitsPerComponentBox");
0878: if (bitBox != null)
0879: bitDepths = bitBox.getBitDepth();
0880:
0881: int[] bits = new int[numComp];
0882: for (int i = 0; i < numComp; i++)
0883: if (bitDepths != null)
0884: bits[i] = (bitDepths[i] & 0x7F) + 1;
0885: else
0886: bits[i] = (bitDepth & 0x7F) + 1;
0887:
0888: int maxBitDepth = 1 + (bitDepth & 0x7F);
0889: if (bitDepths != null)
0890: for (int i = 0; i < numComp; i++)
0891: if (bits[i] > maxBitDepth)
0892: maxBitDepth = bits[i];
0893:
0894: int type = -1;
0895:
0896: if (maxBitDepth <= 8)
0897: type = DataBuffer.TYPE_BYTE;
0898: else if (maxBitDepth <= 16)
0899: type = isSigned ? DataBuffer.TYPE_SHORT
0900: : DataBuffer.TYPE_USHORT;
0901: else if (maxBitDepth <= 32)
0902: type = DataBuffer.TYPE_INT;
0903:
0904: if (type == -1)
0905: return null;
0906:
0907: if (cs != null) {
0908: colorModel = new ComponentColorModel(cs, bits,
0909: hasAlpha, isPremultiplied[0],
0910: hasAlpha ? Transparency.TRANSLUCENT
0911: : Transparency.OPAQUE, type);
0912: }
0913: }
0914:
0915: if (colorModel != null)
0916: return colorModel;
0917:
0918: if (nComp <= 4) {
0919: // XXX: Code essentially duplicated from FileFormatReader.getColorModel().
0920: // Create the ColorModel from the SIZ marker segment parameters.
0921: ColorSpace cs;
0922: if (nComp > 2) {
0923: cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
0924: } else {
0925: cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
0926: }
0927:
0928: int[] bitsPerComponent = new int[nComp];
0929: boolean isSigned = false;
0930: int maxBitDepth = -1;
0931: for (int i = 0; i < nComp; i++) {
0932: bitsPerComponent[i] = (compParam.depth & 0x7f) + 1;
0933: if (maxBitDepth < bitsPerComponent[i]) {
0934: maxBitDepth = bitsPerComponent[i];
0935: }
0936: isSigned |= (compParam.depth & 0x80) != 0;
0937: }
0938:
0939: boolean hasAlpha = nComp % 2 == 0;
0940:
0941: int type = -1;
0942:
0943: if (maxBitDepth <= 8) {
0944: type = DataBuffer.TYPE_BYTE;
0945: } else if (maxBitDepth <= 16) {
0946: type = isSigned ? DataBuffer.TYPE_SHORT
0947: : DataBuffer.TYPE_USHORT;
0948: } else if (maxBitDepth <= 32) {
0949: type = DataBuffer.TYPE_INT;
0950: }
0951:
0952: if (type != -1) {
0953: if (nComp == 1
0954: && (maxBitDepth == 1 || maxBitDepth == 2 || maxBitDepth == 4)) {
0955: colorModel = ImageUtil
0956: .createColorModel(getSampleModel());
0957: } else {
0958: colorModel = new ComponentColorModel(cs,
0959: bitsPerComponent, hasAlpha, false,
0960: hasAlpha ? Transparency.TRANSLUCENT
0961: : Transparency.OPAQUE, type);
0962: }
0963:
0964: return colorModel;
0965: }
0966: }
0967:
0968: return ImageUtil.createColorModel(null, getSampleModel());
0969: }
0970:
0971: public J2KMetadata readImageMetadata() throws IOException {
0972: if (metadata == null) {
0973: metadata = new J2KMetadata();
0974: com.sun.medialib.codec.jp2k.Box mlibBox = null;
0975: com.sun.media.imageioimpl.plugins.jpeg2000.Box box = null;
0976:
0977: while ((mlibBox = decoder.decodeBox()) != null) {
0978: box = null;
0979: Class c = com.sun.media.imageioimpl.plugins.jpeg2000.Box
0980: .getBoxClass(mlibBox.type);
0981: if (c != null) {
0982: try {
0983: Constructor cons = c
0984: .getConstructor(new Class[] { byte[].class });
0985: if (cons != null) {
0986: box = (Box) cons
0987: .newInstance(new Object[] { mlibBox.data });
0988: }
0989: } catch (NoSuchMethodException e) {
0990: try {
0991: Constructor cons = c
0992: .getConstructor(new Class[] {
0993: int.class, byte[].class });
0994: if (cons != null) {
0995: box = (com.sun.media.imageioimpl.plugins.jpeg2000.Box) cons
0996: .newInstance(new Object[] {
0997: new Integer(
0998: mlibBox.type),
0999: mlibBox.data });
1000: }
1001: } catch (NoSuchMethodException e1) {
1002: box = createUnknowBox(mlibBox);
1003: } catch (InvocationTargetException e1) {
1004: box = createUnknowBox(mlibBox);
1005: } catch (IllegalAccessException e1) {
1006: box = createUnknowBox(mlibBox);
1007: } catch (InstantiationException e1) {
1008: box = createUnknowBox(mlibBox);
1009: }
1010: } catch (InvocationTargetException e) {
1011: box = createUnknowBox(mlibBox);
1012: } catch (IllegalAccessException e) {
1013: box = createUnknowBox(mlibBox);
1014: } catch (InstantiationException e) {
1015: box = createUnknowBox(mlibBox);
1016: }
1017: } else {
1018: if (mlibBox.data != null)
1019: box = createUnknowBox(mlibBox);
1020: }
1021: if (box != null)
1022: metadata.addNode(box);
1023: }
1024: }
1025:
1026: return metadata;
1027: }
1028:
1029: private com.sun.media.imageioimpl.plugins.jpeg2000.Box createUnknowBox(
1030: com.sun.medialib.codec.jp2k.Box mlibBox) {
1031: return new com.sun.media.imageioimpl.plugins.jpeg2000.Box(
1032: 8 + mlibBox.data.length, mlibBox.type, mlibBox.data);
1033: }
1034: }
|