/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2001-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.resources.image;
import java.awt.Dimension;
import java.awt.RenderingHints;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferDouble;
import java.awt.image.DataBufferFloat;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.image.WritableRenderedImage;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageReader;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderWriterSpi;
import javax.media.jai.BorderExtender;
import javax.media.jai.BorderExtenderCopy;
import javax.media.jai.BorderExtenderReflect;
import javax.media.jai.ImageLayout;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.OpImage;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.RenderedOp;
import org.geotools.resources.Classes;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import com.sun.media.imageioimpl.common.PackageUtil;
import com.sun.media.jai.operator.ImageReadDescriptor;
/**
* A set of static methods working on images. Some of those methods are useful, but not
* really rigorous. This is why they do not appear in any "official" package, but instead
* in this private one.
*
* <strong>Do not rely on this API!</strong>
*
* It may change in incompatible way in any future version.
*
* @since 2.0
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
* @author Simone Giannecchini
*/
public final class ImageUtilities {
/**
* {@code true} if JAI media lib is available.
*/
private static final boolean mediaLibAvailable;
static {
// do we wrappers at hand?
boolean mediaLib = false;
Class mediaLibImage = null;
try {
mediaLibImage = Class.forName("com.sun.medialib.mlib.Image");
} catch (ClassNotFoundException e) {
}
mediaLib = (mediaLibImage != null);
// npw check if we either wanted to disable explicitly and if we installed the native libs
if(mediaLib){
try {
// explicit disable
mediaLib =
!Boolean.getBoolean("com.sun.media.jai.disableMediaLib");
//native libs installed
if(mediaLib)
{
final Class mImage=mediaLibImage;
mediaLib=AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
try {
//get the method
final Class params[] = {};
Method method= mImage.getDeclaredMethod("isAvailable", params);
//invoke
final Object paramsObj[] = {};
final Object o=mImage.newInstance();
return (Boolean) method.invoke(o, paramsObj);
} catch (Throwable e) {
return false;
}
}
});
}
} catch (Throwable e) {
// Because the property com.sun.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
mediaLib=false;
}
}
mediaLibAvailable=mediaLib;
}
/**
* {@link RenderingHints} used to prevent {@link JAI} operations from expanding
* {@link IndexColorModel}s.
*/
public final static RenderingHints DONT_REPLACE_INDEX_COLOR_MODEL = new RenderingHints(
JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE);
/**
* {@link RenderingHints} used to force {@link JAI} operations to expand
* {@link IndexColorModel}s.
*/
public final static RenderingHints REPLACE_INDEX_COLOR_MODEL = new RenderingHints(
JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE);
/**
* {@link RenderingHints} for requesting Nearest Neighbor intepolation.
*/
public static final RenderingHints NN_INTERPOLATION_HINT = new RenderingHints(
JAI.KEY_INTERPOLATION, Interpolation.getInstance(Interpolation.INTERP_NEAREST));
/**
* {@link RenderingHints} for avoiding caching of {@link JAI} {@link RenderedOp}s.
*/
public static final RenderingHints NOCACHE_HINT = new RenderingHints(
JAI.KEY_TILE_CACHE, null);
/**
* Cached instance of a {@link RenderingHints} for controlling border extension on
* {@link JAI} operations. It contains an instance of a {@link BorderExtenderCopy}.
*/
public final static RenderingHints EXTEND_BORDER_BY_COPYING = new RenderingHints(
JAI.KEY_BORDER_EXTENDER, BorderExtender
.createInstance(BorderExtender.BORDER_COPY));
/**
* Cached instance of a {@link RenderingHints} for controlling border extension on
* {@link JAI} operations. It contains an instance of a {@link BorderExtenderReflect}.
*/
public final static RenderingHints EXTEND_BORDER_BY_REFLECT = new RenderingHints(
JAI.KEY_BORDER_EXTENDER, BorderExtender
.createInstance(BorderExtender.BORDER_REFLECT));
/**
* The default tile size. This default tile size can be
* overridden with a call to {@link JAI#setDefaultTileSize}.
*/
private static final Dimension GEOTOOLS_DEFAULT_TILE_SIZE = new Dimension(512,512);
/**
* The minimum tile size.
*/
private static final int GEOTOOLS_MIN_TILE_SIZE = 256;
/**
* Maximum tile width or height before to consider a tile as a stripe. It tile width or height
* are smaller or equals than this size, then the image will be retiled. That is done because
* there are many formats that use stripes as an alternative to tiles, an example is tiff. A
* stripe can be a performance black hole, users can have stripes as large as 20000 columns x 8
* rows. If we just want to see a chunk of 512x512, this is a lot of uneeded data to load.
*/
private static final int STRIPE_SIZE = 64;
/**
* List of valid names. Note: the "Optimal" type is not
* implemented because currently not provided by JAI.
*/
private static final String[] INTERPOLATION_NAMES = {
"Nearest", // JAI name
"NearestNeighbor", // OpenGIS name
"Bilinear",
"Bicubic",
"Bicubic2" // Not in OpenGIS specification.
};
/**
* Interpolation types (provided by Java Advanced Imaging) for {@link #INTERPOLATION_NAMES}.
*/
private static final int[] INTERPOLATION_TYPES= {
Interpolation.INTERP_NEAREST,
Interpolation.INTERP_NEAREST,
Interpolation.INTERP_BILINEAR,
Interpolation.INTERP_BICUBIC,
Interpolation.INTERP_BICUBIC_2
};
/**
* Do not allow creation of instances of this class.
*/
private ImageUtilities() {
}
/**
* Suggests an {@link ImageLayout} for the specified image. All parameters are initially set
* equal to those of the given {@link RenderedImage}, and then the {@linkplain #toTileSize
* tile size is updated according the image size}. This method never returns {@code null}.
*/
public static ImageLayout getImageLayout(final RenderedImage image) {
return getImageLayout(image, true);
}
/**
* Returns an {@link ImageLayout} for the specified image. If {@code initToImage} is
* {@code true}, then all parameters are initially set equal to those of the given
* {@link RenderedImage} and the returned layout is never {@code null} (except if
* the image is null).
*/
private static ImageLayout getImageLayout(final RenderedImage image, final boolean initToImage) {
if (image == null) {
return null;
}
ImageLayout layout = initToImage ? new ImageLayout(image) : null;
if ((image.getNumXTiles()==1 || image.getTileWidth () <= STRIPE_SIZE) &&
(image.getNumYTiles()==1 || image.getTileHeight() <= STRIPE_SIZE))
{
// If the image was already tiled, reuse the same tile size.
// Otherwise, compute default tile size. If a default tile
// size can't be computed, it will be left unset.
if (layout != null) {
layout = layout.unsetTileLayout();
}
Dimension defaultSize = JAI.getDefaultTileSize();
if (defaultSize == null) {
defaultSize = GEOTOOLS_DEFAULT_TILE_SIZE;
}
int s;
if ((s=toTileSize(image.getWidth(), defaultSize.width)) != 0) {
if (layout == null) {
layout = new ImageLayout();
}
layout = layout.setTileWidth(s);
layout.setTileGridXOffset(image.getMinX());
}
if ((s=toTileSize(image.getHeight(), defaultSize.height)) != 0) {
if (layout == null) {
layout = new ImageLayout();
}
layout = layout.setTileHeight(s);
layout.setTileGridYOffset(image.getMinY());
}
}
return layout;
}
/**
* Suggests a set of {@link RenderingHints} for the specified image.
* The rendering hints may include the following parameters:
*
* <ul>
* <li>{@link JAI#KEY_IMAGE_LAYOUT} with a proposed tile size.</li>
* </ul>
*
* This method may returns {@code null} if no rendering hints is proposed.
*/
public static RenderingHints getRenderingHints(final RenderedImage image) {
final ImageLayout layout = getImageLayout(image, false);
return (layout != null) ? new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout) : null;
}
/**
* Suggests a tile size for the specified image size. On input, {@code size} is the image's
* size. On output, it is the tile size. This method write the result directly in the supplied
* object and returns {@code size} for convenience.
* <p>
* This method it aimed to computing a tile size such that the tile grid would have overlapped
* the image bound in order to avoid having tiles crossing the image bounds and being therefore
* partially empty. This method will never returns a tile size smaller than
* {@value ImageUtilities#GEOTOOLS_MIN_TILE_SIZE}. If this method can't suggest a size,
* then it left the corresponding {@code size} field ({@link Dimension#width width} or
* {@link Dimension#height height}) unchanged.
* <p>
* The {@link Dimension#width width} and {@link Dimension#height height} fields are processed
* independently in the same way. The following discussion use the {@code width} field as an
* example.
* <p>
* This method inspects different tile sizes close to the {@linkplain JAI#getDefaultTileSize()
* default tile size}. Lets {@code width} be the default tile width. Values are tried in the
* following order: {@code width}, {@code width+1}, {@code width-1}, {@code width+2},
* {@code width-2}, {@code width+3}, {@code width-3}, <cite>etc.</cite> until one of the
* following happen:
* <p>
* <ul>
* <li>A suitable tile size is found. More specifically, a size is found which is a dividor
* of the specified image size, and is the closest one of the default tile size. The
* {@link Dimension} field ({@code width} or {@code height}) is set to this value.</li>
*
* <li>An arbitrary limit (both a minimum and a maximum tile size) is reached. In this case,
* this method <strong>may</strong> set the {@link Dimension} field to a value that
* maximize the remainder of <var>image size</var> / <var>tile size</var> (in other
* words, the size that left as few empty pixels as possible).</li>
* </ul>
*/
public static Dimension toTileSize(final Dimension size) {
Dimension defaultSize = JAI.getDefaultTileSize();
if (defaultSize == null) {
defaultSize = GEOTOOLS_DEFAULT_TILE_SIZE;
}
int s;
if ((s=toTileSize(size.width, defaultSize.width )) != 0) size.width = s;
if ((s=toTileSize(size.height, defaultSize.height)) != 0) size.height = s;
return size;
}
/**
* Suggests a tile size close to {@code tileSize} for the specified {@code imageSize}.
* This method it aimed to computing a tile size such that the tile grid would have
* overlapped the image bound in order to avoid having tiles crossing the image bounds
* and being therefore partially empty. This method will never returns a tile size smaller
* than {@value #GEOTOOLS_MIN_TILE_SIZE}. If this method can't suggest a size, then it returns 0.
*
* @param imageSize The image size.
* @param tileSize The preferred tile size, which is often {@value #GEOTOOLS_DEFAULT_TILE_SIZE}.
*/
private static int toTileSize(final int imageSize, final int tileSize) {
final int MAX_TILE_SIZE = Math.min(tileSize*2, imageSize);
final int stop = Math.max(tileSize-GEOTOOLS_MIN_TILE_SIZE, MAX_TILE_SIZE-tileSize);
int sopt = 0; // An "optimal" tile size, to be used if no exact dividor is found.
int rmax = 0; // The remainder of 'imageSize / sopt'. We will try to maximize this value.
/*
* Inspects all tile sizes in the range [GEOTOOLS_MIN_TILE_SIZE .. MAX_TIME_SIZE]. We will begin
* with a tile size equals to the specified 'tileSize'. Next we will try tile sizes of
* 'tileSize+1', 'tileSize-1', 'tileSize+2', 'tileSize-2', 'tileSize+3', 'tileSize-3',
* etc. until a tile size if found suitable.
*
* More generally, the loop below tests the 'tileSize+i' and 'tileSize-i' values. The
* 'stop' constant was computed assuming that MIN_TIME_SIZE < tileSize < MAX_TILE_SIZE.
* If a tile size is found which is a dividor of the image size, than that tile size (the
* closest one to 'tileSize') is returned. Otherwise, the loop continue until all values
* in the range [GEOTOOLS_MIN_TILE_SIZE .. MAX_TIME_SIZE] were tested. In this process, we remind
* the tile size that gave the greatest reminder (rmax). In other words, this is the tile
* size with the smallest amount of empty pixels.
*/
for (int i=0; i<=stop; i++) {
int s;
if ((s = tileSize+i) <= MAX_TILE_SIZE) {
final int r = imageSize % s;
if (r == 0) {
// Found a size >= to 'tileSize' which is a dividor of image size.
return s;
}
if (r > rmax) {
rmax = r;
sopt = s;
}
}
if ((s = tileSize-i) >= GEOTOOLS_MIN_TILE_SIZE) {
final int r = imageSize % s;
if (r == 0) {
// Found a size <= to 'tileSize' which is a dividor of image size.
return s;
}
if (r > rmax) {
rmax = r;
sopt = s;
}
}
}
/*
* No dividor were found in the range [GEOTOOLS_MIN_TILE_SIZE .. MAX_TIME_SIZE]. At this point
* 'sopt' is an "optimal" tile size (the one that left as few empty pixel as possible),
* and 'rmax' is the amount of non-empty pixels using this tile size. We will use this
* "optimal" tile size only if it fill at least 75% of the tile. Otherwise, we arbitrarily
* consider that it doesn't worth to use a "non-standard" tile size. The purpose of this
* arbitrary test is again to avoid too many small tiles (assuming that
*/
return (rmax >= tileSize - tileSize/4) ? sopt : 0;
}
/**
* Computes a new {@link ImageLayout} which is the intersection of the specified
* {@code ImageLayout} and all {@code RenderedImage}s in the supplied list. If the
* {@link ImageLayout#getMinX minX}, {@link ImageLayout#getMinY minY},
* {@link ImageLayout#getWidth width} and {@link ImageLayout#getHeight height}
* properties are not defined in the {@code layout}, then they will be inherited
* from the <strong>first</strong> source for consistency with {@link OpImage} constructor.
*
* @param layout The original layout. This object will not be modified.
* @param sources The list of sources {@link RenderedImage}.
* @return A new {@code ImageLayout}, or the original {@code layout} if no change was needed.
*/
public static ImageLayout createIntersection(final ImageLayout layout, final List<RenderedImage> sources) {
ImageLayout result = layout;
if (result == null) {
result = new ImageLayout();
}
final int n = sources.size();
if (n != 0) {
// If layout is not set, OpImage uses the layout of the *first*
// source image according OpImage constructor javadoc.
RenderedImage source = (RenderedImage) sources.get(0);
int minXL = result.getMinX (source);
int minYL = result.getMinY (source);
int maxXL = result.getWidth (source) + minXL;
int maxYL = result.getHeight(source) + minYL;
for (int i=0; i<n; i++) {
source = (RenderedImage) sources.get(i);
final int minX = source.getMinX ();
final int minY = source.getMinY ();
final int maxX = source.getWidth () + minX;
final int maxY = source.getHeight() + minY;
int mask = 0;
if (minXL < minX) mask |= (1|4); // set minX and width
if (minYL < minY) mask |= (2|8); // set minY and height
if (maxXL > maxX) mask |= (4); // Set width
if (maxYL > maxY) mask |= (8); // Set height
if (mask != 0) {
if (layout == result) {
result = (ImageLayout) layout.clone();
}
if ((mask & 1) != 0) result.setMinX (minXL=minX);
if ((mask & 2) != 0) result.setMinY (minYL=minY);
if ((mask & 4) != 0) result.setWidth ((maxXL=maxX) - minXL);
if ((mask & 8) != 0) result.setHeight((maxYL=maxY) - minYL);
}
}
// If the bounds changed, adjust the tile size.
if (result != layout) {
source = (RenderedImage) sources.get(0);
if (result.isValid(ImageLayout.TILE_WIDTH_MASK)) {
final int oldSize = result.getTileWidth(source);
final int newSize = toTileSize(result.getWidth(source), oldSize);
if (oldSize != newSize) {
result.setTileWidth(newSize);
}
}
if (result.isValid(ImageLayout.TILE_HEIGHT_MASK)) {
final int oldSize = result.getTileHeight(source);
final int newSize = toTileSize(result.getHeight(source), oldSize);
if (oldSize != newSize) {
result.setTileHeight(newSize);
}
}
}
}
return result;
}
/**
* Casts the specified object to an {@link Interpolation object}.
*
* @param type The interpolation type as an {@link Interpolation} or a {@link CharSequence}
* object.
* @return The interpolation object for the specified type.
* @throws IllegalArgumentException if the specified interpolation type is not a know one.
*/
public static Interpolation toInterpolation(final Object type) throws IllegalArgumentException {
if (type instanceof Interpolation) {
return (Interpolation) type;
} else if (type instanceof CharSequence) {
final String name = type.toString();
final int length= INTERPOLATION_NAMES.length;
for (int i=0; i<length; i++) {
if (INTERPOLATION_NAMES[i].equalsIgnoreCase(name)) {
return Interpolation.getInstance(INTERPOLATION_TYPES[i]);
}
}
}
throw new IllegalArgumentException(Errors.format(ErrorKeys.UNKNOW_INTERPOLATION_$1, type));
}
/**
* Returns the interpolation name for the specified interpolation object.
* This method tries to infer the name from the object's class name.
*
* @param Interpolation The interpolation object, or {@code null} for "nearest"
* (which is an other way to say "no interpolation").
*/
public static String getInterpolationName(Interpolation interp) {
if (interp == null) {
interp = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
}
final String prefix = "Interpolation";
for (Class<?> classe = interp.getClass(); classe!=null; classe=classe.getSuperclass()) {
String name = Classes.getShortName(classe);
int index = name.lastIndexOf(prefix);
if (index >= 0) {
return name.substring(index + prefix.length());
}
}
return Classes.getShortClassName(interp);
}
/**
* Allows or disallows native acceleration for the specified image format. By default, the
* image I/O extension for JAI provides native acceleration for PNG and JPEG. Unfortunatly,
* those native codec has bug in their 1.0 version. Invoking this method will force the use
* of standard codec provided in J2SE 1.4.
* <p>
* <strong>Implementation note:</strong> the current implementation assume that JAI codec
* class name start with "CLib". It work for Sun's 1.0 implementation, but may change in
* future versions. If this method doesn't recognize the class name, it does nothing.
*
* @param format The format name (e.g. "png").
* @param category {@code ImageReaderSpi.class} to set the reader, or
* {@code ImageWriterSpi.class} to set the writer.
* @param allowed {@code false} to disallow native acceleration.
*/
public static synchronized <T extends ImageReaderWriterSpi> void allowNativeCodec(
final String format, final Class<T> category, final boolean allowed)
{
T standard = null;
T codeclib = null;
final IIORegistry registry = IIORegistry.getDefaultInstance();
for (final Iterator<T> it = registry.getServiceProviders(category, false); it.hasNext();) {
final T provider = it.next();
final String[] formats = provider.getFormatNames();
for (int i=0; i<formats.length; i++) {
if (formats[i].equalsIgnoreCase(format)) {
if (Classes.getShortClassName(provider).startsWith("CLib")) {
codeclib = provider;
} else {
standard = provider;
}
break;
}
}
}
if (standard!=null && codeclib!=null) {
if (allowed) {
registry.setOrdering(category, codeclib, standard);
} else {
registry.setOrdering(category, standard, codeclib);
}
}
}
/**
* Tiles the specified image.
*
* @todo Usually, the tiling doesn't need to be performed as a separated operation. The
* {@link ImageLayout} hint with tile information can be provided to most JAI operators.
* The {@link #getRenderingHints} method provides such tiling information only if the
* image was not already tiled, so it should not be a cause of tile size mismatch in an
* operation chain. The mean usage for a separated "tile" operation is to tile an image
* before to save it on disk in some format supporting tiling.
*
* @throws IOException If an I/O operation were required (in order to check if the image
* were tiled on disk) and failed.
*
* @since 2.3
*/
public static RenderedOp tileImage(final RenderedOp image) throws IOException {
// /////////////////////////////////////////////////////////////////////
//
// initialization
//
// /////////////////////////////////////////////////////////////////////
final int width = image.getWidth();
final int height = image.getHeight();
final int tileHeight = image.getTileHeight();
final int tileWidth = image.getTileWidth();
boolean needToTile = false;
// /////////////////////////////////////////////////////////////////////
//
// checking if the image comes directly from an image read operation
//
// /////////////////////////////////////////////////////////////////////
// getting the reader
final Object o = image.getProperty(ImageReadDescriptor.PROPERTY_NAME_IMAGE_READER);
if (o instanceof ImageReader) {
final ImageReader reader = (ImageReader) o;
if (!reader.isImageTiled(0)) {
needToTile = true;
}
}
// /////////////////////////////////////////////////////////////////////
//
// If the original image has tileW==W &&tileH==H it is untiled.
//
// /////////////////////////////////////////////////////////////////////
if (!needToTile && tileWidth == width && tileHeight <= height) {
needToTile = true;
}
// /////////////////////////////////////////////////////////////////////
//
// tiling central.
//
// /////////////////////////////////////////////////////////////////////
if (needToTile) {
// tiling the original image by providing a suitable layout
final ImageLayout layout = getImageLayout(image);
layout.unsetValid(ImageLayout.COLOR_MODEL_MASK | ImageLayout.SAMPLE_MODEL_MASK);
// changing parameters related to the tiling
final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
// reading the image
final ParameterBlockJAI pbjFormat = new ParameterBlockJAI("Format");
pbjFormat.addSource(image);
pbjFormat.setParameter("dataType", image.getSampleModel().getDataType());
return JAI.create("Format", pbjFormat, hints);
}
return image;
}
/**
* Sets every samples in the given image to the given value. This method is typically used
* for clearing an image content.
*
* @param image The image to fill.
* @param value The value to to given to every samples.
*/
public static void fill(final WritableRenderedImage image, final Number value) {
int y = image.getMinTileY();
for (int ny = image.getNumYTiles(); --ny >= 0;) {
int x = image.getMinTileX();
for (int nx = image.getNumXTiles(); --nx >= 0;) {
final WritableRaster raster = image.getWritableTile(x, y);
try {
fill(raster.getDataBuffer(), value);
} finally {
image.releaseWritableTile(x, y);
}
}
}
}
/**
* Sets the content of all banks in the given data buffer to the specified value. We do not
* allow setting of different value for invidivual bank because the data buffer "banks" do
* not necessarly match the image "bands".
*
* @param buffer The data buffer to fill.
* @param value The values to be given to every elements in the data buffer.
*/
private static void fill(final DataBuffer buffer, final Number value) {
final int[] offsets = buffer.getOffsets();
final int size = buffer.getSize();
if (buffer instanceof DataBufferByte) {
final DataBufferByte data = (DataBufferByte) buffer;
final byte n = value.byteValue();
for (int i=0; i<offsets.length; i++) {
final int offset = offsets[i];
Arrays.fill(data.getData(i), offset, offset + size, n);
}
} else if (buffer instanceof DataBufferShort) {
final DataBufferShort data = (DataBufferShort) buffer;
final short n = value.shortValue();
for (int i=0; i<offsets.length; i++) {
final int offset = offsets[i];
Arrays.fill(data.getData(i), offset, offset + size, n);
}
} else if (buffer instanceof DataBufferUShort) {
final DataBufferUShort data = (DataBufferUShort) buffer;
final short n = value.shortValue();
for (int i=0; i<offsets.length; i++) {
final int offset = offsets[i];
Arrays.fill(data.getData(i), offset, offset + size, n);
}
} else if (buffer instanceof DataBufferInt) {
final DataBufferInt data = (DataBufferInt) buffer;
final int n = value.intValue();
for (int i=0; i<offsets.length; i++) {
final int offset = offsets[i];
Arrays.fill(data.getData(i), offset, offset + size, n);
}
} else if (buffer instanceof DataBufferFloat) {
final DataBufferFloat data = (DataBufferFloat) buffer;
final float n = value.floatValue();
for (int i=0; i<offsets.length; i++) {
final int offset = offsets[i];
Arrays.fill(data.getData(i), offset, offset + size, n);
}
} else if (buffer instanceof DataBufferDouble) {
final DataBufferDouble data = (DataBufferDouble) buffer;
final double n = value.doubleValue();
for (int i=0; i<offsets.length; i++) {
final int offset = offsets[i];
Arrays.fill(data.getData(i), offset, offset + size, n);
}
} else {
throw new IllegalArgumentException(Errors.format(ErrorKeys.UNSUPPORTED_DATA_TYPE));
}
}
/**
* Tells me whether or not the native libraries for JAI are active or not.
*
* @return <code>false</code> in case the JAI native libs are not in the path, <code>true</code> otherwise.
*/
public static boolean isMediaLibAvailable() {
return mediaLibAvailable;
}
/**
* Tells me whether or not the native libraries for JAI/ImageIO are active or not.
*
* @return <code>false</code> in case the JAI/ImageIO native libs are not in the path, <code>true</code> otherwise.
*/
public static boolean isCLibAvailable() {
return PackageUtil.isCodecLibAvailable();
}
}