home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / awt / image / RescaleOp.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  14.2 KB  |  387 lines

  1. /*
  2.  * @(#)RescaleOp.java    1.30 98/03/18
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.awt.image;
  16.  
  17. import java.awt.GraphicsEnvironment;
  18. import java.awt.color.ColorSpace;
  19. import java.awt.geom.Rectangle2D;
  20. import java.awt.Rectangle;
  21. import java.awt.geom.Point2D;
  22.  
  23. /**
  24.  * This class performs a pixel-by-pixel rescaling of the data in
  25.  * the source image by multiplying each pixel value by a scale factor
  26.  * and then adding an offset. The pixel values are clipped to the
  27.  * minimum/maximum of the destination image data type.
  28.  * <p>
  29.  * For Rasters, rescaling operates on channel elements.  The number of
  30.  * sets of scaling constants may be one, in which case the same constants
  31.  * are applied to all channels, or it must equal the number of Source
  32.  * Raster channels.
  33.  * <p>
  34.  * For BufferedImages, rescaling operates on color and alpha components.
  35.  * The number of sets of scaling constants may be one, in which case the
  36.  * same constants are applied to all color and alpha components, or it
  37.  * must equal the number of Source color components, in which case no
  38.  * scaling of the alpha component (if present) is performed, or it
  39.  * must equal the number of Source color components plus alpha components,
  40.  * in which case all color and alpha components are scaled.
  41.  *
  42.  * Images with an IndexColorModel cannot be rescaled.
  43.  * <p>
  44.  * The pseudo code for the rescaling operation is as follows:
  45.  * <pre>
  46.  *for each pixel from Source object {
  47.  *    for each channel/component of the pixel {
  48.  *        dstElement = (srcElement*scaleFactor) + offset
  49.  *    }
  50.  *}
  51.  * </pre>
  52.  * Note that in-place operation is allowed (i.e. the source and destination can
  53.  * be the same object).
  54.  * @version 10 Feb 1997
  55.  */
  56. public class RescaleOp implements BufferedImageOp, RasterOp {
  57.     float[] scaleFactors;
  58.     float[] offsets;
  59.     int length = 0;
  60.     
  61.     /**
  62.      * Constructs a new RescaleOp with the desired scale factors
  63.      * and offsets.  The length of the scaleFactor and offset arrays
  64.      * must meet the restrictions stated in the class comments above.
  65.      */
  66.     public RescaleOp (float[] scaleFactors, float[] offsets) {
  67.         length = scaleFactors.length;
  68.         if (length > offsets.length) length = offsets.length;
  69.  
  70.         this.scaleFactors = new float[length];
  71.         this.offsets      = new float[length];
  72.         for (int i=0; i < length; i++) {
  73.             this.scaleFactors[i] = scaleFactors[i];
  74.             this.offsets[i]      = offsets[i];
  75.         }
  76.     }
  77.  
  78.     /**
  79.      * Constructs a new RescaleOp with the desired scale factor
  80.      * and offset.  The scaleFactor and offset will be applied to
  81.      * all channels/components in the source Raster/BufferedImage.
  82.      */
  83.     public RescaleOp (float scaleFactor, float offset) {
  84.         length = 1;
  85.         this.scaleFactors = new float[1];
  86.         this.offsets      = new float[1];
  87.         this.scaleFactors[0] = scaleFactor;
  88.         this.offsets[0]       = offset;
  89.     }
  90.  
  91.     /** 
  92.      * Returns the scale factors in the given array. The array is also
  93.      * returned for convenience.
  94.      */
  95.     final public float[] getScaleFactors (float scaleFactors[]) {
  96.         float newScale[] = new float[scaleFactors.length];
  97.         System.arraycopy (scaleFactors, 0, newScale, 0, scaleFactors.length);
  98.         return newScale;
  99.     }
  100.  
  101.     /**
  102.      * Returns the offsets in the given array. The array is also returned
  103.      * for convenience.
  104.      */
  105.     final public float[] getOffsets(float offsets[]) {
  106.         float newOffsets[] = new float[offsets.length];
  107.         System.arraycopy (offsets, 0, newOffsets, 0, offsets.length);
  108.         return newOffsets;
  109.     }
  110.  
  111.     /**
  112.      * Returns the number of scaling factors used in this object.
  113.      */
  114.     final public int getNumFactors() {
  115.         return length;
  116.     }
  117.      
  118.     /**
  119.      * Rescales the BufferedImage.  
  120.      * If the color model in the source image is not the same as that
  121.      * in the destination image, the pixels will be converted
  122.      * in the destination.  If the destination image is null,
  123.      * a BufferedImage will be created with the source ColorModel.
  124.      * Note that the number of scaling factors in this object must
  125.      * meet the restrictions stated in the class comments above.
  126.      * Otherwise, an exception is thrown.
  127.      */
  128.     public BufferedImage filter (BufferedImage src, BufferedImage dst) {
  129.         ColorModel srcCM = src.getColorModel();
  130.         int numBands = srcCM.getNumColorComponents();
  131.  
  132.         if (srcCM instanceof IndexColorModel) {
  133.             throw new
  134.                 IllegalArgumentException("Rescaling cannot be "+
  135.                                          "performed on an indexed image");
  136.         }
  137.         boolean needToConvert = false;
  138.         if (length > 1 && length < numBands) {
  139.             throw new IllegalArgumentException("Number of bands ("+numBands+
  140.                                                ") must match number of "+
  141.                                                " elements ("+length+
  142.                                                ") in scale factors array.");
  143.         }
  144.         // Include alpha
  145.         if (length > numBands && srcCM.hasAlpha()) {
  146.             length = numBands+1;
  147.         }
  148.         
  149.         int width = src.getWidth();
  150.         int height = src.getHeight();
  151.  
  152.         if (dst == null) {
  153.             dst = createCompatibleDestImage(src, null);
  154.         }
  155.         else {
  156.             if (width != dst.getWidth()) {
  157.                 throw new
  158.                     IllegalArgumentException("Src width ("+width+
  159.                                              ") not equal to dst width ("+
  160.                                              dst.getWidth()+")");
  161.             }
  162.             if (height != dst.getHeight()) {
  163.                 throw new
  164.                     IllegalArgumentException("Src height ("+height+
  165.                                              ") not equal to dst height ("+
  166.                                              dst.getHeight()+")");
  167.             }
  168.  
  169.             ColorModel dstCM = dst.getColorModel();
  170.             if (!srcCM.equals(dstCM)) {
  171.                 needToConvert = true;
  172.                 dst = createCompatibleDestImage(src, null);
  173.             }
  174.  
  175.         }
  176.         
  177.         BufferedImage origDst = dst;
  178.         GraphicsEnvironment ge =
  179.             GraphicsEnvironment.getLocalGraphicsEnvironment();
  180.         ImagingLib imlib = ge.getImagingLib();
  181.  
  182.         if (imlib.filter(this, src, dst) == null) {
  183.             if (srcCM instanceof ComponentColorModel) {
  184.                 filter(src.getRaster(), (WritableRaster)dst.getRaster());
  185.             }
  186.             else {
  187.                 // REMIND: Use Byte or Short Lookup Table!
  188.         
  189.                 // Do it the slow way
  190.                 WritableRaster srcRaster = src.getRaster();
  191.                 WritableRaster dstRaster = dst.getRaster();
  192.                 int[] data = null;
  193.                 Object obj = null;
  194.                 int[] components = null;
  195.                 int step;
  196.                 int tidx;
  197.  
  198.                 step = (length > 1) ? 1 : 0;
  199.         
  200.  
  201.                 int sminX = src.getMinXCoord();
  202.                 int sY = src.getMinYCoord();
  203.                 int dminX = dst.getMinXCoord();
  204.                 int dY = dst.getMinYCoord();
  205.                 int sX;
  206.                 int dX;
  207.                 for (int y=0; y < height; y++, sY++, dY++) {
  208.                     dX = dminX;
  209.                     sX = sminX;
  210.                     for (int x = 0; x < width; x++, sX++, dX++) {
  211.                         data = srcRaster.getPixel(sX, sY, data);
  212.                         tidx=0;
  213.                         for (int c = 0; c < numBands; c++, tidx+=step) {
  214.                             data[c] = (int) (data[c]*scaleFactors[tidx] +
  215.                                              offsets[tidx]);
  216.                             // REMIND: Clip to high and low range?
  217.                         }
  218.                         
  219.                         dstRaster.setPixel(dX, dY, data);
  220.                     }
  221.                 }
  222.             }
  223.         }
  224.         
  225.         if (needToConvert) {
  226.             // ColorModels are not the same
  227.             ColorConvertOp ccop = new ColorConvertOp();
  228.             ccop.filter(dst, origDst);
  229.         }
  230.  
  231.         return origDst;
  232.     }
  233.  
  234.     /**
  235.      * Rescales the channel data in the Raster.
  236.      * If the destination Raster is null, a new Raster will be created.
  237.      * The source and destination must have the same number of channels.
  238.      * Otherwise, an exception is thrown.
  239.      * Note that the number of scaling factors in this object must
  240.      * meet the restrictions stated in the class comments above.
  241.      * Otherwise, an exception is thrown.
  242.      */
  243.     public WritableRaster filter (Raster src, WritableRaster dst)  {
  244.         int numBands = src.getNumBands();
  245.         int width  = src.getWidth();
  246.         int height = src.getHeight();
  247.         int[] srcPix = null;
  248.         int step = 0;
  249.         int tidx = 0;
  250.  
  251.         // Create a new destination Raster, if needed
  252.         if (dst == null) {
  253.             dst = createCompatibleDestRaster(src);
  254.         }
  255.         else if (height != dst.getHeight() || width != dst.getWidth()) {
  256.             throw new
  257.                IllegalArgumentException("Width or height of Rasters do not "+
  258.                                         "match");
  259.         }
  260.         else if (numBands != dst.getNumBands()) {
  261.             // Make sure that the number of channels are equal
  262.             throw new IllegalArgumentException("Number of channels in src "
  263.                             + numBands 
  264.                             + " does not equal number of channels in dest "
  265.                             + dst.getNumBands());
  266.         }
  267.  
  268.         // Make sure that the arrays match
  269.         // Make sure that the low/high/constant arrays match
  270.         if (length != 1 && length < numBands) {
  271.             throw new IllegalArgumentException("Number of channels in src "
  272.                         + numBands
  273.                         + " does not equal length of scaling array");
  274.         }
  275.  
  276.         // REMIND: Use Byte or Short Lookup Table!
  277.         
  278.         if (length > 1) {
  279.             step = 1;
  280.         }
  281.  
  282.         int sminX = src.getMinX();
  283.         int sY = src.getMinY();
  284.         int dminX = dst.getMinX();
  285.         int dY = dst.getMinY();
  286.         int sX;
  287.         int dX;
  288.         for (int y=0; y < height; y++, sY++, dY++) {
  289.             dX = dminX;
  290.             sX = sminX;
  291.             for (int x = 0; x < width; x++, sX++, dX++) {
  292.                 // Get data for all bands at this x,y position
  293.                 srcPix = src.getPixel(sX, sY, srcPix);
  294.                 tidx = 0;
  295.                 for (int z=0; z < numBands; z++, tidx += step) {
  296.                     srcPix[z] = (int)(srcPix[z]*scaleFactors[tidx]
  297.                                       + offsets[tidx]);
  298.                 }
  299.  
  300.                 // Put it back for all bands
  301.                 dst.setPixel(dX, dY, srcPix);
  302.             }
  303.         }
  304.         return dst;
  305.     }
  306.  
  307.     /**
  308.      * Returns the bounding box of the rescaled destination.  Since
  309.      * this is not a geometric operation, the bounding box does not
  310.      * change.  Note that the number of scaling factors in this object must
  311.      * meet the restrictions stated in the class comments above.
  312.      * Otherwise, an exception is thrown.
  313.      */
  314.     public Rectangle2D getDestBounds (BufferedImage src) {
  315.          return getDestBounds(src.getRaster());
  316.     }
  317.  
  318.     /**
  319.      * Returns the bounding box of the rescaled destination.  Since
  320.      * this is not a geometric operation, the bounding box does not
  321.      * change.  Note that the number of scaling factors in this object must
  322.      * meet the restrictions stated in the class comments above.
  323.      * Otherwise, an exception is thrown.
  324.      */
  325.     public Rectangle2D getDestBounds (Raster src) {
  326.     //        return new Rectangle (src.getXOffset(), src.getYOffset(),
  327.     //          src.getWidth(), src.getHeight());
  328.     return src.getBounds();
  329.     }
  330.  
  331.     /**
  332.      * Creates an empty destination image with the correct size and number of
  333.      * channels.
  334.      * Note that the number of scaling factors in this object must
  335.      * meet the restrictions stated in the class comments above.
  336.      * Otherwise, an exception is thrown.
  337.      * @param src       Source image for the filter operation.
  338.      * @param destCM    ColorModel of the destination.  If null, the
  339.      *                  ColorModel of the source will be used.
  340.      */
  341.     public BufferedImage createCompatibleDestImage (BufferedImage src,
  342.                                                     ColorModel destCM) {
  343.         BufferedImage image;
  344.         if (destCM == null) {
  345.             ColorModel cm = src.getColorModel();
  346.             image = new BufferedImage(cm,
  347.                                       src.getRaster().createCompatibleWritableRaster(),
  348.                                       cm.isAlphaPremultiplied());
  349.         }
  350.         else {
  351.             int w = src.getWidth();
  352.             int h = src.getHeight();
  353.             image = new BufferedImage (destCM,
  354.                                        destCM.createCompatibleWritableRaster(w, h),
  355.                                        destCM.isAlphaPremultiplied());
  356.         }
  357.  
  358.         return image;
  359.     }
  360.     
  361.     /**
  362.      * Creates an empty destination Raster with the correct size and number 
  363.      * of channels.
  364.      * Note that the number of scaling factors in this object must
  365.      * meet the restrictions stated in the class comments above.
  366.      * Otherwise, an exception is thrown.
  367.      */
  368.     public WritableRaster createCompatibleDestRaster (Raster src) {
  369.         return src.createCompatibleWritableRaster(src.getWidth(), src.getHeight());
  370.     }
  371.     
  372.     /**
  373.      * Returns the location of the destination point given a
  374.      * point in the source image.  If dstPt is non-null, it will
  375.      * be used to hold the return value.  Since this is not a geometric
  376.      * operation, the srcPt will equal the dstPt.
  377.      */
  378.     public Point2D getDestPoint (Point2D srcPt, Point2D dstPt) {
  379.         if (dstPt == null) {
  380.             dstPt = new Point2D.Float();
  381.         }
  382.     dstPt.setLocation(srcPt.getX(), srcPt.getY());
  383.         return dstPt;
  384.     }
  385.     
  386. }
  387.