/*
* $RCSfile: MediaLibAccessor.java,v $
*
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
*
* Use is subject to license terms.
*
* $Revision: 1.2 $
* $Date: 2005/11/23 21:08:28 $
* $State: Exp $
*/
package com.lightcrafts.media.jai.mlib;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.FilePermission;
import java.lang.NoClassDefFoundError;
import java.security.AccessController;
import java.security.PrivilegedAction;
import com.lightcrafts.mediax.jai.ImageLayout;
import com.lightcrafts.mediax.jai.JAI;
import com.lightcrafts.mediax.jai.util.ImagingListener;
import com.lightcrafts.media.jai.util.DataBufferUtils;
import com.lightcrafts.media.jai.util.ImageUtil;
import com.sun.medialib.mlib.*;
/**
* An adapter class for presenting image data in a mediaLibImage
* format, even if the data isn't stored that way. MediaLibAccessor
* is meant to make the common case (ComponentRasters) and allow
* them to be accelerated via medialib. Note that unlike RasterAccessor,
* MediaLibAccessor does not work with all cases. In the event that
* MediaLibAccessor can not deal with a give collection of Rasters,
* findCompatibleTag will return the value MediaLibAccessor.TAG_INCOMPATIBLE.
* OpImages that use MediaLibAccessor should be paired with RIF's
* which check that findCompatibleTag returns a valid tag before
* actually constructing the Mlib OpImage.
*
*/
public class MediaLibAccessor {
/**
* Value indicating how far COPY_MASK info is shifted to avoid
* interfering with the data type info
*/
private static final int COPY_MASK_SHIFT = 7;
/* Value indicating how many bits the COPY_MASK is */
private static final int COPY_MASK_SIZE = 1;
/** The bits of a FormatTag associated with how dataArrays are obtained. */
public static final int COPY_MASK = 0x1 << COPY_MASK_SHIFT;
/** Flag indicating data is raster's data. */
public static final int UNCOPIED = 0x0 << COPY_MASK_SHIFT;
/** Flag indicating data is a copy of the raster's data. */
public static final int COPIED = 0x01 << COPY_MASK_SHIFT;
/** The bits of a FormatTag associated with pixel datatype. */
public static final int DATATYPE_MASK = (0x1 << COPY_MASK_SHIFT) - 1;
/**
* Value indicating how far BINARY_MASK info is shifted to avoid
* interfering with the data type and copying info.
*/
private static final int BINARY_MASK_SHIFT =
COPY_MASK_SHIFT+COPY_MASK_SIZE;
/** Value indicating how many bits the BINARY_MASK is */
private static final int BINARY_MASK_SIZE = 1;
/** The bits of a FormatTag associated with binary data. */
public static final int BINARY_MASK =
((1 << BINARY_MASK_SIZE) - 1) << BINARY_MASK_SHIFT;
/** Flag indicating data are not binary. */
public static final int NONBINARY = 0x0 << BINARY_MASK_SHIFT;
/** Flag indicating data are binary. */
public static final int BINARY = 0x1 << BINARY_MASK_SHIFT;
/** FormatTag indicating data in byte arrays and uncopied. */
public static final int
TAG_BYTE_UNCOPIED = DataBuffer.TYPE_BYTE | UNCOPIED;
/** FormatTag indicating data in unsigned short arrays and uncopied. */
public static final int
TAG_USHORT_UNCOPIED = DataBuffer.TYPE_USHORT | UNCOPIED;
/** FormatTag indicating data in short arrays and uncopied. */
public static final int
TAG_SHORT_UNCOPIED = DataBuffer.TYPE_SHORT | UNCOPIED;
/** FormatTag indicating data in integer arrays and uncopied. */
public static final int
TAG_INT_UNCOPIED = DataBuffer.TYPE_INT | UNCOPIED;
/** FormatTag indicating data in float arrays and uncopied. */
public static final int
TAG_FLOAT_UNCOPIED = DataBuffer.TYPE_FLOAT | UNCOPIED;
/** FormatTag indicating data in double arrays and uncopied. */
public static final int
TAG_DOUBLE_UNCOPIED = DataBuffer.TYPE_DOUBLE | UNCOPIED;
/** FormatTag indicating data in byte arrays and uncopied. */
public static final int
TAG_BYTE_COPIED = DataBuffer.TYPE_BYTE | COPIED;
/** FormatTag indicating data in unsigned short arrays and copied. */
public static final int
TAG_USHORT_COPIED = DataBuffer.TYPE_USHORT | COPIED;
/** FormatTag indicating data in short arrays and copied. */
public static final int
TAG_SHORT_COPIED = DataBuffer.TYPE_SHORT | COPIED;
/** FormatTag indicating data in short arrays and copied. */
public static final int
TAG_INT_COPIED = DataBuffer.TYPE_INT | COPIED;
/** FormatTag indicating data in float arrays and copied. */
public static final int
TAG_FLOAT_COPIED = DataBuffer.TYPE_FLOAT | COPIED;
/** FormatTag indicating data in double arrays and copied. */
public static final int
TAG_DOUBLE_COPIED = DataBuffer.TYPE_DOUBLE | COPIED;
/** The raster that is the source of pixel data. */
protected Raster raster;
/** The rectangle of the raster that MediaLibAccessor addresses. */
protected Rectangle rect;
/** The number of bands per pixel in the data array. */
protected int numBands;
/** The offsets of each band in the src image */
protected int bandOffsets[];
/** Tag indicating the data type of the data and whether its copied */
protected int formatTag;
/** Area of mediaLib images that represent image data */
protected mediaLibImage mlimages[] = null;
/**
* Whether packed data are preferred when processing binary images.
* This tag is ignored if the data are not binary.
*/
private boolean areBinaryDataPacked = false;
private static boolean useMlibVar = false;
private static boolean useMlibVarSet = false;
private static synchronized boolean useMlib() {
if (!useMlibVarSet) {
setUseMlib();
useMlibVarSet = true;
System.out.println("Light Crafts JAI Library - 02/05/07");
}
return useMlibVar;
}
private static void setUseMlib() {
// Fix of 4726600: Disable medialib before the searching of
// medialib library if the property is set.
boolean disableMediaLib = false;
try {
disableMediaLib =
Boolean.getBoolean("com.lightcrafts.media.jai.disableMediaLib");
} catch (java.security.AccessControlException e) {
// Because the property com.lightcrafts.media.jai.disableMediaLib isn't
// defined as public, the users shouldn't know it. In most of
// the cases, it isn't defined, and thus no access permission
// is granted to it in the policy file. When JAI is utilized in
// a security environment, AccessControlException will be thrown.
// In this case, we suppose that the users would like to use
// medialib accelaration. So, the medialib won't be disabled.
// The fix of 4531501
}
// If mediaLib usage has been explicity disabled.
if (disableMediaLib) {
useMlibVar = false;
return;
}
try {
SecurityManager securityManager =
System.getSecurityManager();
if (securityManager != null &&
MediaLibAccessor.class.getClassLoader() != null) {
// a non-null security manager means we're in an applet
// if this.classLoader == null, we're an installed extension
// and the doPrivleged block should be ok.
// native code doesn't currently load on Wintel regardless
// of where the dll's are even if this code is removed.
// At some point we'll need to come up with a better
// solution. For now this is a good work around because
// on Sparc it will cause
// a SecurityException to be thrown instead of an
// ExceptionInInitializerError in the doPriviliged block
// which can't be caught.
// If MediaLib is rewritten so that the Exception is thrown
// after the class is loaded this chunk of code can be
// removed.
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
// The fix of 4531469
if ((osName.equals("Solaris") || osName.equals("SunOS")) &&
osArch.equals("sparc")) {
FilePermission fp =
new FilePermission("/usr/bin/uname","execute");
securityManager.checkPermission(fp);
}
}
Boolean result = (Boolean)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new Boolean(Image.isAvailable());
}
});
useMlibVar = result.booleanValue();
if (!useMlibVar) {
forwardToListener(JaiI18N.getString("MediaLibAccessor2"),
new MediaLibLoadException());
}
} catch (NoClassDefFoundError ncdfe) {
// If mediaLib jar file is not found, fall back to Java code.
useMlibVar = false;
forwardToListener(JaiI18N.getString("MediaLibAccessor3"), ncdfe);
} catch (ClassFormatError cfe) {
// If mediaLib jar file is not found, fall back to Java code.
useMlibVar = false;
forwardToListener(JaiI18N.getString("MediaLibAccessor3"), cfe);
} catch (SecurityException se) {
// If mediaLib jar file is not found, fall back to Java code.
useMlibVar = false;
forwardToListener(JaiI18N.getString("MediaLibAccessor4"), se);
}
if (useMlibVar == false)
return;
}
/**
* Forwards the supplied message and exception to the
* <code>ImagingListener</code> set on the default JAI instance.
* If none is set (which should not happen) the message is simply
* printed to <code>System.err</code>.
*/
private static void forwardToListener(String message,
Throwable thrown) {
ImagingListener listener =
JAI.getDefaultInstance().getImagingListener();
if(listener != null) {
listener.errorOccurred(message, thrown, MediaLibAccessor.class,
false);
} else {
System.err.println(message);
}
}
/**
* Returns <code>true</code> if mediaLib is able to handle the
* source(s) and destination image format. Currently, all of the
* following conditions must be met in order for this method to
* return <code>true</code>.
* <ul>
* <li>MediaLib is available.</li>
* <li>All sources must be <code>RenderedImage</code>.</li>
* <li>All sources and destination must have
* <code>ComponentSampleModel</code> and
* <code>ComponentColorModel</code>.</li>
* <li>All sources and destination must have less than or equal
* to 4 bands of pixel data.</li>
* </ul>
* Additional checks for each individual <code>OpImage</code>
* should be done in its corresponding <code>RIF</code>.
*
* @param args Input arguments that include sources.
* @param layout Destination image layout; may be <code>null</code>.
*/
public static boolean isMediaLibCompatible(ParameterBlock args,
ImageLayout layout) {
if (!isMediaLibCompatible(args)) {
// sources not supported
return false;
}
if (layout != null) { // validate destination
SampleModel sm = layout.getSampleModel(null);
if (sm != null) {
if (!(sm instanceof ComponentSampleModel) ||
sm.getNumBands() > 4) {
return false;
}
}
ColorModel cm = layout.getColorModel(null);
if (cm != null && (!(cm instanceof ComponentColorModel))) {
return false;
}
}
return true;
}
/**
* Returns <code>true</code> if mediaLib is able to handle the
* source(s) image format. Currently, all of the following
* conditions must be met in order for this method to return
* <code>true</code>.
* <ul>
* <li>MediaLib is available.</li>
* <li>All sources must be <code>RenderedImage</code>.</li>
* <li>All sources must have <code>ComponentSampleModel</code> and
* <code>ComponentColorModel</code>.</li>
* <li>All sources must have less than or equal to 4 bands of pixel
* data.</li>
* </ul>
* Additional checks for each individual <code>OpImage</code>
* should be done in its corresponding <code>RIF</code>.
*
* @param args Input arguments that include sources.
*/
public static boolean isMediaLibCompatible(ParameterBlock args) {
if (!useMlib()) { // mediaLib is not available
return false;
}
int numSrcs = args.getNumSources();
for (int i = 0; i < numSrcs; i++) {
Object src = args.getSource(i);
if (!(src instanceof RenderedImage) ||
!isMediaLibCompatible((RenderedImage)src)) {
return false;
}
}
return true;
}
/**
* Returns <code>true</code> if mediaLib is able to handle the
* image. Currently, all of the following conditions must be
* met in order for this method to return <code>true</code>.
* <ul>
* <li>MediaLib is available.</li>
* <li>The image must have <code>ComponentSampleModel</code> and
* <code>ComponentColorModel</code>.</li>
* <li>The image must have less than or equal to 4 bands of pixel
* data.</li>
* </ul>
* Additional checks for each individual <code>OpImage</code>
* should be done in its corresponding <code>RIF</code>.
*
* @param image The image the compatibility of which is to be checked.
*/
public static boolean isMediaLibCompatible(RenderedImage image) {
if (!useMlib()) { // mediaLib is not available
return false;
}
SampleModel sm = image.getSampleModel();
ColorModel cm = image.getColorModel();
return (sm instanceof ComponentSampleModel &&
sm.getNumBands() <= 4 &&
(cm == null || cm instanceof ComponentColorModel));
}
/**
* Returns <code>true</code> if mediaLib is able to handle
* an image having the supplied <code>SampleModel</code> and
* <code>ColorModel</code>. Currently, all of the following conditions
* must be met in order for this method to return <code>true</code>:
* <ul>
* <li>mediaLib is available.</li>
* <li>The <code>SampleModel</code> is an instance of
* <code>ComponentSampleModel</code> or one of its subclasses.</li>
* <li>The <code>ColorModel</code> is <code>null</code> or an
* instance of <code>ComponentColorModel</code> or a subclass thereof.</li>
* <li>The image must have no more than 4 bands of pixel data.</li>
* </ul>
*
* @param sm The image <code>SampleModel</code>.
* @param cm The image <code>ColorModel</code>.
*
* @throws NullPointerException if <code>sm</code> is <code>null</code>.
*/
public static boolean isMediaLibCompatible(SampleModel sm,
ColorModel cm) {
if (!useMlib()) { // mediaLib is not available
return false;
}
return (sm instanceof ComponentSampleModel &&
sm.getNumBands() <= 4 &&
(cm == null || cm instanceof ComponentColorModel));
}
/**
* Returns <code>true</code> if mediaLib is able to handle the
* source(s) and destination image format as binary (also known
* as bit or bilevel) image data. Currently, all of the
* following conditions must be met in order for this method to
* return <code>true</code>.
* <ul>
* <li>MediaLib is available.</li>
* <li>All sources must be <code>RenderedImage</code>s.</li>
* <li>All sources and destination must have a
* <code>MultiPixelPackedSampleModel</code>.</li>
* <li>All sources and destination must have represent
* single-bit data.</li>
* <li>All sources and destination must have
* a single band of pixel data.</li>
* </ul>
* Additional checks for each individual <code>OpImage</code>
* should be done in its corresponding <code>RIF</code>.
*
* @param args Input arguments that include sources.
* @param layout Destination image layout; may be <code>null</code>.
*/
public static boolean isMediaLibBinaryCompatible(ParameterBlock args,
ImageLayout layout) {
if (!useMlib()) { // mediaLib is not available
return false;
}
SampleModel sm = null;
int numSrcs = args.getNumSources();
for (int i = 0; i < numSrcs; i++) { // sources not supported
Object src = args.getSource(i);
if (!(src instanceof RenderedImage) ||
(sm = ((RenderedImage)src).getSampleModel()) == null ||
!ImageUtil.isBinary(sm)) {
return false;
}
}
if (layout != null) { // validate destination
if ((sm = layout.getSampleModel(null)) != null &&
!ImageUtil.isBinary(sm)) {
return false;
}
}
return true;
}
/**
* Returns <code>true</code> if the number of bands of all the
* <code>RenderedImage</code> sources and destination are the same.
*
* @throws ClassCastException if any source is not
* <code>RenderedImage</code>.
*/
public static boolean hasSameNumBands(ParameterBlock args,
ImageLayout layout) {
int numSrcs = args.getNumSources();
if (numSrcs > 0) {
RenderedImage src = args.getRenderedSource(0);
int numBands = src.getSampleModel().getNumBands();
for (int i = 1; i < numSrcs; i++) {
src = args.getRenderedSource(i);
if (src.getSampleModel().getNumBands() != numBands) {
return false;
}
}
if (layout != null) {
SampleModel sm = layout.getSampleModel(null);
if (sm != null && sm.getNumBands() != numBands) {
return false;
}
}
}
return true;
}
/**
* Returns the most efficient FormatTag that is compatible with
* the destination raster and all source rasters.
*
* @param srcs the source <code>Raster</code>; may be <code>null</code>.
* @param dst the destination <code>Raster</code>.
*/
public static int findCompatibleTag(Raster srcs[], Raster dst) {
SampleModel dstSM = dst.getSampleModel();
int dstDT = dstSM.getDataType();
int defaultDataType = dstSM.getDataType();
boolean allComponentSampleModel =
dstSM instanceof ComponentSampleModel;
boolean allBinary = ImageUtil.isBinary(dstSM);
// use highest precision datatype of all srcs & dst
if (srcs != null) {
int numSources = srcs.length;
int i;
for (i = 0; i < numSources; i++) {
SampleModel srcSampleModel = srcs[i].getSampleModel();
if (!(srcSampleModel instanceof ComponentSampleModel)) {
allComponentSampleModel = false;
}
if (!ImageUtil.isBinary(srcSampleModel)) {
allBinary = false;
}
int srcDataType = srcSampleModel.getTransferType();
if (srcDataType > defaultDataType) {
defaultDataType = srcDataType;
}
}
}
if(allBinary) {
// The copy flag is not set until the mediaLibImage is
// created as knowing this information requires too much
// processing to determine here.
return DataBuffer.TYPE_BYTE | BINARY;
}
if (!allComponentSampleModel) {
if ((defaultDataType == DataBuffer.TYPE_BYTE) ||
(defaultDataType == DataBuffer.TYPE_USHORT) ||
(defaultDataType == DataBuffer.TYPE_SHORT)) {
defaultDataType = DataBuffer.TYPE_INT;
}
}
int tag = defaultDataType | COPIED;
if (!allComponentSampleModel) {
return tag;
}
// see if they all have same DT and are pixelSequential
SampleModel srcSM[];
if (srcs == null) {
srcSM = new SampleModel[0];
} else {
srcSM = new SampleModel[srcs.length];
}
for (int i = 0; i < srcSM.length; i++) {
srcSM[i] = srcs[i].getSampleModel();
if (dstDT != srcSM[i].getDataType()) {
return tag;
}
}
if (isPixelSequential(dstSM)) {
for (int i = 0; i < srcSM.length; i++) {
if (!isPixelSequential(srcSM[i])) {
return tag;
}
}
for (int i = 0; i < srcSM.length; i++) {
if (!hasMatchingBandOffsets((ComponentSampleModel)dstSM,
(ComponentSampleModel)srcSM[i])) {
return tag;
}
}
return dstDT | UNCOPIED;
}
return tag;
}
/**
* Determines if the SampleModel stores data in a way that can
* be represented by a mediaLibImage without copying
*/
public static boolean isPixelSequential(SampleModel sm) {
ComponentSampleModel csm = null;
if (sm instanceof ComponentSampleModel) {
csm = (ComponentSampleModel)sm;
} else {
return false;
}
int pixelStride = csm.getPixelStride();
int bandOffsets[] = csm.getBandOffsets();
int bankIndices[] = csm.getBankIndices();
if (pixelStride != bandOffsets.length) {
return false;
}
for (int i = 0; i < bandOffsets.length; i++) {
if (bandOffsets[i] >= pixelStride ||
bankIndices[i] != bankIndices[0]) {
return false;
}
for (int j = i+1; j < bandOffsets.length; j++) {
if (bandOffsets[i] == bandOffsets[j]) {
return false;
}
}
}
return true;
}
/**
* Determines if the src ComponentSampleModel and dst
* ComponentSampleModel have matching band offsets. If they
* don't mediaLib can't deal with the image without a copy.
*/
public static boolean hasMatchingBandOffsets(ComponentSampleModel dst,
ComponentSampleModel src) {
int srcBandOffsets[] = dst.getBandOffsets();
int dstBandOffsets[] = src.getBandOffsets();
if (srcBandOffsets.length != dstBandOffsets.length) {
return false;
}
for (int i = 0; i < srcBandOffsets.length; i++) {
if (srcBandOffsets[i] != dstBandOffsets[i]) {
return false;
}
}
return true;
}
public static int getMediaLibDataType(int formatTag) {
int dataType = formatTag & DATATYPE_MASK;
switch (dataType) {
case DataBuffer.TYPE_BYTE:
return Constants.MLIB_BYTE;
case DataBuffer.TYPE_USHORT:
return Constants.MLIB_USHORT;
case DataBuffer.TYPE_SHORT:
return Constants.MLIB_SHORT;
case DataBuffer.TYPE_INT:
return Constants.MLIB_INT;
case DataBuffer.TYPE_DOUBLE:
return Constants.MLIB_DOUBLE;
case DataBuffer.TYPE_FLOAT:
return Constants.MLIB_FLOAT;
}
return -1;
}
/**
* Constructs a MediaLibAccessor object out of a Raster, Rectangle
* and formatTag returned from MediaLibAccessor.findCompatibleTag().
*
* In the case of binary data the copy mask bits of the formatTag
* will be reset within the constructor according to whether the
* data are in fact copied. This cannot be easily determined before
* the data are actually copied.
*/
public MediaLibAccessor(Raster raster, Rectangle rect, int formatTag,
boolean preferPacked) {
areBinaryDataPacked = preferPacked;
this.raster = raster;
this.rect = new Rectangle(rect);
this.formatTag = formatTag;
if(isBinary()) {
// Set binary-specific fields and return.
numBands = 1;
bandOffsets = new int[] {0};
int mlibType;
int scanlineStride;
byte[] bdata;
mlimages = new mediaLibImage[1];
if(areBinaryDataPacked) {
mlibType = Constants.MLIB_BIT;
scanlineStride = (rect.width+7)/8;
bdata = ImageUtil.getPackedBinaryData(raster, rect);
// Update format tag depending on whether the data were copied.
if(bdata ==
((DataBufferByte)raster.getDataBuffer()).getData()) {
this.formatTag |= UNCOPIED;
} else {
this.formatTag |= COPIED;
}
} else { // unpacked
mlibType = Constants.MLIB_BYTE;
scanlineStride = rect.width;
bdata = ImageUtil.getUnpackedBinaryData(raster, rect);
this.formatTag |= COPIED;
}
mlimages[0] = new mediaLibImage(mlibType,
1,
rect.width,
rect.height,
scanlineStride,
0,
bdata);
return;
}
if ((formatTag & COPY_MASK) == UNCOPIED) {
ComponentSampleModel csm =
(ComponentSampleModel)raster.getSampleModel();
numBands = csm.getNumBands();
bandOffsets = csm.getBandOffsets();
int dataOffset = raster.getDataBuffer().getOffset();
dataOffset +=
(rect.y-raster.getSampleModelTranslateY())*csm.getScanlineStride()+
(rect.x-raster.getSampleModelTranslateX())*csm.getPixelStride();
// dataoffset should and is in terms of dataElements
// scanline stride should be in terms of dataElements
int scanlineStride = csm.getScanlineStride();
switch (formatTag & DATATYPE_MASK) {
case DataBuffer.TYPE_BYTE:
DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer();
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_BYTE,
numBands,
rect.width,
rect.height,
scanlineStride,
dataOffset,
dbb.getData());
break;
case DataBuffer.TYPE_USHORT:
DataBufferUShort dbus =
(DataBufferUShort)raster.getDataBuffer();
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_USHORT,
numBands,
rect.width,
rect.height,
scanlineStride,
dataOffset,
dbus.getData());
break;
case DataBuffer.TYPE_SHORT:
DataBufferShort dbs = (DataBufferShort)raster.getDataBuffer();
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_SHORT,
numBands,
rect.width,
rect.height,
scanlineStride,
dataOffset,
dbs.getData());
break;
case DataBuffer.TYPE_INT:
DataBufferInt dbi = (DataBufferInt)raster.getDataBuffer();
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_INT,
numBands,
rect.width,
rect.height,
scanlineStride,
dataOffset,
dbi.getData());
break;
case DataBuffer.TYPE_FLOAT:
DataBuffer dbf = raster.getDataBuffer();
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_FLOAT,
numBands,
rect.width,
rect.height,
scanlineStride,
dataOffset,
DataBufferUtils.getDataFloat(dbf));
break;
case DataBuffer.TYPE_DOUBLE:
DataBuffer dbd = raster.getDataBuffer();
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_DOUBLE,
numBands,
rect.width,
rect.height,
scanlineStride,
dataOffset,
DataBufferUtils.getDataDouble(dbd));
break;
default:
throw new IllegalArgumentException((formatTag & DATATYPE_MASK) +JaiI18N.getString("MediaLibAccessor1"));
}
} else {
// Copying the data because we can't deal with it
numBands = raster.getNumBands();
bandOffsets = new int[numBands];
for (int i = 0; i < numBands; i++) {
bandOffsets[i] = i;
}
int scanlineStride = rect.width*numBands;
switch (formatTag & DATATYPE_MASK) {
case DataBuffer.TYPE_BYTE:
byte bdata[] = new byte[rect.width*rect.height*numBands];
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_BYTE,
numBands,
rect.width,
rect.height,
scanlineStride,
0,
bdata);
break;
case DataBuffer.TYPE_USHORT:
short usdata[] = new short[rect.width*rect.height*numBands];
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_USHORT,
numBands,
rect.width,
rect.height,
scanlineStride,
0,
usdata);
break;
case DataBuffer.TYPE_SHORT:
short sdata[] = new short[rect.width*rect.height*numBands];
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_SHORT,
numBands,
rect.width,
rect.height,
scanlineStride,
0,
sdata);
break;
case DataBuffer.TYPE_INT:
int idata[] = new int[rect.width*rect.height*numBands];
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_INT,
numBands,
rect.width,
rect.height,
scanlineStride,
0,
idata);
break;
case DataBuffer.TYPE_FLOAT:
float fdata[] = new float[rect.width*rect.height*numBands];
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_FLOAT,
numBands,
rect.width,
rect.height,
scanlineStride,
0,
fdata);
break;
case DataBuffer.TYPE_DOUBLE:
double ddata[] = new double[rect.width*rect.height*numBands];
mlimages = new mediaLibImage[1];
mlimages[0] =
new mediaLibImage(Constants.MLIB_DOUBLE,
numBands,
rect.width,
rect.height,
scanlineStride,
0,
ddata);
break;
default:
throw new IllegalArgumentException((formatTag & DATATYPE_MASK) + JaiI18N.getString("MediaLibAccessor1"));
}
copyDataFromRaster();
}
}
/**
* Constructs a MediaLibAccessor object out of a Raster, Rectangle
* and formatTag returned from MediaLibAccessor.findCompatibleTag().
*/
public MediaLibAccessor(Raster raster, Rectangle rect, int formatTag) {
this(raster, rect, formatTag, false);
}
/**
* Returns <code>true</code> if the <code>MediaLibAccessor</code>
* represents binary data.
*/
public boolean isBinary() {
return ((formatTag & BINARY_MASK) == BINARY);
}
/**
* Returns an array of mediaLibImages which represents the input raster.
* An array is returned instead of a single mediaLibImage because
* in some cases, an input Raster can't be represented by one
* mediaLibImage (unless copying is done) but can be represented
* by several mediaLibImages without copying.
*/
public mediaLibImage[] getMediaLibImages() {
return mlimages;
}
/**
* Returns the data type of the RasterAccessor object. Note that
* this datatype is not necessarily the same data type as the
* underlying raster.
*/
public int getDataType() {
return formatTag & DATATYPE_MASK;
}
/**
* Returns true if the MediaLibAccessors's data is copied from it's
* raster.
*/
public boolean isDataCopy() {
return ((formatTag & COPY_MASK) == COPIED);
}
/** Returns the bandOffsets. */
public int[] getBandOffsets() {
return bandOffsets;
}
/**
* Returns parameters in the appropriate order if MediaLibAccessor
* has reordered the bands or is attempting to make a
* BandSequential image look like multiple PixelSequentialImages
*/
public int[] getIntParameters(int band, int params[]) {
int returnParams[] = new int[numBands];
for (int i = 0; i < numBands; i++) {
returnParams[i] = params[bandOffsets[i+band]];
}
return returnParams;
}
/**
* Returns parameters in the appropriate order if MediaLibAccessor
* has reordered the bands or is attempting to make a
* BandSequential image look like multiple PixelSequentialImages
*/
public int[][] getIntArrayParameters(int band, int[][] params) {
int returnParams[][] = new int[numBands][];
for (int i = 0; i < numBands; i++) {
returnParams[i] = params[bandOffsets[i+band]];
}
return returnParams;
}
/**
* Returns parameters in the appropriate order if MediaLibAccessor
* has reordered the bands or is attempting to make a
* BandSequential image look like multiple PixelSequentialImages
*/
public double[] getDoubleParameters(int band, double params[]) {
double returnParams[] = new double[numBands];
for (int i = 0; i < numBands; i++) {
returnParams[i] = params[bandOffsets[i+band]];
}
return returnParams;
}
/**
* Copy data from Raster to MediaLib image
*/
private void copyDataFromRaster() {
// Writeback should only be necessary on destRasters which
// should be writable so this cast should succeed.
if (raster.getSampleModel() instanceof ComponentSampleModel) {
ComponentSampleModel csm =
(ComponentSampleModel)raster.getSampleModel();
int rasScanlineStride = csm.getScanlineStride();
int rasPixelStride = csm.getPixelStride();
int subRasterOffset =
(rect.y-raster.getSampleModelTranslateY())*rasScanlineStride+
(rect.x-raster.getSampleModelTranslateX())*rasPixelStride;
int rasBankIndices[] = csm.getBankIndices();
int rasBandOffsets[] = csm.getBandOffsets();
int rasDataOffsets[] = raster.getDataBuffer().getOffsets();
if (rasDataOffsets.length == 1) {
for (int i = 0; i < numBands; i++) {
rasBandOffsets[i] += rasDataOffsets[0] +
subRasterOffset;
}
} else if (rasDataOffsets.length == rasBandOffsets.length) {
for (int i = 0; i < numBands; i++) {
rasBandOffsets[i] += rasDataOffsets[i] +
subRasterOffset;
}
}
Object mlibDataArray = null;
switch (getDataType()) {
case DataBuffer.TYPE_BYTE:
byte bArray[][] = new byte[numBands][];
for (int i = 0; i < numBands; i++) {
bArray[i] = mlimages[0].getByteData();
}
mlibDataArray = bArray;
break;
case DataBuffer.TYPE_USHORT:
short usArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
usArray[i] = mlimages[0].getUShortData();
}
mlibDataArray = usArray;
break;
case DataBuffer.TYPE_SHORT:
short sArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
sArray[i] = mlimages[0].getShortData();
}
mlibDataArray = sArray;
break;
case DataBuffer.TYPE_INT:
int iArray[][] = new int[numBands][];
for (int i = 0; i < numBands; i++) {
iArray[i] = mlimages[0].getIntData();
}
mlibDataArray = iArray;
break;
case DataBuffer.TYPE_FLOAT:
float fArray[][] = new float[numBands][];
for (int i = 0; i < numBands; i++) {
fArray[i] = mlimages[0].getFloatData();
}
mlibDataArray = fArray;
break;
case DataBuffer.TYPE_DOUBLE:
double dArray[][] = new double[numBands][];
for (int i = 0; i < numBands; i++) {
dArray[i] = mlimages[0].getDoubleData();
}
mlibDataArray = dArray;
break;
}
Object rasDataArray = null;
switch (csm.getDataType()) {
case DataBuffer.TYPE_BYTE: {
DataBufferByte dbb =
(DataBufferByte)raster.getDataBuffer();
byte rasByteDataArray[][] = new byte[numBands][];
for (int i = 0; i < numBands; i++) {
rasByteDataArray[i] =
dbb.getData(rasBankIndices[i]);
}
rasDataArray = rasByteDataArray;
}
break;
case DataBuffer.TYPE_USHORT: {
DataBufferUShort dbus =
(DataBufferUShort)raster.getDataBuffer();
short rasUShortDataArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
rasUShortDataArray[i] =
dbus.getData(rasBankIndices[i]);
}
rasDataArray = rasUShortDataArray;
}
break;
case DataBuffer.TYPE_SHORT: {
DataBufferShort dbs =
(DataBufferShort)raster.getDataBuffer();
short rasShortDataArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
rasShortDataArray[i] =
dbs.getData(rasBankIndices[i]);
}
rasDataArray = rasShortDataArray;
}
break;
case DataBuffer.TYPE_INT: {
DataBufferInt dbi =
(DataBufferInt)raster.getDataBuffer();
int rasIntDataArray[][] = new int[numBands][];
for (int i = 0; i < numBands; i++) {
rasIntDataArray[i] =
dbi.getData(rasBankIndices[i]);
}
rasDataArray = rasIntDataArray;
}
break;
case DataBuffer.TYPE_FLOAT: {
DataBuffer dbf =
raster.getDataBuffer();
float rasFloatDataArray[][] = new float[numBands][];
for (int i = 0; i < numBands; i++) {
rasFloatDataArray[i] =
DataBufferUtils.getDataFloat(dbf, rasBankIndices[i]);
}
rasDataArray = rasFloatDataArray;
}
break;
case DataBuffer.TYPE_DOUBLE: {
DataBuffer dbd =
raster.getDataBuffer();
double rasDoubleDataArray[][] = new double[numBands][];
for (int i = 0; i < numBands; i++) {
rasDoubleDataArray[i] =
DataBufferUtils.getDataDouble(dbd, rasBankIndices[i]);
}
rasDataArray = rasDoubleDataArray;
}
break;
}
// dst = mlib && src = ras
Image.Reformat(
mlibDataArray,
rasDataArray,
numBands,
rect.width,rect.height,
getMediaLibDataType(this.getDataType()),
bandOffsets,
rect.width*numBands,
numBands,
getMediaLibDataType(csm.getDataType()),
rasBandOffsets,
rasScanlineStride,
rasPixelStride);
} else {
// If COPIED and the raster doesn't have ComponentSampleModel
// data is moved with getPixel/setPixel (even byte/short)
switch (getDataType()) {
case DataBuffer.TYPE_INT:
raster.getPixels(rect.x,rect.y,
rect.width,rect.height,
mlimages[0].getIntData());
break;
case DataBuffer.TYPE_FLOAT:
raster.getPixels(rect.x,rect.y,
rect.width,rect.height,
mlimages[0].getFloatData());
break;
case DataBuffer.TYPE_DOUBLE:
raster.getPixels(rect.x,rect.y,
rect.width,rect.height,
mlimages[0].getDoubleData());
break;
}
}
}
/**
* Copies data back into the MediaLibAccessor's raster. Note that
* the data is casted from the intermediate data format to
* the raster's format. If clamping is needed, the call
* clampDataArrays() method needs to be called before
* calling the copyDataToRaster() method.
*/
public void copyDataToRaster() {
if (isDataCopy()) {
if(isBinary()) {
if(areBinaryDataPacked) {
ImageUtil.setPackedBinaryData(mlimages[0].getBitData(),
(WritableRaster)raster,
rect);
} else { // unpacked
ImageUtil.setUnpackedBinaryData(mlimages[0].getByteData(),
(WritableRaster)raster,
rect);
}
return;
}
// Writeback should only be necessary on destRasters which
// should be writable so this cast should succeed.
WritableRaster wr = (WritableRaster)raster;
if (wr.getSampleModel() instanceof ComponentSampleModel) {
ComponentSampleModel csm =
(ComponentSampleModel)wr.getSampleModel();
int rasScanlineStride = csm.getScanlineStride();
int rasPixelStride = csm.getPixelStride();
int subRasterOffset =
(rect.y-raster.getSampleModelTranslateY())*rasScanlineStride+
(rect.x-raster.getSampleModelTranslateX())*rasPixelStride;
int rasBankIndices[] = csm.getBankIndices();
int rasBandOffsets[] = csm.getBandOffsets();
int rasDataOffsets[] = raster.getDataBuffer().getOffsets();
if (rasDataOffsets.length == 1) {
for (int i = 0; i < numBands; i++) {
rasBandOffsets[i] += rasDataOffsets[0] +
subRasterOffset;
}
} else if (rasDataOffsets.length == rasBandOffsets.length) {
for (int i = 0; i < numBands; i++) {
rasBandOffsets[i] += rasDataOffsets[i] +
subRasterOffset;
}
}
Object mlibDataArray = null;
switch (getDataType()) {
case DataBuffer.TYPE_BYTE:
byte bArray[][] = new byte[numBands][];
for (int i = 0; i < numBands; i++) {
bArray[i] = mlimages[0].getByteData();
}
mlibDataArray = bArray;
break;
case DataBuffer.TYPE_USHORT:
short usArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
usArray[i] = mlimages[0].getUShortData();
}
mlibDataArray = usArray;
break;
case DataBuffer.TYPE_SHORT:
short sArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
sArray[i] = mlimages[0].getShortData();
}
mlibDataArray = sArray;
break;
case DataBuffer.TYPE_INT:
int iArray[][] = new int[numBands][];
for (int i = 0; i < numBands; i++) {
iArray[i] = mlimages[0].getIntData();
}
mlibDataArray = iArray;
break;
case DataBuffer.TYPE_FLOAT:
float fArray[][] = new float[numBands][];
for (int i = 0; i < numBands; i++) {
fArray[i] = mlimages[0].getFloatData();
}
mlibDataArray = fArray;
break;
case DataBuffer.TYPE_DOUBLE:
double dArray[][] = new double[numBands][];
for (int i = 0; i < numBands; i++) {
dArray[i] = mlimages[0].getDoubleData();
}
mlibDataArray = dArray;
break;
}
byte tmpDataArray[] = null;
Object rasDataArray = null;
switch (csm.getDataType()) {
case DataBuffer.TYPE_BYTE: {
DataBufferByte dbb =
(DataBufferByte)raster.getDataBuffer();
byte rasByteDataArray[][] = new byte[numBands][];
for (int i = 0; i < numBands; i++) {
rasByteDataArray[i] =
dbb.getData(rasBankIndices[i]);
}
tmpDataArray = rasByteDataArray[0];
rasDataArray = rasByteDataArray;
}
break;
case DataBuffer.TYPE_USHORT: {
DataBufferUShort dbus =
(DataBufferUShort)raster.getDataBuffer();
short rasUShortDataArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
rasUShortDataArray[i] =
dbus.getData(rasBankIndices[i]);
}
rasDataArray = rasUShortDataArray;
}
break;
case DataBuffer.TYPE_SHORT: {
DataBufferShort dbs =
(DataBufferShort)raster.getDataBuffer();
short rasShortDataArray[][] = new short[numBands][];
for (int i = 0; i < numBands; i++) {
rasShortDataArray[i] =
dbs.getData(rasBankIndices[i]);
}
rasDataArray = rasShortDataArray;
}
break;
case DataBuffer.TYPE_INT: {
DataBufferInt dbi =
(DataBufferInt)raster.getDataBuffer();
int rasIntDataArray[][] = new int[numBands][];
for (int i = 0; i < numBands; i++) {
rasIntDataArray[i] =
dbi.getData(rasBankIndices[i]);
}
rasDataArray = rasIntDataArray;
}
break;
case DataBuffer.TYPE_FLOAT: {
DataBuffer dbf =
raster.getDataBuffer();
float rasFloatDataArray[][] = new float[numBands][];
for (int i = 0; i < numBands; i++) {
rasFloatDataArray[i] =
DataBufferUtils.getDataFloat(dbf, rasBankIndices[i]);
}
rasDataArray = rasFloatDataArray;
}
break;
case DataBuffer.TYPE_DOUBLE: {
DataBuffer dbd =
raster.getDataBuffer();
double rasDoubleDataArray[][] = new double[numBands][];
for (int i = 0; i < numBands; i++) {
rasDoubleDataArray[i] =
DataBufferUtils.getDataDouble(dbd, rasBankIndices[i]);
}
rasDataArray = rasDoubleDataArray;
}
break;
}
// src = mlib && dst = ras
Image.Reformat(
rasDataArray,
mlibDataArray,
numBands,
rect.width,rect.height,
getMediaLibDataType(csm.getDataType()),
rasBandOffsets,
rasScanlineStride,
rasPixelStride,
getMediaLibDataType(this.getDataType()),
bandOffsets,
rect.width*numBands,
numBands);
} else {
// If COPIED and the raster doesn't have ComponentSampleModel
// data is moved with getPixel/setPixel (even byte/short)
switch (getDataType()) {
case DataBuffer.TYPE_INT:
wr.setPixels(rect.x,rect.y,
rect.width,rect.height,
mlimages[0].getIntData());
break;
case DataBuffer.TYPE_FLOAT:
wr.setPixels(rect.x,rect.y,
rect.width,rect.height,
mlimages[0].getFloatData());
break;
case DataBuffer.TYPE_DOUBLE:
wr.setPixels(rect.x,rect.y,
rect.width,rect.height,
mlimages[0].getDoubleData());
break;
}
}
}
}
/**
* Clamps data array values to a range that the underlying raster
* can deal with. For example, if the underlying raster stores
* data as bytes, but the samples ares unpacked into integer arrays by
* the RasterAccessor object for an operation, the operation will
* need to call clampDataArrays() so that the data in the int
* arrays is restricted to the range 0..255 before a setPixels()
* call is made on the underlying raster. Note that some
* operations (for example, lookup) can guarantee that their
* results don't need clamping so they can call
* RasterAccessor.copyDataToRaster() without first calling this
* function.
*/
public void clampDataArrays () {
if (!isDataCopy()) {
return;
}
// additonal medialib check: If it's a componentSampleModel
// we get a free cast when we call medialibWrapper.Reformat
// to copy the data to the source. So we don't need to cast
// here.
if (raster.getSampleModel() instanceof ComponentSampleModel) {
return;
}
int bits[] = raster.getSampleModel().getSampleSize();
// Do we even need a clamp? We do if there's any band
// of the source image stored in that's less than 32 bits
// and is stored in a byte, short or int format. (The automatic
// cast's between floats/doubles and 32-bit ints in setPixel()
// generall do what we want.)
boolean needClamp = false;
boolean uniformBitSize = true;
for (int i = 0; i < bits.length; i++) {
int bitSize = bits[0];
if (bits[i] < 32) {
needClamp = true;
}
if (bits[i] != bitSize) {
uniformBitSize = false;
}
}
if (!needClamp) {
return;
}
int dataType = raster.getDataBuffer().getDataType();
double hiVals[] = new double[bits.length];
double loVals[] = new double[bits.length];
if (dataType == DataBuffer.TYPE_USHORT &&
uniformBitSize && bits[0] == 16) {
for (int i = 0; i < bits.length; i++) {
hiVals[i] = (double)0xFFFF;
loVals[i] = (double)0;
}
} else if (dataType == DataBuffer.TYPE_SHORT &&
uniformBitSize && bits[0] == 16) {
for (int i = 0; i < bits.length; i++) {
hiVals[i] = (double)Short.MAX_VALUE;
loVals[i] = (double)Short.MIN_VALUE;
}
} else if (dataType == DataBuffer.TYPE_INT &&
uniformBitSize && bits[0] == 32) {
for (int i = 0; i < bits.length; i++) {
hiVals[i] = (double)Integer.MAX_VALUE;
loVals[i] = (double)Integer.MIN_VALUE;
}
} else {
for (int i = 0; i < bits.length; i++) {
hiVals[i] = (double)((1 << bits[i]) - 1);
loVals[i] = (double)0;
}
}
clampDataArray(hiVals,loVals);
}
private void clampDataArray(double hiVals[], double loVals[]) {
switch (getDataType()) {
case DataBuffer.TYPE_INT:
clampIntArrays(toIntArray(hiVals),toIntArray(loVals));
break;
case DataBuffer.TYPE_FLOAT:
clampFloatArrays(toFloatArray(hiVals),toFloatArray(loVals));
break;
case DataBuffer.TYPE_DOUBLE:
clampDoubleArrays(hiVals,loVals);
break;
}
}
private int[] toIntArray(double vals[]) {
int returnVals[] = new int[vals.length];
for (int i = 0; i < vals.length; i++) {
returnVals[i] = (int)vals[i];
}
return returnVals;
}
private float[] toFloatArray(double vals[]) {
float returnVals[] = new float[vals.length];
for (int i = 0; i < vals.length; i++) {
returnVals[i] = (float)vals[i];
}
return returnVals;
}
private void clampIntArrays(int hiVals[], int loVals[]) {
int width = rect.width;
int height = rect.height;
int scanlineStride = numBands*width;
for (int k = 0; k < numBands; k++) {
int data[] = mlimages[0].getIntData();
int scanlineOffset = k;
int hiVal = hiVals[k];
int loVal = loVals[k];
for (int j = 0; j < height; j++) {
int pixelOffset = scanlineOffset;
for (int i = 0; i < width; i++) {
int tmp = data[pixelOffset];
if (tmp < loVal) {
data[pixelOffset] = loVal;
} else if (tmp > hiVal) {
data[pixelOffset] = hiVal;
}
pixelOffset += numBands;
}
scanlineOffset += scanlineStride;
}
}
}
private void clampFloatArrays(float hiVals[], float loVals[]) {
int width = rect.width;
int height = rect.height;
int scanlineStride = numBands*width;
for (int k = 0; k < numBands; k++) {
float data[] = mlimages[0].getFloatData();
int scanlineOffset = k;
float hiVal = hiVals[k];
float loVal = loVals[k];
for (int j = 0; j < height; j++) {
int pixelOffset = scanlineOffset;
for (int i = 0; i < width; i++) {
float tmp = data[pixelOffset];
if (tmp < loVal) {
data[pixelOffset] = loVal;
} else if (tmp > hiVal) {
data[pixelOffset] = hiVal;
}
pixelOffset += numBands;
}
scanlineOffset += scanlineStride;
}
}
}
private void clampDoubleArrays(double hiVals[], double loVals[]) {
int width = rect.width;
int height = rect.height;
int scanlineStride = numBands*width;
for (int k = 0; k < numBands; k++) {
double data[] = mlimages[0].getDoubleData();
int scanlineOffset = k;
double hiVal = hiVals[k];
double loVal = loVals[k];
for (int j = 0; j < height; j++) {
int pixelOffset = scanlineOffset;
for (int i = 0; i < width; i++) {
double tmp = data[pixelOffset];
if (tmp < loVal) {
data[pixelOffset] = loVal;
} else if (tmp > hiVal) {
data[pixelOffset] = hiVal;
}
pixelOffset += numBands;
}
scanlineOffset += scanlineStride;
}
}
}
}
class MediaLibLoadException extends Exception {
MediaLibLoadException() {
super();
}
public synchronized Throwable
fillInStackTrace() {
return this;
}
}