| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
package java.awt.image;  | 
 | 
 | 
 | 
import java.awt.color.ColorSpace;  | 
 | 
import java.awt.geom.Rectangle2D;  | 
 | 
import java.awt.Rectangle;  | 
 | 
import java.awt.RenderingHints;  | 
 | 
import java.awt.geom.Point2D;  | 
 | 
import sun.awt.image.ImagingLib;  | 
 | 
 | 
 | 
/**  | 
 | 
 * This class implements a lookup operation from the source  | 
 | 
 * to the destination.  The LookupTable object may contain a single array  | 
 | 
 * or multiple arrays, subject to the restrictions below.  | 
 | 
 * <p>  | 
 | 
 * For Rasters, the lookup operates on bands.  The number of  | 
 | 
 * lookup arrays may be one, in which case the same array is  | 
 | 
 * applied to all bands, or it must equal the number of Source  | 
 | 
 * Raster bands.  | 
 | 
 * <p>  | 
 | 
 * For BufferedImages, the lookup operates on color and alpha components.  | 
 | 
 * The number of lookup arrays may be one, in which case the  | 
 | 
 * same array is applied to all color (but not alpha) components.  | 
 | 
 * Otherwise, the number of lookup arrays may  | 
 | 
 * equal the number of Source color components, in which case no  | 
 | 
 * lookup of the alpha component (if present) is performed.  | 
 | 
 * If neither of these cases apply, the number of lookup arrays  | 
 | 
 * must equal the number of Source color components plus alpha components,  | 
 | 
 * in which case lookup is performed for all color and alpha components.  | 
 | 
 * This allows non-uniform rescaling of multi-band BufferedImages.  | 
 | 
 * <p>  | 
 | 
 * BufferedImage sources with premultiplied alpha data are treated in the same  | 
 | 
 * manner as non-premultiplied images for purposes of the lookup.  That is,  | 
 | 
 * the lookup is done per band on the raw data of the BufferedImage source  | 
 | 
 * without regard to whether the data is premultiplied.  If a color conversion  | 
 | 
 * is required to the destination ColorModel, the premultiplied state of  | 
 | 
 * both source and destination will be taken into account for this step.  | 
 | 
 * <p>  | 
 | 
 * Images with an IndexColorModel cannot be used.  | 
 | 
 * <p>  | 
 | 
 * If a RenderingHints object is specified in the constructor, the  | 
 | 
 * color rendering hint and the dithering hint may be used when color  | 
 | 
 * conversion is required.  | 
 | 
 * <p>  | 
 | 
 * This class allows the Source to be the same as the Destination.  | 
 | 
 *  | 
 | 
 * @see LookupTable  | 
 | 
 * @see java.awt.RenderingHints#KEY_COLOR_RENDERING  | 
 | 
 * @see java.awt.RenderingHints#KEY_DITHERING  | 
 | 
 */  | 
 | 
 | 
 | 
