Home » openjdk-7 » java » awt » image » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2000, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   
   27   package java.awt.image;
   28   
   29   import java.awt.color.ColorSpace;
   30   import java.awt.geom.Rectangle2D;
   31   import java.awt.Rectangle;
   32   import java.awt.RenderingHints;
   33   import java.awt.geom.Point2D;
   34   import sun.awt.image.ImagingLib;
   35   
   36   /**
   37    * This class implements a lookup operation from the source
   38    * to the destination.  The LookupTable object may contain a single array
   39    * or multiple arrays, subject to the restrictions below.
   40    * <p>
   41    * For Rasters, the lookup operates on bands.  The number of
   42    * lookup arrays may be one, in which case the same array is
   43    * applied to all bands, or it must equal the number of Source
   44    * Raster bands.
   45    * <p>
   46    * For BufferedImages, the lookup operates on color and alpha components.
   47    * The number of lookup arrays may be one, in which case the
   48    * same array is applied to all color (but not alpha) components.
   49    * Otherwise, the number of lookup arrays may
   50    * equal the number of Source color components, in which case no
   51    * lookup of the alpha component (if present) is performed.
   52    * If neither of these cases apply, the number of lookup arrays
   53    * must equal the number of Source color components plus alpha components,
   54    * in which case lookup is performed for all color and alpha components.
   55    * This allows non-uniform rescaling of multi-band BufferedImages.
   56    * <p>
   57    * BufferedImage sources with premultiplied alpha data are treated in the same
   58    * manner as non-premultiplied images for purposes of the lookup.  That is,
   59    * the lookup is done per band on the raw data of the BufferedImage source
   60    * without regard to whether the data is premultiplied.  If a color conversion
   61    * is required to the destination ColorModel, the premultiplied state of
   62    * both source and destination will be taken into account for this step.
   63    * <p>
   64    * Images with an IndexColorModel cannot be used.
   65    * <p>
   66    * If a RenderingHints object is specified in the constructor, the
   67    * color rendering hint and the dithering hint may be used when color
   68    * conversion is required.
   69    * <p>
   70    * This class allows the Source to be the same as the Destination.
   71    *
   72    * @see LookupTable
   73    * @see java.awt.RenderingHints#KEY_COLOR_RENDERING
   74    * @see java.awt.RenderingHints#KEY_DITHERING
   75    */
   76   
   77   public class LookupOp implements BufferedImageOp, RasterOp {
   78       private LookupTable ltable;
   79       private int numComponents;
   80       RenderingHints hints;
   81   
   82       /**
   83        * Constructs a <code>LookupOp</code> object given the lookup
   84        * table and a <code>RenderingHints</code> object, which might
   85        * be <code>null</code>.
   86        * @param lookup the specified <code>LookupTable</code>
   87        * @param hints the specified <code>RenderingHints</code>,
   88        *        or <code>null</code>
   89        */
   90       public LookupOp(LookupTable lookup, RenderingHints hints) {
   91           this.ltable = lookup;
   92           this.hints  = hints;
   93           numComponents = ltable.getNumComponents();
   94       }
   95   
   96       /**
   97        * Returns the <code>LookupTable</code>.
   98        * @return the <code>LookupTable</code> of this
   99        *         <code>LookupOp</code>.
  100        */
  101       public final LookupTable getTable() {
  102           return ltable;
  103       }
  104   
  105       /**
  106        * Performs a lookup operation on a <code>BufferedImage</code>.
  107        * If the color model in the source image is not the same as that
  108        * in the destination image, the pixels will be converted
  109        * in the destination.  If the destination image is <code>null</code>,
  110        * a <code>BufferedImage</code> will be created with an appropriate
  111        * <code>ColorModel</code>.  An <code>IllegalArgumentException</code>
  112        * might be thrown if the number of arrays in the
  113        * <code>LookupTable</code> does not meet the restrictions
  114        * stated in the class comment above, or if the source image
  115        * has an <code>IndexColorModel</code>.
  116        * @param src the <code>BufferedImage</code> to be filtered
  117        * @param dst the <code>BufferedImage</code> in which to
  118        *            store the results of the filter operation
  119        * @return the filtered <code>BufferedImage</code>.
  120        * @throws IllegalArgumentException if the number of arrays in the
  121        *         <code>LookupTable</code> does not meet the restrictions
  122        *         described in the class comments, or if the source image
  123        *         has an <code>IndexColorModel</code>.
  124        */
  125       public final BufferedImage filter(BufferedImage src, BufferedImage dst) {
  126           ColorModel srcCM = src.getColorModel();
  127           int numBands = srcCM.getNumColorComponents();
  128           ColorModel dstCM;
  129           if (srcCM instanceof IndexColorModel) {
  130               throw new
  131                   IllegalArgumentException("LookupOp cannot be "+
  132                                            "performed on an indexed image");
  133           }
  134           int numComponents = ltable.getNumComponents();
  135           if (numComponents != 1 &&
  136               numComponents != srcCM.getNumComponents() &&
  137               numComponents != srcCM.getNumColorComponents())
  138           {
  139               throw new IllegalArgumentException("Number of arrays in the "+
  140                                                  " lookup table ("+
  141                                                  numComponents+
  142                                                  " is not compatible with the "+
  143                                                  " src image: "+src);
  144           }
  145   
  146   
  147           boolean needToConvert = false;
  148   
  149           int width = src.getWidth();
  150           int height = src.getHeight();
  151   
  152           if (dst == null) {
  153               dst = createCompatibleDestImage(src, null);
  154               dstCM = srcCM;
  155           }
  156           else {
  157               if (width != dst.getWidth()) {
  158                   throw new
  159                       IllegalArgumentException("Src width ("+width+
  160                                                ") not equal to dst width ("+
  161                                                dst.getWidth()+")");
  162               }
  163               if (height != dst.getHeight()) {
  164                   throw new
  165                       IllegalArgumentException("Src height ("+height+
  166                                                ") not equal to dst height ("+
  167                                                dst.getHeight()+")");
  168               }
  169   
  170               dstCM = dst.getColorModel();
  171               if (srcCM.getColorSpace().getType() !=
  172                   dstCM.getColorSpace().getType())
  173               {
  174                   needToConvert = true;
  175                   dst = createCompatibleDestImage(src, null);
  176               }
  177   
  178           }
  179   
  180           BufferedImage origDst = dst;
  181   
  182           if (ImagingLib.filter(this, src, dst) == null) {
  183               // Do it the slow way
  184               WritableRaster srcRaster = src.getRaster();
  185               WritableRaster dstRaster = dst.getRaster();
  186   
  187               if (srcCM.hasAlpha()) {
  188                   if (numBands-1 == numComponents || numComponents == 1) {
  189                       int minx = srcRaster.getMinX();
  190                       int miny = srcRaster.getMinY();
  191                       int[] bands = new int[numBands-1];
  192                       for (int i=0; i < numBands-1; i++) {
  193                           bands[i] = i;
  194                       }
  195                       srcRaster =
  196                           srcRaster.createWritableChild(minx, miny,
  197                                                         srcRaster.getWidth(),
  198                                                         srcRaster.getHeight(),
  199                                                         minx, miny,
  200                                                         bands);
  201                   }
  202               }
  203               if (dstCM.hasAlpha()) {
  204                   int dstNumBands = dstRaster.getNumBands();
  205                   if (dstNumBands-1 == numComponents || numComponents == 1) {
  206                       int minx = dstRaster.getMinX();
  207                       int miny = dstRaster.getMinY();
  208                       int[] bands = new int[numBands-1];
  209                       for (int i=0; i < numBands-1; i++) {
  210                           bands[i] = i;
  211                       }
  212                       dstRaster =
  213                           dstRaster.createWritableChild(minx, miny,
  214                                                         dstRaster.getWidth(),
  215                                                         dstRaster.getHeight(),
  216                                                         minx, miny,
  217                                                         bands);
  218                   }
  219               }
  220   
  221               filter(srcRaster, dstRaster);
  222           }
  223   
  224           if (needToConvert) {
  225               // ColorModels are not the same
  226               ColorConvertOp ccop = new ColorConvertOp(hints);
  227               ccop.filter(dst, origDst);
  228           }
  229   
  230           return origDst;
  231       }
  232   
  233       /**
  234        * Performs a lookup operation on a <code>Raster</code>.
  235        * If the destination <code>Raster</code> is <code>null</code>,
  236        * a new <code>Raster</code> will be created.
  237        * The <code>IllegalArgumentException</code> might be thrown
  238        * if the source <code>Raster</code> and the destination
  239        * <code>Raster</code> do not have the same
  240        * number of bands or if the number of arrays in the
  241        * <code>LookupTable</code> does not meet the
  242        * restrictions stated in the class comment above.
  243        * @param src the source <code>Raster</code> to filter
  244        * @param dst the destination <code>WritableRaster</code> for the
  245        *            filtered <code>src</code>
  246        * @return the filtered <code>WritableRaster</code>.
  247        * @throws IllegalArgumentException if the source and destinations
  248        *         rasters do not have the same number of bands, or the
  249        *         number of arrays in the <code>LookupTable</code> does
  250        *         not meet the restrictions described in the class comments.
  251        *
  252        */
  253       public final WritableRaster filter (Raster src, WritableRaster dst) {
  254           int numBands  = src.getNumBands();
  255           int dstLength = dst.getNumBands();
  256           int height    = src.getHeight();
  257           int width     = src.getWidth();
  258           int srcPix[]  = new int[numBands];
  259   
  260           // Create a new destination Raster, if needed
  261   
  262           if (dst == null) {
  263               dst = createCompatibleDestRaster(src);
  264           }
  265           else if (height != dst.getHeight() || width != dst.getWidth()) {
  266               throw new
  267                   IllegalArgumentException ("Width or height of Rasters do not "+
  268                                             "match");
  269           }
  270           dstLength = dst.getNumBands();
  271   
  272           if (numBands != dstLength) {
  273               throw new
  274                   IllegalArgumentException ("Number of channels in the src ("
  275                                             + numBands +
  276                                             ") does not match number of channels"
  277                                             + " in the destination ("
  278                                             + dstLength + ")");
  279           }
  280           int numComponents = ltable.getNumComponents();
  281           if (numComponents != 1 && numComponents != src.getNumBands()) {
  282               throw new IllegalArgumentException("Number of arrays in the "+
  283                                                  " lookup table ("+
  284                                                  numComponents+
  285                                                  " is not compatible with the "+
  286                                                  " src Raster: "+src);
  287           }
  288   
  289   
  290           if (ImagingLib.filter(this, src, dst) != null) {
  291               return dst;
  292           }
  293   
  294           // Optimize for cases we know about
  295           if (ltable instanceof ByteLookupTable) {
  296               byteFilter ((ByteLookupTable) ltable, src, dst,
  297                           width, height, numBands);
  298           }
  299           else if (ltable instanceof ShortLookupTable) {
  300               shortFilter ((ShortLookupTable) ltable, src, dst, width,
  301                            height, numBands);
  302           }
  303           else {
  304               // Not one we recognize so do it slowly
  305               int sminX = src.getMinX();
  306               int sY = src.getMinY();
  307               int dminX = dst.getMinX();
  308               int dY = dst.getMinY();
  309               for (int y=0; y < height; y++, sY++, dY++) {
  310                   int sX = sminX;
  311                   int dX = dminX;
  312                   for (int x=0; x < width; x++, sX++, dX++) {
  313                       // Find data for all bands at this x,y position
  314                       src.getPixel(sX, sY, srcPix);
  315   
  316                       // Lookup the data for all bands at this x,y position
  317                       ltable.lookupPixel(srcPix, srcPix);
  318   
  319                       // Put it back for all bands
  320                       dst.setPixel(dX, dY, srcPix);
  321                   }
  322               }
  323           }
  324   
  325           return dst;
  326       }
  327   
  328       /**
  329        * Returns the bounding box of the filtered destination image.  Since
  330        * this is not a geometric operation, the bounding box does not
  331        * change.
  332        * @param src the <code>BufferedImage</code> to be filtered
  333        * @return the bounds of the filtered definition image.
  334        */
  335       public final Rectangle2D getBounds2D (BufferedImage src) {
  336           return getBounds2D(src.getRaster());
  337       }
  338   
  339       /**
  340        * Returns the bounding box of the filtered destination Raster.  Since
  341        * this is not a geometric operation, the bounding box does not
  342        * change.
  343        * @param src the <code>Raster</code> to be filtered
  344        * @return the bounds of the filtered definition <code>Raster</code>.
  345        */
  346       public final Rectangle2D getBounds2D (Raster src) {
  347           return src.getBounds();
  348   
  349       }
  350   
  351       /**
  352        * Creates a zeroed destination image with the correct size and number of
  353        * bands.  If destCM is <code>null</code>, an appropriate
  354        * <code>ColorModel</code> will be used.
  355        * @param src       Source image for the filter operation.
  356        * @param destCM    the destination's <code>ColorModel</code>, which
  357        *                  can be <code>null</code>.
  358        * @return a filtered destination <code>BufferedImage</code>.
  359        */
  360       public BufferedImage createCompatibleDestImage (BufferedImage src,
  361                                                       ColorModel destCM) {
  362           BufferedImage image;
  363           int w = src.getWidth();
  364           int h = src.getHeight();
  365           int transferType = DataBuffer.TYPE_BYTE;
  366           if (destCM == null) {
  367               ColorModel cm = src.getColorModel();
  368               Raster raster = src.getRaster();
  369               if (cm instanceof ComponentColorModel) {
  370                   DataBuffer db = raster.getDataBuffer();
  371                   boolean hasAlpha = cm.hasAlpha();
  372                   boolean isPre    = cm.isAlphaPremultiplied();
  373                   int trans        = cm.getTransparency();
  374                   int[] nbits = null;
  375                   if (ltable instanceof ByteLookupTable) {
  376                       if (db.getDataType() == db.TYPE_USHORT) {
  377                           // Dst raster should be of type byte
  378                           if (hasAlpha) {
  379                               nbits = new int[2];
  380                               if (trans == cm.BITMASK) {
  381                                   nbits[1] = 1;
  382                               }
  383                               else {
  384                                   nbits[1] = 8;
  385                               }
  386                           }
  387                           else {
  388                               nbits = new int[1];
  389                           }
  390                           nbits[0] = 8;
  391                       }
  392                       // For byte, no need to change the cm
  393                   }
  394                   else if (ltable instanceof ShortLookupTable) {
  395                       transferType = DataBuffer.TYPE_USHORT;
  396                       if (db.getDataType() == db.TYPE_BYTE) {
  397                           if (hasAlpha) {
  398                               nbits = new int[2];
  399                               if (trans == cm.BITMASK) {
  400                                   nbits[1] = 1;
  401                               }
  402                               else {
  403                                   nbits[1] = 16;
  404                               }
  405                           }
  406                           else {
  407                               nbits = new int[1];
  408                           }
  409                           nbits[0] = 16;
  410                       }
  411                   }
  412                   if (nbits != null) {
  413                       cm = new ComponentColorModel(cm.getColorSpace(),
  414                                                    nbits, hasAlpha, isPre,
  415                                                    trans, transferType);
  416                   }
  417               }
  418               image = new BufferedImage(cm,
  419                                         cm.createCompatibleWritableRaster(w, h),
  420                                         cm.isAlphaPremultiplied(),
  421                                         null);
  422           }
  423           else {
  424               image = new BufferedImage(destCM,
  425                                         destCM.createCompatibleWritableRaster(w,
  426                                                                               h),
  427                                         destCM.isAlphaPremultiplied(),
  428                                         null);
  429           }
  430   
  431           return image;
  432       }
  433   
  434       /**
  435        * Creates a zeroed-destination <code>Raster</code> with the
  436        * correct size and number of bands, given this source.
  437        * @param src the <code>Raster</code> to be transformed
  438        * @return the zeroed-destination <code>Raster</code>.
  439        */
  440       public WritableRaster createCompatibleDestRaster (Raster src) {
  441           return src.createCompatibleWritableRaster();
  442       }
  443   
  444       /**
  445        * Returns the location of the destination point given a
  446        * point in the source.  If <code>dstPt</code> is not
  447        * <code>null</code>, it will be used to hold the return value.
  448        * Since this is not a geometric operation, the <code>srcPt</code>
  449        * will equal the <code>dstPt</code>.
  450        * @param srcPt a <code>Point2D</code> that represents a point
  451        *        in the source image
  452        * @param dstPt a <code>Point2D</code>that represents the location
  453        *        in the destination
  454        * @return the <code>Point2D</code> in the destination that
  455        *         corresponds to the specified point in the source.
  456        */
  457       public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) {
  458           if (dstPt == null) {
  459               dstPt = new Point2D.Float();
  460           }
  461           dstPt.setLocation(srcPt.getX(), srcPt.getY());
  462   
  463           return dstPt;
  464       }
  465   
  466       /**
  467        * Returns the rendering hints for this op.
  468        * @return the <code>RenderingHints</code> object associated
  469        *         with this op.
  470        */
  471       public final RenderingHints getRenderingHints() {
  472           return hints;
  473       }
  474   
  475       private final void byteFilter(ByteLookupTable lookup, Raster src,
  476                                     WritableRaster dst,
  477                                     int width, int height, int numBands) {
  478           int[] srcPix = null;
  479   
  480           // Find the ref to the table and the offset
  481           byte[][] table = lookup.getTable();
  482           int offset = lookup.getOffset();
  483           int tidx;
  484           int step=1;
  485   
  486           // Check if it is one lookup applied to all bands
  487           if (table.length == 1) {
  488               step=0;
  489           }
  490   
  491           int x;
  492           int y;
  493           int band;
  494           int len = table[0].length;
  495   
  496           // Loop through the data
  497           for ( y=0; y < height; y++) {
  498               tidx = 0;
  499               for ( band=0; band < numBands; band++, tidx+=step) {
  500                   // Find data for this band, scanline
  501                   srcPix = src.getSamples(0, y, width, 1, band, srcPix);
  502   
  503                   for ( x=0; x < width; x++) {
  504                       int index = srcPix[x]-offset;
  505                       if (index < 0 || index > len) {
  506                           throw new
  507                               IllegalArgumentException("index ("+index+
  508                                                        "(out of range: "+
  509                                                        " srcPix["+x+
  510                                                        "]="+ srcPix[x]+
  511                                                        " offset="+ offset);
  512                       }
  513                       // Do the lookup
  514                       srcPix[x] = table[tidx][index];
  515                   }
  516                   // Put it back
  517                   dst.setSamples(0, y, width, 1, band, srcPix);
  518               }
  519           }
  520       }
  521   
  522       private final void shortFilter(ShortLookupTable lookup, Raster src,
  523                                      WritableRaster dst,
  524                                      int width, int height, int numBands) {
  525           int band;
  526           int[] srcPix = null;
  527   
  528           // Find the ref to the table and the offset
  529           short[][] table = lookup.getTable();
  530           int offset = lookup.getOffset();
  531           int tidx;
  532           int step=1;
  533   
  534           // Check if it is one lookup applied to all bands
  535           if (table.length == 1) {
  536               step=0;
  537           }
  538   
  539           int x = 0;
  540           int y = 0;
  541           int index;
  542           int maxShort = (1<<16)-1;
  543           // Loop through the data
  544           for (y=0; y < height; y++) {
  545               tidx = 0;
  546               for ( band=0; band < numBands; band++, tidx+=step) {
  547                   // Find data for this band, scanline
  548                   srcPix = src.getSamples(0, y, width, 1, band, srcPix);
  549   
  550                   for ( x=0; x < width; x++) {
  551                       index = srcPix[x]-offset;
  552                       if (index < 0 || index > maxShort) {
  553                           throw new
  554                               IllegalArgumentException("index out of range "+
  555                                                        index+" x is "+x+
  556                                                        "srcPix[x]="+srcPix[x]
  557                                                        +" offset="+ offset);
  558                       }
  559                       // Do the lookup
  560                       srcPix[x] = table[tidx][index];
  561                   }
  562                   // Put it back
  563                   dst.setSamples(0, y, width, 1, band, srcPix);
  564               }
  565           }
  566       }
  567   }

Home » openjdk-7 » java » awt » image » [javadoc | source]