//
// 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.