0001: /*
0002: * $RCSfile: ImageUtil.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.7 $
0042: * $Date: 2007/08/28 18:45:06 $
0043: * $State: Exp $
0044: */
0045: package com.sun.media.imageioimpl.common;
0046:
0047: import java.awt.Point;
0048: import java.awt.Rectangle;
0049: import java.awt.Transparency;
0050: import java.awt.color.ColorSpace;
0051: import java.awt.color.ICC_ColorSpace;
0052: import java.awt.image.BufferedImage;
0053: import java.awt.image.ColorModel;
0054: import java.awt.image.ComponentColorModel;
0055: import java.awt.image.ComponentSampleModel;
0056: import java.awt.image.DataBuffer;
0057: import java.awt.image.DataBufferByte;
0058: import java.awt.image.DataBufferInt;
0059: import java.awt.image.DataBufferShort;
0060: import java.awt.image.DataBufferUShort;
0061: import java.awt.image.DirectColorModel;
0062: import java.awt.image.IndexColorModel;
0063: import java.awt.image.MultiPixelPackedSampleModel;
0064: import java.awt.image.Raster;
0065: import java.awt.image.RenderedImage;
0066: import java.awt.image.SampleModel;
0067: import java.awt.image.SinglePixelPackedSampleModel;
0068: import java.awt.image.WritableRaster;
0069: import java.io.IOException;
0070: import java.util.Arrays;
0071: import java.util.ArrayList;
0072: import java.util.Iterator;
0073: import java.util.List;
0074: import java.util.Locale;
0075:
0076: //import javax.imageio.ImageTypeSpecifier;
0077:
0078: import javax.imageio.IIOException;
0079: import javax.imageio.IIOImage;
0080: import javax.imageio.ImageReadParam;
0081: import javax.imageio.ImageTypeSpecifier;
0082: import javax.imageio.ImageWriter;
0083: import javax.imageio.spi.IIORegistry;
0084: import javax.imageio.spi.ImageReaderSpi;
0085: import javax.imageio.spi.ImageReaderWriterSpi;
0086: import javax.imageio.spi.ImageWriterSpi;
0087: import javax.imageio.spi.ServiceRegistry;
0088: import javax.imageio.stream.ImageInputStream;
0089:
0090: public class ImageUtil {
0091: /* XXX testing only
0092: public static void main(String[] args) {
0093: ImageTypeSpecifier bilevel =
0094: ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255},
0095: new byte[] {(byte)0, (byte)255},
0096: new byte[] {(byte)0, (byte)255},
0097: null, 1,
0098: DataBuffer.TYPE_BYTE);
0099: ImageTypeSpecifier gray =
0100: ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false);
0101: ImageTypeSpecifier grayAlpha =
0102: ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false,
0103: false);
0104: ImageTypeSpecifier rgb =
0105: ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
0106: new int[] {0, 1, 2},
0107: DataBuffer.TYPE_BYTE,
0108: false,
0109: false);
0110: ImageTypeSpecifier rgba =
0111: ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
0112: new int[] {0, 1, 2, 3},
0113: DataBuffer.TYPE_BYTE,
0114: true,
0115: false);
0116: ImageTypeSpecifier packed =
0117: ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
0118: 0xff000000,
0119: 0x00ff0000,
0120: 0x0000ff00,
0121: 0x000000ff,
0122: DataBuffer.TYPE_BYTE,
0123: false);
0124:
0125: SampleModel bandedSM =
0126: new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE,
0127: 1, 1, 15);
0128:
0129: System.out.println(createColorModel(bilevel.getSampleModel()));
0130: System.out.println(createColorModel(gray.getSampleModel()));
0131: System.out.println(createColorModel(grayAlpha.getSampleModel()));
0132: System.out.println(createColorModel(rgb.getSampleModel()));
0133: System.out.println(createColorModel(rgba.getSampleModel()));
0134: System.out.println(createColorModel(packed.getSampleModel()));
0135: System.out.println(createColorModel(bandedSM));
0136: }
0137: */
0138:
0139: /**
0140: * Creates a <code>ColorModel</code> that may be used with the
0141: * specified <code>SampleModel</code>. If a suitable
0142: * <code>ColorModel</code> cannot be found, this method returns
0143: * <code>null</code>.
0144: *
0145: * <p> Suitable <code>ColorModel</code>s are guaranteed to exist
0146: * for all instances of <code>ComponentSampleModel</code>.
0147: * For 1- and 3- banded <code>SampleModel</code>s, the returned
0148: * <code>ColorModel</code> will be opaque. For 2- and 4-banded
0149: * <code>SampleModel</code>s, the output will use alpha transparency
0150: * which is not premultiplied. 1- and 2-banded data will use a
0151: * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB
0152: * <code>ColorSpace</code>. Data with 5 or more bands will have a
0153: * <code>BogusColorSpace</code>.</p>
0154: *
0155: * <p>An instance of <code>DirectColorModel</code> will be created for
0156: * instances of <code>SinglePixelPackedSampleModel</code> with no more
0157: * than 4 bands.</p>
0158: *
0159: * <p>An instance of <code>IndexColorModel</code> will be created for
0160: * instances of <code>MultiPixelPackedSampleModel</code>. The colormap
0161: * will be a grayscale ramp with <code>1 << numberOfBits</code>
0162: * entries ranging from zero to at most 255.</p>
0163: *
0164: * @return An instance of <code>ColorModel</code> that is suitable for
0165: * the supplied <code>SampleModel</code>, or <code>null</code>.
0166: *
0167: * @throws IllegalArgumentException If <code>sampleModel</code> is
0168: * <code>null</code>.
0169: */
0170: public static final ColorModel createColorModel(
0171: SampleModel sampleModel) {
0172: // Check the parameter.
0173: if (sampleModel == null) {
0174: throw new IllegalArgumentException("sampleModel == null!");
0175: }
0176:
0177: // Get the data type.
0178: int dataType = sampleModel.getDataType();
0179:
0180: // Check the data type
0181: switch (dataType) {
0182: case DataBuffer.TYPE_BYTE:
0183: case DataBuffer.TYPE_USHORT:
0184: case DataBuffer.TYPE_SHORT:
0185: case DataBuffer.TYPE_INT:
0186: case DataBuffer.TYPE_FLOAT:
0187: case DataBuffer.TYPE_DOUBLE:
0188: break;
0189: default:
0190: // Return null for other types.
0191: return null;
0192: }
0193:
0194: // The return variable.
0195: ColorModel colorModel = null;
0196:
0197: // Get the sample size.
0198: int[] sampleSize = sampleModel.getSampleSize();
0199:
0200: // Create a Component ColorModel.
0201: if (sampleModel instanceof ComponentSampleModel) {
0202: // Get the number of bands.
0203: int numBands = sampleModel.getNumBands();
0204:
0205: // Determine the color space.
0206: ColorSpace colorSpace = null;
0207: if (numBands <= 2) {
0208: colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
0209: } else if (numBands <= 4) {
0210: colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
0211: } else {
0212: colorSpace = new BogusColorSpace(numBands);
0213: }
0214:
0215: boolean hasAlpha = (numBands == 2) || (numBands == 4);
0216: boolean isAlphaPremultiplied = false;
0217: int transparency = hasAlpha ? Transparency.TRANSLUCENT
0218: : Transparency.OPAQUE;
0219:
0220: colorModel = new ComponentColorModel(colorSpace,
0221: sampleSize, hasAlpha, isAlphaPremultiplied,
0222: transparency, dataType);
0223: } else if (sampleModel.getNumBands() <= 4
0224: && sampleModel instanceof SinglePixelPackedSampleModel) {
0225: SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sampleModel;
0226:
0227: int[] bitMasks = sppsm.getBitMasks();
0228: int rmask = 0;
0229: int gmask = 0;
0230: int bmask = 0;
0231: int amask = 0;
0232:
0233: int numBands = bitMasks.length;
0234: if (numBands <= 2) {
0235: rmask = gmask = bmask = bitMasks[0];
0236: if (numBands == 2) {
0237: amask = bitMasks[1];
0238: }
0239: } else {
0240: rmask = bitMasks[0];
0241: gmask = bitMasks[1];
0242: bmask = bitMasks[2];
0243: if (numBands == 4) {
0244: amask = bitMasks[3];
0245: }
0246: }
0247:
0248: int bits = 0;
0249: for (int i = 0; i < sampleSize.length; i++) {
0250: bits += sampleSize[i];
0251: }
0252:
0253: return new DirectColorModel(bits, rmask, gmask, bmask,
0254: amask);
0255:
0256: } else if (sampleModel instanceof MultiPixelPackedSampleModel) {
0257: // Load the colormap with a ramp.
0258: int bitsPerSample = sampleSize[0];
0259: int numEntries = 1 << bitsPerSample;
0260: byte[] map = new byte[numEntries];
0261: for (int i = 0; i < numEntries; i++) {
0262: map[i] = (byte) (i * 255 / (numEntries - 1));
0263: }
0264:
0265: colorModel = new IndexColorModel(bitsPerSample, numEntries,
0266: map, map, map);
0267:
0268: }
0269:
0270: return colorModel;
0271: }
0272:
0273: /**
0274: * For the case of binary data (<code>isBinary()</code> returns
0275: * <code>true</code>), return the binary data as a packed byte array.
0276: * The data will be packed as eight bits per byte with no bit offset,
0277: * i.e., the first bit in each image line will be the left-most of the
0278: * first byte of the line. The line stride in bytes will be
0279: * <code>(int)((getWidth()+7)/8)</code>. The length of the returned
0280: * array will be the line stride multiplied by <code>getHeight()</code>
0281: *
0282: * @return the binary data as a packed array of bytes with zero offset
0283: * of <code>null</code> if the data are not binary.
0284: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0285: * <code>false</code> with the <code>SampleModel</code> of the
0286: * supplied <code>Raster</code> as argument.
0287: */
0288: public static byte[] getPackedBinaryData(Raster raster,
0289: Rectangle rect) {
0290: SampleModel sm = raster.getSampleModel();
0291: if (!isBinary(sm)) {
0292: throw new IllegalArgumentException(I18N
0293: .getString("ImageUtil0"));
0294: }
0295:
0296: int rectX = rect.x;
0297: int rectY = rect.y;
0298: int rectWidth = rect.width;
0299: int rectHeight = rect.height;
0300:
0301: DataBuffer dataBuffer = raster.getDataBuffer();
0302:
0303: int dx = rectX - raster.getSampleModelTranslateX();
0304: int dy = rectY - raster.getSampleModelTranslateY();
0305:
0306: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0307: int lineStride = mpp.getScanlineStride();
0308: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0309: int bitOffset = mpp.getBitOffset(dx);
0310:
0311: int numBytesPerRow = (rectWidth + 7) / 8;
0312: if (dataBuffer instanceof DataBufferByte
0313: && eltOffset == 0
0314: && bitOffset == 0
0315: && numBytesPerRow == lineStride
0316: && ((DataBufferByte) dataBuffer).getData().length == numBytesPerRow
0317: * rectHeight) {
0318: return ((DataBufferByte) dataBuffer).getData();
0319: }
0320:
0321: byte[] binaryDataArray = new byte[numBytesPerRow * rectHeight];
0322:
0323: int b = 0;
0324:
0325: if (bitOffset == 0) {
0326: if (dataBuffer instanceof DataBufferByte) {
0327: byte[] data = ((DataBufferByte) dataBuffer).getData();
0328: int stride = numBytesPerRow;
0329: int offset = 0;
0330: for (int y = 0; y < rectHeight; y++) {
0331: System.arraycopy(data, eltOffset, binaryDataArray,
0332: offset, stride);
0333: offset += stride;
0334: eltOffset += lineStride;
0335: }
0336: } else if (dataBuffer instanceof DataBufferShort
0337: || dataBuffer instanceof DataBufferUShort) {
0338: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0339: .getData()
0340: : ((DataBufferUShort) dataBuffer).getData();
0341:
0342: for (int y = 0; y < rectHeight; y++) {
0343: int xRemaining = rectWidth;
0344: int i = eltOffset;
0345: while (xRemaining > 8) {
0346: short datum = data[i++];
0347: binaryDataArray[b++] = (byte) ((datum >>> 8) & 0xFF);
0348: binaryDataArray[b++] = (byte) (datum & 0xFF);
0349: xRemaining -= 16;
0350: }
0351: if (xRemaining > 0) {
0352: binaryDataArray[b++] = (byte) ((data[i] >>> 8) & 0XFF);
0353: }
0354: eltOffset += lineStride;
0355: }
0356: } else if (dataBuffer instanceof DataBufferInt) {
0357: int[] data = ((DataBufferInt) dataBuffer).getData();
0358:
0359: for (int y = 0; y < rectHeight; y++) {
0360: int xRemaining = rectWidth;
0361: int i = eltOffset;
0362: while (xRemaining > 24) {
0363: int datum = data[i++];
0364: binaryDataArray[b++] = (byte) ((datum >>> 24) & 0xFF);
0365: binaryDataArray[b++] = (byte) ((datum >>> 16) & 0xFF);
0366: binaryDataArray[b++] = (byte) ((datum >>> 8) & 0xFF);
0367: binaryDataArray[b++] = (byte) (datum & 0xFF);
0368: xRemaining -= 32;
0369: }
0370: int shift = 24;
0371: while (xRemaining > 0) {
0372: binaryDataArray[b++] = (byte) ((data[i] >>> shift) & 0xFF);
0373: shift -= 8;
0374: xRemaining -= 8;
0375: }
0376: eltOffset += lineStride;
0377: }
0378: }
0379: } else { // bitOffset != 0
0380: if (dataBuffer instanceof DataBufferByte) {
0381: byte[] data = ((DataBufferByte) dataBuffer).getData();
0382:
0383: if ((bitOffset & 7) == 0) {
0384: int stride = numBytesPerRow;
0385: int offset = 0;
0386: for (int y = 0; y < rectHeight; y++) {
0387: System.arraycopy(data, eltOffset,
0388: binaryDataArray, offset, stride);
0389: offset += stride;
0390: eltOffset += lineStride;
0391: }
0392: } else { // bitOffset % 8 != 0
0393: int leftShift = bitOffset & 7;
0394: int rightShift = 8 - leftShift;
0395: for (int y = 0; y < rectHeight; y++) {
0396: int i = eltOffset;
0397: int xRemaining = rectWidth;
0398: while (xRemaining > 0) {
0399: if (xRemaining > rightShift) {
0400: binaryDataArray[b++] = (byte) (((data[i++] & 0xFF) << leftShift) | ((data[i] & 0xFF) >>> rightShift));
0401: } else {
0402: binaryDataArray[b++] = (byte) ((data[i] & 0xFF) << leftShift);
0403: }
0404: xRemaining -= 8;
0405: }
0406: eltOffset += lineStride;
0407: }
0408: }
0409: } else if (dataBuffer instanceof DataBufferShort
0410: || dataBuffer instanceof DataBufferUShort) {
0411: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0412: .getData()
0413: : ((DataBufferUShort) dataBuffer).getData();
0414:
0415: for (int y = 0; y < rectHeight; y++) {
0416: int bOffset = bitOffset;
0417: for (int x = 0; x < rectWidth; x += 8, bOffset += 8) {
0418: int i = eltOffset + bOffset / 16;
0419: int mod = bOffset % 16;
0420: int left = data[i] & 0xFFFF;
0421: if (mod <= 8) {
0422: binaryDataArray[b++] = (byte) (left >>> (8 - mod));
0423: } else {
0424: int delta = mod - 8;
0425: int right = data[i + 1] & 0xFFFF;
0426: binaryDataArray[b++] = (byte) ((left << delta) | (right >>> (16 - delta)));
0427: }
0428: }
0429: eltOffset += lineStride;
0430: }
0431: } else if (dataBuffer instanceof DataBufferInt) {
0432: int[] data = ((DataBufferInt) dataBuffer).getData();
0433:
0434: for (int y = 0; y < rectHeight; y++) {
0435: int bOffset = bitOffset;
0436: for (int x = 0; x < rectWidth; x += 8, bOffset += 8) {
0437: int i = eltOffset + bOffset / 32;
0438: int mod = bOffset % 32;
0439: int left = data[i];
0440: if (mod <= 24) {
0441: binaryDataArray[b++] = (byte) (left >>> (24 - mod));
0442: } else {
0443: int delta = mod - 24;
0444: int right = data[i + 1];
0445: binaryDataArray[b++] = (byte) ((left << delta) | (right >>> (32 - delta)));
0446: }
0447: }
0448: eltOffset += lineStride;
0449: }
0450: }
0451: }
0452:
0453: return binaryDataArray;
0454: }
0455:
0456: /**
0457: * Returns the binary data unpacked into an array of bytes.
0458: * The line stride will be the width of the <code>Raster</code>.
0459: *
0460: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0461: * <code>false</code> with the <code>SampleModel</code> of the
0462: * supplied <code>Raster</code> as argument.
0463: */
0464: public static byte[] getUnpackedBinaryData(Raster raster,
0465: Rectangle rect) {
0466: SampleModel sm = raster.getSampleModel();
0467: if (!isBinary(sm)) {
0468: throw new IllegalArgumentException(I18N
0469: .getString("ImageUtil0"));
0470: }
0471:
0472: int rectX = rect.x;
0473: int rectY = rect.y;
0474: int rectWidth = rect.width;
0475: int rectHeight = rect.height;
0476:
0477: DataBuffer dataBuffer = raster.getDataBuffer();
0478:
0479: int dx = rectX - raster.getSampleModelTranslateX();
0480: int dy = rectY - raster.getSampleModelTranslateY();
0481:
0482: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0483: int lineStride = mpp.getScanlineStride();
0484: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0485: int bitOffset = mpp.getBitOffset(dx);
0486:
0487: byte[] bdata = new byte[rectWidth * rectHeight];
0488: int maxY = rectY + rectHeight;
0489: int maxX = rectX + rectWidth;
0490: int k = 0;
0491:
0492: if (dataBuffer instanceof DataBufferByte) {
0493: byte[] data = ((DataBufferByte) dataBuffer).getData();
0494: for (int y = rectY; y < maxY; y++) {
0495: int bOffset = eltOffset * 8 + bitOffset;
0496: for (int x = rectX; x < maxX; x++) {
0497: byte b = data[bOffset / 8];
0498: bdata[k++] = (byte) ((b >>> (7 - bOffset & 7)) & 0x0000001);
0499: bOffset++;
0500: }
0501: eltOffset += lineStride;
0502: }
0503: } else if (dataBuffer instanceof DataBufferShort
0504: || dataBuffer instanceof DataBufferUShort) {
0505: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0506: .getData()
0507: : ((DataBufferUShort) dataBuffer).getData();
0508: for (int y = rectY; y < maxY; y++) {
0509: int bOffset = eltOffset * 16 + bitOffset;
0510: for (int x = rectX; x < maxX; x++) {
0511: short s = data[bOffset / 16];
0512: bdata[k++] = (byte) ((s >>> (15 - bOffset % 16)) & 0x0000001);
0513: bOffset++;
0514: }
0515: eltOffset += lineStride;
0516: }
0517: } else if (dataBuffer instanceof DataBufferInt) {
0518: int[] data = ((DataBufferInt) dataBuffer).getData();
0519: for (int y = rectY; y < maxY; y++) {
0520: int bOffset = eltOffset * 32 + bitOffset;
0521: for (int x = rectX; x < maxX; x++) {
0522: int i = data[bOffset / 32];
0523: bdata[k++] = (byte) ((i >>> (31 - bOffset % 32)) & 0x0000001);
0524: bOffset++;
0525: }
0526: eltOffset += lineStride;
0527: }
0528: }
0529:
0530: return bdata;
0531: }
0532:
0533: /**
0534: * Sets the supplied <code>Raster</code>'s data from an array
0535: * of packed binary data of the form returned by
0536: * <code>getPackedBinaryData()</code>.
0537: *
0538: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0539: * <code>false</code> with the <code>SampleModel</code> of the
0540: * supplied <code>Raster</code> as argument.
0541: */
0542: public static void setPackedBinaryData(byte[] binaryDataArray,
0543: WritableRaster raster, Rectangle rect) {
0544: SampleModel sm = raster.getSampleModel();
0545: if (!isBinary(sm)) {
0546: throw new IllegalArgumentException(I18N
0547: .getString("ImageUtil0"));
0548: }
0549:
0550: int rectX = rect.x;
0551: int rectY = rect.y;
0552: int rectWidth = rect.width;
0553: int rectHeight = rect.height;
0554:
0555: DataBuffer dataBuffer = raster.getDataBuffer();
0556:
0557: int dx = rectX - raster.getSampleModelTranslateX();
0558: int dy = rectY - raster.getSampleModelTranslateY();
0559:
0560: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0561: int lineStride = mpp.getScanlineStride();
0562: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0563: int bitOffset = mpp.getBitOffset(dx);
0564:
0565: int b = 0;
0566:
0567: if (bitOffset == 0) {
0568: if (dataBuffer instanceof DataBufferByte) {
0569: byte[] data = ((DataBufferByte) dataBuffer).getData();
0570: if (data == binaryDataArray) {
0571: // Optimal case: simply return.
0572: return;
0573: }
0574: int stride = (rectWidth + 7) / 8;
0575: int offset = 0;
0576: for (int y = 0; y < rectHeight; y++) {
0577: System.arraycopy(binaryDataArray, offset, data,
0578: eltOffset, stride);
0579: offset += stride;
0580: eltOffset += lineStride;
0581: }
0582: } else if (dataBuffer instanceof DataBufferShort
0583: || dataBuffer instanceof DataBufferUShort) {
0584: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0585: .getData()
0586: : ((DataBufferUShort) dataBuffer).getData();
0587:
0588: for (int y = 0; y < rectHeight; y++) {
0589: int xRemaining = rectWidth;
0590: int i = eltOffset;
0591: while (xRemaining > 8) {
0592: data[i++] = (short) (((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF));
0593: xRemaining -= 16;
0594: }
0595: if (xRemaining > 0) {
0596: data[i++] = (short) ((binaryDataArray[b++] & 0xFF) << 8);
0597: }
0598: eltOffset += lineStride;
0599: }
0600: } else if (dataBuffer instanceof DataBufferInt) {
0601: int[] data = ((DataBufferInt) dataBuffer).getData();
0602:
0603: for (int y = 0; y < rectHeight; y++) {
0604: int xRemaining = rectWidth;
0605: int i = eltOffset;
0606: while (xRemaining > 24) {
0607: data[i++] = (int) (((binaryDataArray[b++] & 0xFF) << 24)
0608: | ((binaryDataArray[b++] & 0xFF) << 16)
0609: | ((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF));
0610: xRemaining -= 32;
0611: }
0612: int shift = 24;
0613: while (xRemaining > 0) {
0614: data[i] |= (int) ((binaryDataArray[b++] & 0xFF) << shift);
0615: shift -= 8;
0616: xRemaining -= 8;
0617: }
0618: eltOffset += lineStride;
0619: }
0620: }
0621: } else { // bitOffset != 0
0622: int stride = (rectWidth + 7) / 8;
0623: int offset = 0;
0624: if (dataBuffer instanceof DataBufferByte) {
0625: byte[] data = ((DataBufferByte) dataBuffer).getData();
0626:
0627: if ((bitOffset & 7) == 0) {
0628: for (int y = 0; y < rectHeight; y++) {
0629: System.arraycopy(binaryDataArray, offset, data,
0630: eltOffset, stride);
0631: offset += stride;
0632: eltOffset += lineStride;
0633: }
0634: } else { // bitOffset % 8 != 0
0635: int rightShift = bitOffset & 7;
0636: int leftShift = 8 - rightShift;
0637: int leftShift8 = 8 + leftShift;
0638: int mask = (byte) (255 << leftShift);
0639: int mask1 = (byte) ~mask;
0640:
0641: for (int y = 0; y < rectHeight; y++) {
0642: int i = eltOffset;
0643: int xRemaining = rectWidth;
0644: while (xRemaining > 0) {
0645: byte datum = binaryDataArray[b++];
0646:
0647: if (xRemaining > leftShift8) {
0648: // when all the bits in this BYTE will be set
0649: // into the data buffer.
0650: data[i] = (byte) ((data[i] & mask) | ((datum & 0xFF) >>> rightShift));
0651: data[++i] = (byte) ((datum & 0xFF) << leftShift);
0652: } else if (xRemaining > leftShift) {
0653: // All the "leftShift" high bits will be set
0654: // into the data buffer. But not all the
0655: // "rightShift" low bits will be set.
0656: data[i] = (byte) ((data[i] & mask) | ((datum & 0xFF) >>> rightShift));
0657: i++;
0658: data[i] = (byte) ((data[i] & mask1) | ((datum & 0xFF) << leftShift));
0659: } else {
0660: // Less than "leftShift" high bits will be set.
0661: int remainMask = (1 << leftShift
0662: - xRemaining) - 1;
0663: data[i] = (byte) ((data[i] & (mask | remainMask)) | (datum & 0xFF) >>> rightShift
0664: & ~remainMask);
0665: }
0666: xRemaining -= 8;
0667: }
0668: eltOffset += lineStride;
0669: }
0670: }
0671: } else if (dataBuffer instanceof DataBufferShort
0672: || dataBuffer instanceof DataBufferUShort) {
0673: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0674: .getData()
0675: : ((DataBufferUShort) dataBuffer).getData();
0676:
0677: int rightShift = bitOffset & 7;
0678: int leftShift = 8 - rightShift;
0679: int leftShift16 = 16 + leftShift;
0680: int mask = (short) (~(255 << leftShift));
0681: int mask1 = (short) (65535 << leftShift);
0682: int mask2 = (short) ~mask1;
0683:
0684: for (int y = 0; y < rectHeight; y++) {
0685: int bOffset = bitOffset;
0686: int xRemaining = rectWidth;
0687: for (int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) {
0688: int i = eltOffset + (bOffset >> 4);
0689: int mod = bOffset & 15;
0690: int datum = binaryDataArray[b++] & 0xFF;
0691: if (mod <= 8) {
0692: // This BYTE is set into one SHORT
0693: if (xRemaining < 8) {
0694: // Mask the bits to be set.
0695: datum &= 255 << 8 - xRemaining;
0696: }
0697: data[i] = (short) ((data[i] & mask) | (datum << leftShift));
0698: } else if (xRemaining > leftShift16) {
0699: // This BYTE will be set into two SHORTs
0700: data[i] = (short) ((data[i] & mask1) | ((datum >>> rightShift) & 0xFFFF));
0701: data[++i] = (short) ((datum << leftShift) & 0xFFFF);
0702: } else if (xRemaining > leftShift) {
0703: // This BYTE will be set into two SHORTs;
0704: // But not all the low bits will be set into SHORT
0705: data[i] = (short) ((data[i] & mask1) | ((datum >>> rightShift) & 0xFFFF));
0706: i++;
0707: data[i] = (short) ((data[i] & mask2) | ((datum << leftShift) & 0xFFFF));
0708: } else {
0709: // Only some of the high bits will be set into
0710: // SHORTs
0711: int remainMask = (1 << leftShift
0712: - xRemaining) - 1;
0713: data[i] = (short) ((data[i] & (mask1 | remainMask)) | ((datum >>> rightShift) & 0xFFFF & ~remainMask));
0714: }
0715: }
0716: eltOffset += lineStride;
0717: }
0718: } else if (dataBuffer instanceof DataBufferInt) {
0719: int[] data = ((DataBufferInt) dataBuffer).getData();
0720: int rightShift = bitOffset & 7;
0721: int leftShift = 8 - rightShift;
0722: int leftShift32 = 32 + leftShift;
0723: int mask = 0xFFFFFFFF << leftShift;
0724: int mask1 = ~mask;
0725:
0726: for (int y = 0; y < rectHeight; y++) {
0727: int bOffset = bitOffset;
0728: int xRemaining = rectWidth;
0729: for (int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) {
0730: int i = eltOffset + (bOffset >> 5);
0731: int mod = bOffset & 31;
0732: int datum = binaryDataArray[b++] & 0xFF;
0733: if (mod <= 24) {
0734: // This BYTE is set into one INT
0735: int shift = 24 - mod;
0736: if (xRemaining < 8) {
0737: // Mask the bits to be set.
0738: datum &= 255 << 8 - xRemaining;
0739: }
0740: data[i] = (data[i] & (~(255 << shift)))
0741: | (datum << shift);
0742: } else if (xRemaining > leftShift32) {
0743: // All the bits of this BYTE will be set into two INTs
0744: data[i] = (data[i] & mask)
0745: | (datum >>> rightShift);
0746: data[++i] = datum << leftShift;
0747: } else if (xRemaining > leftShift) {
0748: // This BYTE will be set into two INTs;
0749: // But not all the low bits will be set into INT
0750: data[i] = (data[i] & mask)
0751: | (datum >>> rightShift);
0752: i++;
0753: data[i] = (data[i] & mask1)
0754: | (datum << leftShift);
0755: } else {
0756: // Only some of the high bits will be set into INT
0757: int remainMask = (1 << leftShift
0758: - xRemaining) - 1;
0759: data[i] = (data[i] & (mask | remainMask))
0760: | (datum >>> rightShift & ~remainMask);
0761: }
0762: }
0763: eltOffset += lineStride;
0764: }
0765: }
0766: }
0767: }
0768:
0769: /**
0770: * Copies data into the packed array of the <code>Raster</code>
0771: * from an array of unpacked data of the form returned by
0772: * <code>getUnpackedBinaryData()</code>.
0773: *
0774: * <p> If the data are binary, then the target bit will be set if
0775: * and only if the corresponding byte is non-zero.
0776: *
0777: * @throws IllegalArgumentException if <code>isBinary()</code> returns
0778: * <code>false</code> with the <code>SampleModel</code> of the
0779: * supplied <code>Raster</code> as argument.
0780: */
0781: public static void setUnpackedBinaryData(byte[] bdata,
0782: WritableRaster raster, Rectangle rect) {
0783: SampleModel sm = raster.getSampleModel();
0784: if (!isBinary(sm)) {
0785: throw new IllegalArgumentException(I18N
0786: .getString("ImageUtil0"));
0787: }
0788:
0789: int rectX = rect.x;
0790: int rectY = rect.y;
0791: int rectWidth = rect.width;
0792: int rectHeight = rect.height;
0793:
0794: DataBuffer dataBuffer = raster.getDataBuffer();
0795:
0796: int dx = rectX - raster.getSampleModelTranslateX();
0797: int dy = rectY - raster.getSampleModelTranslateY();
0798:
0799: MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) sm;
0800: int lineStride = mpp.getScanlineStride();
0801: int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
0802: int bitOffset = mpp.getBitOffset(dx);
0803:
0804: int k = 0;
0805:
0806: if (dataBuffer instanceof DataBufferByte) {
0807: byte[] data = ((DataBufferByte) dataBuffer).getData();
0808: for (int y = 0; y < rectHeight; y++) {
0809: int bOffset = eltOffset * 8 + bitOffset;
0810: for (int x = 0; x < rectWidth; x++) {
0811: if (bdata[k++] != (byte) 0) {
0812: data[bOffset / 8] |= (byte) (0x00000001 << (7 - bOffset & 7));
0813: }
0814: bOffset++;
0815: }
0816: eltOffset += lineStride;
0817: }
0818: } else if (dataBuffer instanceof DataBufferShort
0819: || dataBuffer instanceof DataBufferUShort) {
0820: short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort) dataBuffer)
0821: .getData()
0822: : ((DataBufferUShort) dataBuffer).getData();
0823: for (int y = 0; y < rectHeight; y++) {
0824: int bOffset = eltOffset * 16 + bitOffset;
0825: for (int x = 0; x < rectWidth; x++) {
0826: if (bdata[k++] != (byte) 0) {
0827: data[bOffset / 16] |= (short) (0x00000001 << (15 - bOffset % 16));
0828: }
0829: bOffset++;
0830: }
0831: eltOffset += lineStride;
0832: }
0833: } else if (dataBuffer instanceof DataBufferInt) {
0834: int[] data = ((DataBufferInt) dataBuffer).getData();
0835: for (int y = 0; y < rectHeight; y++) {
0836: int bOffset = eltOffset * 32 + bitOffset;
0837: for (int x = 0; x < rectWidth; x++) {
0838: if (bdata[k++] != (byte) 0) {
0839: data[bOffset / 32] |= (int) (0x00000001 << (31 - bOffset % 32));
0840: }
0841: bOffset++;
0842: }
0843: eltOffset += lineStride;
0844: }
0845: }
0846: }
0847:
0848: public static boolean isBinary(SampleModel sm) {
0849: return sm instanceof MultiPixelPackedSampleModel
0850: && ((MultiPixelPackedSampleModel) sm)
0851: .getPixelBitStride() == 1
0852: && sm.getNumBands() == 1;
0853: }
0854:
0855: public static ColorModel createColorModel(ColorSpace colorSpace,
0856: SampleModel sampleModel) {
0857: ColorModel colorModel = null;
0858:
0859: if (sampleModel == null) {
0860: throw new IllegalArgumentException(I18N
0861: .getString("ImageUtil1"));
0862: }
0863:
0864: int numBands = sampleModel.getNumBands();
0865: if (numBands < 1 || numBands > 4) {
0866: return null;
0867: }
0868:
0869: int dataType = sampleModel.getDataType();
0870: if (sampleModel instanceof ComponentSampleModel) {
0871: if (dataType < DataBuffer.TYPE_BYTE ||
0872: //dataType == DataBuffer.TYPE_SHORT ||
0873: dataType > DataBuffer.TYPE_DOUBLE) {
0874: return null;
0875: }
0876:
0877: if (colorSpace == null)
0878: colorSpace = numBands <= 2 ? ColorSpace
0879: .getInstance(ColorSpace.CS_GRAY) : ColorSpace
0880: .getInstance(ColorSpace.CS_sRGB);
0881:
0882: boolean useAlpha = (numBands == 2) || (numBands == 4);
0883: int transparency = useAlpha ? Transparency.TRANSLUCENT
0884: : Transparency.OPAQUE;
0885:
0886: boolean premultiplied = false;
0887:
0888: int dataTypeSize = DataBuffer.getDataTypeSize(dataType);
0889: int[] bits = new int[numBands];
0890: for (int i = 0; i < numBands; i++) {
0891: bits[i] = dataTypeSize;
0892: }
0893:
0894: colorModel = new ComponentColorModel(colorSpace, bits,
0895: useAlpha, premultiplied, transparency, dataType);
0896: } else if (sampleModel instanceof SinglePixelPackedSampleModel) {
0897: SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sampleModel;
0898:
0899: int[] bitMasks = sppsm.getBitMasks();
0900: int rmask = 0;
0901: int gmask = 0;
0902: int bmask = 0;
0903: int amask = 0;
0904:
0905: numBands = bitMasks.length;
0906: if (numBands <= 2) {
0907: rmask = gmask = bmask = bitMasks[0];
0908: if (numBands == 2) {
0909: amask = bitMasks[1];
0910: }
0911: } else {
0912: rmask = bitMasks[0];
0913: gmask = bitMasks[1];
0914: bmask = bitMasks[2];
0915: if (numBands == 4) {
0916: amask = bitMasks[3];
0917: }
0918: }
0919:
0920: int[] sampleSize = sppsm.getSampleSize();
0921: int bits = 0;
0922: for (int i = 0; i < sampleSize.length; i++) {
0923: bits += sampleSize[i];
0924: }
0925:
0926: if (colorSpace == null)
0927: colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
0928:
0929: colorModel = new DirectColorModel(colorSpace, bits, rmask,
0930: gmask, bmask, amask, false, sampleModel
0931: .getDataType());
0932: } else if (sampleModel instanceof MultiPixelPackedSampleModel) {
0933: int bits = ((MultiPixelPackedSampleModel) sampleModel)
0934: .getPixelBitStride();
0935: int size = 1 << bits;
0936: byte[] comp = new byte[size];
0937:
0938: for (int i = 0; i < size; i++)
0939: comp[i] = (byte) (255 * i / (size - 1));
0940:
0941: colorModel = new IndexColorModel(bits, size, comp, comp,
0942: comp);
0943: }
0944:
0945: return colorModel;
0946: }
0947:
0948: public static int getElementSize(SampleModel sm) {
0949: int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
0950:
0951: if (sm instanceof MultiPixelPackedSampleModel) {
0952: MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel) sm;
0953: return mppsm.getSampleSize(0) * mppsm.getNumBands();
0954: } else if (sm instanceof ComponentSampleModel) {
0955: return sm.getNumBands() * elementSize;
0956: } else if (sm instanceof SinglePixelPackedSampleModel) {
0957: return elementSize;
0958: }
0959:
0960: return elementSize * sm.getNumBands();
0961:
0962: }
0963:
0964: public static long getTileSize(SampleModel sm) {
0965: int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
0966:
0967: if (sm instanceof MultiPixelPackedSampleModel) {
0968: MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel) sm;
0969: return (mppsm.getScanlineStride() * mppsm.getHeight() + (mppsm
0970: .getDataBitOffset()
0971: + elementSize - 1)
0972: / elementSize)
0973: * ((elementSize + 7) / 8);
0974: } else if (sm instanceof ComponentSampleModel) {
0975: ComponentSampleModel csm = (ComponentSampleModel) sm;
0976: int[] bandOffsets = csm.getBandOffsets();
0977: int maxBandOff = bandOffsets[0];
0978: for (int i = 1; i < bandOffsets.length; i++)
0979: maxBandOff = Math.max(maxBandOff, bandOffsets[i]);
0980:
0981: long size = 0;
0982: int pixelStride = csm.getPixelStride();
0983: int scanlineStride = csm.getScanlineStride();
0984: if (maxBandOff >= 0)
0985: size += maxBandOff + 1;
0986: if (pixelStride > 0)
0987: size += pixelStride * (sm.getWidth() - 1);
0988: if (scanlineStride > 0)
0989: size += scanlineStride * (sm.getHeight() - 1);
0990:
0991: int[] bankIndices = csm.getBankIndices();
0992: maxBandOff = bankIndices[0];
0993: for (int i = 1; i < bankIndices.length; i++)
0994: maxBandOff = Math.max(maxBandOff, bankIndices[i]);
0995: return size * (maxBandOff + 1) * ((elementSize + 7) / 8);
0996: } else if (sm instanceof SinglePixelPackedSampleModel) {
0997: SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
0998: long size = sppsm.getScanlineStride()
0999: * (sppsm.getHeight() - 1) + sppsm.getWidth();
1000: return size * ((elementSize + 7) / 8);
1001: }
1002:
1003: return 0;
1004: }
1005:
1006: public static long getBandSize(SampleModel sm) {
1007: int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
1008:
1009: if (sm instanceof ComponentSampleModel) {
1010: ComponentSampleModel csm = (ComponentSampleModel) sm;
1011: int pixelStride = csm.getPixelStride();
1012: int scanlineStride = csm.getScanlineStride();
1013: long size = Math.min(pixelStride, scanlineStride);
1014:
1015: if (pixelStride > 0)
1016: size += pixelStride * (sm.getWidth() - 1);
1017: if (scanlineStride > 0)
1018: size += scanlineStride * (sm.getHeight() - 1);
1019: return size * ((elementSize + 7) / 8);
1020: } else
1021: return getTileSize(sm);
1022: }
1023:
1024: /**
1025: * Tests whether the color indices represent a gray-scale image with
1026: * the indicated number of bits over the color component range [0,255].
1027: * The grayscale mapping may be inverted, i.e., 0 -> 255 and
1028: * mapSize -> 0.
1029: *
1030: * @param icm The gray-to-color mapping.
1031: * @return Whether the <code>IndexColorModel</code> maps index
1032: * <code>i</code> to <code>((255*i)/icm.getMapSize()-1)</code>.
1033: * @throws IllegalArgumentException if <code>icm</code> is
1034: * <code>null</code>.
1035: */
1036: public static boolean isGrayscaleMapping(IndexColorModel icm) {
1037: if (icm == null) {
1038: throw new IllegalArgumentException("icm == null!");
1039: }
1040:
1041: // Get colormap size and contents.
1042: int mapSize = icm.getMapSize();
1043:
1044: byte[] r = new byte[mapSize];
1045: byte[] g = new byte[mapSize];
1046: byte[] b = new byte[mapSize];
1047:
1048: icm.getReds(r);
1049: icm.getGreens(g);
1050: icm.getBlues(b);
1051:
1052: boolean isGrayToColor = true;
1053:
1054: // Check ascending ramp.
1055: for (int i = 0; i < mapSize; i++) {
1056: byte temp = (byte) (i * 255 / (mapSize - 1));
1057:
1058: if (r[i] != temp || g[i] != temp || b[i] != temp) {
1059: isGrayToColor = false;
1060: break;
1061: }
1062: }
1063:
1064: if (!isGrayToColor) {
1065: isGrayToColor = true;
1066:
1067: // Check descending ramp.
1068: for (int i = 0, j = mapSize - 1; i < mapSize; i++, j--) {
1069: byte temp = (byte) (j * 255 / (mapSize - 1));
1070:
1071: if (r[i] != temp || g[i] != temp || b[i] != temp) {
1072: isGrayToColor = false;
1073: break;
1074: }
1075: }
1076: }
1077:
1078: return isGrayToColor;
1079: }
1080:
1081: /**
1082: * Tests whether the color indices represent a gray-scale image.
1083: *
1084: * @param r The red channel color indices.
1085: * @param g The green channel color indices.
1086: * @param b The blue channel color indices.
1087: * @return If all the indices have 256 entries, and are identical mappings,
1088: * return <code>true</code>; otherwise, return <code>false</code>.
1089: */
1090: public static boolean isIndicesForGrayscale(byte[] r, byte[] g,
1091: byte[] b) {
1092: if (r.length != g.length || r.length != b.length)
1093: return false;
1094:
1095: int size = r.length;
1096:
1097: if (size != 256)
1098: return false;
1099:
1100: for (int i = 0; i < size; i++) {
1101: byte temp = (byte) i;
1102:
1103: if (r[i] != temp || g[i] != temp || b[i] != temp)
1104: return false;
1105: }
1106:
1107: return true;
1108: }
1109:
1110: /** Converts the provided object to <code>String</code> */
1111: public static String convertObjectToString(Object obj) {
1112: if (obj == null)
1113: return "";
1114:
1115: String s = "";
1116: if (obj instanceof byte[]) {
1117: byte[] bArray = (byte[]) obj;
1118: for (int i = 0; i < bArray.length; i++)
1119: s += bArray[i] + " ";
1120: return s;
1121: }
1122:
1123: if (obj instanceof int[]) {
1124: int[] iArray = (int[]) obj;
1125: for (int i = 0; i < iArray.length; i++)
1126: s += iArray[i] + " ";
1127: return s;
1128: }
1129:
1130: if (obj instanceof short[]) {
1131: short[] sArray = (short[]) obj;
1132: for (int i = 0; i < sArray.length; i++)
1133: s += sArray[i] + " ";
1134: return s;
1135: }
1136:
1137: return obj.toString();
1138:
1139: }
1140:
1141: /** Checks that the provided <code>ImageWriter</code> can encode
1142: * the provided <code>ImageTypeSpecifier</code> or not. If not, an
1143: * <code>IIOException</code> will be thrown.
1144: * @param writer The provided <code>ImageWriter</code>.
1145: * @param type The image to be tested.
1146: * @throws IIOException If the writer cannot encoded the provided image.
1147: */
1148: public static final void canEncodeImage(ImageWriter writer,
1149: ImageTypeSpecifier type) throws IIOException {
1150: ImageWriterSpi spi = writer.getOriginatingProvider();
1151:
1152: if (type != null && spi != null && !spi.canEncodeImage(type)) {
1153: throw new IIOException(I18N.getString("ImageUtil2") + " "
1154: + writer.getClass().getName());
1155: }
1156: }
1157:
1158: /** Checks that the provided <code>ImageWriter</code> can encode
1159: * the provided <code>ColorModel</code> and <code>SampleModel</code>.
1160: * If not, an <code>IIOException</code> will be thrown.
1161: * @param writer The provided <code>ImageWriter</code>.
1162: * @param colorModel The provided <code>ColorModel</code>.
1163: * @param sampleModel The provided <code>SampleModel</code>.
1164: * @throws IIOException If the writer cannot encoded the provided image.
1165: */
1166: public static final void canEncodeImage(ImageWriter writer,
1167: ColorModel colorModel, SampleModel sampleModel)
1168: throws IIOException {
1169: ImageTypeSpecifier type = null;
1170: if (colorModel != null && sampleModel != null)
1171: type = new ImageTypeSpecifier(colorModel, sampleModel);
1172: canEncodeImage(writer, type);
1173: }
1174:
1175: /**
1176: * Returns whether the image has contiguous data across rows.
1177: */
1178: public static final boolean imageIsContiguous(RenderedImage image) {
1179: SampleModel sm;
1180: if (image instanceof BufferedImage) {
1181: WritableRaster ras = ((BufferedImage) image).getRaster();
1182: sm = ras.getSampleModel();
1183: } else {
1184: sm = image.getSampleModel();
1185: }
1186:
1187: if (sm instanceof ComponentSampleModel) {
1188: // Ensure image rows samples are stored contiguously
1189: // in a single bank.
1190: ComponentSampleModel csm = (ComponentSampleModel) sm;
1191:
1192: if (csm.getPixelStride() != csm.getNumBands()) {
1193: return false;
1194: }
1195:
1196: int[] bandOffsets = csm.getBandOffsets();
1197: for (int i = 0; i < bandOffsets.length; i++) {
1198: if (bandOffsets[i] != i) {
1199: return false;
1200: }
1201: }
1202:
1203: int[] bankIndices = csm.getBankIndices();
1204: for (int i = 0; i < bandOffsets.length; i++) {
1205: if (bankIndices[i] != 0) {
1206: return false;
1207: }
1208: }
1209:
1210: return true;
1211: }
1212:
1213: // Otherwise true if and only if it's a bilevel image with
1214: // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit
1215: // pixel stride.
1216: return ImageUtil.isBinary(sm);
1217: }
1218:
1219: /**
1220: * Gets the destination image type.
1221: */
1222: // NOTE: Code shamelessly copied from ImageReader.getDestination().
1223: public static final ImageTypeSpecifier getDestinationType(
1224: ImageReadParam param, Iterator imageTypes)
1225: throws IIOException {
1226:
1227: if (imageTypes == null || !imageTypes.hasNext()) {
1228: throw new IllegalArgumentException(
1229: "imageTypes null or empty!");
1230: }
1231:
1232: ImageTypeSpecifier imageType = null;
1233:
1234: // If param is non-null, use it
1235: if (param != null) {
1236: imageType = param.getDestinationType();
1237: }
1238:
1239: // No info from param, use fallback image type
1240: if (imageType == null) {
1241: Object o = imageTypes.next();
1242: if (!(o instanceof ImageTypeSpecifier)) {
1243: throw new IllegalArgumentException(
1244: "Non-ImageTypeSpecifier retrieved from imageTypes!");
1245: }
1246: imageType = (ImageTypeSpecifier) o;
1247: } else {
1248: boolean foundIt = false;
1249: while (imageTypes.hasNext()) {
1250: ImageTypeSpecifier type = (ImageTypeSpecifier) imageTypes
1251: .next();
1252: if (type.equals(imageType)) {
1253: foundIt = true;
1254: break;
1255: }
1256: }
1257:
1258: if (!foundIt) {
1259: throw new IIOException(
1260: "Destination type from ImageReadParam does not match!");
1261: }
1262: }
1263:
1264: return imageType;
1265: }
1266:
1267: /**
1268: * Returns <code>true</code> if the given <code>ColorSpace</code> object
1269: * is an instance of <code>ICC_ColorSpace</code> but is not one of the
1270: * standard <code>ColorSpace</code>s returned by
1271: * <code>ColorSpace.getInstance()</code>.
1272: *
1273: * @param cs The <code>ColorSpace</code> to test.
1274: */
1275: public static boolean isNonStandardICCColorSpace(ColorSpace cs) {
1276: boolean retval = false;
1277:
1278: try {
1279: // Check the standard ColorSpaces in decreasing order of
1280: // likelihood except check CS_PYCC last as in some JREs
1281: // PYCC.pf used not to be installed.
1282: retval = (cs instanceof ICC_ColorSpace)
1283: && !(cs.isCS_sRGB()
1284: || cs
1285: .equals(ColorSpace
1286: .getInstance(ColorSpace.CS_LINEAR_RGB))
1287: || cs.equals(ColorSpace
1288: .getInstance(ColorSpace.CS_GRAY))
1289: || cs.equals(ColorSpace
1290: .getInstance(ColorSpace.CS_CIEXYZ)) || cs
1291: .equals(ColorSpace
1292: .getInstance(ColorSpace.CS_PYCC)));
1293: } catch (IllegalArgumentException e) {
1294: // PYCC.pf not installed: ignore it - 'retval' is still 'false'.
1295: }
1296:
1297: return retval;
1298: }
1299:
1300: // Method to return JDK core ImageReaderSPI/ImageWriterSPI for a
1301: // given formatName.
1302: public static List getJDKImageReaderWriterSPI(
1303: ServiceRegistry registry, String formatName,
1304: boolean isReader) {
1305:
1306: IIORegistry iioRegistry = (IIORegistry) registry;
1307:
1308: Class spiClass;
1309: String descPart;
1310: if (isReader) {
1311: spiClass = ImageReaderSpi.class;
1312: descPart = " image reader";
1313: } else {
1314: spiClass = ImageWriterSpi.class;
1315: descPart = " image writer";
1316: }
1317:
1318: Iterator iter = iioRegistry.getServiceProviders(spiClass, true); // useOrdering
1319:
1320: String formatNames[];
1321: ImageReaderWriterSpi provider;
1322: String desc = "standard " + formatName + descPart;
1323: String jiioPath = "com.sun.media.imageioimpl";
1324: Locale locale = Locale.getDefault();
1325: ArrayList list = new ArrayList();
1326: while (iter.hasNext()) {
1327: provider = (ImageReaderWriterSpi) iter.next();
1328:
1329: // Look for JDK core ImageWriterSpi's
1330: if (provider.getVendorName().startsWith("Sun Microsystems")
1331: && desc.equalsIgnoreCase(provider
1332: .getDescription(locale)) &&
1333: // not JAI Image I/O plugins
1334: !provider.getPluginClassName().startsWith(jiioPath)) {
1335:
1336: // Get the formatNames supported by this Spi
1337: formatNames = provider.getFormatNames();
1338: for (int i = 0; i < formatNames.length; i++) {
1339: if (formatNames[i].equalsIgnoreCase(formatName)) {
1340: // Must be a JDK provided ImageReader/ImageWriter
1341: list.add(provider);
1342: break;
1343: }
1344: }
1345: }
1346: }
1347:
1348: return list;
1349: }
1350:
1351: public static void processOnRegistration(ServiceRegistry registry,
1352: Class category, String formatName,
1353: ImageReaderWriterSpi spi, int deregisterJvmVersion,
1354: int priorityJvmVersion) {
1355:
1356: // Check which JVM we are running on
1357: String jvmVendor = System.getProperty("java.vendor");
1358: String jvmVersionString = System
1359: .getProperty("java.specification.version");
1360: int verIndex = jvmVersionString.indexOf("1.");
1361: // Skip the "1." part to get to the part of the version number that
1362: // actually changes from version to version
1363: // The assumption here is that "java.specification.version" is
1364: // always of the format "x.y" and not "x.y.z" since that is what has
1365: // been practically observed in all JDKs to-date, an examination of
1366: // the code returning this property bears this out. However this does
1367: // not guarantee that the format won't change in the future,
1368: // though that seems unlikely.
1369: jvmVersionString = jvmVersionString.substring(verIndex + 2);
1370:
1371: int jvmVersion = Integer.parseInt(jvmVersionString);
1372:
1373: if (jvmVendor.equals("Sun Microsystems Inc.")) {
1374:
1375: List list;
1376: if (spi instanceof ImageReaderSpi)
1377: list = getJDKImageReaderWriterSPI(registry, formatName,
1378: true);
1379: else
1380: list = getJDKImageReaderWriterSPI(registry, formatName,
1381: false);
1382:
1383: if (jvmVersion >= deregisterJvmVersion && list.size() != 0) {
1384: // De-register JIIO's plug-in
1385: registry.deregisterServiceProvider(spi, category);
1386: } else {
1387: for (int i = 0; i < list.size(); i++) {
1388: if (jvmVersion >= priorityJvmVersion) {
1389: // Set JIIO plug-in to lower priority
1390: registry
1391: .setOrdering(category, list.get(i), spi);
1392: } else {
1393: // Set JIIO plug-in to higher priority
1394: registry
1395: .setOrdering(category, spi, list.get(i));
1396: }
1397: }
1398: }
1399: }
1400: }
1401:
1402: public static int readMultiByteInteger(ImageInputStream iis)
1403: throws IOException {
1404: int value = iis.readByte();
1405: int result = value & 0x7f;
1406: while ((value & 0x80) == 0x80) {
1407: result <<= 7;
1408: value = iis.readByte();
1409: result |= (value & 0x7f);
1410: }
1411: return result;
1412: }
1413: }
|