Java/2D Graphics GUI/Image Filter

Материал из Java эксперт
Перейти к: навигация, поиск

Copy Raster

  
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* $Id: GraphicsUtil.java 603243 2007-12-11 13:49:04Z jeremias $ */

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ruponentSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
/**
 * Set of utility methods for Graphics.
 * These generally bypass broken methods in Java2D or provide tweaked
 * implementations.
 *
 * @author 
 * @version $Id: GraphicsUtil.java 603243 2007-12-11 13:49:04Z jeremias $
 */
public class GraphicsUtil {
    public static AffineTransform IDENTITY = new AffineTransform();
    /**
     * Standard prebuilt Linear_sRGB color model with no alpha */
    public static final ColorModel Linear_sRGB =
        new DirectColorModel(ColorSpace.getInstance
                             (ColorSpace.CS_LINEAR_RGB), 24,
                             0x00FF0000, 0x0000FF00,
                             0x000000FF, 0x0, false,
                             DataBuffer.TYPE_INT);
    /**
     * Standard prebuilt Linear_sRGB color model with premultiplied alpha.
     */
    public static final ColorModel Linear_sRGB_Pre =
        new DirectColorModel(ColorSpace.getInstance
                             (ColorSpace.CS_LINEAR_RGB), 32,
                             0x00FF0000, 0x0000FF00,
                             0x000000FF, 0xFF000000, true,
                             DataBuffer.TYPE_INT);
    /**
     * Standard prebuilt Linear_sRGB color model with unpremultiplied alpha.
     */
    public static final ColorModel Linear_sRGB_Unpre =
        new DirectColorModel(ColorSpace.getInstance
                             (ColorSpace.CS_LINEAR_RGB), 32,
                             0x00FF0000, 0x0000FF00,
                             0x000000FF, 0xFF000000, false,
                             DataBuffer.TYPE_INT);
    /**
     * Standard prebuilt sRGB color model with no alpha.
     */
    public static final ColorModel sRGB =
        new DirectColorModel(ColorSpace.getInstance
                             (ColorSpace.CS_sRGB), 24,
                             0x00FF0000, 0x0000FF00,
                             0x000000FF, 0x0, false,
                             DataBuffer.TYPE_INT);
    /**
     * Standard prebuilt sRGB color model with premultiplied alpha.
     */
    public static final ColorModel sRGB_Pre =
        new DirectColorModel(ColorSpace.getInstance
                             (ColorSpace.CS_sRGB), 32,
                             0x00FF0000, 0x0000FF00,
                             0x000000FF, 0xFF000000, true,
                             DataBuffer.TYPE_INT);
    /**
     * Standard prebuilt sRGB color model with unpremultiplied alpha.
     */
    public static final ColorModel sRGB_Unpre =
        new DirectColorModel(ColorSpace.getInstance
                             (ColorSpace.CS_sRGB), 32,
                             0x00FF0000, 0x0000FF00,
                             0x000000FF, 0xFF000000, false,
                             DataBuffer.TYPE_INT);
    /**
     * Method that returns either Linear_sRGB_Pre or Linear_sRGB_UnPre
     * based on premult flag.
     * @param premult True if the ColorModel should have premultiplied alpha.
     * @return        a ColorMdoel with Linear sRGB colorSpace and
     *                the alpha channel set in accordance with
     *                <tt>premult</tt>
     */
    public static ColorModel makeLinear_sRGBCM(boolean premult) {
         return premult ? Linear_sRGB_Pre : Linear_sRGB_Unpre;
    }
    /**
     * Constructs a BufferedImage with a linear sRGB colorModel, and alpha.
     * @param width   The desired width of the BufferedImage
     * @param height  The desired height of the BufferedImage
     * @param premult The desired state of alpha premultiplied
     * @return        The requested BufferedImage.
     */
    public static BufferedImage makeLinearBufferedImage(int width,
                                                        int height,
                                                        boolean premult) {
        ColorModel cm = makeLinear_sRGBCM(premult);
        WritableRaster wr = cm.createCompatibleWritableRaster(width, height);
        return new BufferedImage(cm, wr, premult, null);
    }
    /**
     * Creates a new raster that has a <b>copy</b> of the data in
     * <tt>ras</tt>.  This is highly optimized for speed.  There is
     * no provision for changing any aspect of the SampleModel.
     *
     * This method should be used when you need to change the contents
     * of a Raster that you do not "own" (ie the result of a
     * <tt>getData</tt> call).
     * @param ras The Raster to copy.
     * @return    A writable copy of <tt>ras</tt>
     */
    public static WritableRaster copyRaster(Raster ras) {
        return copyRaster(ras, ras.getMinX(), ras.getMinY());
    }

