0001: /*
0002: * $RCSfile: ImageUtil.java,v $
0003: *
0004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Use is subject to license terms.
0007: *
0008: * $Revision: 1.2 $
0009: * $Date: 2006/07/21 20:53:28 $
0010: * $State: Exp $
0011: */
0012: package com.sun.media.jai.util;
0013:
0014: import java.awt.Point;
0015: import java.awt.Rectangle;
0016: import java.awt.RenderingHints;
0017: import java.awt.image.ColorModel;
0018: import java.awt.image.ComponentSampleModel;
0019: import java.awt.image.DataBuffer;
0020: import java.awt.image.DataBufferByte;
0021: import java.awt.image.DataBufferInt;
0022: import java.awt.image.DataBufferShort;
0023: import java.awt.image.DataBufferUShort;
0024: import java.awt.image.MultiPixelPackedSampleModel;
0025: import java.awt.image.Raster;
0026: import java.awt.image.SampleModel;
0027: import java.awt.image.SinglePixelPackedSampleModel;
0028: import java.awt.image.WritableRaster;
0029: import java.awt.image.renderable.ParameterBlock;
0030: import java.awt.image.renderable.RenderContext;
0031: import java.lang.reflect.Method;
0032: import java.lang.reflect.Modifier;
0033: import java.math.BigInteger;
0034: import java.util.Arrays;
0035: import java.util.Map;
0036: import java.util.Vector;
0037: import javax.media.jai.DeferredData;
0038: import javax.media.jai.JAI;
0039: import javax.media.jai.KernelJAI;
0040: import javax.media.jai.PixelAccessor;
0041: import javax.media.jai.RasterAccessor;
0042: import javax.media.jai.PlanarImage;
0043: import javax.media.jai.UnpackedImageData;
0044: import javax.media.jai.util.ImagingException;
0045: import javax.media.jai.util.ImagingListener;
0046: import java.io.ByteArrayOutputStream;
0047: import java.io.PrintStream;
0048:
0049: public final class ImageUtil {
0050:
0051: /** The minimum value of a float. */
0052: private static final float FLOAT_MIN = -Float.MAX_VALUE;
0053:
0054: /** The counter for images that use the method generateID to create
0055: * a UID.
0056: */
0057: private static long counter;
0058:
0059: /** A constant used to extract a byte from a short or an int. */
0060: public static final int BYTE_MASK = 0xFF;
0061:
0062: /** A constant used to extract an unsigned short from an int. */
0063: public static final int USHORT_MASK = 0xFFFF;
0064:
0065: /** Clamps a number to the range supported by byte data type. */
0066: public static final byte clampByte(int in) {
0067: return (in > 0xFF ? (byte) 0xFF : (in >= 0 ? (byte) in
0068: : (byte) 0));
0069: }
0070:
0071: /** Clamps a number to the range supported by unsigned short data type. */
0072: public static final short clampUShort(int in) {
0073: return (in > 0xFFFF ? (short) 0xFFFF : (in >= 0 ? (short) in
0074: : (short) 0));
0075: }
0076:
0077: /** Clamps a number to the range supported by short data type. */
0078: public static final short clampShort(int in) {
0079: return (in > Short.MAX_VALUE ? Short.MAX_VALUE
0080: : (in >= Short.MIN_VALUE ? (short) in : Short.MIN_VALUE));
0081: }
0082:
0083: /** Clamps a number to the range supported by integer data type. */
0084: public static final int clampInt(long in) {
0085: return (in > Integer.MAX_VALUE ? Integer.MAX_VALUE
0086: : (in >= Integer.MIN_VALUE ? (int) in
0087: : Integer.MIN_VALUE));
0088: }
0089:
0090: /** Clamps a number to the range supported by float data type. */
0091: public static final float clampFloat(double in) {
0092: return (in > Float.MAX_VALUE ? Float.MAX_VALUE
0093: : (in >= FLOAT_MIN ? (float) in : FLOAT_MIN));
0094: }
0095:
0096: /**
0097: * Clamps and rounds a number to the range supported by
0098: * byte data type. The input number is float.
0099: */
0100: public static final byte clampRoundByte(float in) {
0101: return (in > 0xFF ? (byte) 0xFF : (in >= 0 ? (byte) (in + 0.5F)
0102: : (byte) 0));
0103: }
0104:
0105: /**
0106: * Clamps and rounds a number to the range supported by
0107: * byte data type. The input number is double.
0108: */
0109: public static final byte clampRoundByte(double in) {
0110: return (in > 0xFF ? (byte) 0xFF : (in >= 0 ? (byte) (in + 0.5)
0111: : (byte) 0));
0112: }
0113:
0114: /**
0115: * Clamps and rounds a number to the range supported by
0116: * unsigned short data type. The input number is float.
0117: */
0118: public static final short clampRoundUShort(float in) {
0119: return (in > 0xFFFF ? (short) 0xFFFF
0120: : (in >= 0 ? (short) (in + 0.5F) : (short) 0));
0121: }
0122:
0123: /**
0124: * Clamps and rounds a number to the range supported by
0125: * unsigned short data type. The input number is double.
0126: */
0127: public static final short clampRoundUShort(double in) {
0128: return (in > 0xFFFF ? (short) 0xFFFF
0129: : (in >= 0 ? (short) (in + 0.5) : (short) 0));
0130: }
0131:
0132: /**
0133: * Clamps and rounds a number to the range supported by
0134: * short data type. The input number is float.
0135: */
0136: public static final short clampRoundShort(float in) {
0137: return (in > Short.MAX_VALUE ? Short.MAX_VALUE
0138: : (in >= Short.MIN_VALUE ? (short) Math
0139: .floor(in + 0.5F) : Short.MIN_VALUE));
0140: }
0141:
0142: /**
0143: * Clamps and rounds a number to the range supported by
0144: * short data type. The input number is double.
0145: */
0146: public static final short clampRoundShort(double in) {
0147: return (in > Short.MAX_VALUE ? Short.MAX_VALUE
0148: : (in >= Short.MIN_VALUE ? (short) Math.floor(in + 0.5)
0149: : Short.MIN_VALUE));
0150: }
0151:
0152: /**
0153: * Clamps and rounds a number to the range supported by
0154: * integer data type. The input number is float.
0155: */
0156: public static final int clampRoundInt(float in) {
0157: return (in > Integer.MAX_VALUE ? Integer.MAX_VALUE
0158: : (in >= Integer.MIN_VALUE ? (int) Math
0159: .floor(in + 0.5F) : Integer.MIN_VALUE));
0160: }
0161:
0162: /**
0163: * Clamps and rounds a number to the range supported by
0164: * integer data type. The input number is double.
0165: */
0166: public static final int clampRoundInt(double in) {
0167: return (in > Integer.MAX_VALUE ? Integer.MAX_VALUE
0168: : (in >= Integer.MIN_VALUE ? (int) Math.floor(in + 0.5)
0169: : Integer.MIN_VALUE));
0170: }
0171:
0172: /** Clamps a positive number to the range supported by byte data type. */
0173: public static final byte clampBytePositive(int in) {
0174: return (in > 0xFF ? (byte) 0xFF : (byte) in);
0175: }
0176:
0177: /** Clamps a negative number to the range supported by byte data type. */
0178: public static final byte clampByteNegative(int in) {
0179: return (in < 0 ? (byte) 0 : (byte) in);
0180: }
0181:
0182: /**
0183: * Clamps a positive number to the range supported by
0184: * unsigned short data type.
0185: */
0186: public static final short clampUShortPositive(int in) {
0187: return (in > 0xFFFF ? (short) 0xFFFF : (short) in);
0188: }
0189:
0190: /*
0191: * Clamps a negative number to the range supported by
0192: * unsigned short data type.
0193: */
0194: public static final short clampUShortNegative(int in) {
0195: return (in < 0 ? (short) 0 : (short) in);
0196: }
0197:
0198: public static final void copyRaster(RasterAccessor src,
0199: RasterAccessor dst) {
0200: int srcPixelStride = src.getPixelStride();
0201: int srcLineStride = src.getScanlineStride();
0202: int[] srcBandOffsets = src.getBandOffsets();
0203:
0204: int dstPixelStride = dst.getPixelStride();
0205: int dstLineStride = dst.getScanlineStride();
0206: int[] dstBandOffsets = dst.getBandOffsets();
0207:
0208: int width = dst.getWidth() * dstPixelStride;
0209: int height = dst.getHeight() * dstLineStride;
0210: int bands = dst.getNumBands();
0211:
0212: switch (dst.getDataType()) {
0213: case DataBuffer.TYPE_BYTE:
0214: byte[][] bSrcData = src.getByteDataArrays();
0215: byte[][] bDstData = dst.getByteDataArrays();
0216:
0217: for (int b = 0; b < bands; b++) {
0218: byte[] s = bSrcData[b];
0219: byte[] d = bDstData[b];
0220:
0221: int heightEnd = dstBandOffsets[b] + height;
0222:
0223: for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) {
0224:
0225: int widthEnd = dstLineOffset + width;
0226:
0227: for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) {
0228:
0229: d[dstPixelOffset] = s[srcPixelOffset];
0230: }
0231: }
0232: }
0233: break;
0234:
0235: case DataBuffer.TYPE_USHORT:
0236: case DataBuffer.TYPE_SHORT:
0237: short[][] sSrcData = src.getShortDataArrays();
0238: short[][] sDstData = dst.getShortDataArrays();
0239:
0240: for (int b = 0; b < bands; b++) {
0241: short[] s = sSrcData[b];
0242: short[] d = sDstData[b];
0243:
0244: int heightEnd = dstBandOffsets[b] + height;
0245:
0246: for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) {
0247:
0248: int widthEnd = dstLineOffset + width;
0249:
0250: for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) {
0251:
0252: d[dstPixelOffset] = s[srcPixelOffset];
0253: }
0254: }
0255: }
0256: break;
0257:
0258: case DataBuffer.TYPE_INT:
0259: int[][] iSrcData = src.getIntDataArrays();
0260: int[][] iDstData = dst.getIntDataArrays();
0261:
0262: for (int b = 0; b < bands; b++) {
0263: int[] s = iSrcData[b];
0264: int[] d = iDstData[b];
0265:
0266: int heightEnd = dstBandOffsets[b] + height;
0267:
0268: for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) {
0269:
0270: int widthEnd = dstLineOffset + width;
0271:
0272: for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) {
0273:
0274: d[dstPixelOffset] = s[srcPixelOffset];
0275: }
0276: }
0277: }
0278: break;
0279:
0280: case DataBuffer.TYPE_FLOAT:
0281: float[][] fSrcData = src.getFloatDataArrays();
0282: float[][] fDstData = dst.getFloatDataArrays();
0283:
0284: for (int b = 0; b < bands; b++) {
0285: float[] s = fSrcData[b];
0286: float[] d = fDstData[b];
0287:
0288: int heightEnd = dstBandOffsets[b] + height;
0289:
0290: for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) {
0291:
0292: int widthEnd = dstLineOffset + width;
0293:
0294: for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) {
0295:
0296: d[dstPixelOffset] = s[srcPixelOffset];
0297: }
0298: }
0299: }
0300: break;
0301:
0302: case DataBuffer.TYPE_DOUBLE:
0303: double[][] dSrcData = src.getDoubleDataArrays();
0304: double[][] dDstData = dst.getDoubleDataArrays();
0305:
0306: for (int b = 0; b < bands; b++) {
0307: double[] s = dSrcData[b];
0308: double[] d = dDstData[b];
0309:
0310: int heightEnd = dstBandOffsets[b] + height;
0311:
0312: for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) {
0313:
0314: int widthEnd = dstLineOffset + width;
0315:
0316: for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) {
0317:
0318: d[dstPixelOffset] = s[srcPixelOffset];
0319: }
0320: }
0321: }
0322: break;
0323: }
0324:
0325: if (dst.isDataCopy()) {
0326: dst.clampDataArrays();
0327: dst.copyDataToRaster();
0328: }
0329: }
0330:
0331: /**
0332: * Determines whether two SampleModels are "equal", i.e.,
0333: * assignment-compatible. This signifies that the two SampleModels
0334: * are either the very same object or are two different objects
0335: * with identical characteristics.
0336: */
0337: public boolean areEqualSampleModels(SampleModel sm1, SampleModel sm2) {
0338: if (sm1 == sm2) {
0339: // Identical objects.
0340: return true;
0341: } else if (sm1.getClass() == sm2.getClass()
0342: && sm1.getDataType() == sm2.getDataType()
0343: && sm1.getTransferType() == sm2.getTransferType()
0344: && sm1.getWidth() == sm2.getWidth()
0345: && sm1.getHeight() == sm2.getHeight()) {
0346: // At this point all common attributes are equivalent. Next test
0347: // those specific to the known direct subclasses of SampleModel.
0348: // Subclasses which are not known will always return false.
0349: if (sm1 instanceof ComponentSampleModel) {
0350: ComponentSampleModel csm1 = (ComponentSampleModel) sm1;
0351: ComponentSampleModel csm2 = (ComponentSampleModel) sm2;
0352: return csm1.getPixelStride() == csm2.getPixelStride()
0353: && csm1.getScanlineStride() == csm2
0354: .getScanlineStride()
0355: && Arrays.equals(csm1.getBankIndices(), csm2
0356: .getBankIndices())
0357: && Arrays.equals(csm1.getBandOffsets(), csm2
0358: .getBandOffsets());
0359: } else if (sm1 instanceof MultiPixelPackedSampleModel) {
0360: MultiPixelPackedSampleModel mpp1 = (MultiPixelPackedSampleModel) sm1;
0361: MultiPixelPackedSampleModel mpp2 = (MultiPixelPackedSampleModel) sm2;
0362: return mpp1.getPixelBitStride() == mpp2
0363: .getPixelBitStride()
0364: && mpp1.getScanlineStride() == mpp2
0365: .getScanlineStride()
0366: && mpp1.getDataBitOffset() == mpp2
0367: .getDataBitOffset();
0368: } else if (sm1 instanceof SinglePixelPackedSampleModel) {
0369: SinglePixelPackedSampleModel spp1 = (SinglePixelPackedSampleModel) sm1;
0370: SinglePixelPackedSampleModel spp2 = (SinglePixelPackedSampleModel) sm2;
0371: return spp1.getScanlineStride() == spp2
0372: .getScanlineStride()
0373: && Arrays.equals(spp1.getBitMasks(), spp2
0374: .getBitMasks());
0375: }
0376: }
0377:
0378: return false;
0379: }
0380:
0381: /// ---- BEGIN Binary data handling methods ----
0382:
0383: /**
0384: * Check whether a <code>SampleModel</code> represents a binary
0385: * data set, i.e., a single band of data with one bit per pixel
0386: * packed into a <code>MultiPixelPackedSampleModel</code>.
0387: */
0388: public static boolean isBinary(SampleModel sm) {
0389: return sm instanceof MultiPixelPackedSampleModel
0390: && ((MultiPixelPackedSampleModel) sm)
0391: .getPixelBitStride() == 1
0392: && sm.getNumBands() == 1;
0393: }
0394:
0395: /**
0396: * For the case of binary data (<code>isBinary()</code> returns
0397: * <code>true</code>), return the binary data as a packed byte array.
0398: * The data will be packed as eight bits per byte with no bit offset,
0399: * i.e., the first bit in each image line will be the left-most of the
0400: * first byte of the line. The line stride in bytes will be
0401: * <code>(int)((getWidth()+7)/8)</code>. The length of the returned
0402: * array will be the line stride multiplied by <code>getHeight()</code>
0403: *
0404: * @return the binary data as a packed array of bytes with zero offset
0405: * of <code>null</code> if the data are not binary.
0406: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0407: * <code>false</code> with the <code>SampleModel</code> of the
0408: * supplied <code>Raster</code> as argument.
0409: */
0410: public static byte[] getPackedBinaryData(Raster raster,
0411: Rectangle rect) {
0412: SampleModel sm = raster.getSampleModel();
0413: if (!isBinary(sm)) {
0414: throw new IllegalArgumentException(JaiI18N
0415: .getString("ImageUtil0"));
0416: }
0417:
0418: int rectX = rect.x;
0419: int rectY = rect.y;
0420: int rectWidth = rect.width;
0421: int rectHeight = rect.height;
0422:
0423: DataBuffer dataBuffer = raster.getDataBuffer();
0424:
0425: int dx = rectX - raster.getSampleModelTranslateX();
0426: int dy = rectY - raster.getSampleModelTranslateY();
0427:
0428: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0429: int lineStride = mpp.getScanlineStride();
0430: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0431: int bitOffset = mpp.getBitOffset(dx);
0432:
0433: int numBytesPerRow = (rectWidth + 7) / 8;
0434: if (dataBuffer instanceof DataBufferByte
0435: && eltOffset == 0
0436: && bitOffset == 0
0437: && numBytesPerRow == lineStride
0438: && ((DataBufferByte) dataBuffer).getData().length == numBytesPerRow
0439: * rectHeight) {
0440: return ((DataBufferByte) dataBuffer).getData();
0441: }
0442:
0443: byte[] binaryDataArray = new byte[numBytesPerRow * rectHeight];
0444:
0445: int b = 0;
0446:
0447: if (bitOffset == 0) {
0448: if (dataBuffer instanceof DataBufferByte) {
0449: byte[] data = ((DataBufferByte) dataBuffer).getData();
0450: int stride = numBytesPerRow;
0451: int offset = 0;
0452: for (int y = 0; y < rectHeight; y++) {
0453: System.arraycopy(data, eltOffset, binaryDataArray,
0454: offset, stride);
0455: offset += stride;
0456: eltOffset += lineStride;
0457: }
0458: } else if (dataBuffer instanceof DataBufferShort
0459: || dataBuffer instanceof DataBufferUShort) {
0460: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0461: .getData()
0462: : ((DataBufferUShort) dataBuffer).getData();
0463:
0464: for (int y = 0; y < rectHeight; y++) {
0465: int xRemaining = rectWidth;
0466: int i = eltOffset;
0467: while (xRemaining > 8) {
0468: short datum = data[i++];
0469: binaryDataArray[b++] = (byte) ((datum >>> 8) & 0xFF);
0470: binaryDataArray[b++] = (byte) (datum & 0xFF);
0471: xRemaining -= 16;
0472: }
0473: if (xRemaining > 0) {
0474: binaryDataArray[b++] = (byte) ((data[i] >>> 8) & 0XFF);
0475: }
0476: eltOffset += lineStride;
0477: }
0478: } else if (dataBuffer instanceof DataBufferInt) {
0479: int[] data = ((DataBufferInt) dataBuffer).getData();
0480:
0481: for (int y = 0; y < rectHeight; y++) {
0482: int xRemaining = rectWidth;
0483: int i = eltOffset;
0484: while (xRemaining > 24) {
0485: int datum = data[i++];
0486: binaryDataArray[b++] = (byte) ((datum >>> 24) & 0xFF);
0487: binaryDataArray[b++] = (byte) ((datum >>> 16) & 0xFF);
0488: binaryDataArray[b++] = (byte) ((datum >>> 8) & 0xFF);
0489: binaryDataArray[b++] = (byte) (datum & 0xFF);
0490: xRemaining -= 32;
0491: }
0492: int shift = 24;
0493: while (xRemaining > 0) {
0494: binaryDataArray[b++] = (byte) ((data[i] >>> shift) & 0xFF);
0495: shift -= 8;
0496: xRemaining -= 8;
0497: }
0498: eltOffset += lineStride;
0499: }
0500: }
0501: } else { // bitOffset != 0
0502: if (dataBuffer instanceof DataBufferByte) {
0503: byte[] data = ((DataBufferByte) dataBuffer).getData();
0504:
0505: if ((bitOffset & 7) == 0) {
0506: int stride = numBytesPerRow;
0507: int offset = 0;
0508: for (int y = 0; y < rectHeight; y++) {
0509: System.arraycopy(data, eltOffset,
0510: binaryDataArray, offset, stride);
0511: offset += stride;
0512: eltOffset += lineStride;
0513: }
0514: } else { // bitOffset % 8 != 0
0515: int leftShift = bitOffset & 7;
0516: int rightShift = 8 - leftShift;
0517: for (int y = 0; y < rectHeight; y++) {
0518: int i = eltOffset;
0519: int xRemaining = rectWidth;
0520: while (xRemaining > 0) {
0521: if (xRemaining > rightShift) {
0522: binaryDataArray[b++] = (byte) (((data[i++] & 0xFF) << leftShift) | ((data[i] & 0xFF) >>> rightShift));
0523: } else {
0524: binaryDataArray[b++] = (byte) ((data[i] & 0xFF) << leftShift);
0525: }
0526: xRemaining -= 8;
0527: }
0528: eltOffset += lineStride;
0529: }
0530: }
0531: } else if (dataBuffer instanceof DataBufferShort
0532: || dataBuffer instanceof DataBufferUShort) {
0533: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0534: .getData()
0535: : ((DataBufferUShort) dataBuffer).getData();
0536:
0537: for (int y = 0; y < rectHeight; y++) {
0538: int bOffset = bitOffset;
0539: for (int x = 0; x < rectWidth; x += 8, bOffset += 8) {
0540: int i = eltOffset + bOffset / 16;
0541: int mod = bOffset % 16;
0542: int left = data[i] & 0xFFFF;
0543: if (mod <= 8) {
0544: binaryDataArray[b++] = (byte) (left >>> (8 - mod));
0545: } else {
0546: int delta = mod - 8;
0547: int right = data[i + 1] & 0xFFFF;
0548: binaryDataArray[b++] = (byte) ((left << delta) | (right >>> (16 - delta)));
0549: }
0550: }
0551: eltOffset += lineStride;
0552: }
0553: } else if (dataBuffer instanceof DataBufferInt) {
0554: int[] data = ((DataBufferInt) dataBuffer).getData();
0555:
0556: for (int y = 0; y < rectHeight; y++) {
0557: int bOffset = bitOffset;
0558: for (int x = 0; x < rectWidth; x += 8, bOffset += 8) {
0559: int i = eltOffset + bOffset / 32;
0560: int mod = bOffset % 32;
0561: int left = data[i];
0562: if (mod <= 24) {
0563: binaryDataArray[b++] = (byte) (left >>> (24 - mod));
0564: } else {
0565: int delta = mod - 24;
0566: int right = data[i + 1];
0567: binaryDataArray[b++] = (byte) ((left << delta) | (right >>> (32 - delta)));
0568: }
0569: }
0570: eltOffset += lineStride;
0571: }
0572: }
0573: }
0574:
0575: return binaryDataArray;
0576: }
0577:
0578: /**
0579: * Returns the binary data unpacked into an array of bytes.
0580: * The line stride will be the width of the <code>Raster</code>.
0581: *
0582: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0583: * <code>false</code> with the <code>SampleModel</code> of the
0584: * supplied <code>Raster</code> as argument.
0585: */
0586: public static byte[] getUnpackedBinaryData(Raster raster,
0587: Rectangle rect) {
0588: SampleModel sm = raster.getSampleModel();
0589: if (!isBinary(sm)) {
0590: throw new IllegalArgumentException(JaiI18N
0591: .getString("ImageUtil0"));
0592: }
0593:
0594: int rectX = rect.x;
0595: int rectY = rect.y;
0596: int rectWidth = rect.width;
0597: int rectHeight = rect.height;
0598:
0599: DataBuffer dataBuffer = raster.getDataBuffer();
0600:
0601: int dx = rectX - raster.getSampleModelTranslateX();
0602: int dy = rectY - raster.getSampleModelTranslateY();
0603:
0604: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0605: int lineStride = mpp.getScanlineStride();
0606: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0607: int bitOffset = mpp.getBitOffset(dx);
0608:
0609: byte[] bdata = new byte[rectWidth * rectHeight];
0610: int maxY = rectY + rectHeight;
0611: int maxX = rectX + rectWidth;
0612: int k = 0;
0613:
0614: if (dataBuffer instanceof DataBufferByte) {
0615: byte[] data = ((DataBufferByte) dataBuffer).getData();
0616: for (int y = rectY; y < maxY; y++) {
0617: int bOffset = eltOffset * 8 + bitOffset;
0618: for (int x = rectX; x < maxX; x++) {
0619: byte b = data[bOffset / 8];
0620: bdata[k++] = (byte) ((b >>> (7 - bOffset & 7)) & 0x0000001);
0621: bOffset++;
0622: }
0623: eltOffset += lineStride;
0624: }
0625: } else if (dataBuffer instanceof DataBufferShort
0626: || dataBuffer instanceof DataBufferUShort) {
0627: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0628: .getData()
0629: : ((DataBufferUShort) dataBuffer).getData();
0630: for (int y = rectY; y < maxY; y++) {
0631: int bOffset = eltOffset * 16 + bitOffset;
0632: for (int x = rectX; x < maxX; x++) {
0633: short s = data[bOffset / 16];
0634: bdata[k++] = (byte) ((s >>> (15 - bOffset % 16)) & 0x0000001);
0635: bOffset++;
0636: }
0637: eltOffset += lineStride;
0638: }
0639: } else if (dataBuffer instanceof DataBufferInt) {
0640: int[] data = ((DataBufferInt) dataBuffer).getData();
0641: for (int y = rectY; y < maxY; y++) {
0642: int bOffset = eltOffset * 32 + bitOffset;
0643: for (int x = rectX; x < maxX; x++) {
0644: int i = data[bOffset / 32];
0645: bdata[k++] = (byte) ((i >>> (31 - bOffset % 32)) & 0x0000001);
0646: bOffset++;
0647: }
0648: eltOffset += lineStride;
0649: }
0650: }
0651:
0652: return bdata;
0653: }
0654:
0655: /**
0656: * Sets the supplied <code>Raster</code>'s data from an array
0657: * of packed binary data of the form returned by
0658: * <code>getPackedBinaryData()</code>.
0659: *
0660: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0661: * <code>false</code> with the <code>SampleModel</code> of the
0662: * supplied <code>Raster</code> as argument.
0663: */
0664: public static void setPackedBinaryData(byte[] binaryDataArray,
0665: WritableRaster raster, Rectangle rect) {
0666: SampleModel sm = raster.getSampleModel();
0667: if (!isBinary(sm)) {
0668: throw new IllegalArgumentException(JaiI18N
0669: .getString("ImageUtil0"));
0670: }
0671:
0672: int rectX = rect.x;
0673: int rectY = rect.y;
0674: int rectWidth = rect.width;
0675: int rectHeight = rect.height;
0676:
0677: DataBuffer dataBuffer = raster.getDataBuffer();
0678:
0679: int dx = rectX - raster.getSampleModelTranslateX();
0680: int dy = rectY - raster.getSampleModelTranslateY();
0681:
0682: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0683: int lineStride = mpp.getScanlineStride();
0684: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0685: int bitOffset = mpp.getBitOffset(dx);
0686:
0687: int b = 0;
0688:
0689: if (bitOffset == 0) {
0690: if (dataBuffer instanceof DataBufferByte) {
0691: byte[] data = ((DataBufferByte) dataBuffer).getData();
0692: if (data == binaryDataArray) {
0693: // Optimal case: simply return.
0694: return;
0695: }
0696: int stride = (rectWidth + 7) / 8;
0697: int offset = 0;
0698: for (int y = 0; y < rectHeight; y++) {
0699: System.arraycopy(binaryDataArray, offset, data,
0700: eltOffset, stride);
0701: offset += stride;
0702: eltOffset += lineStride;
0703: }
0704: } else if (dataBuffer instanceof DataBufferShort
0705: || dataBuffer instanceof DataBufferUShort) {
0706: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0707: .getData()
0708: : ((DataBufferUShort) dataBuffer).getData();
0709:
0710: for (int y = 0; y < rectHeight; y++) {
0711: int xRemaining = rectWidth;
0712: int i = eltOffset;
0713: while (xRemaining > 8) {
0714: data[i++] = (short) (((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF));
0715: xRemaining -= 16;
0716: }
0717: if (xRemaining > 0) {
0718: data[i++] = (short) ((binaryDataArray[b++] & 0xFF) << 8);
0719: }
0720: eltOffset += lineStride;
0721: }
0722: } else if (dataBuffer instanceof DataBufferInt) {
0723: int[] data = ((DataBufferInt) dataBuffer).getData();
0724:
0725: for (int y = 0; y < rectHeight; y++) {
0726: int xRemaining = rectWidth;
0727: int i = eltOffset;
0728: while (xRemaining > 24) {
0729: data[i++] = (int) (((binaryDataArray[b++] & 0xFF) << 24)
0730: | ((binaryDataArray[b++] & 0xFF) << 16)
0731: | ((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF));
0732: xRemaining -= 32;
0733: }
0734: int shift = 24;
0735: while (xRemaining > 0) {
0736: data[i] |= (int) ((binaryDataArray[b++] & 0xFF) << shift);
0737: shift -= 8;
0738: xRemaining -= 8;
0739: }
0740: eltOffset += lineStride;
0741: }
0742: }
0743: } else { // bitOffset != 0
0744: int stride = (rectWidth + 7) / 8;
0745: int offset = 0;
0746: if (dataBuffer instanceof DataBufferByte) {
0747: byte[] data = ((DataBufferByte) dataBuffer).getData();
0748:
0749: if ((bitOffset & 7) == 0) {
0750: for (int y = 0; y < rectHeight; y++) {
0751: System.arraycopy(binaryDataArray, offset, data,
0752: eltOffset, stride);
0753: offset += stride;
0754: eltOffset += lineStride;
0755: }
0756: } else { // bitOffset % 8 != 0
0757: int rightShift = bitOffset & 7;
0758: int leftShift = 8 - rightShift;
0759: int leftShift8 = 8 + leftShift;
0760: int mask = (byte) (255 << leftShift);
0761: int mask1 = (byte) ~mask;
0762:
0763: for (int y = 0; y < rectHeight; y++) {
0764: int i = eltOffset;
0765: int xRemaining = rectWidth;
0766: while (xRemaining > 0) {
0767: byte datum = binaryDataArray[b++];
0768:
0769: if (xRemaining > leftShift8) {
0770: // when all the bits in this BYTE will be set
0771: // into the data buffer.
0772: data[i] = (byte) ((data[i] & mask) | ((datum & 0xFF) >>> rightShift));
0773: data[++i] = (byte) ((datum & 0xFF) << leftShift);
0774: } else if (xRemaining > leftShift) {
0775: // All the "leftShift" high bits will be set
0776: // into the data buffer. But not all the
0777: // "rightShift" low bits will be set.
0778: data[i] = (byte) ((data[i] & mask) | ((datum & 0xFF) >>> rightShift));
0779: i++;
0780: data[i] = (byte) ((data[i] & mask1) | ((datum & 0xFF) << leftShift));
0781: } else {
0782: // Less than "leftShift" high bits will be set.
0783: int remainMask = (1 << leftShift
0784: - xRemaining) - 1;
0785: data[i] = (byte) ((data[i] & (mask | remainMask)) | (datum & 0xFF) >>> rightShift
0786: & ~remainMask);
0787: }
0788: xRemaining -= 8;
0789: }
0790: eltOffset += lineStride;
0791: }
0792: }
0793: } else if (dataBuffer instanceof DataBufferShort
0794: || dataBuffer instanceof DataBufferUShort) {
0795: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0796: .getData()
0797: : ((DataBufferUShort) dataBuffer).getData();
0798:
0799: int rightShift = bitOffset & 7;
0800: int leftShift = 8 - rightShift;
0801: int leftShift16 = 16 + leftShift;
0802: int mask = (short) (~(255 << leftShift));
0803: int mask1 = (short) (65535 << leftShift);
0804: int mask2 = (short) ~mask1;
0805:
0806: for (int y = 0; y < rectHeight; y++) {
0807: int bOffset = bitOffset;
0808: int xRemaining = rectWidth;
0809: for (int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) {
0810: int i = eltOffset + (bOffset >> 4);
0811: int mod = bOffset & 15;
0812: int datum = binaryDataArray[b++] & 0xFF;
0813: if (mod <= 8) {
0814: // This BYTE is set into one SHORT
0815: if (xRemaining < 8) {
0816: // Mask the bits to be set.
0817: datum &= 255 << 8 - xRemaining;
0818: }
0819: data[i] = (short) ((data[i] & mask) | (datum << leftShift));
0820: } else if (xRemaining > leftShift16) {
0821: // This BYTE will be set into two SHORTs
0822: data[i] = (short) ((data[i] & mask1) | ((datum >>> rightShift) & 0xFFFF));
0823: data[++i] = (short) ((datum << leftShift) & 0xFFFF);
0824: } else if (xRemaining > leftShift) {
0825: // This BYTE will be set into two SHORTs;
0826: // But not all the low bits will be set into SHORT
0827: data[i] = (short) ((data[i] & mask1) | ((datum >>> rightShift) & 0xFFFF));
0828: i++;
0829: data[i] = (short) ((data[i] & mask2) | ((datum << leftShift) & 0xFFFF));
0830: } else {
0831: // Only some of the high bits will be set into
0832: // SHORTs
0833: int remainMask = (1 << leftShift
0834: - xRemaining) - 1;
0835: data[i] = (short) ((data[i] & (mask1 | remainMask)) | ((datum >>> rightShift) & 0xFFFF & ~remainMask));
0836: }
0837: }
0838: eltOffset += lineStride;
0839: }
0840: } else if (dataBuffer instanceof DataBufferInt) {
0841: int[] data = ((DataBufferInt) dataBuffer).getData();
0842: int rightShift = bitOffset & 7;
0843: int leftShift = 8 - rightShift;
0844: int leftShift32 = 32 + leftShift;
0845: int mask = 0xFFFFFFFF << leftShift;
0846: int mask1 = ~mask;
0847:
0848: for (int y = 0; y < rectHeight; y++) {
0849: int bOffset = bitOffset;
0850: int xRemaining = rectWidth;
0851: for (int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) {
0852: int i = eltOffset + (bOffset >> 5);
0853: int mod = bOffset & 31;
0854: int datum = binaryDataArray[b++] & 0xFF;
0855: if (mod <= 24) {
0856: // This BYTE is set into one INT
0857: int shift = 24 - mod;
0858: if (xRemaining < 8) {
0859: // Mask the bits to be set.
0860: datum &= 255 << 8 - xRemaining;
0861: }
0862: data[i] = (data[i] & (~(255 << shift)))
0863: | (datum << shift);
0864: } else if (xRemaining > leftShift32) {
0865: // All the bits of this BYTE will be set into two INTs
0866: data[i] = (data[i] & mask)
0867: | (datum >>> rightShift);
0868: data[++i] = datum << leftShift;
0869: } else if (xRemaining > leftShift) {
0870: // This BYTE will be set into two INTs;
0871: // But not all the low bits will be set into INT
0872: data[i] = (data[i] & mask)
0873: | (datum >>> rightShift);
0874: i++;
0875: data[i] = (data[i] & mask1)
0876: | (datum << leftShift);
0877: } else {
0878: // Only some of the high bits will be set into INT
0879: int remainMask = (1 << leftShift
0880: - xRemaining) - 1;
0881: data[i] = (data[i] & (mask | remainMask))
0882: | (datum >>> rightShift & ~remainMask);
0883: }
0884: }
0885: eltOffset += lineStride;
0886: }
0887: }
0888: }
0889: }
0890:
0891: /**
0892: * Copies data into the packed array of the <code>Raster</code>
0893: * from an array of unpacked data of the form returned by
0894: * <code>getUnpackedBinaryData()</code>.
0895: *
0896: * <p> If the data are binary, then the target bit will be set if
0897: * and only if the corresponding byte is non-zero.
0898: *
0899: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0900: * <code>false</code> with the <code>SampleModel</code> of the
0901: * supplied <code>Raster</code> as argument.
0902: */
0903: public static void setUnpackedBinaryData(byte[] bdata,
0904: WritableRaster raster, Rectangle rect) {
0905: SampleModel sm = raster.getSampleModel();
0906: if (!isBinary(sm)) {
0907: throw new IllegalArgumentException(JaiI18N
0908: .getString("ImageUtil0"));
0909: }
0910:
0911: int rectX = rect.x;
0912: int rectY = rect.y;
0913: int rectWidth = rect.width;
0914: int rectHeight = rect.height;
0915:
0916: DataBuffer dataBuffer = raster.getDataBuffer();
0917:
0918: int dx = rectX - raster.getSampleModelTranslateX();
0919: int dy = rectY - raster.getSampleModelTranslateY();
0920:
0921: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0922: int lineStride = mpp.getScanlineStride();
0923: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0924: int bitOffset = mpp.getBitOffset(dx);
0925:
0926: int k = 0;
0927:
0928: if (dataBuffer instanceof DataBufferByte) {
0929: byte[] data = ((DataBufferByte) dataBuffer).getData();
0930: for (int y = 0; y < rectHeight; y++) {
0931: int bOffset = eltOffset * 8 + bitOffset;
0932: for (int x = 0; x < rectWidth; x++) {
0933: if (bdata[k++] != (byte) 0) {
0934: data[bOffset / 8] |= (byte) (0x00000001 << (7 - bOffset & 7));
0935: }
0936: bOffset++;
0937: }
0938: eltOffset += lineStride;
0939: }
0940: } else if (dataBuffer instanceof DataBufferShort
0941: || dataBuffer instanceof DataBufferUShort) {
0942: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0943: .getData()
0944: : ((DataBufferUShort) dataBuffer).getData();
0945: for (int y = 0; y < rectHeight; y++) {
0946: int bOffset = eltOffset * 16 + bitOffset;
0947: for (int x = 0; x < rectWidth; x++) {
0948: if (bdata[k++] != (byte) 0) {
0949: data[bOffset / 16] |= (short) (0x00000001 << (15 - bOffset % 16));
0950: }
0951: bOffset++;
0952: }
0953: eltOffset += lineStride;
0954: }
0955: } else if (dataBuffer instanceof DataBufferInt) {
0956: int[] data = ((DataBufferInt) dataBuffer).getData();
0957: for (int y = 0; y < rectHeight; y++) {
0958: int bOffset = eltOffset * 32 + bitOffset;
0959: for (int x = 0; x < rectWidth; x++) {
0960: if (bdata[k++] != (byte) 0) {
0961: data[bOffset / 32] |= (int) (0x00000001 << (31 - bOffset % 32));
0962: }
0963: bOffset++;
0964: }
0965: eltOffset += lineStride;
0966: }
0967: }
0968: }
0969:
0970: /** Fill the specified rectangle of <code>raster</code> with the provided
0971: * background values. Suppose the raster is initialized to 0. Thus,
0972: * for binary data, if the provided background values are 0, do nothing.
0973: */
0974: public static void fillBackground(WritableRaster raster,
0975: Rectangle rect, double[] backgroundValues) {
0976: rect = rect.intersection(raster.getBounds());
0977: int numBands = raster.getSampleModel().getNumBands();
0978: SampleModel sm = raster.getSampleModel();
0979: PixelAccessor accessor = new PixelAccessor(sm, null);
0980:
0981: if (isBinary(sm)) {
0982: //fill binary data
0983: byte value = (byte) (((int) backgroundValues[0]) & 1);
0984: if (value == 0)
0985: return;
0986: int rectX = rect.x;
0987: int rectY = rect.y;
0988: int rectWidth = rect.width;
0989: int rectHeight = rect.height;
0990:
0991: int dx = rectX - raster.getSampleModelTranslateX();
0992: int dy = rectY - raster.getSampleModelTranslateY();
0993:
0994: DataBuffer dataBuffer = raster.getDataBuffer();
0995: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0996: int lineStride = mpp.getScanlineStride();
0997: int eltOffset = dataBuffer.getOffset()
0998: + mpp.getOffset(dx, dy);
0999: int bitOffset = mpp.getBitOffset(dx);
1000:
1001: switch (sm.getDataType()) {
1002: case DataBuffer.TYPE_BYTE: {
1003: byte[] data = ((DataBufferByte) dataBuffer).getData();
1004: int bits = bitOffset & 7;
1005: int otherBits = (bits == 0) ? 0 : 8 - bits;
1006:
1007: byte mask = (byte) (255 >> bits);
1008: int lineLength = (rectWidth - otherBits) / 8;
1009: int bits1 = (rectWidth - otherBits) & 7;
1010: byte mask1 = (byte) (255 << (8 - bits1));
1011: // If operating within a single byte, merge masks into one
1012: // and don't apply second mask after while loop
1013: if (lineLength == 0) {
1014: mask &= mask1;
1015: bits1 = 0;
1016: }
1017:
1018: for (int y = 0; y < rectHeight; y++) {
1019: int start = eltOffset;
1020: int end = start + lineLength;
1021: if (bits != 0)
1022: data[start++] |= mask;
1023: while (start < end)
1024: data[start++] = (byte) 255;
1025: if (bits1 != 0)
1026: data[start] |= mask1;
1027: eltOffset += lineStride;
1028: }
1029: break;
1030: }
1031: case DataBuffer.TYPE_USHORT: {
1032: short[] data = ((DataBufferUShort) dataBuffer)
1033: .getData();
1034: int bits = bitOffset & 15;
1035: int otherBits = (bits == 0) ? 0 : 16 - bits;
1036:
1037: short mask = (short) (65535 >> bits);
1038: int lineLength = (rectWidth - otherBits) / 16;
1039: int bits1 = (rectWidth - otherBits) & 15;
1040: short mask1 = (short) (65535 << (16 - bits1));
1041: // If operating within a single byte, merge masks into one
1042: // and don't apply second mask after while loop
1043: if (lineLength == 0) {
1044: mask &= mask1;
1045: bits1 = 0;
1046: }
1047:
1048: for (int y = 0; y < rectHeight; y++) {
1049: int start = eltOffset;
1050: int end = start + lineLength;
1051: if (bits != 0)
1052: data[start++] |= mask;
1053: while (start < end)
1054: data[start++] = (short) 0xFFFF;
1055: if (bits1 != 0)
1056: data[start++] |= mask1;
1057: eltOffset += lineStride;
1058: }
1059: break;
1060: }
1061: case DataBuffer.TYPE_INT: {
1062: int[] data = ((DataBufferInt) dataBuffer).getData();
1063: int bits = bitOffset & 31;
1064: int otherBits = (bits == 0) ? 0 : 32 - bits;
1065:
1066: int mask = 0xFFFFFFFF >> bits;
1067: int lineLength = (rectWidth - otherBits) / 32;
1068: int bits1 = (rectWidth - otherBits) & 31;
1069: int mask1 = 0xFFFFFFFF << (32 - bits1);
1070: // If operating within a single byte, merge masks into one
1071: // and don't apply second mask after while loop
1072: if (lineLength == 0) {
1073: mask &= mask1;
1074: bits1 = 0;
1075: }
1076:
1077: for (int y = 0; y < rectHeight; y++) {
1078: int start = eltOffset;
1079: int end = start + lineLength;
1080: if (bits != 0)
1081: data[start++] |= mask;
1082: while (start < end)
1083: data[start++] = 0xFFFFFFFF;
1084: if (bits1 != 0)
1085: data[start++] |= mask1;
1086: eltOffset += lineStride;
1087: }
1088: break;
1089: }
1090:
1091: }
1092: } else {
1093: int srcSampleType = accessor.sampleType == PixelAccessor.TYPE_BIT ? DataBuffer.TYPE_BYTE
1094: : accessor.sampleType;
1095: UnpackedImageData uid = accessor.getPixels(raster, rect,
1096: srcSampleType, false);
1097: rect = uid.rect;
1098: int lineStride = uid.lineStride;
1099: int pixelStride = uid.pixelStride;
1100:
1101: switch (uid.type) {
1102: case DataBuffer.TYPE_BYTE:
1103: byte[][] bdata = uid.getByteData();
1104: for (int b = 0; b < accessor.numBands; b++) {
1105: byte value = (byte) backgroundValues[b];
1106: byte[] bd = bdata[b];
1107: int lastLine = uid.bandOffsets[b] + rect.height
1108: * lineStride;
1109:
1110: for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) {
1111: int lastPixel = lo + rect.width * pixelStride;
1112: for (int po = lo; po < lastPixel; po += pixelStride) {
1113: bd[po] = value;
1114: }
1115: }
1116: }
1117: break;
1118: case DataBuffer.TYPE_USHORT:
1119: case DataBuffer.TYPE_SHORT:
1120: short[][] sdata = uid.getShortData();
1121: for (int b = 0; b < accessor.numBands; b++) {
1122: short value = (short) backgroundValues[b];
1123: short[] sd = sdata[b];
1124: int lastLine = uid.bandOffsets[b] + rect.height
1125: * lineStride;
1126:
1127: for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) {
1128: int lastPixel = lo + rect.width * pixelStride;
1129: for (int po = lo; po < lastPixel; po += pixelStride) {
1130: sd[po] = value;
1131: }
1132: }
1133: }
1134: break;
1135: case DataBuffer.TYPE_INT:
1136: int[][] idata = uid.getIntData();
1137: for (int b = 0; b < accessor.numBands; b++) {
1138: int value = (int) backgroundValues[b];
1139: int[] id = idata[b];
1140: int lastLine = uid.bandOffsets[b] + rect.height
1141: * lineStride;
1142:
1143: for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) {
1144: int lastPixel = lo + rect.width * pixelStride;
1145: for (int po = lo; po < lastPixel; po += pixelStride) {
1146: id[po] = value;
1147: }
1148: }
1149: }
1150: break;
1151: case DataBuffer.TYPE_FLOAT:
1152: float[][] fdata = uid.getFloatData();
1153: for (int b = 0; b < accessor.numBands; b++) {
1154: float value = (float) backgroundValues[b];
1155: float[] fd = fdata[b];
1156: int lastLine = uid.bandOffsets[b] + rect.height
1157: * lineStride;
1158:
1159: for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) {
1160: int lastPixel = lo + rect.width * pixelStride;
1161: for (int po = lo; po < lastPixel; po += pixelStride) {
1162: fd[po] = value;
1163: }
1164: }
1165: }
1166: break;
1167: case DataBuffer.TYPE_DOUBLE:
1168: double[][] ddata = uid.getDoubleData();
1169: for (int b = 0; b < accessor.numBands; b++) {
1170: double value = backgroundValues[b];
1171: double[] dd = ddata[b];
1172: int lastLine = uid.bandOffsets[b] + rect.height
1173: * lineStride;
1174:
1175: for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) {
1176: int lastPixel = lo + rect.width * pixelStride;
1177: for (int po = lo; po < lastPixel; po += pixelStride) {
1178: dd[po] = value;
1179: }
1180: }
1181: }
1182: break;
1183: }
1184: }
1185: }
1186:
1187: /** When the destination rectangle is not the same as the image bounds,
1188: * should fill the border.
1189: */
1190: public static void fillBordersWithBackgroundValues(
1191: Rectangle outerRect, Rectangle innerRect,
1192: WritableRaster raster, double[] backgroundValues) {
1193: int outerMaxX = outerRect.x + outerRect.width;
1194: int outerMaxY = outerRect.y + outerRect.height;
1195:
1196: int innerMaxX = innerRect.x + innerRect.width;
1197: int innerMaxY = innerRect.y + innerRect.height;
1198:
1199: if (outerRect.x < innerRect.x) {
1200: Rectangle rect = new Rectangle(outerRect.x, innerRect.y,
1201: innerRect.x - outerRect.x, outerMaxY - innerRect.y);
1202: fillBackground(raster, rect, backgroundValues);
1203: }
1204:
1205: if (outerRect.y < innerRect.y) {
1206: Rectangle rect = new Rectangle(outerRect.x, outerRect.y,
1207: innerMaxX - outerRect.x, innerRect.y - outerRect.y);
1208: fillBackground(raster, rect, backgroundValues);
1209: }
1210:
1211: if (outerMaxX > innerMaxX) {
1212: Rectangle rect = new Rectangle(innerMaxX, outerRect.y,
1213: outerMaxX - innerMaxX, innerMaxY - outerRect.y);
1214: fillBackground(raster, rect, backgroundValues);
1215: }
1216:
1217: if (outerMaxY > innerMaxY) {
1218: Rectangle rect = new Rectangle(innerRect.x, innerMaxY,
1219: outerMaxX - innerRect.x, outerMaxY - innerMaxY);
1220: fillBackground(raster, rect, backgroundValues);
1221: }
1222: }
1223:
1224: /// ---- END Binary data handling methods ----
1225:
1226: /**
1227: * Given a kernel and the gain (sharpness) factor of an
1228: * UnsharpMask operation, compute a modified kernel that
1229: * would be equivalent to the specified unsharp operation.
1230: *
1231: * for UnsharpMask function we have the following formula:
1232: *
1233: * dst(i,j) = src(i,j) + gain *
1234: * (src(i,j) - SUM SUM K(l,m) * src(i+l,j+m))
1235: * l m
1236: *
1237: * Which can be written as :
1238: *
1239: * dst(i,j) = SUM SUM Q(l,m) * src(i+l,j+m),
1240: * l m
1241: *
1242: * where Q(0,0) = 1 + gain * (1 - K(0,0)), and
1243: * Q(l,m) = - gain * K(l,m) otherwise
1244: *
1245: * @param kernel the unsharp mask kernel
1246: * @param gain the unsharp mask gain (sharpness) factor.
1247: *
1248: * @return an equivalent convolution KernelJAI
1249: */
1250: public static KernelJAI getUnsharpMaskEquivalentKernel(
1251: KernelJAI kernel, float gain) {
1252:
1253: int width = kernel.getWidth();
1254: int height = kernel.getHeight();
1255: int xOrigin = kernel.getXOrigin();
1256: int yOrigin = kernel.getYOrigin();
1257:
1258: float oldData[] = kernel.getKernelData();
1259: float newData[] = new float[oldData.length];
1260:
1261: int k;
1262:
1263: for (k = 0; k < width * height; k++)
1264: newData[k] = -gain * oldData[k];
1265:
1266: k = yOrigin * width + xOrigin;
1267: newData[k] = 1.0f + gain * (1.0f - oldData[k]);
1268:
1269: return new KernelJAI(width, height, xOrigin, yOrigin, newData);
1270: }
1271:
1272: /**
1273: * Retrieve the indices of a set of tiles in row-major order with
1274: * the given tile index bounds in x and y.
1275: */
1276: public static final Point[] getTileIndices(int txmin, int txmax,
1277: int tymin, int tymax) {
1278: if (txmin > txmax || tymin > tymax) {
1279: return null;
1280: }
1281:
1282: Point[] tileIndices = new Point[(txmax - txmin + 1)
1283: * (tymax - tymin + 1)];
1284: int k = 0;
1285: for (int tj = tymin; tj <= tymax; tj++) {
1286: for (int ti = txmin; ti <= txmax; ti++) {
1287: tileIndices[k++] = new Point(ti, tj);
1288: }
1289: }
1290:
1291: return tileIndices;
1292: }
1293:
1294: /// Method for handling DeferrdData objects in ParameterBlocks.
1295:
1296: /**
1297: * If any <code>DeferredData</code> components are detected,
1298: * the argument is cloned and the <code>DeferredData</code>
1299: * object is replaced with what its <code>getData()</code> returns.
1300: */
1301: public static Vector evaluateParameters(Vector parameters) {
1302: if (parameters == null) {
1303: throw new IllegalArgumentException();
1304: }
1305:
1306: Vector paramEval = parameters;
1307:
1308: int size = parameters.size();
1309: for (int i = 0; i < size; i++) {
1310: Object element = parameters.get(i);
1311: if (element instanceof DeferredData) {
1312: if (paramEval == parameters) {
1313: paramEval = (Vector) parameters.clone();
1314: }
1315: paramEval.set(i, ((DeferredData) element).getData());
1316: }
1317: }
1318:
1319: return paramEval;
1320: }
1321:
1322: /**
1323: * If any <code>DeferredData</code> parameters are detected,
1324: * a new <code>ParameterBlock</code> is constructed and the
1325: * <code>DeferredData</code> object is replaced with what its
1326: * <code>getData()</code> returns.
1327: */
1328: public static ParameterBlock evaluateParameters(ParameterBlock pb) {
1329: if (pb == null) {
1330: throw new IllegalArgumentException();
1331: }
1332:
1333: Vector parameters = pb.getParameters();
1334: Vector paramEval = evaluateParameters(parameters);
1335: return paramEval == parameters ? pb : new ParameterBlock(pb
1336: .getSources(), paramEval);
1337: }
1338:
1339: /**
1340: * Derive a compatible <code>ColorModel</code> for the supplied
1341: * <code>SampleModel</code> using the method specified via the
1342: * <code>OpImage</code> configuration <code>Map</code>.
1343: *
1344: * @return a compatible <code>ColorModel</code> or <code>null</code>.
1345: */
1346: public static ColorModel getCompatibleColorModel(SampleModel sm,
1347: Map config) {
1348: ColorModel cm = null;
1349:
1350: if (config == null
1351: || !Boolean.FALSE.equals(config
1352: .get(JAI.KEY_DEFAULT_COLOR_MODEL_ENABLED))) {
1353:
1354: // Set the default ColorModel
1355:
1356: if (config != null
1357: && config
1358: .containsKey(JAI.KEY_DEFAULT_COLOR_MODEL_METHOD)) {
1359: // Attempt to retrieve the default CM Method.
1360: Method cmMethod = (Method) config
1361: .get(JAI.KEY_DEFAULT_COLOR_MODEL_METHOD);
1362:
1363: // Check method compatibility.
1364: Class[] paramTypes = cmMethod.getParameterTypes();
1365: if ((cmMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
1366: // Method must be static.
1367: throw new RuntimeException(JaiI18N
1368: .getString("ImageUtil1"));
1369: } else if (cmMethod.getReturnType() != ColorModel.class) {
1370: // Method must return a ColorModel.
1371: throw new RuntimeException(JaiI18N
1372: .getString("ImageUtil2"));
1373: } else if (paramTypes.length != 1
1374: || !paramTypes[0].equals(SampleModel.class)) {
1375: // Unique Method parameter must be a SampleModel.
1376: throw new RuntimeException(JaiI18N
1377: .getString("ImageUtil3"));
1378: }
1379:
1380: // Set the default ColorModel.
1381: try {
1382: // Attempt to use the supplied Method.
1383: Object[] args = new Object[] { sm };
1384: cm = (ColorModel) cmMethod.invoke(null, args);
1385: } catch (Exception e) {
1386: String message = JaiI18N.getString("ImageUtil4")
1387: + cmMethod.getName();
1388: sendExceptionToListener(message,
1389: new ImagingException(message, e));
1390: /*
1391: // XXX Is this a reasonable Exception to throw?
1392: throw new RuntimeException(cmMethod.getName()+" "+
1393: e.getMessage());
1394: */
1395: }
1396: } else { // No default method hint set.
1397: // Use PlanarImage method.
1398: cm = PlanarImage.createColorModel(sm);
1399: }
1400: }
1401:
1402: return cm;
1403: }
1404:
1405: /**
1406: * Converts the supplied <code>Exception</code>'s stack trace
1407: * to a <code>String</code>.
1408: */
1409: public static String getStackTraceString(Exception e) {
1410: ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
1411: PrintStream printStream = new PrintStream(byteStream);
1412: e.printStackTrace(printStream);
1413: printStream.flush();
1414: String stackTraceString = byteStream.toString();
1415: printStream.close();
1416: return stackTraceString;
1417: }
1418:
1419: public static ImagingListener getImagingListener(
1420: RenderingHints hints) {
1421: ImagingListener listener = null;
1422: if (hints != null)
1423: listener = (ImagingListener) hints
1424: .get(JAI.KEY_IMAGING_LISTENER);
1425:
1426: if (listener == null)
1427: listener = JAI.getDefaultInstance().getImagingListener();
1428: return listener;
1429: }
1430:
1431: public static ImagingListener getImagingListener(
1432: RenderContext context) {
1433: return getImagingListener(context.getRenderingHints());
1434: }
1435:
1436: /**
1437: * Generates a UID for the provided <code>Object</code>.
1438: * The counter for the objects that request an ID, the hashcode of the
1439: * class of the provided object, the hashcode of the provided object,
1440: * the current time in milli seconds, and a random number are
1441: * concatenated together in a <code>BigInteger</code>. This
1442: * <code>BigInteger</code> is returned as the unique ID.
1443: */
1444: public static synchronized Object generateID(Object owner) {
1445: Class c = owner.getClass();
1446: counter++;
1447:
1448: byte[] uid = new byte[32];
1449: int k = 0;
1450: for (int i = 7, j = 0; i >= 0; i--, j += 8)
1451: uid[k++] = (byte) (counter >> j);
1452: int hash = c.hashCode();
1453: for (int i = 3, j = 0; i >= 0; i--, j += 8)
1454: uid[k++] = (byte) (hash >> j);
1455: hash = owner.hashCode();
1456: for (int i = 3, j = 0; i >= 0; i--, j += 8)
1457: uid[k++] = (byte) (hash >> j);
1458: long time = System.currentTimeMillis();
1459: for (int i = 7, j = 0; i >= 0; i--, j += 8)
1460: uid[k++] = (byte) (time >> j);
1461: long rand = Double.doubleToLongBits(new Double(Math.random())
1462: .doubleValue());
1463: for (int i = 7, j = 0; i >= 0; i--, j += 8)
1464: uid[k++] = (byte) (rand >> j);
1465: return new BigInteger(uid);
1466: }
1467:
1468: static void sendExceptionToListener(String message, Exception e) {
1469: ImagingListener listener = getImagingListener((RenderingHints) null);
1470: listener.errorOccurred(message, e, ImageUtil.class, false);
1471: }
1472: }
|