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 / ThresholdOp.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  15.0 KB  |  417 lines

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