home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 14.2 KB | 387 lines |
- /*
- * @(#)RescaleOp.java 1.30 98/03/18
- *
- * Copyright 1997, 1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.awt.image;
-
- import java.awt.GraphicsEnvironment;
- import java.awt.color.ColorSpace;
- import java.awt.geom.Rectangle2D;
- import java.awt.Rectangle;
- import java.awt.geom.Point2D;
-
- /**
- * This class performs a pixel-by-pixel rescaling of the data in
- * the source image by multiplying each pixel value by a scale factor
- * and then adding an offset. The pixel values are clipped to the
- * minimum/maximum of the destination image data type.
- * <p>
- * For Rasters, rescaling operates on channel elements. The number of
- * sets of scaling constants may be one, in which case the same constants
- * are applied to all channels, or it must equal the number of Source
- * Raster channels.
- * <p>
- * For BufferedImages, rescaling operates on color and alpha components.
- * The number of sets of scaling constants may be one, in which case the
- * same constants are applied to all color and alpha components, or it
- * must equal the number of Source color components, in which case no
- * scaling of the alpha component (if present) is performed, or it
- * must equal the number of Source color components plus alpha components,
- * in which case all color and alpha components are scaled.
- *
- * Images with an IndexColorModel cannot be rescaled.
- * <p>
- * The pseudo code for the rescaling operation is as follows:
- * <pre>
- *for each pixel from Source object {
- * for each channel/component of the pixel {
- * dstElement = (srcElement*scaleFactor) + offset
- * }
- *}
- * </pre>
- * Note that in-place operation is allowed (i.e. the source and destination can
- * be the same object).
- * @version 10 Feb 1997
- */
- public class RescaleOp implements BufferedImageOp, RasterOp {
- float[] scaleFactors;
- float[] offsets;
- int length = 0;
-
- /**
- * Constructs a new RescaleOp with the desired scale factors
- * and offsets. The length of the scaleFactor and offset arrays
- * must meet the restrictions stated in the class comments above.
- */
- public RescaleOp (float[] scaleFactors, float[] offsets) {
- length = scaleFactors.length;
- if (length > offsets.length) length = offsets.length;
-
- this.scaleFactors = new float[length];
- this.offsets = new float[length];
- for (int i=0; i < length; i++) {
- this.scaleFactors[i] = scaleFactors[i];
- this.offsets[i] = offsets[i];
- }
- }
-
- /**
- * Constructs a new RescaleOp with the desired scale factor
- * and offset. The scaleFactor and offset will be applied to
- * all channels/components in the source Raster/BufferedImage.
- */
- public RescaleOp (float scaleFactor, float offset) {
- length = 1;
- this.scaleFactors = new float[1];
- this.offsets = new float[1];
- this.scaleFactors[0] = scaleFactor;
- this.offsets[0] = offset;
- }
-
- /**
- * Returns the scale factors in the given array. The array is also
- * returned for convenience.
- */
- final public float[] getScaleFactors (float scaleFactors[]) {
- float newScale[] = new float[scaleFactors.length];
- System.arraycopy (scaleFactors, 0, newScale, 0, scaleFactors.length);
- return newScale;
- }
-
- /**
- * Returns the offsets in the given array. The array is also returned
- * for convenience.
- */
- final public float[] getOffsets(float offsets[]) {
- float newOffsets[] = new float[offsets.length];
- System.arraycopy (offsets, 0, newOffsets, 0, offsets.length);
- return newOffsets;
- }
-
- /**
- * Returns the number of scaling factors used in this object.
- */
- final public int getNumFactors() {
- return length;
- }
-
- /**
- * Rescales the BufferedImage.
- * If the color model in the source image is not the same as that
- * in the destination image, the pixels will be converted
- * in the destination. If the destination image is null,
- * a BufferedImage will be created with the source ColorModel.
- * Note that the number of scaling factors in this object must
- * meet the restrictions stated in the class comments above.
- * Otherwise, an exception is thrown.
- */
- public BufferedImage filter (BufferedImage src, BufferedImage dst) {
- ColorModel srcCM = src.getColorModel();
- int numBands = srcCM.getNumColorComponents();
-
- if (srcCM instanceof IndexColorModel) {
- throw new
- IllegalArgumentException("Rescaling cannot be "+
- "performed on an indexed image");
- }
- boolean needToConvert = false;
- if (length > 1 && length < numBands) {
- throw new IllegalArgumentException("Number of bands ("+numBands+
- ") must match number of "+
- " elements ("+length+
- ") in scale factors array.");
- }
- // Include alpha
- if (length > numBands && srcCM.hasAlpha()) {
- length = numBands+1;
- }
-
- int width = src.getWidth();
- int height = src.getHeight();
-
- if (dst == null) {
- dst = createCompatibleDestImage(src, null);
- }
- else {
- if (width != dst.getWidth()) {
- throw new
- IllegalArgumentException("Src width ("+width+
- ") not equal to dst width ("+
- dst.getWidth()+")");
- }
- if (height != dst.getHeight()) {
- throw new
- IllegalArgumentException("Src height ("+height+
- ") not equal to dst height ("+
- dst.getHeight()+")");
- }
-
- ColorModel dstCM = dst.getColorModel();
- if (!srcCM.equals(dstCM)) {
- needToConvert = true;
- dst = createCompatibleDestImage(src, null);
- }
-
- }
-
- BufferedImage origDst = dst;
- GraphicsEnvironment ge =
- GraphicsEnvironment.getLocalGraphicsEnvironment();
- ImagingLib imlib = ge.getImagingLib();
-
- if (imlib.filter(this, src, dst) == null) {
- if (srcCM instanceof ComponentColorModel) {
- filter(src.getRaster(), (WritableRaster)dst.getRaster());
- }
- else {
- // REMIND: Use Byte or Short Lookup Table!
-
- // Do it the slow way
- WritableRaster srcRaster = src.getRaster();
- WritableRaster dstRaster = dst.getRaster();
- int[] data = null;
- Object obj = null;
- int[] components = null;
- int step;
- int tidx;
-
- step = (length > 1) ? 1 : 0;
-
-
- int sminX = src.getMinXCoord();
- int sY = src.getMinYCoord();
- int dminX = dst.getMinXCoord();
- int dY = dst.getMinYCoord();
- int sX;
- int dX;
- for (int y=0; y < height; y++, sY++, dY++) {
- dX = dminX;
- sX = sminX;
- for (int x = 0; x < width; x++, sX++, dX++) {
- data = srcRaster.getPixel(sX, sY, data);
- tidx=0;
- for (int c = 0; c < numBands; c++, tidx+=step) {
- data[c] = (int) (data[c]*scaleFactors[tidx] +
- offsets[tidx]);
- // REMIND: Clip to high and low range?
- }
-
- dstRaster.setPixel(dX, dY, data);
- }
- }
- }
- }
-
- if (needToConvert) {
- // ColorModels are not the same
- ColorConvertOp ccop = new ColorConvertOp();
- ccop.filter(dst, origDst);
- }
-
- return origDst;
- }
-
- /**
- * Rescales the channel data in the Raster.
- * If the destination Raster is null, a new Raster will be created.
- * The source and destination must have the same number of channels.
- * Otherwise, an exception is thrown.
- * Note that the number of scaling factors in this object must
- * meet the restrictions stated in the class comments above.
- * Otherwise, an exception is thrown.
- */
- public WritableRaster filter (Raster src, WritableRaster dst) {
- int numBands = src.getNumBands();
- int width = src.getWidth();
- int height = src.getHeight();
- int[] srcPix = null;
- int step = 0;
- int tidx = 0;
-
- // Create a new destination Raster, if needed
- if (dst == null) {
- dst = createCompatibleDestRaster(src);
- }
- else if (height != dst.getHeight() || width != dst.getWidth()) {
- throw new
- IllegalArgumentException("Width or height of Rasters do not "+
- "match");
- }
- else if (numBands != dst.getNumBands()) {
- // Make sure that the number of channels are equal
- throw new IllegalArgumentException("Number of channels in src "
- + numBands
- + " does not equal number of channels in dest "
- + dst.getNumBands());
- }
-
- // Make sure that the arrays match
- // Make sure that the low/high/constant arrays match
- if (length != 1 && length < numBands) {
- throw new IllegalArgumentException("Number of channels in src "
- + numBands
- + " does not equal length of scaling array");
- }
-
- // REMIND: Use Byte or Short Lookup Table!
-
- if (length > 1) {
- step = 1;
- }
-
- int sminX = src.getMinX();
- int sY = src.getMinY();
- int dminX = dst.getMinX();
- int dY = dst.getMinY();
- int sX;
- int dX;
- for (int y=0; y < height; y++, sY++, dY++) {
- dX = dminX;
- sX = sminX;
- for (int x = 0; x < width; x++, sX++, dX++) {
- // Get data for all bands at this x,y position
- srcPix = src.getPixel(sX, sY, srcPix);
- tidx = 0;
- for (int z=0; z < numBands; z++, tidx += step) {
- srcPix[z] = (int)(srcPix[z]*scaleFactors[tidx]
- + offsets[tidx]);
- }
-
- // Put it back for all bands
- dst.setPixel(dX, dY, srcPix);
- }
- }
- return dst;
- }
-
- /**
- * Returns the bounding box of the rescaled destination. Since
- * this is not a geometric operation, the bounding box does not
- * change. Note that the number of scaling factors in this object must
- * meet the restrictions stated in the class comments above.
- * Otherwise, an exception is thrown.
- */
- public Rectangle2D getDestBounds (BufferedImage src) {
- return getDestBounds(src.getRaster());
- }
-
- /**
- * Returns the bounding box of the rescaled destination. Since
- * this is not a geometric operation, the bounding box does not
- * change. Note that the number of scaling factors in this object must
- * meet the restrictions stated in the class comments above.
- * Otherwise, an exception is thrown.
- */
- public Rectangle2D getDestBounds (Raster src) {
- // return new Rectangle (src.getXOffset(), src.getYOffset(),
- // src.getWidth(), src.getHeight());
- return src.getBounds();
- }
-
- /**
- * Creates an empty destination image with the correct size and number of
- * channels.
- * Note that the number of scaling factors in this object must
- * meet the restrictions stated in the class comments above.
- * Otherwise, an exception is thrown.
- * @param src Source image for the filter operation.
- * @param destCM ColorModel of the destination. If null, the
- * ColorModel of the source will be used.
- */
- public BufferedImage createCompatibleDestImage (BufferedImage src,
- ColorModel destCM) {
- BufferedImage image;
- if (destCM == null) {
- ColorModel cm = src.getColorModel();
- image = new BufferedImage(cm,
- src.getRaster().createCompatibleWritableRaster(),
- cm.isAlphaPremultiplied());
- }
- else {
- int w = src.getWidth();
- int h = src.getHeight();
- image = new BufferedImage (destCM,
- destCM.createCompatibleWritableRaster(w, h),
- destCM.isAlphaPremultiplied());
- }
-
- return image;
- }
-
- /**
- * Creates an empty destination Raster with the correct size and number
- * of channels.
- * Note that the number of scaling factors in this object must
- * meet the restrictions stated in the class comments above.
- * Otherwise, an exception is thrown.
- */
- public WritableRaster createCompatibleDestRaster (Raster src) {
- return src.createCompatibleWritableRaster(src.getWidth(), src.getHeight());
- }
-
- /**
- * Returns the location of the destination point given a
- * point in the source image. If dstPt is non-null, it will
- * be used to hold the return value. Since this is not a geometric
- * operation, the srcPt will equal the dstPt.
- */
- public Point2D getDestPoint (Point2D srcPt, Point2D dstPt) {
- if (dstPt == null) {
- dstPt = new Point2D.Float();
- }
- dstPt.setLocation(srcPt.getX(), srcPt.getY());
- return dstPt;
- }
-
- }
-