/* * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.common; import java.awt.Point; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.util.Arrays; //import javax.imageio.ImageTypeSpecifier; import javax.imageio.IIOException; import javax.imageio.IIOImage; import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageWriter; import javax.imageio.spi.ImageWriterSpi; public class ImageUtil { /* XXX testing only public static void main(String[] args) { ImageTypeSpecifier bilevel = ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255}, new byte[] {(byte)0, (byte)255}, new byte[] {(byte)0, (byte)255}, null, 1, DataBuffer.TYPE_BYTE); ImageTypeSpecifier gray = ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false); ImageTypeSpecifier grayAlpha = ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false, false); ImageTypeSpecifier rgb = ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {0, 1, 2}, DataBuffer.TYPE_BYTE, false, false); ImageTypeSpecifier rgba = ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {0, 1, 2, 3}, DataBuffer.TYPE_BYTE, true, false); ImageTypeSpecifier packed = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB), 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, DataBuffer.TYPE_BYTE, false); SampleModel bandedSM = new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 15); System.out.println(createColorModel(bilevel.getSampleModel())); System.out.println(createColorModel(gray.getSampleModel())); System.out.println(createColorModel(grayAlpha.getSampleModel())); System.out.println(createColorModel(rgb.getSampleModel())); System.out.println(createColorModel(rgba.getSampleModel())); System.out.println(createColorModel(packed.getSampleModel())); System.out.println(createColorModel(bandedSM)); } */ /** * Creates a <code>ColorModel</code> that may be used with the * specified <code>SampleModel</code>. If a suitable * <code>ColorModel</code> cannot be found, this method returns * <code>null</code>. * * <p> Suitable <code>ColorModel</code>s are guaranteed to exist * for all instances of <code>ComponentSampleModel</code>. * For 1- and 3- banded <code>SampleModel</code>s, the returned * <code>ColorModel</code> will be opaque. For 2- and 4-banded * <code>SampleModel</code>s, the output will use alpha transparency * which is not premultiplied. 1- and 2-banded data will use a * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB * <code>ColorSpace</code>. Data with 5 or more bands will have a * <code>BogusColorSpace</code>.</p> * * <p>An instance of <code>DirectColorModel</code> will be created for * instances of <code>SinglePixelPackedSampleModel</code> with no more * than 4 bands.</p> * * <p>An instance of <code>IndexColorModel</code> will be created for * instances of <code>MultiPixelPackedSampleModel</code>. The colormap * will be a grayscale ramp with <code>1 << numberOfBits</code> * entries ranging from zero to at most 255.</p> * * @return An instance of <code>ColorModel</code> that is suitable for * the supplied <code>SampleModel</code>, or <code>null</code>. * * @throws IllegalArgumentException If <code>sampleModel</code> is * <code>null</code>. */ public static final ColorModel createColorModel(SampleModel sampleModel) { // Check the parameter. if(sampleModel == null) { throw new IllegalArgumentException("sampleModel == null!"); } // Get the data type. int dataType = sampleModel.getDataType(); // Check the data type switch(dataType) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: break; default: // Return null for other types. return null; } // The return variable. ColorModel colorModel = null; // Get the sample size. int[] sampleSize = sampleModel.getSampleSize(); // Create a Component ColorModel. if(sampleModel instanceof ComponentSampleModel) { // Get the number of bands. int numBands = sampleModel.getNumBands(); // Determine the color space. ColorSpace colorSpace = null; if(numBands <= 2) { colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); } else if(numBands <= 4) { colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); } else { colorSpace = new BogusColorSpace(numBands); } boolean hasAlpha = (numBands == 2) || (numBands == 4); boolean isAlphaPremultiplied = false; int transparency = hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE; colorModel = new ComponentColorModel(colorSpace, sampleSize, hasAlpha, isAlphaPremultiplied, transparency, dataType); } else if (sampleModel.getNumBands() <= 4 && sampleModel instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sampleModel; int[] bitMasks = sppsm.getBitMasks(); int rmask = 0; int gmask = 0; int bmask = 0; int amask = 0; int numBands = bitMasks.length; if (numBands <= 2) { rmask = gmask = bmask = bitMasks[0]; if (numBands == 2) { amask = bitMasks[1]; } } else { rmask = bitMasks[0]; gmask = bitMasks[1]; bmask = bitMasks[2]; if (numBands == 4) { amask = bitMasks[3]; } } int bits = 0; for (int i = 0; i < sampleSize.length; i++) { bits += sampleSize[i]; } return new DirectColorModel(bits, rmask, gmask, bmask, amask); } else if(sampleModel instanceof MultiPixelPackedSampleModel) { // Load the colormap with a ramp. int bitsPerSample = sampleSize[0]; int numEntries = 1 << bitsPerSample; byte[] map = new byte[numEntries]; for (int i = 0; i < numEntries; i++) { map[i] = (byte)(i*255/(numEntries - 1)); } colorModel = new IndexColorModel(bitsPerSample, numEntries, map, map, map); } return colorModel; } /** * For the case of binary data (<code>isBinary()</code> returns * <code>true</code>), return the binary data as a packed byte array. * The data will be packed as eight bits per byte with no bit offset, * i.e., the first bit in each image line will be the left-most of the * first byte of the line. The line stride in bytes will be * <code>(int)((getWidth()+7)/8)</code>. The length of the returned * array will be the line stride multiplied by <code>getHeight()</code> * * @return the binary data as a packed array of bytes with zero offset * of <code>null</code> if the data are not binary. * @throws IllegalArgumentException if <code>isBinary()</code> returns * <code>false</code> with the <code>SampleModel</code> of the * supplied <code>Raster</code> as argument. */ public static byte[] getPackedBinaryData(Raster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(I18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); int numBytesPerRow = (rectWidth + 7)/8; if(dataBuffer instanceof DataBufferByte && eltOffset == 0 && bitOffset == 0 && numBytesPerRow == lineStride && ((DataBufferByte)dataBuffer).getData().length == numBytesPerRow*rectHeight) { return ((DataBufferByte)dataBuffer).getData(); } byte[] binaryDataArray = new byte[numBytesPerRow*rectHeight]; int b = 0; if(bitOffset == 0) { if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); int stride = numBytesPerRow; int offset = 0; for(int y = 0; y < rectHeight; y++) { System.arraycopy(data, eltOffset, binaryDataArray, offset, stride); offset += stride; eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 8) { short datum = data[i++]; binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); binaryDataArray[b++] = (byte)(datum & 0xFF); xRemaining -= 16; } if(xRemaining > 0) { binaryDataArray[b++] = (byte)((data[i] >>> 8) & 0XFF); } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 24) { int datum = data[i++]; binaryDataArray[b++] = (byte)((datum >>> 24) & 0xFF); binaryDataArray[b++] = (byte)((datum >>> 16) & 0xFF); binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); binaryDataArray[b++] = (byte)(datum & 0xFF); xRemaining -= 32; } int shift = 24; while(xRemaining > 0) { binaryDataArray[b++] = (byte)((data[i] >>> shift) & 0xFF); shift -= 8; xRemaining -= 8; } eltOffset += lineStride; } } } else { // bitOffset != 0 if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); if((bitOffset & 7) == 0) { int stride = numBytesPerRow; int offset = 0; for(int y = 0; y < rectHeight; y++) { System.arraycopy(data, eltOffset, binaryDataArray, offset, stride); offset += stride; eltOffset += lineStride; } } else { // bitOffset % 8 != 0 int leftShift = bitOffset & 7; int rightShift = 8 - leftShift; for(int y = 0; y < rectHeight; y++) { int i = eltOffset; int xRemaining = rectWidth; while(xRemaining > 0) { if(xRemaining > rightShift) { binaryDataArray[b++] = (byte)(((data[i++]&0xFF) << leftShift) | ((data[i]&0xFF) >>> rightShift)); } else { binaryDataArray[b++] = (byte)((data[i]&0xFF) << leftShift); } xRemaining -= 8; } eltOffset += lineStride; } } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { int i = eltOffset + bOffset/16; int mod = bOffset % 16; int left = data[i] & 0xFFFF; if(mod <= 8) { binaryDataArray[b++] = (byte)(left >>> (8 - mod)); } else { int delta = mod - 8; int right = data[i+1] & 0xFFFF; binaryDataArray[b++] = (byte)((left << delta) | (right >>> (16 - delta))); } } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { int i = eltOffset + bOffset/32; int mod = bOffset % 32; int left = data[i]; if(mod <= 24) { binaryDataArray[b++] = (byte)(left >>> (24 - mod)); } else { int delta = mod - 24; int right = data[i+1]; binaryDataArray[b++] = (byte)((left << delta) | (right >>> (32 - delta))); } } eltOffset += lineStride; } } } return binaryDataArray; } /** * Returns the binary data unpacked into an array of bytes. * The line stride will be the width of the <code>Raster</code>. * * @throws IllegalArgumentException if <code>isBinary()</code> returns * <code>false</code> with the <code>SampleModel</code> of the * supplied <code>Raster</code> as argument. */ public static byte[] getUnpackedBinaryData(Raster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(I18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); byte[] bdata = new byte[rectWidth*rectHeight]; int maxY = rectY + rectHeight; int maxX = rectX + rectWidth; int k = 0; if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); for(int y = rectY; y < maxY; y++) { int bOffset = eltOffset*8 + bitOffset; for(int x = rectX; x < maxX; x++) { byte b = data[bOffset/8]; bdata[k++] = (byte)((b >>> (7 - bOffset & 7)) & 0x0000001); bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = rectY; y < maxY; y++) { int bOffset = eltOffset*16 + bitOffset; for(int x = rectX; x < maxX; x++) { short s = data[bOffset/16]; bdata[k++] = (byte)((s >>> (15 - bOffset % 16)) & 0x0000001); bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = rectY; y < maxY; y++) { int bOffset = eltOffset*32 + bitOffset; for(int x = rectX; x < maxX; x++) { int i = data[bOffset/32]; bdata[k++] = (byte)((i >>> (31 - bOffset % 32)) & 0x0000001); bOffset++; } eltOffset += lineStride; } } return bdata; } /** * Sets the supplied <code>Raster</code>'s data from an array * of packed binary data of the form returned by * <code>getPackedBinaryData()</code>. * * @throws IllegalArgumentException if <code>isBinary()</code> returns * <code>false</code> with the <code>SampleModel</code> of the * supplied <code>Raster</code> as argument. */ public static void setPackedBinaryData(byte[] binaryDataArray, WritableRaster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(I18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); int b = 0; if(bitOffset == 0) { if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); if(data == binaryDataArray) { // Optimal case: simply return. return; } int stride = (rectWidth + 7)/8; int offset = 0; for(int y = 0; y < rectHeight; y++) { System.arraycopy(binaryDataArray, offset, data, eltOffset, stride); offset += stride; eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 8) { data[i++] = (short)(((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF)); xRemaining -= 16; } if(xRemaining > 0) { data[i++] = (short)((binaryDataArray[b++] & 0xFF) << 8); } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 24) { data[i++] = (int)(((binaryDataArray[b++] & 0xFF) << 24) | ((binaryDataArray[b++] & 0xFF) << 16) | ((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF)); xRemaining -= 32; } int shift = 24; while(xRemaining > 0) { data[i] |= (int)((binaryDataArray[b++] & 0xFF) << shift); shift -= 8; xRemaining -= 8; } eltOffset += lineStride; } } } else { // bitOffset != 0 int stride = (rectWidth + 7)/8; int offset = 0; if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); if((bitOffset & 7) == 0) { for(int y = 0; y < rectHeight; y++) { System.arraycopy(binaryDataArray, offset, data, eltOffset, stride); offset += stride; eltOffset += lineStride; } } else { // bitOffset % 8 != 0 int rightShift = bitOffset & 7; int leftShift = 8 - rightShift; int leftShift8 = 8 + leftShift; int mask = (byte)(255<<leftShift); int mask1 = (byte)~mask; for(int y = 0; y < rectHeight; y++) { int i = eltOffset; int xRemaining = rectWidth; while(xRemaining > 0) { byte datum = binaryDataArray[b++]; if (xRemaining > leftShift8) { // when all the bits in this BYTE will be set // into the data buffer. data[i] = (byte)((data[i] & mask ) | ((datum&0xFF) >>> rightShift)); data[++i] = (byte)((datum & 0xFF) << leftShift); } else if (xRemaining > leftShift) { // All the "leftShift" high bits will be set // into the data buffer. But not all the // "rightShift" low bits will be set. data[i] = (byte)((data[i] & mask ) | ((datum&0xFF) >>> rightShift)); i++; data[i] = (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift)); } else { // Less than "leftShift" high bits will be set. int remainMask = (1 << leftShift - xRemaining) - 1; data[i] = (byte)((data[i] & (mask | remainMask)) | (datum&0xFF) >>> rightShift & ~remainMask); } xRemaining -= 8; } eltOffset += lineStride; } } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); int rightShift = bitOffset & 7; int leftShift = 8 - rightShift; int leftShift16 = 16 + leftShift; int mask = (short)(~(255 << leftShift)); int mask1 = (short)(65535 << leftShift); int mask2 = (short)~mask1; for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; int xRemaining = rectWidth; for(int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) { int i = eltOffset + (bOffset >> 4); int mod = bOffset & 15; int datum = binaryDataArray[b++] & 0xFF; if(mod <= 8) { // This BYTE is set into one SHORT if (xRemaining < 8) { // Mask the bits to be set. datum &= 255 << 8 - xRemaining; } data[i] = (short)((data[i] & mask) | (datum << leftShift)); } else if (xRemaining > leftShift16) { // This BYTE will be set into two SHORTs data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); data[++i] = (short)((datum << leftShift)&0xFFFF); } else if (xRemaining > leftShift) { // This BYTE will be set into two SHORTs; // But not all the low bits will be set into SHORT data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); i++; data[i] = (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF)); } else { // Only some of the high bits will be set into // SHORTs int remainMask = (1 << leftShift - xRemaining) - 1; data[i] = (short)((data[i] & (mask1 | remainMask)) | ((datum >>> rightShift)&0xFFFF & ~remainMask)); } } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); int rightShift = bitOffset & 7; int leftShift = 8 - rightShift; int leftShift32 = 32 + leftShift; int mask = 0xFFFFFFFF << leftShift; int mask1 = ~mask; for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; int xRemaining = rectWidth; for(int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) { int i = eltOffset + (bOffset >> 5); int mod = bOffset & 31; int datum = binaryDataArray[b++] & 0xFF; if(mod <= 24) { // This BYTE is set into one INT int shift = 24 - mod; if (xRemaining < 8) { // Mask the bits to be set. datum &= 255 << 8 - xRemaining; } data[i] = (data[i] & (~(255 << shift))) | (datum << shift); } else if (xRemaining > leftShift32) { // All the bits of this BYTE will be set into two INTs data[i] = (data[i] & mask) | (datum >>> rightShift); data[++i] = datum << leftShift; } else if (xRemaining > leftShift) { // This BYTE will be set into two INTs; // But not all the low bits will be set into INT data[i] = (data[i] & mask) | (datum >>> rightShift); i++; data[i] = (data[i] & mask1) | (datum << leftShift); } else { // Only some of the high bits will be set into INT int remainMask = (1 << leftShift - xRemaining) - 1; data[i] = (data[i] & (mask | remainMask)) | (datum >>> rightShift & ~remainMask); } } eltOffset += lineStride; } } } } /** * Copies data into the packed array of the <code>Raster</code> * from an array of unpacked data of the form returned by * <code>getUnpackedBinaryData()</code>. * * <p> If the data are binary, then the target bit will be set if * and only if the corresponding byte is non-zero. * * @throws IllegalArgumentException if <code>isBinary()</code> returns * <code>false</code> with the <code>SampleModel</code> of the * supplied <code>Raster</code> as argument. */ public static void setUnpackedBinaryData(byte[] bdata, WritableRaster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(I18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); int k = 0; if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = eltOffset*8 + bitOffset; for(int x = 0; x < rectWidth; x++) { if(bdata[k++] != (byte)0) { data[bOffset/8] |= (byte)(0x00000001 << (7 - bOffset & 7)); } bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = eltOffset*16 + bitOffset; for(int x = 0; x < rectWidth; x++) { if(bdata[k++] != (byte)0) { data[bOffset/16] |= (short)(0x00000001 << (15 - bOffset % 16)); } bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = eltOffset*32 + bitOffset; for(int x = 0; x < rectWidth; x++) { if(bdata[k++] != (byte)0) { data[bOffset/32] |= (int)(0x00000001 << (31 - bOffset % 32)); } bOffset++; } eltOffset += lineStride; } } } public static boolean isBinary(SampleModel sm) { return sm instanceof MultiPixelPackedSampleModel && ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 && sm.getNumBands() == 1; } public static ColorModel createColorModel(ColorSpace colorSpace, SampleModel sampleModel) { ColorModel colorModel = null; if(sampleModel == null) { throw new IllegalArgumentException(I18N.getString("ImageUtil1")); } int numBands = sampleModel.getNumBands(); if (numBands < 1 || numBands > 4) { return null; } int dataType = sampleModel.getDataType(); if (sampleModel instanceof ComponentSampleModel) { if (dataType < DataBuffer.TYPE_BYTE || //dataType == DataBuffer.TYPE_SHORT || dataType > DataBuffer.TYPE_DOUBLE) { return null; } if (colorSpace == null) colorSpace = numBands <= 2 ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpace.getInstance(ColorSpace.CS_sRGB); boolean useAlpha = (numBands == 2) || (numBands == 4); int transparency = useAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE; boolean premultiplied = false; int dataTypeSize = DataBuffer.getDataTypeSize(dataType); int[] bits = new int[numBands]; for (int i = 0; i < numBands; i++) { bits[i] = dataTypeSize; } colorModel = new ComponentColorModel(colorSpace, bits, useAlpha, premultiplied, transparency, dataType); } else if (sampleModel instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sampleModel; int[] bitMasks = sppsm.getBitMasks(); int rmask = 0; int gmask = 0; int bmask = 0; int amask = 0; numBands = bitMasks.length; if (numBands <= 2) { rmask = gmask = bmask = bitMasks[0]; if (numBands == 2) { amask = bitMasks[1]; } } else { rmask = bitMasks[0]; gmask = bitMasks[1]; bmask = bitMasks[2]; if (numBands == 4) { amask = bitMasks[3]; } } int[] sampleSize = sppsm.getSampleSize(); int bits = 0; for (int i = 0; i < sampleSize.length; i++) { bits += sampleSize[i]; } if (colorSpace == null) colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); colorModel = new DirectColorModel(colorSpace, bits, rmask, gmask, bmask, amask, false, sampleModel.getDataType()); } else if (sampleModel instanceof MultiPixelPackedSampleModel) { int bits = ((MultiPixelPackedSampleModel)sampleModel).getPixelBitStride(); int size = 1 << bits; byte[] comp = new byte[size]; for (int i = 0; i < size; i++) comp[i] = (byte)(255 * i / (size - 1)); colorModel = new IndexColorModel(bits, size, comp, comp, comp); } return colorModel; } public static int getElementSize(SampleModel sm) { int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); if (sm instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel)sm; return mppsm.getSampleSize(0) * mppsm.getNumBands(); } else if (sm instanceof ComponentSampleModel) { return sm.getNumBands() * elementSize; } else if (sm instanceof SinglePixelPackedSampleModel) { return elementSize; } return elementSize * sm.getNumBands(); } public static long getTileSize(SampleModel sm) { int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); if (sm instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel)sm; return (mppsm.getScanlineStride() * mppsm.getHeight() + (mppsm.getDataBitOffset() + elementSize -1) / elementSize) * ((elementSize + 7) / 8); } else if (sm instanceof ComponentSampleModel) { ComponentSampleModel csm = (ComponentSampleModel)sm; int[] bandOffsets = csm.getBandOffsets(); int maxBandOff = bandOffsets[0]; for (int i=1; i<bandOffsets.length; i++) maxBandOff = Math.max(maxBandOff, bandOffsets[i]); long size = 0; int pixelStride = csm.getPixelStride(); int scanlineStride = csm.getScanlineStride(); if (maxBandOff >= 0) size += maxBandOff + 1; if (pixelStride > 0) size += pixelStride * (sm.getWidth() - 1); if (scanlineStride > 0) size += scanlineStride * (sm.getHeight() - 1); int[] bankIndices = csm.getBankIndices(); maxBandOff = bankIndices[0]; for (int i=1; i<bankIndices.length; i++) maxBandOff = Math.max(maxBandOff, bankIndices[i]); return size * (maxBandOff + 1) * ((elementSize + 7) / 8); } else if (sm instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sm; long size = sppsm.getScanlineStride() * (sppsm.getHeight() - 1) + sppsm.getWidth(); return size * ((elementSize + 7) / 8); } return 0; } public static long getBandSize(SampleModel sm) { int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); if (sm instanceof ComponentSampleModel) { ComponentSampleModel csm = (ComponentSampleModel)sm; int pixelStride = csm.getPixelStride(); int scanlineStride = csm.getScanlineStride(); long size = Math.min(pixelStride, scanlineStride); if (pixelStride > 0) size += pixelStride * (sm.getWidth() - 1); if (scanlineStride > 0) size += scanlineStride * (sm.getHeight() - 1); return size * ((elementSize + 7) / 8); } else return getTileSize(sm); } /** * Tests whether the color indices represent a gray-scale image. * * @param r The red channel color indices. * @param g The green channel color indices. * @param b The blue channel color indices. * @return If all the indices have 256 entries, and are identical mappings, * return <code>true</code>; otherwise, return <code>false</code>. */ public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) { if (r.length != g.length || r.length != b.length) return false; int size = r.length; if (size != 256) return false; for (int i = 0; i < size; i++) { byte temp = (byte) i; if (r[i] != temp || g[i] != temp || b[i] != temp) return false; } return true; } /** Converts the provided object to <code>String</code> */ public static String convertObjectToString(Object obj) { if (obj == null) return ""; String s = ""; if (obj instanceof byte[]) { byte[] bArray = (byte[])obj; for (int i = 0; i < bArray.length; i++) s += bArray[i] + " "; return s; } if (obj instanceof int[]) { int[] iArray = (int[])obj; for (int i = 0; i < iArray.length; i++) s += iArray[i] + " " ; return s; } if (obj instanceof short[]) { short[] sArray = (short[])obj; for (int i = 0; i < sArray.length; i++) s += sArray[i] + " " ; return s; } return obj.toString(); } /** Checks that the provided <code>ImageWriter</code> can encode * the provided <code>ImageTypeSpecifier</code> or not. If not, an * <code>IIOException</code> will be thrown. * @param writer The provided <code>ImageWriter</code>. * @param type The image to be tested. * @throws IIOException If the writer cannot encoded the provided image. */ public static final void canEncodeImage(ImageWriter writer, ImageTypeSpecifier type) throws IIOException { ImageWriterSpi spi = writer.getOriginatingProvider(); if(type != null && spi != null && !spi.canEncodeImage(type)) { throw new IIOException(I18N.getString("ImageUtil2")+" "+ writer.getClass().getName()); } } /** Checks that the provided <code>ImageWriter</code> can encode * the provided <code>ColorModel</code> and <code>SampleModel</code>. * If not, an <code>IIOException</code> will be thrown. * @param writer The provided <code>ImageWriter</code>. * @param colorModel The provided <code>ColorModel</code>. * @param sampleModel The provided <code>SampleModel</code>. * @throws IIOException If the writer cannot encoded the provided image. */ public static final void canEncodeImage(ImageWriter writer, ColorModel colorModel, SampleModel sampleModel) throws IIOException { ImageTypeSpecifier type = null; if (colorModel != null && sampleModel != null) type = new ImageTypeSpecifier(colorModel, sampleModel); canEncodeImage(writer, type); } /** * Returns whether the image has contiguous data across rows. */ public static final boolean imageIsContiguous(RenderedImage image) { SampleModel sm; if(image instanceof BufferedImage) { WritableRaster ras = ((BufferedImage)image).getRaster(); sm = ras.getSampleModel(); } else { sm = image.getSampleModel(); } if (sm instanceof ComponentSampleModel) { // Ensure image rows samples are stored contiguously // in a single bank. ComponentSampleModel csm = (ComponentSampleModel)sm; if (csm.getPixelStride() != csm.getNumBands()) { return false; } int[] bandOffsets = csm.getBandOffsets(); for (int i = 0; i < bandOffsets.length; i++) { if (bandOffsets[i] != i) { return false; } } int[] bankIndices = csm.getBankIndices(); for (int i = 0; i < bandOffsets.length; i++) { if (bankIndices[i] != 0) { return false; } } return true; } // Otherwise true if and only if it's a bilevel image with // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit // pixel stride. return ImageUtil.isBinary(sm); } }