Mega Code Archive

 
Categories / Java / 2D Graphics GUI
 

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.ComponentSampleModel; 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 <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>  * @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;     }           }