public class LookupOp implements BufferedImageOp, RasterOp { | 
 | 
    private LookupTable ltable;  | 
 | 
    private int numComponents;  | 
 | 
    RenderingHints hints;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public LookupOp(LookupTable lookup, RenderingHints hints) { | 
 | 
        this.ltable = lookup;  | 
 | 
        this.hints  = hints;  | 
 | 
        numComponents = ltable.getNumComponents();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final LookupTable getTable() { | 
 | 
        return ltable;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final BufferedImage filter(BufferedImage src, BufferedImage dst) { | 
 | 
        ColorModel srcCM = src.getColorModel();  | 
 | 
        int numBands = srcCM.getNumColorComponents();  | 
 | 
        ColorModel dstCM;  | 
 | 
        if (srcCM instanceof IndexColorModel) { | 
 | 
            throw new  | 
 | 
                IllegalArgumentException("LookupOp cannot be "+ | 
 | 
                                         "performed on an indexed image");  | 
 | 
        }  | 
 | 
        int numComponents = ltable.getNumComponents();  | 
 | 
        if (numComponents != 1 &&  | 
 | 
            numComponents != srcCM.getNumComponents() &&  | 
 | 
            numComponents != srcCM.getNumColorComponents())  | 
 | 
        { | 
 | 
            throw new IllegalArgumentException("Number of arrays in the "+ | 
 | 
                                               " lookup table ("+ | 
 | 
                                               numComponents+  | 
 | 
                                               " is not compatible with the "+  | 
 | 
                                               " src image: "+src);  | 
 | 
        }  | 
 | 
 | 
 | 
 | 
 | 
        boolean needToConvert = false;  | 
 | 
 | 
 | 
        int width = src.getWidth();  | 
 | 
        int height = src.getHeight();  | 
 | 
 | 
 | 
        if (dst == null) { | 
 | 
            dst = createCompatibleDestImage(src, null);  | 
 | 
            dstCM = srcCM;  | 
 | 
        }  | 
 | 
        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()+")");  | 
 | 
            }  | 
 | 
 | 
 | 
            dstCM = dst.getColorModel();  | 
 | 
            if (srcCM.getColorSpace().getType() !=  | 
 | 
                dstCM.getColorSpace().getType())  | 
 | 
            { | 
 | 
                needToConvert = true;  | 
 | 
                dst = createCompatibleDestImage(src, null);  | 
 | 
            }  | 
 | 
 | 
 | 
        }  | 
 | 
 | 
 | 
        BufferedImage origDst = dst;  | 
 | 
 | 
 | 
        if (ImagingLib.filter(this, src, dst) == null) { | 
 | 
              | 
 | 
            WritableRaster srcRaster = src.getRaster();  | 
 | 
            WritableRaster dstRaster = dst.getRaster();  | 
 | 
 | 
 | 
            if (srcCM.hasAlpha()) { | 
 | 
                if (numBands-1 == numComponents || numComponents == 1) { | 
 | 
                    int minx = srcRaster.getMinX();  | 
 | 
                    int miny = srcRaster.getMinY();  | 
 | 
                    int[] bands = new int[numBands-1];  | 
 | 
                    for (int i=0; i < numBands-1; i++) { | 
 | 
                        bands[i] = i;  | 
 | 
                    }  | 
 | 
                    srcRaster =  | 
 | 
                        srcRaster.createWritableChild(minx, miny,  | 
 | 
                                                      srcRaster.getWidth(),  | 
 | 
                                                      srcRaster.getHeight(),  | 
 | 
                                                      minx, miny,  | 
 | 
                                                      bands);  | 
 | 
                }  | 
 | 
            }  | 
 | 
            if (dstCM.hasAlpha()) { | 
 | 
                int dstNumBands = dstRaster.getNumBands();  | 
 | 
                if (dstNumBands-1 == numComponents || numComponents == 1) { | 
 | 
                    int minx = dstRaster.getMinX();  | 
 | 
                    int miny = dstRaster.getMinY();  | 
 | 
                    int[] bands = new int[numBands-1];  | 
 | 
                    for (int i=0; i < numBands-1; i++) { | 
 | 
                        bands[i] = i;  | 
 | 
                    }  | 
 | 
                    dstRaster =  | 
 | 
                        dstRaster.createWritableChild(minx, miny,  | 
 | 
                                                      dstRaster.getWidth(),  | 
 | 
                                                      dstRaster.getHeight(),  | 
 | 
                                                      minx, miny,  | 
 | 
                                                      bands);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            filter(srcRaster, dstRaster);  | 
 | 
        }  | 
 | 
 | 
 | 
        if (needToConvert) { | 
 | 
              | 
 | 
            ColorConvertOp ccop = new ColorConvertOp(hints);  | 
 | 
            ccop.filter(dst, origDst);  | 
 | 
        }  | 
 | 
 | 
 | 
        return origDst;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final WritableRaster filter (Raster src, WritableRaster dst) { | 
 | 
        int numBands  = src.getNumBands();  | 
 | 
        int dstLength = dst.getNumBands();  | 
 | 
        int height    = src.getHeight();  | 
 | 
        int width     = src.getWidth();  | 
 | 
        int srcPix[]  = new int[numBands];  | 
 | 
 | 
 | 
        // 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");  | 
 | 
        }  | 
 | 
        dstLength = dst.getNumBands();  | 
 | 
 | 
 | 
        if (numBands != dstLength) { | 
 | 
            throw new  | 
 | 
                IllegalArgumentException ("Number of channels in the src (" | 
 | 
                                          + numBands +  | 
 | 
                                          ") does not match number of channels"  | 
 | 
                                          + " in the destination (" | 
 | 
                                          + dstLength + ")");  | 
 | 
        }  | 
 | 
        int numComponents = ltable.getNumComponents();  | 
 | 
        if (numComponents != 1 && numComponents != src.getNumBands()) { | 
 | 
            throw new IllegalArgumentException("Number of arrays in the "+ | 
 | 
                                               " lookup table ("+ | 
 | 
                                               numComponents+  | 
 | 
                                               " is not compatible with the "+  | 
 | 
                                               " src Raster: "+src);  | 
 | 
        }  | 
 | 
 | 
 | 
 | 
 | 
        if (ImagingLib.filter(this, src, dst) != null) { | 
 | 
            return dst;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (ltable instanceof ByteLookupTable) { | 
 | 
            byteFilter ((ByteLookupTable) ltable, src, dst,  | 
 | 
                        width, height, numBands);  | 
 | 
        }  | 
 | 
        else if (ltable instanceof ShortLookupTable) { | 
 | 
            shortFilter ((ShortLookupTable) ltable, src, dst, width,  | 
 | 
                         height, numBands);  | 
 | 
        }  | 
 | 
        else { | 
 | 
              | 
 | 
            int sminX = src.getMinX();  | 
 | 
            int sY = src.getMinY();  | 
 | 
            int dminX = dst.getMinX();  | 
 | 
            int dY = dst.getMinY();  | 
 | 
            for (int y=0; y < height; y++, sY++, dY++) { | 
 | 
                int sX = sminX;  | 
 | 
                int dX = dminX;  | 
 | 
                for (int x=0; x < width; x++, sX++, dX++) { | 
 | 
                      | 
 | 
                    src.getPixel(sX, sY, srcPix);  | 
 | 
 | 
 | 
                      | 
 | 
                    ltable.lookupPixel(srcPix, srcPix);  | 
 | 
 | 
 | 
                      | 
 | 
                    dst.setPixel(dX, dY, srcPix);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        return dst;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final Rectangle2D getBounds2D (BufferedImage src) { | 
 | 
        return getBounds2D(src.getRaster());  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final Rectangle2D getBounds2D (Raster src) { | 
 | 
        return src.getBounds();  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public BufferedImage createCompatibleDestImage (BufferedImage src,  | 
 | 
                                                    ColorModel destCM) { | 
 | 
        BufferedImage image;  | 
 | 
        int w = src.getWidth();  | 
 | 
        int h = src.getHeight();  | 
 | 
        int transferType = DataBuffer.TYPE_BYTE;  | 
 | 
        if (destCM == null) { | 
 | 
            ColorModel cm = src.getColorModel();  | 
 | 
            Raster raster = src.getRaster();  | 
 | 
            if (cm instanceof ComponentColorModel) { | 
 | 
                DataBuffer db = raster.getDataBuffer();  | 
 | 
                boolean hasAlpha = cm.hasAlpha();  | 
 | 
                boolean isPre    = cm.isAlphaPremultiplied();  | 
 | 
                int trans        = cm.getTransparency();  | 
 | 
                int[] nbits = null;  | 
 | 
                if (ltable instanceof ByteLookupTable) { | 
 | 
                    if (db.getDataType() == db.TYPE_USHORT) { | 
 | 
                          | 
 | 
                        if (hasAlpha) { | 
 | 
                            nbits = new int[2];  | 
 | 
                            if (trans == cm.BITMASK) { | 
 | 
                                nbits[1] = 1;  | 
 | 
                            }  | 
 | 
                            else { | 
 | 
                                nbits[1] = 8;  | 
 | 
                            }  | 
 | 
                        }  | 
 | 
                        else { | 
 | 
                            nbits = new int[1];  | 
 | 
                        }  | 
 | 
                        nbits[0] = 8;  | 
 | 
                    }  | 
 | 
                    // For byte, no need to change the cm  | 
 | 
                }  | 
 | 
                else if (ltable instanceof ShortLookupTable) { | 
 | 
                    transferType = DataBuffer.TYPE_USHORT;  | 
 | 
                    if (db.getDataType() == db.TYPE_BYTE) { | 
 | 
                        if (hasAlpha) { | 
 | 
                            nbits = new int[2];  | 
 | 
                            if (trans == cm.BITMASK) { | 
 | 
                                nbits[1] = 1;  | 
 | 
                            }  | 
 | 
                            else { | 
 | 
                                nbits[1] = 16;  | 
 | 
                            }  | 
 | 
                        }  | 
 | 
                        else { | 
 | 
                            nbits = new int[1];  | 
 | 
                        }  | 
 | 
                        nbits[0] = 16;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                if (nbits != null) { | 
 | 
                    cm = new ComponentColorModel(cm.getColorSpace(),  | 
 | 
                                                 nbits, hasAlpha, isPre,  | 
 | 
                                                 trans, transferType);  | 
 | 
                }  | 
 | 
            }  | 
 | 
            image = new BufferedImage(cm,  | 
 | 
                                      cm.createCompatibleWritableRaster(w, h),  | 
 | 
                                      cm.isAlphaPremultiplied(),  | 
 | 
                                      null);  | 
 | 
        }  | 
 | 
        else { | 
 | 
            image = new BufferedImage(destCM,  | 
 | 
                                      destCM.createCompatibleWritableRaster(w,  | 
 | 
                                                                            h),  | 
 | 
                                      destCM.isAlphaPremultiplied(),  | 
 | 
                                      null);  | 
 | 
        }  | 
 | 
 | 
 | 
        return image;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public WritableRaster createCompatibleDestRaster (Raster src) { | 
 | 
        return src.createCompatibleWritableRaster();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) { | 
 | 
        if (dstPt == null) { | 
 | 
            dstPt = new Point2D.Float();  | 
 | 
        }  | 
 | 
        dstPt.setLocation(srcPt.getX(), srcPt.getY());  | 
 | 
 | 
 | 
        return dstPt;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final RenderingHints getRenderingHints() { | 
 | 
        return hints;  | 
 | 
    }  | 
 | 
 | 
 | 
    private final void byteFilter(ByteLookupTable lookup, Raster src,  | 
 | 
                                  WritableRaster dst,  | 
 | 
                                  int width, int height, int numBands) { | 
 | 
        int[] srcPix = null;  | 
 | 
 | 
 | 
          | 
 | 
        byte[][] table = lookup.getTable();  | 
 | 
        int offset = lookup.getOffset();  | 
 | 
        int tidx;  | 
 | 
        int step=1;  | 
 | 
 | 
 | 
          | 
 | 
        if (table.length == 1) { | 
 | 
            step=0;  | 
 | 
        }  | 
 | 
 | 
 | 
        int x;  | 
 | 
        int y;  | 
 | 
        int band;  | 
 | 
        int len = table[0].length;  | 
 | 
 | 
 | 
          | 
 | 
        for ( y=0; y < height; y++) { | 
 | 
            tidx = 0;  | 
 | 
            for ( band=0; band < numBands; band++, tidx+=step) { | 
 | 
                  | 
 | 
                srcPix = src.getSamples(0, y, width, 1, band, srcPix);  | 
 | 
 | 
 | 
                for ( x=0; x < width; x++) { | 
 | 
                    int index = srcPix[x]-offset;  | 
 | 
                    if (index < 0 || index > len) { | 
 | 
                        throw new  | 
 | 
                            IllegalArgumentException("index ("+index+ | 
 | 
                                                     "(out of range: "+  | 
 | 
                                                     " srcPix["+x+  | 
 | 
                                                     "]="+ srcPix[x]+  | 
 | 
                                                     " offset="+ offset);  | 
 | 
                    }  | 
 | 
                      | 
 | 
                    srcPix[x] = table[tidx][index];  | 
 | 
                }  | 
 | 
                  | 
 | 
                dst.setSamples(0, y, width, 1, band, srcPix);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private final void shortFilter(ShortLookupTable lookup, Raster src,  | 
 | 
                                   WritableRaster dst,  | 
 | 
                                   int width, int height, int numBands) { | 
 | 
        int band;  | 
 | 
        int[] srcPix = null;  | 
 | 
 | 
 | 
          | 
 | 
        short[][] table = lookup.getTable();  | 
 | 
        int offset = lookup.getOffset();  | 
 | 
        int tidx;  | 
 | 
        int step=1;  | 
 | 
 | 
 | 
          | 
 | 
        if (table.length == 1) { | 
 | 
            step=0;  | 
 | 
        }  | 
 | 
 | 
 | 
        int x = 0;  | 
 | 
        int y = 0;  | 
 | 
        int index;  | 
 | 
        int maxShort = (1<<16)-1;  | 
 | 
          | 
 | 
        for (y=0; y < height; y++) { | 
 | 
            tidx = 0;  | 
 | 
            for ( band=0; band < numBands; band++, tidx+=step) { | 
 | 
                  | 
 | 
                srcPix = src.getSamples(0, y, width, 1, band, srcPix);  | 
 | 
 | 
 | 
                for ( x=0; x < width; x++) { | 
 | 
                    index = srcPix[x]-offset;  | 
 | 
                    if (index < 0 || index > maxShort) { | 
 | 
                        throw new  | 
 | 
                            IllegalArgumentException("index out of range "+ | 
 | 
                                                     index+" x is "+x+  | 
 | 
                                                     "srcPix[x]="+srcPix[x]  | 
 | 
                                                     +" offset="+ offset);  | 
 | 
                    }  | 
 | 
                      | 
 | 
                    srcPix[x] = table[tidx][index];  | 
 | 
                }  | 
 | 
                  | 
 | 
                dst.setSamples(0, y, width, 1, band, srcPix);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |