//
// ImageFlatField.java
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad;
import java.awt.Graphics2D;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.rmi.RemoteException;
import java.util.Arrays;
/**
* ImageFlatField is a VisAD FlatField backed by a java.awt.image.BufferedImage
* object, instead of the usual float[][] or double[][] samples array.
* Expands the samples into floats or doubles on demand using
* {@link #unpackFloats(boolean)}, which can be expensive for repeated
* operations of certain types. Such calls can be avoided for certain types of
* visualization using an 8-bit image with
* {@link visad.bom.ShadowImageFunctionTypeJ3D}.
*/
public class ImageFlatField extends FlatField {
// -- Constants --
/** Debugging flag. */
public static final boolean DEBUG = false;
// -- Fields --
/** The image backing this FlatField. */
protected BufferedImage image;
/** Dimensions of the image. */
protected int num, width, height;
// -- Static methods --
/**
* Converts the given BufferedImage to a format efficient
* with ImageFlatField's grabBytes method and usable with
* Java3D texturing by reference.
*/
public static BufferedImage make3ByteRGB(BufferedImage image) {
// create BufferedImage of "TYPE_3BYTE_RGB" (TYPE_CUSTOM)
// This type of image is efficient with grabBytes, and is
// compatible with Java3D's texturing by reference support.
if (image == null) return null;
int dataType = DataBuffer.TYPE_BYTE;
ColorModel colorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
false, false, ColorModel.TRANSLUCENT, dataType);
int w = image.getWidth(), h = image.getHeight();
byte[][] data = new byte[3][w * h];
SampleModel model = new BandedSampleModel(dataType, w, h, data.length);
DataBuffer buffer = new DataBufferByte(data, data[0].length);
WritableRaster raster = Raster.createWritableRaster(model, buffer, null);
BufferedImage result = new BufferedImage(colorModel, raster, false, null);
// paint image into buffered image
Graphics2D g = result.createGraphics();
g.drawRenderedImage(image, null);
g.dispose();
g = null;
return result;
}
/** Constructs a FunctionType suitable for use with the given image. */
public static FunctionType makeFunctionType(BufferedImage img)
throws VisADException
{
if (img == null) throw new VisADException("image cannot be null");
RealType x = RealType.getRealType("ImageElement");
RealType y = RealType.getRealType("ImageLine");
RealTupleType xy = new RealTupleType(x, y);
int num = img.getRaster().getNumBands();
MathType range = null;
if (num == 4) {
RealType r = RealType.getRealType("Red");
RealType g = RealType.getRealType("Green");
RealType b = RealType.getRealType("Blue");
RealType a = RealType.getRealType("Alpha");
range = new RealTupleType(r, g, b, a);
}
else if (num == 3) {
RealType r = RealType.getRealType("Red");
RealType g = RealType.getRealType("Green");
RealType b = RealType.getRealType("Blue");
range = new RealTupleType(r, g, b);
}
else if (num == 1) range = RealType.getRealType("Intensity");
else throw new VisADException("Unsupported # of bands (" + num + ")");
return new FunctionType(xy, range);
}
/** Constructs a domain Set suitable for use with the given image. */
public static Set makeDomainSet(BufferedImage img) throws VisADException {
RealType x = RealType.getRealType("ImageElement");
RealType y = RealType.getRealType("ImageLine");
RealTupleType xy = new RealTupleType(x, y);
int w = img.getWidth(), h = img.getHeight();
return new Linear2DSet(xy, 0, w - 1, w, h - 1, 0, h);
}
// -- Constructors --
/** Constructs an ImageFlatField around the given BufferedImage. */
public ImageFlatField(BufferedImage img)
throws VisADException, RemoteException
{
this(makeFunctionType(img), makeDomainSet(img));
setImage(img);
}
public ImageFlatField(FunctionType type) throws VisADException {
this(type, type.getDomain().getDefaultSet(), null, null, null, null);
}
public ImageFlatField(FunctionType type, Set domain_set)
throws VisADException {
this(type, domain_set, null, null, null, null);
}
public ImageFlatField(FunctionType type, Set domain_set,
CoordinateSystem range_coord_sys, Set[] range_sets,
Unit[] units) throws VisADException {
this(type, domain_set, range_coord_sys, null, range_sets, units);
}
public ImageFlatField(FunctionType type, Set domain_set,
CoordinateSystem[] range_coord_syses, Set[] range_sets,
Unit[] units) throws VisADException {
this(type, domain_set, null, range_coord_syses, range_sets, units);
}
public ImageFlatField(FunctionType type, Set domain_set,
CoordinateSystem range_coord_sys,
CoordinateSystem[] range_coord_syses,
Set[] range_sets, Unit[] units) throws VisADException {
super(type, domain_set, range_coord_sys,
range_coord_syses, range_sets, units);
RealTupleType domain = type.getDomain();
if (domain.getNumberOfRealComponents() != 2) {
throw new VisADException(
"FunctionType domain must be flat with 2 components");
}
MathType range = type.getRange();
if (range instanceof RealType) num = 1;
else if (range instanceof RealTupleType) {
num = ((RealTupleType) range).getNumberOfRealComponents();
}
if (num != 1 && num != 3 && num != 4) {
throw new VisADException(
"FunctionType range must be flat with 1, 3 or 4 components");
}
if (domain_set instanceof Gridded2DSet) {
int[] len = ((Gridded2DSet) domain_set).getLengths();
width = len[0];
height = len[1];
}
else {
throw new VisADException(
"Domain set must be Gridded2DSet");
}
}
// -- ImageFlatField API methods --
/** Gets the image backing this FlatField. */
public BufferedImage getImage() {
pr ("getImage");
return image;
}
/** Sets the image backing this FlatField. */
public void setImage(BufferedImage image)
throws VisADException, RemoteException
{
// pr ("setImage");
if (image == null) throw new VisADException("image cannot be null");
if (image.getWidth() != width || image.getHeight() != height) {
throw new VisADException("Image dimensions do not match domain set");
}
if (image.getRaster().getNumBands() != num) {
throw new VisADException(
"Image component count does not match FunctionType range");
}
this.image = image;
clearMissing();
notifyReferences();
}
/** Gets RealType for each domain component (X and Y). */
public RealType[] getDomainTypes() {
RealTupleType domain = ((FunctionType) getType()).getDomain();
return domain.getRealComponents();
}
/** Gets RealType for each range component. */
public RealType[] getRangeTypes() {
MathType range = ((FunctionType) getType()).getRange();
RealType[] v = null;
if (range instanceof RealType) v = new RealType[] {(RealType) range};
else if (range instanceof TupleType) {
v = ((TupleType) range).getRealComponents();
}
else v = new RealType[0];
return v;
}
// -- FlatField API methods --
public void setSamples(Data[] range, boolean copy)
throws VisADException, RemoteException
{
throw new VisADException("Use setImage(Image) for ImageFlatField");
}
/**
* This method has been overridden to avoid a call to
* unpackValues or unpackFloats during range computation.
*/
public DataShadow computeRanges(ShadowType type, DataShadow shadow)
throws VisADException
{
if (isMissing()) return shadow;
ShadowRealTupleType domainType = ((ShadowFunctionType) type).getDomain();
int n = domainType.getDimension();
double[][] ranges = new double[2][n];
// DomainSet.computeRanges handles Reference
shadow = getDomainSet().computeRanges(domainType, shadow, ranges, true);
ShadowRealTupleType shadRef;
// skip range if no range components are mapped
int[] indices = ((ShadowFunctionType) type).getRangeDisplayIndices();
boolean anyMapped = false;
for (int i=0; i= 0) anyMapped = true;
}
if (!anyMapped) return shadow;
// check for any range coordinate systems
boolean anyRangeRef = (RangeCoordinateSystem != null);
if (RangeCoordinateSystems != null) {
for (int i=0; i= 0 || anyRangeRef) {
Unit dunit = ((RealType) ((FunctionType)
Type).getFlatRange().getComponent(i)).getDefaultUnit();
if (dunit != null && !dunit.equals(RangeUnits[i])) {
min[i] = dunit.toThis(min[i], RangeUnits[i]);
max[i] = dunit.toThis(max[i], RangeUnits[i]);
}
if (anyRangeRef) {
ranges[0][i] = Math.min(ranges[0][i], min[i]);
ranges[1][i] = Math.max(ranges[1][i], max[i]);
}
if (k >= 0 && k < shadow.ranges[0].length) {
shadow.ranges[0][k] = Math.min(shadow.ranges[0][k], min[i]);
shadow.ranges[1][k] = Math.max(shadow.ranges[1][k], max[i]);
}
}
}
if (RangeCoordinateSystem != null) {
// computeRanges for Reference (relative to range) RealTypes
ShadowRealTupleType rangeType =
(ShadowRealTupleType) ((ShadowFunctionType) type).getRange();
shadRef = rangeType.getReference();
shadow = computeReferenceRanges(rangeType, RangeCoordinateSystem,
RangeUnits, shadow, shadRef, ranges);
}
else if (RangeCoordinateSystems != null) {
TupleType rangeTupleType = (TupleType) ((FunctionType) Type).getRange();
int j = 0;
for (int i=0; i num) {
byte[][] bytes = new byte[num][];
System.arraycopy(data, 0, bytes, 0, num);
data = bytes;
}
return data;
}
public static byte[][] grabBytes(BufferedImage image) {
WritableRaster raster = image.getRaster();
if (raster.getTransferType() != DataBuffer.TYPE_BYTE) return null;
DataBuffer buffer = raster.getDataBuffer();
if (buffer instanceof DataBufferByte) {
SampleModel model = raster.getSampleModel();
if (model instanceof BandedSampleModel) {
// fastest way to extract bytes; no copy
if (DEBUG) System.err.println("grabBytes: FAST");
byte[][] data = ((DataBufferByte) buffer).getBankData();
return data;
}
else if (model instanceof ComponentSampleModel) {
// medium speed way to extract bytes; direct array copy
if (DEBUG) System.err.println("grabBytes: MEDIUM");
byte[][] data = ((DataBufferByte) buffer).getBankData();
ComponentSampleModel csm = (ComponentSampleModel) model;
int[] bandOffsets = csm.getBandOffsets();
int[] bankIndices = csm.getBankIndices();
int pixelStride = csm.getPixelStride();
int scanlineStride = csm.getScanlineStride();
int numBands = bandOffsets.length;
int width = image.getWidth();
int height = image.getHeight();
int numPixels = width * height;
byte[][] bytes = new byte[numBands][numPixels];
for (int c=0; c> 24) & 0xff;
int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = pixel & 0xff;
bytes[0][i] = (byte) r;
if (num >= 2) bytes[1][i] = (byte) g;
if (num >= 3) bytes[2][i] = (byte) b;
if (num >= 4) bytes[3][i] = (byte) a;
}
return bytes;
*/
}
}
Ads help maintain this website.