    /**
     * Creates a new raster that has a <b>copy</b> of the data in
     * <tt>ras</tt>.  This is highly optimized for speed.  There is
     * no provision for changing any aspect of the SampleModel.
     * However you can specify a new location for the returned raster.
     *
     * This method should be used when you need to change the contents
     * of a Raster that you do not "own" (ie the result of a
     * <tt>getData</tt> call).
     *
     * @param ras The Raster to copy.
     *
     * @param minX The x location for the upper left corner of the
     *             returned WritableRaster.
     *
     * @param minY The y location for the upper left corner of the
     *             returned WritableRaster.
     *
     * @return    A writable copy of <tt>ras</tt>
     */
    public static WritableRaster copyRaster(Raster ras, int minX, int minY) {
        WritableRaster ret = Raster.createWritableRaster
            (ras.getSampleModel(),
             new Point(0,0));
        ret = ret.createWritableChild
            (ras.getMinX()-ras.getSampleModelTranslateX(),
             ras.getMinY()-ras.getSampleModelTranslateY(),
             ras.getWidth(), ras.getHeight(),
             minX, minY, null);
        // Use System.arraycopy to copy the data between the two...
        DataBuffer srcDB = ras.getDataBuffer();
        DataBuffer retDB = ret.getDataBuffer();
        if (srcDB.getDataType() != retDB.getDataType()) {
            throw new IllegalArgumentException
                ("New DataBuffer doesn"t match original");
        }
        int len   = srcDB.getSize();
        int banks = srcDB.getNumBanks();
        int [] offsets = srcDB.getOffsets();
        for (int b=0; b< banks; b++) {
            switch (srcDB.getDataType()) {
            case DataBuffer.TYPE_BYTE: {
                DataBufferByte srcDBT = (DataBufferByte)srcDB;
                DataBufferByte retDBT = (DataBufferByte)retDB;
                System.arraycopy(srcDBT.getData(b), offsets[b],
                                 retDBT.getData(b), offsets[b], len);
                break;
            }
            case DataBuffer.TYPE_INT: {
                DataBufferInt srcDBT = (DataBufferInt)srcDB;
                DataBufferInt retDBT = (DataBufferInt)retDB;
                System.arraycopy(srcDBT.getData(b), offsets[b],
                                 retDBT.getData(b), offsets[b], len);
                break;
            }
            case DataBuffer.TYPE_SHORT: {
                DataBufferShort srcDBT = (DataBufferShort)srcDB;
                DataBufferShort retDBT = (DataBufferShort)retDB;
                System.arraycopy(srcDBT.getData(b), offsets[b],
                                 retDBT.getData(b), offsets[b], len);
                break;
            }
            case DataBuffer.TYPE_USHORT: {
                DataBufferUShort srcDBT = (DataBufferUShort)srcDB;
                DataBufferUShort retDBT = (DataBufferUShort)retDB;
                System.arraycopy(srcDBT.getData(b), offsets[b],
                                 retDBT.getData(b), offsets[b], len);
                break;
            }
            }
        }
        return ret;
    }
    /**
     * Coerces <tt>ras</tt> to be writable.  The returned Raster continues to
     * reference the DataBuffer from ras, so modifications to the returned
     * WritableRaster will be seen in ras.<p>
     *
     * This method should only be used if you need a WritableRaster due to
     * an interface (such as to construct a BufferedImage), but have no
     * intention of modifying the contents of the returned Raster.  If
     * you have any doubt about other users of the data in <tt>ras</tt>,
     * use copyRaster (above).
     * @param ras The raster to make writable.
     * @return    A Writable version of ras (shares DataBuffer with
     *            <tt>ras</tt>).
     */
    public static WritableRaster makeRasterWritable(Raster ras) {
        return makeRasterWritable(ras, ras.getMinX(), ras.getMinY());
    }
    /**
     * Coerces <tt>ras</tt> to be writable.  The returned Raster continues to
     * reference the DataBuffer from ras, so modifications to the returned
     * WritableRaster will be seen in ras.<p>
     *
     * You can specify a new location for the returned WritableRaster, this
     * is especially useful for constructing BufferedImages which require
     * the Raster to be at (0,0).
     *
     * This method should only be used if you need a WritableRaster due to
     * an interface (such as to construct a BufferedImage), but have no
     * intention of modifying the contents of the returned Raster.  If
     * you have any doubt about other users of the data in <tt>ras</tt>,
     * use copyRaster (above).
     *
     * @param ras The raster to make writable.
     *
     * @param minX The x location for the upper left corner of the
     *             returned WritableRaster.
     *
     * @param minY The y location for the upper left corner of the
     *             returned WritableRaster.
     *
     * @return A Writable version of <tT>ras</tt> with it"s upper left
     *         hand coordinate set to minX, minY (shares it"s DataBuffer
     *         with <tt>ras</tt>).
     */
    public static WritableRaster makeRasterWritable(Raster ras,
                                                    int minX, int minY) {
        WritableRaster ret = Raster.createWritableRaster
            (ras.getSampleModel(),
             ras.getDataBuffer(),
             new Point(0,0));
        ret = ret.createWritableChild
            (ras.getMinX()-ras.getSampleModelTranslateX(),
             ras.getMinY()-ras.getSampleModelTranslateY(),
             ras.getWidth(), ras.getHeight(),
             minX, minY, null);
        return ret;
    }
    /**
     * Create a new ColorModel with it"s alpha premultiplied state matching
     * newAlphaPreMult.
     * @param cm The ColorModel to change the alpha premult state of.
     * @param newAlphaPreMult The new state of alpha premult.
     * @return   A new colorModel that has isAlphaPremultiplied()
     *           equal to newAlphaPreMult.
     */
    public static ColorModel
        coerceColorModel(ColorModel cm, boolean newAlphaPreMult) {
        if (cm.isAlphaPremultiplied() == newAlphaPreMult)
            return cm;
        // Easiest way to build proper colormodel for new Alpha state...
        // Eventually this should switch on known ColorModel types and
        // only fall back on this hack when the CM type is unknown.
        WritableRaster wr = cm.createCompatibleWritableRaster(1,1);
        return cm.coerceData(wr, newAlphaPreMult);
    }
    /**
     * Coerces data within a bufferedImage to match newAlphaPreMult,
     * Note that this can not change the colormodel of bi so you
     *
     * @param wr The raster to change the state of.
     * @param cm The colormodel currently associated with data in wr.
     * @param newAlphaPreMult The desired state of alpha Premult for raster.
     * @return A new colormodel that matches newAlphaPreMult.
     */
    public static ColorModel
        coerceData(WritableRaster wr, ColorModel cm, boolean newAlphaPreMult) {
        // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() +
        //                    " Out: " + newAlphaPreMult);
        if (!cm.hasAlpha())
            // Nothing to do no alpha channel
            return cm;
        if (cm.isAlphaPremultiplied() == newAlphaPreMult)
            // nothing to do alpha state matches...
            return cm;
        // System.out.println("CoerceData: " + wr.getSampleModel());
        if (newAlphaPreMult) {
            multiplyAlpha(wr);
        } else {
            divideAlpha(wr);
        }
        return coerceColorModel(cm, newAlphaPreMult);
    }
    public static void multiplyAlpha(WritableRaster wr) {
        if (is_BYTE_COMP_Data(wr.getSampleModel()))
            mult_BYTE_COMP_Data(wr);
        else if (is_INT_PACK_Data(wr.getSampleModel(), true))
            mult_INT_PACK_Data(wr);
        else {
            int [] pixel = null;
            int    bands = wr.getNumBands();
            float  norm = 1f/255f;
            int x0, x1, y0, y1, a, b;
            float alpha;
            x0 = wr.getMinX();
            x1 = x0+wr.getWidth();
            y0 = wr.getMinY();
            y1 = y0+wr.getHeight();
            for (int y=y0; y<y1; y++)
                for (int x=x0; x<x1; x++) {
                    pixel = wr.getPixel(x,y,pixel);
                    a = pixel[bands-1];
                    if ((a >= 0) && (a < 255)) {
                        alpha = a*norm;
                        for (b=0; b<bands-1; b++)
                            pixel[b] = (int)(pixel[b]*alpha+0.5f);
                        wr.setPixel(x,y,pixel);
                    }
                }
        }
    }
    public static void divideAlpha(WritableRaster wr) {
        if (is_BYTE_COMP_Data(wr.getSampleModel()))
            divide_BYTE_COMP_Data(wr);
        else if (is_INT_PACK_Data(wr.getSampleModel(), true))
            divide_INT_PACK_Data(wr);
        else {
            int x0, x1, y0, y1, a, b;
            float ialpha;
            int    bands = wr.getNumBands();
            int [] pixel = null;
            x0 = wr.getMinX();
            x1 = x0+wr.getWidth();
            y0 = wr.getMinY();
            y1 = y0+wr.getHeight();
            for (int y=y0; y<y1; y++)
                for (int x=x0; x<x1; x++) {
                    pixel = wr.getPixel(x,y,pixel);
                    a = pixel[bands-1];
                    if ((a > 0) && (a < 255)) {
                        ialpha = 255/(float)a;
                        for (b=0; b<bands-1; b++)
                            pixel[b] = (int)(pixel[b]*ialpha+0.5f);
                        wr.setPixel(x,y,pixel);
                    }
                }
        }
    }
    public static void copyBand(Raster         src, int srcBand,
                                WritableRaster dst, int dstBand) {
        Rectangle sR   = src.getBounds();
        Rectangle dR   = dst.getBounds();
        Rectangle cpR  = sR.intersection(dR);
        copyBand(src, cpR, srcBand, dst, cpR, dstBand);
    }
    public static void copyBand(Raster         src, Rectangle sR, int sBand,
                                WritableRaster dst, Rectangle dR, int dBand) {
        int dy = dR.y -sR.y;
        int dx = dR.x -sR.x;
        sR = sR.intersection(src.getBounds());
        dR = dR.intersection(dst.getBounds());
        int width, height;
        if (dR.width  < sR.width)  width  = dR.width;
        else                       width  = sR.width;
        if (dR.height < sR.height) height = dR.height;
        else                       height = sR.height;
        int x = sR.x+dx;
        int [] samples = null;
        for (int y=sR.y; y< sR.y+height; y++) {
            samples = src.getSamples(sR.x, y, width, 1, sBand, samples);
            dst.setSamples(x, y+dy, width, 1, dBand, samples);
        }
    }
    public static boolean is_INT_PACK_Data(SampleModel sm,
                                           boolean requireAlpha) {
        // Check ColorModel is of type DirectColorModel
        if(!(sm instanceof SinglePixelPackedSampleModel)) return false;
        // Check transfer type
        if(sm.getDataType() != DataBuffer.TYPE_INT)       return false;
        SinglePixelPackedSampleModel sppsm;
        sppsm = (SinglePixelPackedSampleModel)sm;
        int [] masks = sppsm.getBitMasks();
        if (masks.length == 3) {
            if (requireAlpha) return false;
        } else if (masks.length != 4)
            return false;
        if(masks[0] != 0x00ff0000) return false;
        if(masks[1] != 0x0000ff00) return false;
        if(masks[2] != 0x000000ff) return false;
        if ((masks.length == 4) &&
            (masks[3] != 0xff000000)) return false;
        return true;
    }
        public static boolean is_BYTE_COMP_Data(SampleModel sm) {
            // Check ColorModel is of type DirectColorModel
            if(!(sm instanceof ComponentSampleModel))    return false;
            // Check transfer type
            if(sm.getDataType() != DataBuffer.TYPE_BYTE) return false;
            return true;
        }
    protected static void divide_INT_PACK_Data(WritableRaster wr) {
        // System.out.println("Divide Int");
        SinglePixelPackedSampleModel sppsm;
        sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel();
        final int width = wr.getWidth();
        final int scanStride = sppsm.getScanlineStride();
        DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
        final int base
            = (db.getOffset() +
               sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
                               wr.getMinY()-wr.getSampleModelTranslateY()));
        // Access the pixel data array
        final int[] pixels = db.getBankData()[0];
        for (int y=0; y<wr.getHeight(); y++) {
            int sp = base + y*scanStride;
            final int end = sp + width;
            while (sp < end) {
                int pixel = pixels[sp];
                int a = pixel>>>24;
                if (a<=0) {
                    pixels[sp] = 0x00FFFFFF;
                } else if (a<255) {
                    int aFP = (0x00FF0000/a);
                    pixels[sp] =
                        ((a << 24) |
                         (((((pixel&0xFF0000)>>16)*aFP)&0xFF0000)    ) |
                         (((((pixel&0x00FF00)>>8) *aFP)&0xFF0000)>>8 ) |
                         (((((pixel&0x0000FF))    *aFP)&0xFF0000)>>16));
                }
                sp++;
            }
        }
    }
    protected static void mult_INT_PACK_Data(WritableRaster wr) {
        // System.out.println("Multiply Int: " + wr);
        SinglePixelPackedSampleModel sppsm;
        sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel();
        final int width = wr.getWidth();
        final int scanStride = sppsm.getScanlineStride();
        DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
        final int base
            = (db.getOffset() +
               sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
                               wr.getMinY()-wr.getSampleModelTranslateY()));
        // Access the pixel data array
        final int[] pixels = db.getBankData()[0];
        for (int y=0; y<wr.getHeight(); y++) {
            int sp = base + y*scanStride;
            final int end = sp + width;
            while (sp < end) {
                int pixel = pixels[sp];
                int a = pixel>>>24;
                if ((a>=0) && (a<255)) {   // this does NOT include a == 255 (0xff) !
                    pixels[sp] = ((a << 24) |
                                  ((((pixel&0xFF0000)*a)>>8)&0xFF0000) |
                                  ((((pixel&0x00FF00)*a)>>8)&0x00FF00) |
                                  ((((pixel&0x0000FF)*a)>>8)&0x0000FF));
                }
                sp++;
            }
        }
    }

    protected static void divide_BYTE_COMP_Data(WritableRaster wr) {
        // System.out.println("Multiply Int: " + wr);
        ComponentSampleModel csm;
        csm = (ComponentSampleModel)wr.getSampleModel();
        final int width = wr.getWidth();
        final int scanStride = csm.getScanlineStride();
        final int pixStride  = csm.getPixelStride();
        final int [] bandOff = csm.getBandOffsets();
        DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
        final int base
            = (db.getOffset() +
               csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
                             wr.getMinY()-wr.getSampleModelTranslateY()));
        int aOff = bandOff[bandOff.length-1];
        int bands = bandOff.length-1;
        // Access the pixel data array
        final byte[] pixels = db.getBankData()[0];
        for (int y=0; y<wr.getHeight(); y++) {
            int sp = base + y*scanStride;
            final int end = sp + width*pixStride;
            while (sp < end) {
              int a = pixels[sp+aOff]&0xFF;
              if (a==0) {
                for (int b = 0; b < bands; b++)
                  pixels[sp+bandOff[b]] = (byte)0xFF;
              } else if (a<255) {         // this does NOT include a == 255 (0xff) !
                int aFP = (0x00FF0000/a);
                for (int b = 0; b < bands; b++) {
                  int i = sp+bandOff[b];
                  pixels[i] = (byte)(((pixels[i]&0xFF)*aFP)>>>16);
                }
              }
              sp+=pixStride;
            }
        }
    }
    protected static void mult_BYTE_COMP_Data(WritableRaster wr) {
        // System.out.println("Multiply Int: " + wr);
        ComponentSampleModel csm;
        csm = (ComponentSampleModel)wr.getSampleModel();
        final int width = wr.getWidth();
        final int scanStride = csm.getScanlineStride();
        final int pixStride  = csm.getPixelStride();
        final int [] bandOff = csm.getBandOffsets();
        DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
        final int base
            = (db.getOffset() +
               csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
                             wr.getMinY()-wr.getSampleModelTranslateY()));

        int aOff = bandOff[bandOff.length-1];
        int bands = bandOff.length-1;
        // Access the pixel data array
        final byte[] pixels = db.getBankData()[0];
        for (int y=0; y<wr.getHeight(); y++) {
            int sp = base + y*scanStride;
            final int end = sp + width*pixStride;
            while (sp < end) {
              int a = pixels[sp+aOff]&0xFF;
              if (a!=0xFF)
                for (int b = 0; b < bands; b++) {
                  int i = sp+bandOff[b];
                  pixels[i] = (byte)(((pixels[i]&0xFF)*a)>>8);
                }
              sp+=pixStride;
            }
        }
    }
