0001: /*
0002: * $RCSfile: J2KReadState.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.8 $
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.ImageReader;
0049: import javax.imageio.ImageReadParam;
0050: import javax.imageio.ImageTypeSpecifier;
0051: import javax.imageio.metadata.IIOMetadata;
0052: import javax.imageio.spi.ImageReaderSpi;
0053: import javax.imageio.stream.ImageInputStream;
0054:
0055: import java.awt.Point;
0056: import java.awt.Rectangle;
0057: import java.awt.Transparency;
0058: import java.awt.color.ColorSpace;
0059: import java.awt.image.BufferedImage;
0060: import java.awt.image.DataBuffer;
0061: import java.awt.image.DataBufferByte;
0062: import java.awt.image.ColorModel;
0063: import java.awt.image.ComponentColorModel;
0064: import java.awt.image.ComponentSampleModel;
0065: import java.awt.image.DirectColorModel;
0066: import java.awt.image.IndexColorModel;
0067: import java.awt.image.MultiPixelPackedSampleModel;
0068: import java.awt.image.PixelInterleavedSampleModel;
0069: import java.awt.image.Raster;
0070: import java.awt.image.RenderedImage;
0071: import java.awt.image.SampleModel;
0072: import java.awt.image.SinglePixelPackedSampleModel;
0073: import java.awt.image.WritableRaster;
0074:
0075: import java.io.*;
0076: import java.util.ArrayList;
0077: import java.util.List;
0078: import java.util.Hashtable;
0079: import java.util.Iterator;
0080:
0081: import jj2000.j2k.quantization.dequantizer.*;
0082: import jj2000.j2k.wavelet.synthesis.*;
0083: import jj2000.j2k.image.invcomptransf.*;
0084: import jj2000.j2k.fileformat.reader.*;
0085: import jj2000.j2k.codestream.reader.*;
0086: import jj2000.j2k.entropy.decoder.*;
0087: import jj2000.j2k.codestream.*;
0088: import jj2000.j2k.decoder.*;
0089: import jj2000.j2k.image.*;
0090: import jj2000.j2k.util.*;
0091: import jj2000.j2k.roi.*;
0092: import jj2000.j2k.io.*;
0093: import jj2000.j2k.*;
0094:
0095: import com.sun.media.imageioimpl.common.ImageUtil;
0096:
0097: public class J2KReadState {
0098: /** The input stream we read from */
0099: private ImageInputStream iis = null;
0100:
0101: private FileFormatReader ff;
0102: private HeaderInfo hi;
0103: private HeaderDecoder hd;
0104: private RandomAccessIO in;
0105: private BitstreamReaderAgent breader;
0106: private EntropyDecoder entdec;
0107: private ROIDeScaler roids;
0108: private Dequantizer deq;
0109: private InverseWT invWT;
0110: private InvCompTransf ictransf;
0111: private ImgDataConverter converter, converter2;
0112: private DecoderSpecs decSpec = null;
0113: private J2KImageReadParamJava j2krparam = null;
0114: private int[] destinationBands = null;
0115: private int[] sourceBands = null;
0116:
0117: private int[] levelShift = null; // level shift for each component
0118: private int[] minValues = null; // The min values
0119: private int[] maxValues = null; // The max values
0120: private int[] fracBits = null; // fractional bits for each component
0121: private DataBlkInt[] dataBlocks = null; // data-blocks to request data from src
0122:
0123: private int[] bandOffsets = null;
0124: private int maxDepth = 0;
0125: private boolean isSigned = false;
0126:
0127: private ColorModel colorModel = null;
0128: private SampleModel sampleModel = null;
0129: private int nComp = 0;
0130: private int tileWidth = 0;
0131: private int tileHeight = 0;
0132:
0133: /** Source to destination transform */
0134: private int scaleX, scaleY, xOffset, yOffset;
0135: private Rectangle destinationRegion = null;
0136: private Point sourceOrigin;
0137:
0138: /** Tile grid offsets of the source, also used for destination. */
0139: private int tileXOffset, tileYOffset;
0140:
0141: private int width;
0142: private int height;
0143: private int[] pixbuf = null;
0144: private byte[] bytebuf = null;
0145: private int[] channelMap = null;
0146:
0147: private boolean noTransform = true;
0148:
0149: /** The resolution level requested. */
0150: private int resolution;
0151:
0152: /** The subsampling step sizes. */
0153: private int stepX, stepY;
0154:
0155: /** Tile step sizes. */
0156: private int tileStepX, tileStepY;
0157:
0158: private J2KMetadata metadata;
0159:
0160: private BufferedImage destImage;
0161:
0162: /** Cache the <code>J2KImageReader</code> which creates this object. This
0163: * variable is used to monitor the abortion.
0164: */
0165: private J2KImageReader reader;
0166:
0167: /** Constructs <code>J2KReadState</code>.
0168: * @param iis The input stream.
0169: * @param param The reading parameters.
0170: * @param metadata The <code>J2KMetadata</code> to cache the metadata read
0171: * from the input stream.
0172: * @param reader The <code>J2KImageReader</code> which holds this state.
0173: * It is necessary for processing abortion.
0174: * @throw IllegalArgumentException If the provided <code>iis</code>,
0175: * <code>param</code> or <code>metadata</code> is <code>null</code>.
0176: */
0177: public J2KReadState(ImageInputStream iis,
0178: J2KImageReadParamJava param, J2KMetadata metadata,
0179: J2KImageReader reader) {
0180: if (iis == null || param == null || metadata == null)
0181: throw new IllegalArgumentException(I18N
0182: .getString("J2KReadState0"));
0183:
0184: this .iis = iis;
0185: this .j2krparam = param;
0186: this .metadata = metadata;
0187: this .reader = reader;
0188:
0189: initializeRead(0, param, metadata);
0190: }
0191:
0192: /** Constructs <code>J2KReadState</code>.
0193: * @param iis The input stream.
0194: * @param param The reading parameters.
0195: * @param reader The <code>J2KImageReader</code> which holds this state.
0196: * It is necessary for processing abortion.
0197: * @throw IllegalArgumentException If the provided <code>iis</code>,
0198: * or <code>param</code> is <code>null</code>.
0199: */
0200: public J2KReadState(ImageInputStream iis,
0201: J2KImageReadParamJava param, J2KImageReader reader) {
0202: if (iis == null || param == null)
0203: throw new IllegalArgumentException(I18N
0204: .getString("J2KReadState0"));
0205:
0206: this .iis = iis;
0207: this .j2krparam = param;
0208: this .reader = reader;
0209: initializeRead(0, param, null);
0210: }
0211:
0212: public int getWidth() throws IOException {
0213: return width;
0214: }
0215:
0216: public int getHeight() throws IOException {
0217: return height;
0218: }
0219:
0220: public HeaderDecoder getHeader() {
0221: return hd;
0222: }
0223:
0224: public Raster getTile(int tileX, int tileY, WritableRaster raster)
0225: throws IOException {
0226: Point nT = ictransf.getNumTiles(null);
0227:
0228: if (noTransform) {
0229: if (tileX >= nT.x || tileY >= nT.y)
0230: throw new IllegalArgumentException(I18N
0231: .getString("J2KImageReader0"));
0232:
0233: ictransf.setTile(tileX * tileStepX, tileY * tileStepY);
0234:
0235: // The offset of the active tiles is the same for all components,
0236: // since we don't support different component dimensions.
0237: int tOffx;
0238: int tOffy;
0239: int cTileWidth;
0240: int cTileHeight;
0241: if (raster != null
0242: && (this .resolution < hd.getDecoderSpecs().dls
0243: .getMin()) || stepX != 1 || stepY != 1) {
0244: tOffx = raster.getMinX();
0245: tOffy = raster.getMinY();
0246: cTileWidth = Math.min(raster.getWidth(), ictransf
0247: .getTileWidth());
0248: cTileHeight = Math.min(raster.getHeight(), ictransf
0249: .getTileHeight());
0250: } else {
0251: tOffx = ictransf.getCompULX(0)
0252: - (ictransf.getImgULX()
0253: + ictransf.getCompSubsX(0) - 1)
0254: / ictransf.getCompSubsX(0)
0255: + destinationRegion.x;
0256: tOffy = ictransf.getCompULY(0)
0257: - (ictransf.getImgULY()
0258: + ictransf.getCompSubsY(0) - 1)
0259: / ictransf.getCompSubsY(0)
0260: + destinationRegion.y;
0261: cTileWidth = ictransf.getTileWidth();
0262: cTileHeight = ictransf.getTileHeight();
0263: }
0264:
0265: if (raster == null)
0266: raster = Raster.createWritableRaster(sampleModel,
0267: new Point(tOffx, tOffy));
0268:
0269: int numBands = sampleModel.getNumBands();
0270:
0271: if (tOffx + cTileWidth >= destinationRegion.width
0272: + destinationRegion.x)
0273: cTileWidth = destinationRegion.width
0274: + destinationRegion.x - tOffx;
0275:
0276: if (tOffy + cTileHeight >= destinationRegion.height
0277: + destinationRegion.y)
0278: cTileHeight = destinationRegion.height
0279: + destinationRegion.y - tOffy;
0280:
0281: //create the line buffer for pixel data if it is not large enough
0282: // or null
0283: if (pixbuf == null || pixbuf.length < cTileWidth * numBands)
0284: pixbuf = new int[cTileWidth * numBands];
0285: boolean prog = false;
0286:
0287: // Deliver in lines to reduce memory usage
0288: for (int l = 0; l < cTileHeight; l++) {
0289: if (reader.getAbortRequest())
0290: break;
0291:
0292: // Request line data
0293: for (int i = 0; i < numBands; i++) {
0294: if (reader.getAbortRequest())
0295: break;
0296: DataBlkInt db = dataBlocks[i];
0297: db.ulx = 0;
0298: db.uly = l;
0299: db.w = cTileWidth;
0300: db.h = 1;
0301: ictransf.getInternCompData(db,
0302: channelMap[sourceBands[i]]);
0303: prog = prog || db.progressive;
0304:
0305: int[] data = db.data;
0306: int k1 = db.offset + cTileWidth - 1;
0307:
0308: int fracBit = fracBits[i];
0309: int lS = levelShift[i];
0310: int min = minValues[i];
0311: int max = maxValues[i];
0312:
0313: if (ImageUtil.isBinary(sampleModel)) {
0314: // Force min max to 0 and 1.
0315: min = 0;
0316: max = 1;
0317: if (bytebuf == null
0318: || bytebuf.length < cTileWidth
0319: * numBands)
0320: bytebuf = new byte[cTileWidth * numBands];
0321: for (int j = cTileWidth - 1; j >= 0; j--) {
0322: int tmp = (data[k1--] >> fracBit) + lS;
0323: bytebuf[j] = (byte) ((tmp < min) ? min
0324: : ((tmp > max) ? max : tmp));
0325: }
0326:
0327: ImageUtil.setUnpackedBinaryData(bytebuf,
0328: raster, new Rectangle(tOffx, tOffy + l,
0329: cTileWidth, 1));
0330: } else {
0331:
0332: for (int j = cTileWidth - 1; j >= 0; j--) {
0333: int tmp = (data[k1--] >> fracBit) + lS;
0334: pixbuf[j] = (tmp < min) ? min
0335: : ((tmp > max) ? max : tmp);
0336: }
0337:
0338: raster.setSamples(tOffx, tOffy + l, cTileWidth,
0339: 1, destinationBands[i], pixbuf);
0340: }
0341: }
0342: }
0343: } else {
0344: readSubsampledRaster(raster);
0345: }
0346:
0347: return raster;
0348: }
0349:
0350: public Rectangle getDestinationRegion() {
0351: return destinationRegion;
0352: }
0353:
0354: public BufferedImage readBufferedImage() throws IOException {
0355: colorModel = getColorModel();
0356: sampleModel = getSampleModel();
0357: WritableRaster raster = null;
0358: BufferedImage image = j2krparam.getDestination();
0359:
0360: int x = destinationRegion.x;
0361: int y = destinationRegion.y;
0362: destinationRegion.setLocation(j2krparam.getDestinationOffset());
0363: if (image == null) {
0364: // If the destination type is specified, use the color model of it.
0365: ImageTypeSpecifier type = j2krparam.getDestinationType();
0366: if (type != null)
0367: colorModel = type.getColorModel();
0368:
0369: raster = Raster.createWritableRaster(sampleModel
0370: .createCompatibleSampleModel(destinationRegion.x
0371: + destinationRegion.width,
0372: destinationRegion.y
0373: + destinationRegion.height),
0374: new Point(0, 0));
0375: image = new BufferedImage(colorModel, raster, colorModel
0376: .isAlphaPremultiplied(), new Hashtable());
0377: } else
0378: raster = image.getWritableTile(0, 0);
0379:
0380: destImage = image;
0381: readSubsampledRaster(raster);
0382: destinationRegion.setLocation(x, y);
0383: destImage = null;
0384: return image;
0385: }
0386:
0387: public Raster readAsRaster() throws IOException {
0388: BufferedImage image = j2krparam.getDestination();
0389: WritableRaster raster = null;
0390:
0391: if (image == null) {
0392: raster = Raster.createWritableRaster(sampleModel
0393: .createCompatibleSampleModel(destinationRegion.x
0394: + destinationRegion.width,
0395: destinationRegion.y
0396: + destinationRegion.height),
0397: new Point(0, 0));
0398: } else
0399: raster = image.getWritableTile(0, 0);
0400:
0401: readSubsampledRaster(raster);
0402: return raster;
0403: }
0404:
0405: private void initializeRead(int imageIndex,
0406: J2KImageReadParamJava param, J2KMetadata metadata) {
0407: try {
0408: iis.mark();
0409: in = new IISRandomAccessIO(iis);
0410:
0411: // **** File Format ****
0412: // If the codestream is wrapped in the jp2 fileformat, Read the
0413: // file format wrapper
0414: ff = new FileFormatReader(in, metadata);
0415: ff.readFileFormat();
0416: in.seek(ff.getFirstCodeStreamPos());
0417:
0418: hi = new HeaderInfo();
0419: try {
0420: hd = new HeaderDecoder(in, j2krparam, hi);
0421: } catch (EOFException e) {
0422: throw new RuntimeException(I18N
0423: .getString("J2KReadState2"));
0424: } catch (IOException ioe) {
0425: throw new RuntimeException(ioe);
0426: }
0427:
0428: this .width = hd.getImgWidth();
0429: this .height = hd.getImgHeight();
0430:
0431: Rectangle sourceRegion = param.getSourceRegion();
0432: sourceOrigin = new Point();
0433: sourceRegion = new Rectangle(hd.getImgULX(),
0434: hd.getImgULY(), this .width, this .height);
0435:
0436: // if the subsample rate for components are not consistent
0437: boolean compConsistent = true;
0438: stepX = hd.getCompSubsX(0);
0439: stepY = hd.getCompSubsY(0);
0440: for (int i = 1; i < nComp; i++) {
0441: if (stepX != hd.getCompSubsX(i)
0442: || stepY != hd.getCompSubsY(i))
0443: throw new RuntimeException(I18N
0444: .getString("J2KReadState12"));
0445: }
0446:
0447: // Get minimum number of resolution levels available across
0448: // all tile-components.
0449: int minResLevels = hd.getDecoderSpecs().dls.getMin();
0450:
0451: // Set current resolution level.
0452: this .resolution = param != null ? param.getResolution()
0453: : minResLevels;
0454: if (resolution < 0 || resolution > minResLevels) {
0455: resolution = minResLevels;
0456: }
0457:
0458: // Convert source region to lower resolution level.
0459: if (resolution != minResLevels || stepX != 1 || stepY != 1) {
0460: sourceRegion = J2KImageReader.getReducedRect(
0461: sourceRegion, minResLevels, resolution, stepX,
0462: stepY);
0463: }
0464:
0465: destinationRegion = (Rectangle) sourceRegion.clone();
0466:
0467: J2KImageReader.computeRegionsWrapper(param, false,
0468: this .width, this .height, param.getDestination(),
0469: sourceRegion, destinationRegion);
0470:
0471: sourceOrigin = new Point(sourceRegion.x, sourceRegion.y);
0472: scaleX = param.getSourceXSubsampling();
0473: scaleY = param.getSourceYSubsampling();
0474: xOffset = param.getSubsamplingXOffset();
0475: yOffset = param.getSubsamplingYOffset();
0476:
0477: this .width = destinationRegion.width;
0478: this .height = destinationRegion.height;
0479:
0480: Point tileOffset = hd.getTilingOrigin(null);
0481:
0482: this .tileWidth = hd.getNomTileWidth();
0483: this .tileHeight = hd.getNomTileHeight();
0484:
0485: // Convert tile 0 to lower resolution level.
0486: if (resolution != minResLevels || stepX != 1 || stepY != 1) {
0487: Rectangle tileRect = new Rectangle(tileOffset);
0488: tileRect.width = tileWidth;
0489: tileRect.height = tileHeight;
0490: tileRect = J2KImageReader.getReducedRect(tileRect,
0491: minResLevels, resolution, stepX, stepY);
0492: tileOffset = tileRect.getLocation();
0493: tileWidth = tileRect.width;
0494: tileHeight = tileRect.height;
0495: }
0496:
0497: tileXOffset = tileOffset.x;
0498: tileYOffset = tileOffset.y;
0499:
0500: // Set the tile step sizes. These values are used because it
0501: // is possible that tiles will be empty. In particular at lower
0502: // resolution levels when subsampling is used this may be the
0503: // case. This method of calculation will work at least for
0504: // Profile-0 images.
0505: if (tileWidth * (1 << (minResLevels - resolution)) * stepX > hd
0506: .getNomTileWidth()) {
0507: tileStepX = (tileWidth
0508: * (1 << (minResLevels - resolution)) * stepX
0509: + hd.getNomTileWidth() - 1)
0510: / hd.getNomTileWidth();
0511: } else {
0512: tileStepX = 1;
0513: }
0514:
0515: if (tileHeight * (1 << (minResLevels - resolution)) * stepY > hd
0516: .getNomTileHeight()) {
0517: tileStepY = (tileHeight
0518: * (1 << (minResLevels - resolution)) * stepY
0519: + hd.getNomTileHeight() - 1)
0520: / hd.getNomTileHeight();
0521: } else {
0522: tileStepY = 1;
0523: }
0524:
0525: if (!destinationRegion.equals(sourceRegion))
0526: noTransform = false;
0527:
0528: // **** Header decoder ****
0529: // Instantiate header decoder and read main header
0530: decSpec = hd.getDecoderSpecs();
0531:
0532: // **** Instantiate decoding chain ****
0533: // Get demixed bitdepths
0534: nComp = hd.getNumComps();
0535:
0536: int[] depth = new int[nComp];
0537: for (int i = 0; i < nComp; i++)
0538: depth[i] = hd.getOriginalBitDepth(i);
0539:
0540: //Get channel mapping
0541: ChannelDefinitionBox cdb = null;
0542: if (metadata != null)
0543: cdb = (ChannelDefinitionBox) metadata
0544: .getElement("JPEG2000ChannelDefinitionBox");
0545:
0546: channelMap = new int[nComp];
0547: if (cdb != null
0548: && metadata.getElement("JPEG2000PaletteBox") == null) {
0549: short[] assoc = cdb.getAssociation();
0550: short[] types = cdb.getTypes();
0551: short[] channels = cdb.getChannel();
0552:
0553: for (int i = 0; i < types.length; i++)
0554: if (types[i] == 0)
0555: channelMap[channels[i]] = assoc[i] - 1;
0556: else if (types[i] == 1 || types[i] == 2)
0557: channelMap[channels[i]] = channels[i];
0558: } else {
0559: for (int i = 0; i < nComp; i++)
0560: channelMap[i] = i;
0561: }
0562:
0563: // **** Bitstream reader ****
0564: try {
0565: boolean logJJ2000Messages = Boolean
0566: .getBoolean("jj2000.j2k.decoder.log");
0567: breader = BitstreamReaderAgent.createInstance(in, hd,
0568: j2krparam, decSpec, logJJ2000Messages, hi);
0569: } catch (IOException e) {
0570: throw new RuntimeException(I18N
0571: .getString("J2KReadState3")
0572: + " "
0573: + ((e.getMessage() != null) ? (":\n" + e
0574: .getMessage()) : ""));
0575: } catch (IllegalArgumentException e) {
0576: throw new RuntimeException(I18N
0577: .getString("J2KReadState4")
0578: + " "
0579: + ((e.getMessage() != null) ? (":\n" + e
0580: .getMessage()) : ""));
0581: }
0582:
0583: // **** Entropy decoder ****
0584: try {
0585: entdec = hd.createEntropyDecoder(breader, j2krparam);
0586: } catch (IllegalArgumentException e) {
0587: throw new RuntimeException(I18N
0588: .getString("J2KReadState5")
0589: + " "
0590: + ((e.getMessage() != null) ? (":\n" + e
0591: .getMessage()) : ""));
0592: }
0593:
0594: // **** ROI de-scaler ****
0595: try {
0596: roids = hd
0597: .createROIDeScaler(entdec, j2krparam, decSpec);
0598: } catch (IllegalArgumentException e) {
0599: throw new RuntimeException(I18N
0600: .getString("J2KReadState6")
0601: + " "
0602: + ((e.getMessage() != null) ? (":\n" + e
0603: .getMessage()) : ""));
0604: }
0605:
0606: // **** Dequantizer ****
0607: try {
0608: deq = hd.createDequantizer(roids, depth, decSpec);
0609: } catch (IllegalArgumentException e) {
0610: throw new RuntimeException(I18N
0611: .getString("J2KReadState7")
0612: + " "
0613: + ((e.getMessage() != null) ? (":\n" + e
0614: .getMessage()) : ""));
0615: }
0616:
0617: // **** Inverse wavelet transform ***
0618: try {
0619: // full page inverse wavelet transform
0620: invWT = InverseWT.createInstance(deq, decSpec);
0621: } catch (IllegalArgumentException e) {
0622: throw new RuntimeException(I18N
0623: .getString("J2KReadState8")
0624: + " "
0625: + ((e.getMessage() != null) ? (":\n" + e
0626: .getMessage()) : ""));
0627: }
0628:
0629: int res = breader.getImgRes();
0630: int mrl = decSpec.dls.getMin();
0631: invWT.setImgResLevel(res);
0632:
0633: // **** Data converter **** (after inverse transform module)
0634: converter = new ImgDataConverter(invWT, 0);
0635:
0636: // **** Inverse component transformation ****
0637: ictransf = new InvCompTransf(converter, decSpec, depth);
0638:
0639: // If the destination band is set used it
0640: sourceBands = j2krparam.getSourceBands();
0641:
0642: if (sourceBands == null) {
0643: sourceBands = new int[nComp];
0644: for (int i = 0; i < nComp; i++)
0645: sourceBands[i] = i;
0646: }
0647:
0648: nComp = sourceBands.length;
0649:
0650: destinationBands = j2krparam.getDestinationBands();
0651: if (destinationBands == null) {
0652: destinationBands = new int[nComp];
0653: for (int i = 0; i < nComp; i++)
0654: destinationBands[i] = i;
0655: }
0656:
0657: J2KImageReader.checkReadParamBandSettingsWrapper(param, hd
0658: .getNumComps(), destinationBands.length);
0659:
0660: levelShift = new int[nComp];
0661: minValues = new int[nComp];
0662: maxValues = new int[nComp];
0663: fracBits = new int[nComp];
0664: dataBlocks = new DataBlkInt[nComp];
0665:
0666: depth = new int[nComp];
0667: bandOffsets = new int[nComp];
0668: maxDepth = 0;
0669: isSigned = false;
0670: for (int i = 0; i < nComp; i++) {
0671: depth[i] = hd.getOriginalBitDepth(sourceBands[i]);
0672: if (depth[i] > maxDepth)
0673: maxDepth = depth[i];
0674: dataBlocks[i] = new DataBlkInt();
0675:
0676: //XXX: may need to change if ChannelDefinition is used to
0677: // define the color channels, such as BGR order
0678: bandOffsets[i] = i;
0679: if (hd.isOriginalSigned(sourceBands[i]))
0680: isSigned = true;
0681: else {
0682: levelShift[i] = 1 << (ictransf
0683: .getNomRangeBits(sourceBands[i]) - 1);
0684: }
0685:
0686: // Get the number of bits in the image, and decide what the max
0687: // value should be, depending on whether it is signed or not
0688: int nomRangeBits = ictransf
0689: .getNomRangeBits(sourceBands[i]);
0690: maxValues[i] = (1 << (isSigned == true ? (nomRangeBits - 1)
0691: : nomRangeBits)) - 1;
0692: minValues[i] = isSigned ? -(maxValues[i] + 1) : 0;
0693:
0694: fracBits[i] = ictransf.getFixedPoint(sourceBands[i]);
0695: }
0696:
0697: iis.reset();
0698: } catch (IllegalArgumentException e) {
0699: throw new RuntimeException(e.getMessage(), e);
0700: } catch (Error e) {
0701: if (e.getMessage() != null)
0702: throw new RuntimeException(e.getMessage(), e);
0703: else {
0704: throw new RuntimeException(I18N
0705: .getString("J2KReadState9"), e);
0706: }
0707: } catch (RuntimeException e) {
0708: if (e.getMessage() != null)
0709: throw new RuntimeException(I18N
0710: .getString("J2KReadState10")
0711: + " " + e.getMessage(), e);
0712: else {
0713: throw new RuntimeException(I18N
0714: .getString("J2KReadState10"), e);
0715: }
0716: } catch (Throwable e) {
0717: throw new RuntimeException(
0718: I18N.getString("J2KReadState10"), e);
0719: }
0720: }
0721:
0722: private Raster readSubsampledRaster(WritableRaster raster)
0723: throws IOException {
0724: if (raster == null)
0725: raster = Raster
0726: .createWritableRaster(
0727: sampleModel.createCompatibleSampleModel(
0728: destinationRegion.x
0729: + destinationRegion.width,
0730: destinationRegion.y
0731: + destinationRegion.height),
0732: new Point(destinationRegion.x,
0733: destinationRegion.y));
0734:
0735: int pixbuf[] = null; // line buffer for pixel data
0736: boolean prog = false; // Flag for progressive data
0737: Point nT = ictransf.getNumTiles(null);
0738: int numBands = sourceBands.length;
0739:
0740: Rectangle destRect = raster.getBounds().intersection(
0741: destinationRegion);
0742:
0743: int offx = destinationRegion.x;
0744: int offy = destinationRegion.y;
0745:
0746: int sourceSX = (destRect.x - offx) * scaleX + sourceOrigin.x;
0747: int sourceSY = (destRect.y - offy) * scaleY + sourceOrigin.y;
0748: int sourceEX = (destRect.width - 1) * scaleX + sourceSX;
0749: int sourceEY = (destRect.height - 1) * scaleY + sourceSY;
0750:
0751: int startXTile = (sourceSX - tileXOffset) / tileWidth;
0752: int startYTile = (sourceSY - tileYOffset) / tileHeight;
0753: int endXTile = (sourceEX - tileXOffset) / tileWidth;
0754: int endYTile = (sourceEY - tileYOffset) / tileHeight;
0755:
0756: startXTile = clip(startXTile, 0, nT.x - 1);
0757: startYTile = clip(startYTile, 0, nT.y - 1);
0758: endXTile = clip(endXTile, 0, nT.x - 1);
0759: endYTile = clip(endYTile, 0, nT.y - 1);
0760:
0761: int totalXTiles = endXTile - startXTile + 1;
0762: int totalYTiles = endYTile - startYTile + 1;
0763: int totalTiles = totalXTiles * totalYTiles;
0764:
0765: // Start the data delivery to the cached consumers tile by tile
0766: for (int y = startYTile; y <= endYTile; y++) {
0767: if (reader.getAbortRequest())
0768: break;
0769:
0770: // Loop on horizontal tiles
0771: for (int x = startXTile; x <= endXTile; x++) {
0772: if (reader.getAbortRequest())
0773: break;
0774:
0775: float initialFraction = (x - startXTile + (y - startYTile)
0776: * totalXTiles)
0777: / totalTiles;
0778:
0779: ictransf.setTile(x * tileStepX, y * tileStepY);
0780:
0781: int sx = hd.getCompSubsX(0);
0782: int cTileWidth = (ictransf.getTileWidth() + sx - 1)
0783: / sx;
0784: int sy = hd.getCompSubsY(0);
0785: int cTileHeight = (ictransf.getTileHeight() + sy - 1)
0786: / sy;
0787:
0788: // Offsets within the tile.
0789: int tx = 0;
0790: int ty = 0;
0791:
0792: // The region for this tile
0793: int startX = tileXOffset + x * tileWidth;
0794: int startY = tileYOffset + y * tileHeight;
0795:
0796: // sourceSX is guaranteed to be >= startX
0797: if (sourceSX > startX) {
0798: if (startX >= hd.getImgULX()) {
0799: tx = sourceSX - startX; // Intra-tile offset.
0800: cTileWidth -= tx; // Reduce effective width.
0801: }
0802: startX = sourceSX; // Absolute position.
0803: }
0804:
0805: // sourceSY is guaranteed to be >= startY
0806: if (sourceSY > startY) {
0807: if (startY >= hd.getImgULY()) {
0808: ty = sourceSY - startY; // Intra-tile offset.
0809: cTileHeight -= ty; // Reduce effective width.
0810: }
0811: startY = sourceSY; // Absolute position.
0812: }
0813:
0814: // Decrement dimensions if end position is within tile.
0815: if (sourceEX < startX + cTileWidth - 1) {
0816: cTileWidth += sourceEX - startX - cTileWidth + 1;
0817: }
0818: if (sourceEY < startY + cTileHeight - 1) {
0819: cTileHeight += sourceEY - startY - cTileHeight + 1;
0820: }
0821:
0822: // The start X in the destination
0823: int x1 = (startX + scaleX - 1 - sourceOrigin.x)
0824: / scaleX;
0825: int x2 = (startX + scaleX - 1 + cTileWidth - sourceOrigin.x)
0826: / scaleX;
0827: int lineLength = x2 - x1;
0828: if (pixbuf == null || pixbuf.length < lineLength)
0829: pixbuf = new int[lineLength]; // line buffer for pixel data
0830: x2 = (x2 - 1) * scaleX + sourceOrigin.x - startX;
0831:
0832: int y1 = (startY + scaleY - 1 - sourceOrigin.y)
0833: / scaleY;
0834:
0835: x1 += offx;
0836: y1 += offy;
0837:
0838: // Deliver in lines to reduce memory usage
0839: for (int l = ty, m = y1; l < ty + cTileHeight; l += scaleY, m++) {
0840: if (reader.getAbortRequest())
0841: break;
0842: // Request line data
0843: for (int i = 0; i < numBands; i++) {
0844: DataBlkInt db = dataBlocks[i];
0845: db.ulx = tx;
0846: db.uly = l;
0847: db.w = cTileWidth;
0848: db.h = 1;
0849: ictransf.getInternCompData(db,
0850: channelMap[sourceBands[i]]);
0851: prog = prog || db.progressive;
0852:
0853: int[] data = db.data;
0854: int k1 = db.offset + x2;
0855:
0856: int fracBit = fracBits[i];
0857: int lS = levelShift[i];
0858: int min = minValues[i];
0859: int max = maxValues[i];
0860:
0861: if (ImageUtil.isBinary(sampleModel)) {
0862: // Force min max to 0 and 1.
0863: min = 0;
0864: max = 1;
0865: if (bytebuf == null
0866: || bytebuf.length < cTileWidth
0867: * numBands)
0868: bytebuf = new byte[cTileWidth
0869: * numBands];
0870: for (int j = lineLength - 1; j >= 0; j--, k1 -= scaleX) {
0871: int tmp = (data[k1] >> fracBit) + lS;
0872: bytebuf[j] = (byte) ((tmp < min) ? min
0873: : ((tmp > max) ? max : tmp));
0874: }
0875:
0876: ImageUtil.setUnpackedBinaryData(bytebuf,
0877: raster, new Rectangle(x1, m,
0878: lineLength, 1));
0879: } else {
0880: for (int j = lineLength - 1; j >= 0; j--, k1 -= scaleX) {
0881: int tmp = (data[k1] >> fracBit) + lS;
0882: pixbuf[j] = (tmp < min) ? min
0883: : ((tmp > max) ? max : tmp);
0884: }
0885:
0886: // Send the line data to the BufferedImage
0887: raster.setSamples(x1, m, lineLength, 1,
0888: destinationBands[i], pixbuf);
0889: }
0890: }
0891:
0892: if (destImage != null)
0893: reader.processImageUpdateWrapper(destImage, x1,
0894: m, cTileWidth, 1, 1, 1,
0895: destinationBands);
0896:
0897: float fraction = initialFraction + (l - ty + 1.0F)
0898: / cTileHeight / totalTiles;
0899: reader
0900: .processImageProgressWrapper(100.0f * fraction);
0901: }
0902: } // End loop on horizontal tiles
0903: } // End loop on vertical tiles
0904:
0905: return raster;
0906: }
0907:
0908: public ImageTypeSpecifier getImageType() throws IOException {
0909:
0910: getSampleModel();
0911: getColorModel();
0912:
0913: return new ImageTypeSpecifier(colorModel, sampleModel);
0914: }
0915:
0916: public SampleModel getSampleModel() {
0917: if (sampleModel != null)
0918: return sampleModel;
0919:
0920: if (nComp == 1
0921: && (maxDepth == 1 || maxDepth == 2 || maxDepth == 4))
0922: sampleModel = new MultiPixelPackedSampleModel(
0923: DataBuffer.TYPE_BYTE, tileWidth, tileHeight,
0924: maxDepth);
0925: else if (maxDepth <= 8)
0926: sampleModel = new PixelInterleavedSampleModel(
0927: DataBuffer.TYPE_BYTE, tileWidth, tileHeight, nComp,
0928: tileWidth * nComp, bandOffsets);
0929: else if (maxDepth <= 16)
0930: sampleModel = new PixelInterleavedSampleModel(
0931: isSigned ? DataBuffer.TYPE_SHORT
0932: : DataBuffer.TYPE_USHORT, tileWidth,
0933: tileHeight, nComp, tileWidth * nComp, bandOffsets);
0934: else if (maxDepth <= 32)
0935: sampleModel = new PixelInterleavedSampleModel(
0936: DataBuffer.TYPE_INT, tileWidth, tileHeight, nComp,
0937: tileWidth * nComp, bandOffsets);
0938: else
0939: throw new IllegalArgumentException(I18N
0940: .getString("J2KReadState11")
0941: + " " + +maxDepth);
0942: return sampleModel;
0943: }
0944:
0945: public ColorModel getColorModel() {
0946:
0947: if (colorModel != null)
0948: return colorModel;
0949:
0950: // Attempt to get the ColorModel from the JP2 boxes.
0951: colorModel = ff.getColorModel();
0952: if (colorModel != null)
0953: return colorModel;
0954:
0955: if (hi.siz.csiz <= 4) {
0956: // XXX: Code essentially duplicated from FileFormatReader.getColorModel().
0957: // Create the ColorModel from the SIZ marker segment parameters.
0958: ColorSpace cs;
0959: if (hi.siz.csiz > 2) {
0960: cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
0961: } else {
0962: cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
0963: }
0964:
0965: int[] bitsPerComponent = new int[hi.siz.csiz];
0966: boolean isSigned = false;
0967: int maxBitDepth = -1;
0968: for (int i = 0; i < hi.siz.csiz; i++) {
0969: bitsPerComponent[i] = hi.siz.getOrigBitDepth(i);
0970: if (maxBitDepth < bitsPerComponent[i]) {
0971: maxBitDepth = bitsPerComponent[i];
0972: }
0973: isSigned |= hi.siz.isOrigSigned(i);
0974: }
0975:
0976: boolean hasAlpha = hi.siz.csiz % 2 == 0;
0977:
0978: int type = -1;
0979:
0980: if (maxBitDepth <= 8) {
0981: type = DataBuffer.TYPE_BYTE;
0982: } else if (maxBitDepth <= 16) {
0983: type = isSigned ? DataBuffer.TYPE_SHORT
0984: : DataBuffer.TYPE_USHORT;
0985: } else if (maxBitDepth <= 32) {
0986: type = DataBuffer.TYPE_INT;
0987: }
0988:
0989: if (type != -1) {
0990: if (hi.siz.csiz == 1
0991: && (maxBitDepth == 1 || maxBitDepth == 2 || maxBitDepth == 4)) {
0992: colorModel = ImageUtil
0993: .createColorModel(getSampleModel());
0994: } else {
0995: colorModel = new ComponentColorModel(cs,
0996: bitsPerComponent, hasAlpha, false,
0997: hasAlpha ? Transparency.TRANSLUCENT
0998: : Transparency.OPAQUE, type);
0999: }
1000:
1001: return colorModel;
1002: }
1003: }
1004:
1005: if (sampleModel == null) {
1006: sampleModel = getSampleModel();
1007: }
1008:
1009: if (sampleModel == null)
1010: return null;
1011:
1012: return ImageUtil.createColorModel(null, sampleModel);
1013: }
1014:
1015: /**
1016: * Returns the bounding rectangle of the upper left tile at
1017: * the current resolution level.
1018: */
1019: Rectangle getTile0Rect() {
1020: return new Rectangle(tileXOffset, tileYOffset, tileWidth,
1021: tileHeight);
1022: }
1023:
1024: private int clip(int value, int min, int max) {
1025: if (value < min)
1026: value = min;
1027: if (value > max)
1028: value = max;
1029: return value;
1030: }
1031:
1032: private void clipDestination(Rectangle dest) {
1033: Point offset = j2krparam.getDestinationOffset();
1034: if (dest.x < offset.x) {
1035: dest.width += dest.x - offset.x;
1036: dest.x = offset.x;
1037: }
1038: if (dest.y < offset.y) {
1039: dest.height += dest.y - offset.y;
1040: dest.y = offset.y;
1041: }
1042: }
1043: }
|