You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1168 lines
47 KiB
1168 lines
47 KiB
/*
|
|
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
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);
|
|
}
|
|
}
|