/*
  This is skanky debugging code that might be useful in the future:
            if (count == 33) {
                String label = "sub [" + x + ", " + y + "]: ";
                org.ImageDisplay.showImage
                    (label, subBI);
                org.ImageDisplay.printImage
                    (label, subBI,
                     new Rectangle(75-iR.x, 90-iR.y, 32, 32));
            }

            // if ((count++ % 50) == 10)
            //     org.ImageDisplay.showImage("foo: ", subBI);

            Graphics2D realG2D = g2d;
            while (realG2D instanceof sun.java2d.ProxyGraphics2D) {
                realG2D = ((sun.java2d.ProxyGraphics2D)realG2D).getDelegate();
            }
            if (realG2D instanceof sun.awt.image.BufferedImageGraphics2D) {
                count++;
                if (count == 34) {
                    RenderedImage ri;
                    ri = ((sun.awt.image.BufferedImageGraphics2D)realG2D).bufImg;
                    // g2d.setComposite(SVGComposite.OVER);
                    // org.ImageDisplay.showImage("Bar: " + count, cr);
                    org.ImageDisplay.printImage("Bar: " + count, cr,
                                                new Rectangle(75, 90, 32, 32));
                    org.ImageDisplay.showImage ("Foo: " + count, ri);
                    org.ImageDisplay.printImage("Foo: " + count, ri,
                                                new Rectangle(75, 90, 32, 32));
                    System.out.println("BI: "   + ri);
                    System.out.println("BISM: " + ri.getSampleModel());
                    System.out.println("BICM: " + ri.getColorModel());
                    System.out.println("BICM class: " + ri.getColorModel().getClass());
                    System.out.println("BICS: " + ri.getColorModel().getColorSpace());
                    System.out.println
                        ("sRGB CS: " +
                         ColorSpace.getInstance(ColorSpace.CS_sRGB));
                    System.out.println("G2D info");
                    System.out.println("\tComposite: " + g2d.getComposite());
                    System.out.println("\tTransform" + g2d.getTransform());
                    java.awt.RenderingHints rh = g2d.getRenderingHints();
                    java.util.Set keys = rh.keySet();
                    java.util.Iterator iter = keys.iterator();
                    while (iter.hasNext()) {
                        Object o = iter.next();
                        System.out.println("\t" + o.toString() + " -> " +
                                           rh.get(o).toString());
                    }
                    ri = cr;
                    System.out.println("RI: "   + ri);
                    System.out.println("RISM: " + ri.getSampleModel());
                    System.out.println("RICM: " + ri.getColorModel());
                    System.out.println("RICM class: " + ri.getColorModel().getClass());
                    System.out.println("RICS: " + ri.getColorModel().getColorSpace());
                }
            }
*/
    /**
     * Extracts an alpha raster from a RenderedImage. The method tries to avoid copying data
     * unnecessarily by checking if the RenderedImage is a BufferedImage which offers suitable
     * direct methods.
     * @param image the image
     * @return the alpha raster
     */
    public static Raster getAlphaRaster(RenderedImage image) {
        ColorModel cm = image.getColorModel();
        if (!cm.hasAlpha() || cm.getTransparency() != ColorModel.TRANSLUCENT) {
            throw new IllegalStateException("Image doesn"t have an alpha channel");
        }
        Raster alpha;
        if (image instanceof BufferedImage) {
            //Optimization possible with BufferedImage (No copying)
            alpha = ((BufferedImage)image).getAlphaRaster();
        } else {
            WritableRaster wraster = GraphicsUtil.makeRasterWritable(image.getData());
            alpha = image.getColorModel().getAlphaRaster(wraster);
        }
        return alpha;
    }
    
    
}



Dithering a 24-bit RGB image to a monochrome (1-bit or bilevel) image

    
//-----------------------------------------------------------------------//
//                                                                       //
//                        R G B T o B i l e v e l                        //
//                                                                       //
//-----------------------------------------------------------------------//
/*
 * Copyright (c) 2002 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that Software is not designed,licensed or intended for use in
 * the design, construction, operation or maintenance of any nuclear facility.
 */
package omr.jai;
import java.awt.*;
import java.awt.color.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.image.renderable.*;
import java.util.*;
import javax.media.jai.*;
import javax.media.jai.operator.*;
import javax.media.jai.widget.*;
/**
 * Demo code for dithering a 24-bit RGB image to a monochrome (1-bit
 * or bilevel) image. The source image must be an 24-bit RGB image.
 * The result is displayed.
 *
 * Usage: java RGBToBilevel filename [true]
 *
 * If the second argument is present and equal to "true" then error diffusion
 * will be used; otherwise ordered dithering will be used.
 */
public class RGBToBilevel extends Frame {
    public static void main(String[] args) {
        new RGBToBilevel(args[0],
                         args.length > 1 ? args[1].equals("true") : false);
    }
    RGBToBilevel(final String fileName,
                 boolean isErrorDiffusion)
    {
        // Load the file.
        PlanarImage src = JAI.create("fileload", fileName);
        // Load the ParameterBlock for the dithering operation
        // and set the operation name.
        ParameterBlock pb = new ParameterBlock();
        pb.addSource(src);
        String opName = null;
        if(isErrorDiffusion) {
            opName = "errordiffusion";
            LookupTableJAI lut =
                new LookupTableJAI(new byte[][] {{(byte)0x00, (byte)0xff},
                                                 {(byte)0x00, (byte)0xff},
                                                 {(byte)0x00, (byte)0xff}});
            pb.add(lut);
            pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG);
        } else {
            opName = "ordereddither";
            ColorCube cube = ColorCube.createColorCube(DataBuffer.TYPE_BYTE,
                                                       0, new int[] {2, 2, 2});
            pb.add(cube);
            pb.add(KernelJAI.DITHER_MASK_443);
        }
        // Create a layout containing an IndexColorModel which maps
        // zero to zero and unity to 255; force SampleModel to be bilevel.
        ImageLayout layout = new ImageLayout();
        byte[] map = new byte[] {(byte)0x00, (byte)0xff};
        ColorModel cm = new IndexColorModel(1, 2, map, map, map);
        layout.setColorModel(cm);
        SampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
                                                         src.getWidth(),
                                                         src.getHeight(),
                                                         1);
        layout.setSampleModel(sm);
        // Create a hint containing the layout.
        RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT,
                                                  layout);
        // Dither the image.
        final PlanarImage dst = JAI.create(opName, pb, hints);
        // Exit on window closing.
        addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent we) {
                    JAI.create("filestore", dst, fileName + ".out", "PNG", null);
                    System.exit(0);
                }
            });

        // Display the result.
        //// ATTENTION A REMPLACER : add(new ScrollingImagePanel(dst, dst.getWidth(), dst.getHeight()));
        pack();
        setVisible(true);
    }
}



Gray scale image operation

  

/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.ru/javaexamples2.
 */
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ByteLookupTable;
import java.awt.image.ColorConvertOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.LookupOp;
import java.awt.image.RescaleOp;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** A demonstration of various image processing filters */
public class ImageOps extends JPanel {
    static final int WIDTH = 600, HEIGHT = 675; // Size of our example
    public String getName() {
        return "Image Processing";
    }
    public int getWidth() {
        return WIDTH;
    }
    public int getHeight() {
        return HEIGHT;
    }
    Image image;
    /** This constructor loads the image we will manipulate */
public ImageOps() {
    image = Toolkit.getDefaultToolkit().getImage("a.jpg");
  }
    // These arrays of bytes are used by the LookupImageOp image filters below
    static byte[] brightenTable = new byte[256];
    static byte[] thresholdTable = new byte[256];
    static { // Initialize the arrays
        for (int i = 0; i < 256; i++) {
            brightenTable[i] = (byte) (Math.sqrt(i / 255.0) * 255);
            thresholdTable[i] = (byte) ((i < 225) ? 0 : i);
        }
    }
    // This AffineTransform is used by one of the image filters below
    static AffineTransform mirrorTransform;
    static { // Create and initialize the AffineTransform
        mirrorTransform = AffineTransform.getTranslateInstance(127, 0);
        mirrorTransform.scale(-1.0, 1.0); // flip horizontally
    }
    // These are the labels we"ll display for each of the filtered images
    static String[] filterNames = new String[] { "Original", "Gray Scale", "Negative", "Brighten (linear)", "Brighten (sqrt)", "Threshold", "Blur", "Sharpen", "Edge Detect", "Mirror", "Rotate (center)", "Rotate (lower left)" };
    // The following BufferedImageOp image filter objects perform
    // different types of image processing operations.
    static BufferedImageOp[] filters = new BufferedImageOp[] {
    // 1) No filter here. We"ll display the original image
                                                              null,
                                                              // 2) Convert to Grayscale color space
                                                              new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null),
                                                              // 3) Image negative. Multiply each color value by -1.0 and add 255
                                                              new RescaleOp(-1.0f, 255f, null),
                                                              // 4) Brighten using a linear formula that increases all color
                                                              // values
                                                              new RescaleOp(1.25f, 0, null),
                                                              // 5) Brighten using the lookup table defined above
                                                              new LookupOp(new ByteLookupTable(0, brightenTable), null),
                                                              // 6) Threshold using the lookup table defined above
                                                              new LookupOp(new ByteLookupTable(0, thresholdTable), null),
                                                              // 7) Blur by "convolving" the image with a matrix
                                                              new ConvolveOp(new Kernel(3, 3, new float[] { .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, })),
                                                              // 8) Sharpen by using a different matrix
                                                              new ConvolveOp(new Kernel(3, 3, new float[] { 0.0f, -0.75f, 0.0f, -0.75f, 4.0f, -0.75f, 0.0f, -0.75f, 0.0f })),
                                                              // 9) Edge detect using yet another matrix
                                                              new ConvolveOp(new Kernel(3, 3, new float[] { 0.0f, -0.75f, 0.0f, -0.75f, 3.0f, -0.75f, 0.0f, -0.75f, 0.0f })),
                                                              // 10) Compute a mirror image using the transform defined above
                                                              new AffineTransformOp(mirrorTransform, AffineTransformOp.TYPE_BILINEAR),
                                                              // 11) Rotate the image 180 degrees about its center point
                                                              new AffineTransformOp(AffineTransform.getRotateInstance(Math.PI, 64, 95), AffineTransformOp.TYPE_NEAREST_NEIGHBOR),
                                                              // 12) Rotate the image 15 degrees about the bottom left
                                                              new AffineTransformOp(AffineTransform.getRotateInstance(Math.PI / 12, 0, 190), AffineTransformOp.TYPE_NEAREST_NEIGHBOR), };
    /** Draw the example */
    public void paint(Graphics g1) {
        Graphics2D g = (Graphics2D) g1;
        // Create a BufferedImage big enough to hold the Image loaded
        // in the constructor. Then copy that image into the new
        // BufferedImage object so that we can process it.
        BufferedImage bimage = new BufferedImage(image.getWidth(this), image.getHeight(this), BufferedImage.TYPE_INT_RGB);
        Graphics2D ig = bimage.createGraphics();
        ig.drawImage(image, 0, 0, this); // copy the image
        // Set some default graphics attributes
        g.setFont(new Font("SansSerif", Font.BOLD, 12)); // 12pt bold text
        g.setColor(Color.green); // Draw in green
        g.translate(10, 10); // Set some margins
        // Loop through the filters
        for (int i = 0; i < filters.length; i++) {
            // If the filter is null, draw the original image, otherwise,
            // draw the image as processed by the filter
            if (filters[i] == null)
                g.drawImage(bimage, 0, 0, this);
            else
                g.drawImage(filters[i].filter(bimage, null), 0, 0, this);
            g.drawString(filterNames[i], 0, 205); // Label the image
            g.translate(137, 0); // Move over
            if (i % 4 == 3)
                g.translate(-137 * 4, 215); // Move down after 4
        }
    }
    public static void main(String[] a) {
        JFrame f = new JFrame();
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        f.setContentPane(new ImageOps());
        f.pack();
        f.setVisible(true);
    }
}



If the image has transparent pixels

    
/*
 * Copyright (c) JForum Team
 * All rights reserved.
 * Redistribution and use in source and binary forms, 
 * with or without modification, are permitted provided 
 * that the following conditions are met:
 * 1) Redistributions of source code must retain the above 
 * copyright notice, this list of conditions and the 
 * following  disclaimer.
 * 2)  Redistributions in binary form must reproduce the 
 * above copyright notice, this list of conditions and 
 * the following disclaimer in the documentation and/or 
 * other materials provided with the distribution.
 * 3) Neither the name of "Rafael Steil" nor 
 * the names of its contributors may be used to endorse 
 * or promote products derived from this software without 
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT 
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
 * 
 * This file creation date: 21/04/2004 - 19:54:16
 * The JForum Project
 * http://www.jforum.net
 */
import java.awt.Dimension;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

/**
 * Utilities methods for image manipulation. It does not support writting of GIF images, but it can
 * read from. GIF images will be saved as PNG.
 * 
 * @author Rafael Steil
 * @version $Id: ImageUtils.java,v 1.23 2007/09/09 01:05:22 rafaelsteil Exp $
 */
public class ImageUtils
{
  public static final int IMAGE_UNKNOWN = -1;
  public static final int IMAGE_JPEG = 0;
  public static final int IMAGE_PNG = 1;
  public static final int IMAGE_GIF = 2;
  /**
   * Resizes an image
   * 
   * @param imgName The image name to resize. Must be the complet path to the file
   * @param type int
   * @param maxWidth The image"s max width
   * @param maxHeight The image"s max height
   * @return A resized <code>BufferedImage</code>
   */
  public static BufferedImage resizeImage(String imgName, int type, int maxWidth, int maxHeight)
  {
    try {
      return resizeImage(ImageIO.read(new File(imgName)), type, maxWidth, maxHeight);
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  /**
   * Resizes an image.
   * 
   * @param image
   *            The image to resize
   * @param maxWidth
   *            The image"s max width
   * @param maxHeight
   *            The image"s max height
   * @return A resized <code>BufferedImage</code>
   * @param type
   *            int
   */
  public static BufferedImage resizeImage(BufferedImage image, int type, int maxWidth, int maxHeight)
  {
    Dimension largestDimension = new Dimension(maxWidth, maxHeight);
    // Original size
    int imageWidth = image.getWidth(null);
    int imageHeight = image.getHeight(null);
    float aspectRatio = (float) imageWidth / imageHeight;
    if (imageWidth > maxWidth || imageHeight > maxHeight) {
      if ((float) largestDimension.width / largestDimension.height > aspectRatio) {
        largestDimension.width = (int) Math.ceil(largestDimension.height * aspectRatio);
      }
      else {
        largestDimension.height = (int) Math.ceil(largestDimension.width / aspectRatio);
      }
      imageWidth = largestDimension.width;
      imageHeight = largestDimension.height;
    }
    return createHeadlessSmoothBufferedImage(image, type, imageWidth, imageHeight);
  }
  /**
   * Saves an image to the disk.
   * 
   * @param image  The image to save
   * @param toFileName The filename to use
   * @param type The image type. Use <code>ImageUtils.IMAGE_JPEG</code> to save as JPEG images,
   *  or <code>ImageUtils.IMAGE_PNG</code> to save as PNG.
   * @return <code>false</code> if no appropriate writer is found
   */
  public static boolean saveImage(BufferedImage image, String toFileName, int type)
  {
    try {
      return ImageIO.write(image, type == IMAGE_JPEG ? "jpg" : "png", new File(toFileName));
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  /**
   * Compress and save an image to the disk. Currently this method only supports JPEG images.
   * 
   * @param image The image to save
   * @param toFileName The filename to use
   * @param type The image type. Use <code>ImageUtils.IMAGE_JPEG</code> to save as JPEG images,
   * or <code>ImageUtils.IMAGE_PNG</code> to save as PNG.
   */
  public static void saveCompressedImage(BufferedImage image, String toFileName, int type)
  {
    try {
      if (type == IMAGE_PNG) {
        throw new UnsupportedOperationException("PNG compression not implemented");
      }
      Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
      ImageWriter writer;
      writer = (ImageWriter) iter.next();
      ImageOutputStream ios = ImageIO.createImageOutputStream(new File(toFileName));
      writer.setOutput(ios);
      ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
      iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
      iwparam.setCompressionQuality(0.7F);
      writer.write(null, new IIOImage(image, null, null), iwparam);
      ios.flush();
      writer.dispose();
      ios.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  /**
   * Creates a <code>BufferedImage</code> from an <code>Image</code>. This method can
   * function on a completely headless system. This especially includes Linux and Unix systems
   * that do not have the X11 libraries installed, which are required for the AWT subsystem to
   * operate. This method uses nearest neighbor approximation, so it"s quite fast. Unfortunately,
   * the result is nowhere near as nice looking as the createHeadlessSmoothBufferedImage method.
   * 
   * @param image  The image to convert
   * @param w The desired image width
   * @param h The desired image height
   * @return The converted image
   * @param type int
   */
  public static BufferedImage createHeadlessBufferedImage(BufferedImage image, int type, int width, int height)
  {
    if (type == ImageUtils.IMAGE_PNG && hasAlpha(image)) {
      type = BufferedImage.TYPE_INT_ARGB;
    }
    else {
      type = BufferedImage.TYPE_INT_RGB;
    }
    BufferedImage bi = new BufferedImage(width, height, type);
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        bi.setRGB(x, y, image.getRGB(x * image.getWidth() / width, y * image.getHeight() / height));
      }
    }
    return bi;
  }
  /**
   * Creates a <code>BufferedImage</code> from an <code>Image</code>. This method can
   * function on a completely headless system. This especially includes Linux and Unix systems
   * that do not have the X11 libraries installed, which are required for the AWT subsystem to
   * operate. The resulting image will be smoothly scaled using bilinear filtering.
   * 
   * @param source The image to convert
   * @param w The desired image width
   * @param h The desired image height
   * @return The converted image
   * @param type  int
   */
  public static BufferedImage createHeadlessSmoothBufferedImage(BufferedImage source, int type, int width, int height)
  {
    if (type == ImageUtils.IMAGE_PNG && hasAlpha(source)) {
      type = BufferedImage.TYPE_INT_ARGB;
    }
    else {
      type = BufferedImage.TYPE_INT_RGB;
    }
    BufferedImage dest = new BufferedImage(width, height, type);
    int sourcex;
    int sourcey;
    double scalex = (double) width / source.getWidth();
    double scaley = (double) height / source.getHeight();
    int x1;
    int y1;
    double xdiff;
    double ydiff;
    int rgb;
    int rgb1;
    int rgb2;
    for (int y = 0; y < height; y++) {
      sourcey = y * source.getHeight() / dest.getHeight();
      ydiff = scale(y, scaley) - sourcey;
      for (int x = 0; x < width; x++) {
        sourcex = x * source.getWidth() / dest.getWidth();
        xdiff = scale(x, scalex) - sourcex;
        x1 = Math.min(source.getWidth() - 1, sourcex + 1);
        y1 = Math.min(source.getHeight() - 1, sourcey + 1);
        rgb1 = getRGBInterpolation(source.getRGB(sourcex, sourcey), source.getRGB(x1, sourcey), xdiff);
        rgb2 = getRGBInterpolation(source.getRGB(sourcex, y1), source.getRGB(x1, y1), xdiff);
        rgb = getRGBInterpolation(rgb1, rgb2, ydiff);
        dest.setRGB(x, y, rgb);
      }
    }
    return dest;
  }
  private static double scale(int point, double scale)
  {
    return point / scale;
  }
  private static int getRGBInterpolation(int value1, int value2, double distance)
  {
    int alpha1 = (value1 & 0xFF000000) >>> 24;
    int red1 = (value1 & 0x00FF0000) >> 16;
    int green1 = (value1 & 0x0000FF00) >> 8;
    int blue1 = (value1 & 0x000000FF);
    int alpha2 = (value2 & 0xFF000000) >>> 24;
    int red2 = (value2 & 0x00FF0000) >> 16;
    int green2 = (value2 & 0x0000FF00) >> 8;
    int blue2 = (value2 & 0x000000FF);
    int rgb = ((int) (alpha1 * (1.0 - distance) + alpha2 * distance) << 24)
      | ((int) (red1 * (1.0 - distance) + red2 * distance) << 16)
      | ((int) (green1 * (1.0 - distance) + green2 * distance) << 8)
      | (int) (blue1 * (1.0 - distance) + blue2 * distance);
    return rgb;
  }
  /**
   * Determines if the image has transparent pixels.
   * 
   * @param image The image to check for transparent pixel.s
   * @return <code>true</code> of <code>false</code>, according to the result
   */
  public static boolean hasAlpha(Image image)
  {
    try {
      PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
      pg.grabPixels();
      return pg.getColorModel().hasAlpha();
    }
    catch (InterruptedException e) {
      return false;
    }
  }
}



Image Color Operation: dark, bright, contrast, negative

  
/*
 * Apollo - Motion capture and animation system
 * Copyright (c) 2005 Apollo
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @author Giovane.Kuhn - brain@netuno.ru.br
 *
 */
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.ByteLookupTable;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.ShortLookupTable;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
public class ColorApp extends JFrame {
    DisplayPanel displayPanel;
    JButton brightenButton, darkenButton, contrastIncButton, contrastDecButton, reverseButton, resetButton;
    public ColorApp() {
        super();
        Container container = getContentPane();
        displayPanel = new DisplayPanel();
        container.add(displayPanel);
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(3, 2));
        panel.setBorder(new TitledBorder("Click a Button to Perform the Associated Operation and Reset..."));
        brightenButton = new JButton("Brightness >>");
        brightenButton.addActionListener(new ButtonListener());
        darkenButton = new JButton("Darkness >>");
        darkenButton.addActionListener(new ButtonListener());
        contrastIncButton = new JButton("Contrast >>");
        contrastIncButton.addActionListener(new ButtonListener());
        contrastDecButton = new JButton("Contrast <<");
        contrastDecButton.addActionListener(new ButtonListener());
        reverseButton = new JButton("Negative");
        reverseButton.addActionListener(new ButtonListener());
        resetButton = new JButton("Reset");
        resetButton.addActionListener(new ButtonListener());
        panel.add(brightenButton);
        panel.add(darkenButton);
        panel.add(contrastIncButton);
        panel.add(contrastDecButton);
        panel.add(reverseButton);
        panel.add(resetButton);
        container.add(BorderLayout.SOUTH, panel);
        addWindowListener(new WindowEventHandler());
        setSize(displayPanel.getWidth(), displayPanel.getHeight() + 25);
        show();
    }
    class WindowEventHandler extends WindowAdapter {
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    }
    public static void main(String arg[]) {
        new ColorApp();
    }
    class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JButton button = (JButton) e.getSource();
            if (button.equals(brightenButton)) {
                displayPanel.brightenLUT();
                displayPanel.applyFilter();
                displayPanel.repaint();
            } else if (button.equals(darkenButton)) {
                displayPanel.darkenLUT();
                displayPanel.applyFilter();
                displayPanel.repaint();
            } else if (button.equals(contrastIncButton)) {
                displayPanel.contrastIncLUT();
                displayPanel.applyFilter();
                displayPanel.repaint();
            } else if (button.equals(contrastDecButton)) {
                displayPanel.contrastDecLUT();
                displayPanel.applyFilter();
                displayPanel.repaint();
            } else if (button.equals(reverseButton)) {
                displayPanel.reverseLUT();
                displayPanel.applyFilter();
                displayPanel.repaint();
            } else if (button.equals(resetButton)) {
                displayPanel.reset();
                displayPanel.repaint();
            }
        }
    }
}
class DisplayPanel extends JPanel {
    Image displayImage;
    BufferedImage bi;
    Graphics2D big;
    LookupTable lookupTable;
    DisplayPanel() {
        setBackground(Color.black); // panel background color
        loadImage();
        setSize(displayImage.getWidth(this), displayImage.getWidth(this)); // panel
        createBufferedImage();
    }
    public void loadImage() {
        displayImage = Toolkit.getDefaultToolkit().getImage("a.jpg");
        MediaTracker mt = new MediaTracker(this);
        mt.addImage(displayImage, 1);
        try {
            mt.waitForAll();
        } catch (Exception e) {
            System.out.println("Exception while loading.");
        }
        if (displayImage.getWidth(this) == -1) {
            System.out.println("No jpg file");
            System.exit(0);
        }
    }
    public void createBufferedImage() {
        bi = new BufferedImage(displayImage.getWidth(this), displayImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
        big = bi.createGraphics();
        big.drawImage(displayImage, 0, 0, this);
    }
    public void brightenLUT() {
        short brighten[] = new short[256];
        for (int i = 0; i < 256; i++) {
            short pixelValue = (short) (i + 10);
            if (pixelValue > 255)
                pixelValue = 255;
            else if (pixelValue < 0)
                pixelValue = 0;
            brighten[i] = pixelValue;
        }
        lookupTable = new ShortLookupTable(0, brighten);
    }
    public void darkenLUT() {
        short brighten[] = new short[256];
        for (int i = 0; i < 256; i++) {
            short pixelValue = (short) (i - 10);
            if (pixelValue > 255)
                pixelValue = 255;
            else if (pixelValue < 0)
                pixelValue = 0;
            brighten[i] = pixelValue;
        }
        lookupTable = new ShortLookupTable(0, brighten);
    }
    public void contrastIncLUT() {
        short brighten[] = new short[256];
        for (int i = 0; i < 256; i++) {
            short pixelValue = (short) (i * 1.2);
            if (pixelValue > 255)
                pixelValue = 255;
            else if (pixelValue < 0)
                pixelValue = 0;
            brighten[i] = pixelValue;
        }
        lookupTable = new ShortLookupTable(0, brighten);
    }
    public void contrastDecLUT() {
        short brighten[] = new short[256];
        for (int i = 0; i < 256; i++) {
            short pixelValue = (short) (i / 1.2);
            if (pixelValue > 255)
                pixelValue = 255;
            else if (pixelValue < 0)
                pixelValue = 0;
            brighten[i] = pixelValue;
        }
        lookupTable = new ShortLookupTable(0, brighten);
    }
    public void reverseLUT() {
        byte reverse[] = new byte[256];
        for (int i = 0; i < 256; i++) {
            reverse[i] = (byte) (255 - i);
        }
        lookupTable = new ByteLookupTable(0, reverse);
    }
    public void reset() {
        big.setColor(Color.black);
        big.clearRect(0, 0, bi.getWidth(this), bi.getHeight(this));
        big.drawImage(displayImage, 0, 0, this);
    }
    public void applyFilter() {
        LookupOp lop = new LookupOp(lookupTable, null);
        lop.filter(bi, bi);
    }
    public void update(Graphics g) {
        g.clearRect(0, 0, getWidth(), getHeight());
        paintComponent(g);
    }
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D) g;
        g2D.drawImage(bi, 0, 0, this);
    }
}



ImageComparator compares a byte[] for equality by creating 2 hashes for the bytearray and comparing thoose hashes.

  
/**
 * 
 * JFreeReport : a free Java reporting library
 * 
 *
 * Project Info:  http://reporting.pentaho.org/
 *
 * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
 * in the United States and other countries.]
 *
 * ------------
 * ImageComparator.java
 * ------------
 * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
 */
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
 * The ImageComparator tries to compare a byte[] for equality by creating 2 hashes for the
 * bytearray and comparing thoose hashes. If no digest algorithms are available, then the
 * complete byte[] is used for comparison.
 * <p>
 * Using the digest method trades computing time for space.
 *
 * @author Thomas Morgner
 */
public class ImageComparator
{
  /**
   * A ImageCompareData that uses the complete image data for comparison.
   */
  private static final class CompleteImageCompareData
  {
    /**
     * The image content.
     */
    private final byte[] image;
    /**
     * Create a new CompleteImageCompareData instance.
     *
     * @param image the image data used for comparison.
     */
    private CompleteImageCompareData (final byte[] image)
    {
      this.image = image;
    }
    /**
     * Checks whether the given Object equals this object.
     *
     * @param o the to be compared object
     * @return true, if both objects are equal
     */
    public boolean equals (final Object o)
    {
      if (this == o)
      {
        return true;
      }
      if (!(o instanceof CompleteImageCompareData))
      {
        return false;
      }
      final CompleteImageCompareData data = (CompleteImageCompareData) o;
      if (!Arrays.equals(image, data.image))
      {
        return false;
      }
      return true;
    }
    /**
     * returns a hashcode for this class.
     *
     * @return always 0.
     */
    public int hashCode ()
    {
      return image.length;
    }
  }
  /**
   * An ImageComparator which uses precomputed Message-Digests to compare the image.
   */
  private static final class DigestImageCompareData
  {
    /**
     * An MD5 digest.
     */
    private byte[] digestMD5Data;
    /**
     * An SHA digest.
     */
    private byte[] digestSHAData;
    /**
     * Creates a new DigestImageCompareData instance.
     *
     * @param digestMD5Data the MD5 digest data
     * @param digestSHAData the SHA1 digest data
     */
    private DigestImageCompareData (final byte[] digestMD5Data,
                                    final byte[] digestSHAData)
    {
      if (digestMD5Data == null || digestSHAData == null)
      {
        throw new NullPointerException();
      }
      this.digestMD5Data = digestMD5Data;
      this.digestSHAData = digestSHAData;
    }
    /**
     * Checks whether the given Object equals this object.
     *
     * @param o the to be compared object
     * @return true, if both objects are equal
     */
    public boolean equals (final Object o)
    {
      if (this == o)
      {
        return true;
      }
      if (!(o instanceof DigestImageCompareData))
      {
        return false;
      }
      final DigestImageCompareData data = (DigestImageCompareData) o;
      if (!Arrays.equals(digestMD5Data, data.digestMD5Data))
      {
        return false;
      }
      if (!Arrays.equals(digestSHAData, data.digestSHAData))
      {
        return false;
      }
      return true;
    }
    /**
     * returns a hashcode for this class.
     *
     * @return always 0.
     */
    public int hashCode ()
    {
      return 0;
    }
  }
  /**
   * An MD5 message digest.
   */
  private MessageDigest digestMD5;
  /**
   * An SHA message digest.
   */
  private MessageDigest digestSHA;
  /**
   * Creates a new ImageComparator. The comparator checks whether the MD5 and the SHA
   * message digest implementations are available. If they are not available, an
   * alternative comparison method is used, which is more memory consuming.
   */
  public ImageComparator ()
  {
    try
    {
      digestMD5 = MessageDigest.getInstance("MD5");
    }
    catch (NoSuchAlgorithmException nse)
    {
      System.out.println("No MD5 algorithm available");
    }
    try
    {
      digestSHA = MessageDigest.getInstance("SHA");
    }
    catch (NoSuchAlgorithmException nse)
    {
      System.out.println("No SHA algorithm available");
    }
  }
  /**
   * Creates 2 comparable objects. These objects can be compared for equality.
   *
   * @param image the image data which should be prepared for comparison
   * @param fast  whether to prefer the memory intensive faster compare method to the
   *              digest based comparation. This may result in outofmemory errors on huge
   *              reports or images.
   * @return the prepared image data.
   */
  public Object createCompareData (final byte[] image, final boolean fast)
  {
    if (fast == false && (digestMD5 != null && digestSHA != null))
    {
      final byte[] dataMD5 = digestMD5.digest(image);
      final byte[] dataSHA = digestSHA.digest(image);
      if (dataSHA != null && dataMD5 != null)
      {
        return new DigestImageCompareData(dataMD5, dataSHA);
      }
    }
    return new CompleteImageCompareData(image);
  }
}



Image Filter

  
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BandCombineOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ByteLookupTable;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.LookupOp;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
public class Java2DExample extends JFrame {
  private JMenu filterMenu = new JMenu("Image Filters");
  private ImagePanel imagePanel;
  private MyFilter invertFilter = new InvertFilter();
  private MyFilter sharpenFilter = new SharpenFilter();
  private MyFilter blurFilter = new BlurFilter();
  private MyFilter colorFilter = new ColorFilter();
  public Java2DExample() {
    super("Java 2D Image Processing Demo");
    imagePanel = new ImagePanel(Java2DExample.class.getResource("yourImage.png"));
    JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);
    filterMenu.setMnemonic("I");
    JMenuItem originalMenuItem = new JMenuItem("Display Original");
    originalMenuItem.setMnemonic("O");
    originalMenuItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent action) {
        imagePanel.displayOriginalImage();
      }
    });
    JMenuItem invertMenuItem = createMenuItem("Invert", "I", invertFilter);
    JMenuItem sharpenMenuItem = createMenuItem("Sharpen", "S", sharpenFilter);
    JMenuItem blurMenuItem = createMenuItem("Blur", "B", blurFilter);
    JMenuItem changeColorsMenuItem = createMenuItem("Change Colors", "C", colorFilter);
    filterMenu.add(originalMenuItem);
    filterMenu.add(invertMenuItem);
    filterMenu.add(sharpenMenuItem);
    filterMenu.add(blurMenuItem);
    filterMenu.add(changeColorsMenuItem);
    menuBar.add(filterMenu);
    getContentPane().add(imagePanel, BorderLayout.CENTER);
  }
  public JMenuItem createMenuItem(String menuItemName, char mnemonic, final MyFilter filter) {
    JMenuItem menuItem = new JMenuItem(menuItemName);
    menuItem.setMnemonic(mnemonic);
    menuItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent action) {
        imagePanel.applyFilter(filter);
      }
    });
    return menuItem;
  }
  public static void main(String args[]) {
    Java2DExample application = new Java2DExample();
    application.setDefaultCloseOperation(EXIT_ON_CLOSE);
    application.pack();
    application.setVisible(true);
  }
}
interface MyFilter {
  public abstract BufferedImage processImage(BufferedImage image);
}
class BlurFilter implements MyFilter {
  public BufferedImage processImage(BufferedImage image) {
    float[] blurMatrix = { 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f,
        1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f };
    BufferedImageOp blurFilter = new ConvolveOp(new Kernel(3, 3, blurMatrix),
        ConvolveOp.EDGE_NO_OP, null);
    return blurFilter.filter(image, null);
  }
}
class ImagePanel extends JPanel {
  private BufferedImage displayImage;
  private BufferedImage originalImage;
  private Image image;
  public ImagePanel(URL imageURL) {
    image = Toolkit.getDefaultToolkit().createImage(imageURL);
    MediaTracker mediaTracker = new MediaTracker(this);
    mediaTracker.addImage(image, 0);
    try {
      mediaTracker.waitForAll();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    originalImage = new BufferedImage(image.getWidth(null), image.getHeight(null),
        BufferedImage.TYPE_INT_RGB);
    displayImage = originalImage;
    Graphics2D graphics = displayImage.createGraphics();
    graphics.drawImage(image, null, null);
  }
  public void applyFilter(MyFilter filter) {
    displayImage = filter.processImage(displayImage);
    repaint();
  }
  public void displayOriginalImage() {
    displayImage = new BufferedImage(image.getWidth(null), image.getHeight(null),
        BufferedImage.TYPE_INT_RGB);
    Graphics2D graphics = displayImage.createGraphics();
    graphics.drawImage(originalImage, null, null);
    repaint();
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D graphics = (Graphics2D) g;
    graphics.drawImage(displayImage, 0, 0, null);
  }
  public Dimension getPreferredSize() {
    return new Dimension(displayImage.getWidth(), displayImage.getHeight());
  }
  public Dimension getMinimumSize() {
    return getPreferredSize();
  }
}
class SharpenFilter implements MyFilter {
  public BufferedImage processImage(BufferedImage image) {
    float[] sharpenMatrix = { 0.0f, -1.0f, 0.0f, -1.0f, 5.0f, -1.0f, 0.0f, -1.0f, 0.0f };
    BufferedImageOp sharpenFilter = new ConvolveOp(new Kernel(3, 3, sharpenMatrix),
        ConvolveOp.EDGE_NO_OP, null);
    return sharpenFilter.filter(image, null);
  }
}
class InvertFilter implements MyFilter {
  public BufferedImage processImage(BufferedImage image) {
    byte[] invertArray = new byte[256];
    for (int counter = 0; counter < 256; counter++)
      invertArray[counter] = (byte) (255 - counter);
    BufferedImageOp invertFilter = new LookupOp(new ByteLookupTable(0, invertArray), null);
    return invertFilter.filter(image, null);
  }
}
class ColorFilter implements MyFilter {
  public BufferedImage processImage(BufferedImage image) {
    float[][] colorMatrix = { { 1f, 0f, 0f }, { 0.5f, 1.0f, 0.5f }, { 0.2f, 0.4f, 0.6f } };
    BandCombineOp changeColors = new BandCombineOp(colorMatrix, null);
    Raster sourceRaster = image.getRaster();
    WritableRaster displayRaster = sourceRaster.createCompatibleWritableRaster();
    changeColors.filter(sourceRaster, displayRaster);
    return new BufferedImage(image.getColorModel(), displayRaster, true, null);
  }
}