001/*****************************************************************************
002 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
003 * ------------------------------------------------------------------------- *
004 * This software is published under the terms of the Apache Software License *
005 * version 1.1, a copy of which has been included with this distribution in  *
006 * the LICENSE file.                                                         *
007 *****************************************************************************/
008
009package com.kitfox.svg.batik;
010
011import java.awt.Color;
012import java.awt.Composite;
013import java.awt.Graphics2D;
014import java.awt.GraphicsConfiguration;
015import java.awt.GraphicsDevice;
016import java.awt.Point;
017import java.awt.Rectangle;
018import java.awt.RenderingHints;
019import java.awt.Shape;
020import java.awt.color.ColorSpace;
021import java.awt.geom.AffineTransform;
022import java.awt.geom.Rectangle2D;
023import java.awt.image.BufferedImage;
024import java.awt.image.ColorModel;
025import java.awt.image.ComponentSampleModel;
026import java.awt.image.DataBuffer;
027import java.awt.image.DataBufferByte;
028import java.awt.image.DataBufferInt;
029import java.awt.image.DataBufferShort;
030import java.awt.image.DataBufferUShort;
031import java.awt.image.DirectColorModel;
032import java.awt.image.Raster;
033import java.awt.image.RenderedImage;
034import java.awt.image.SampleModel;
035import java.awt.image.SinglePixelPackedSampleModel;
036import java.awt.image.WritableRaster;
037import java.awt.image.renderable.RenderContext;
038import java.awt.image.renderable.RenderableImage;
039import java.lang.ref.Reference;
040import java.lang.ref.WeakReference;
041
042/**
043 *
044 * @author  kitfox
045 */
046public class GraphicsUtil
047{
048    
049    /** Creates a new instance of GraphicsUtil */
050    public GraphicsUtil()
051    {
052    }
053    
054    /**
055     * Create a new ColorModel with it's alpha premultiplied state matching
056     * newAlphaPreMult.
057     * @param cm The ColorModel to change the alpha premult state of.
058     * @param newAlphaPreMult The new state of alpha premult.
059     * @return   A new colorModel that has isAlphaPremultiplied()
060     *           equal to newAlphaPreMult.
061     */
062    public static ColorModel coerceColorModel(ColorModel cm, boolean newAlphaPreMult)
063    {
064        if (cm.isAlphaPremultiplied() == newAlphaPreMult)
065            return cm;
066        
067        // Easiest way to build proper colormodel for new Alpha state...
068        // Eventually this should switch on known ColorModel types and
069        // only fall back on this hack when the CM type is unknown.
070        WritableRaster wr = cm.createCompatibleWritableRaster(1,1);
071        return cm.coerceData(wr, newAlphaPreMult);
072    }
073    
074    /**
075     * Coerces data within a bufferedImage to match newAlphaPreMult,
076     * Note that this can not change the colormodel of bi so you
077     *
078     * @param wr The raster to change the state of.
079     * @param cm The colormodel currently associated with data in wr.
080     * @param newAlphaPreMult The desired state of alpha Premult for raster.
081     * @return A new colormodel that matches newAlphaPreMult.
082     */
083    public static ColorModel coerceData(WritableRaster wr, ColorModel cm, boolean newAlphaPreMult)
084    {
085        
086        // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() +
087        //                    " Out: " + newAlphaPreMult);
088        if (cm.hasAlpha()== false)
089            // Nothing to do no alpha channel
090            return cm;
091        
092        if (cm.isAlphaPremultiplied() == newAlphaPreMult)
093            // nothing to do alpha state matches...
094            return cm;
095        
096        // System.out.println("CoerceData: " + wr.getSampleModel());
097        
098        int [] pixel = null;
099        int    bands = wr.getNumBands();
100        float  norm;
101        if (newAlphaPreMult)
102        {
103            if (is_BYTE_COMP_Data(wr.getSampleModel()))
104                mult_BYTE_COMP_Data(wr);
105            else if (is_INT_PACK_Data(wr.getSampleModel(), true))
106                mult_INT_PACK_Data(wr);
107            else
108            {
109                norm = 1f/255f;
110                int x0, x1, y0, y1, a, b;
111                float alpha;
112                x0 = wr.getMinX();
113                x1 = x0+wr.getWidth();
114                y0 = wr.getMinY();
115                y1 = y0+wr.getHeight();
116                for (int y=y0; y<y1; y++)
117                    for (int x=x0; x<x1; x++)
118                    {
119                        pixel = wr.getPixel(x,y,pixel);
120                        a = pixel[bands-1];
121                        if ((a >= 0) && (a < 255))
122                        {
123                            alpha = a*norm;
124                            for (b=0; b<bands-1; b++)
125                                pixel[b] = (int)(pixel[b]*alpha+0.5f);
126                            wr.setPixel(x,y,pixel);
127                        }
128                    }
129            }
130        } else
131        {
132            if (is_BYTE_COMP_Data(wr.getSampleModel()))
133                divide_BYTE_COMP_Data(wr);
134            else if (is_INT_PACK_Data(wr.getSampleModel(), true))
135                divide_INT_PACK_Data(wr);
136            else
137            {
138                int x0, x1, y0, y1, a, b;
139                float ialpha;
140                x0 = wr.getMinX();
141                x1 = x0+wr.getWidth();
142                y0 = wr.getMinY();
143                y1 = y0+wr.getHeight();
144                for (int y=y0; y<y1; y++)
145                    for (int x=x0; x<x1; x++)
146                    {
147                        pixel = wr.getPixel(x,y,pixel);
148                        a = pixel[bands-1];
149                        if ((a > 0) && (a < 255))
150                        {
151                            ialpha = 255/(float)a;
152                            for (b=0; b<bands-1; b++)
153                                pixel[b] = (int)(pixel[b]*ialpha+0.5f);
154                            wr.setPixel(x,y,pixel);
155                        }
156                    }
157            }
158        }
159        
160        return coerceColorModel(cm, newAlphaPreMult);
161    }
162    
163    
164    public static boolean is_INT_PACK_Data(SampleModel sm,
165    boolean requireAlpha)
166    {
167        // Check ColorModel is of type DirectColorModel
168        if(!(sm instanceof SinglePixelPackedSampleModel)) return false;
169        
170        // Check transfer type
171        if(sm.getDataType() != DataBuffer.TYPE_INT)       return false;
172        
173        SinglePixelPackedSampleModel sppsm;
174        sppsm = (SinglePixelPackedSampleModel)sm;
175        
176        int [] masks = sppsm.getBitMasks();
177        if (masks.length == 3)
178        {
179            if (requireAlpha) return false;
180        } else if (masks.length != 4)
181            return false;
182        
183        if(masks[0] != 0x00ff0000) return false;
184        if(masks[1] != 0x0000ff00) return false;
185        if(masks[2] != 0x000000ff) return false;
186        if ((masks.length == 4) &&
187        (masks[3] != 0xff000000)) return false;
188        
189        return true;
190    }
191    
192    protected static void mult_INT_PACK_Data(WritableRaster wr)
193    {
194        // System.out.println("Multiply Int: " + wr);
195        
196        SinglePixelPackedSampleModel sppsm;
197        sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel();
198        
199        final int width = wr.getWidth();
200        
201        final int scanStride = sppsm.getScanlineStride();
202        DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
203        final int base
204        = (db.getOffset() +
205        sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
206        wr.getMinY()-wr.getSampleModelTranslateY()));
207        int n=0;
208        // Access the pixel data array
209        final int pixels[] = db.getBankData()[0];
210        for (int y=0; y<wr.getHeight(); y++)
211        {
212            int sp = base + y*scanStride;
213            final int end = sp + width;
214            while (sp < end)
215            {
216                int pixel = pixels[sp];
217                int a = pixel>>>24;
218                if ((a>=0) && (a<255))
219                {
220                    pixels[sp] = ((a << 24) |
221                    ((((pixel&0xFF0000)*a)>>8)&0xFF0000) |
222                    ((((pixel&0x00FF00)*a)>>8)&0x00FF00) |
223                    ((((pixel&0x0000FF)*a)>>8)&0x0000FF));
224                }
225                sp++;
226            }
227        }
228    }
229    
230    protected static void divide_INT_PACK_Data(WritableRaster wr)
231    {
232        // System.out.println("Divide Int");
233        
234        SinglePixelPackedSampleModel sppsm;
235        sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel();
236        
237        final int width = wr.getWidth();
238        
239        final int scanStride = sppsm.getScanlineStride();
240        DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
241        final int base
242        = (db.getOffset() +
243        sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
244        wr.getMinY()-wr.getSampleModelTranslateY()));
245        int pixel, a, aFP, n=0;
246        // Access the pixel data array
247        final int pixels[] = db.getBankData()[0];
248        for (int y=0; y<wr.getHeight(); y++)
249        {
250            int sp = base + y*scanStride;
251            final int end = sp + width;
252            while (sp < end)
253            {
254                pixel = pixels[sp];
255                a = pixel>>>24;
256                if (a<=0)
257                {
258                    pixels[sp] = 0x00FFFFFF;
259                }
260                else if (a<255)
261                {
262                    aFP = (0x00FF0000/a);
263                    pixels[sp] =
264                    ((a << 24) |
265                    (((((pixel&0xFF0000)>>16)*aFP)&0xFF0000)    ) |
266                    (((((pixel&0x00FF00)>>8) *aFP)&0xFF0000)>>8 ) |
267                    (((((pixel&0x0000FF))    *aFP)&0xFF0000)>>16));
268                }
269                sp++;
270            }
271        }
272    }
273    
274    public static boolean is_BYTE_COMP_Data(SampleModel sm)
275    {
276        // Check ColorModel is of type DirectColorModel
277        if(!(sm instanceof ComponentSampleModel))    return false;
278        
279        // Check transfer type
280        if(sm.getDataType() != DataBuffer.TYPE_BYTE) return false;
281        
282        return true;
283    }
284    
285    protected static void mult_BYTE_COMP_Data(WritableRaster wr)
286    {
287        // System.out.println("Multiply Int: " + wr);
288        
289        ComponentSampleModel csm;
290        csm = (ComponentSampleModel)wr.getSampleModel();
291        
292        final int width = wr.getWidth();
293        
294        final int scanStride = csm.getScanlineStride();
295        final int pixStride  = csm.getPixelStride();
296        final int [] bandOff = csm.getBandOffsets();
297        
298        DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
299        final int base
300        = (db.getOffset() +
301        csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
302        wr.getMinY()-wr.getSampleModelTranslateY()));
303        
304        
305        int a=0;
306        int aOff = bandOff[bandOff.length-1];
307        int bands = bandOff.length-1;
308        int b, i;
309        
310        // Access the pixel data array
311        final byte pixels[] = db.getBankData()[0];
312        for (int y=0; y<wr.getHeight(); y++)
313        {
314            int sp = base + y*scanStride;
315            final int end = sp + width*pixStride;
316            while (sp < end)
317            {
318                a = pixels[sp+aOff]&0xFF;
319                if (a!=0xFF)
320                    for (b=0; b<bands; b++)
321                    {
322                        i = sp+bandOff[b];
323                        pixels[i] = (byte)(((pixels[i]&0xFF)*a)>>8);
324                    }
325                sp+=pixStride;
326            }
327        }
328    }
329    
330    protected static void divide_BYTE_COMP_Data(WritableRaster wr)
331    {
332        // System.out.println("Multiply Int: " + wr);
333        
334        ComponentSampleModel csm;
335        csm = (ComponentSampleModel)wr.getSampleModel();
336        
337        final int width = wr.getWidth();
338        
339        final int scanStride = csm.getScanlineStride();
340        final int pixStride  = csm.getPixelStride();
341        final int [] bandOff = csm.getBandOffsets();
342        
343        DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
344        final int base
345        = (db.getOffset() +
346        csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
347        wr.getMinY()-wr.getSampleModelTranslateY()));
348        
349        
350        int a=0;
351        int aOff = bandOff[bandOff.length-1];
352        int bands = bandOff.length-1;
353        int b, i;
354        // Access the pixel data array
355        final byte pixels[] = db.getBankData()[0];
356        for (int y=0; y<wr.getHeight(); y++)
357        {
358            int sp = base + y*scanStride;
359            final int end = sp + width*pixStride;
360            while (sp < end)
361            {
362                a = pixels[sp+aOff]&0xFF;
363                if (a==0)
364                {
365                    for (b=0; b<bands; b++)
366                        pixels[sp+bandOff[b]] = (byte)0xFF;
367                } else if (a<255)
368                {
369                    int aFP = (0x00FF0000/a);
370                    for (b=0; b<bands; b++)
371                    {
372                        i = sp+bandOff[b];
373                        pixels[i] = (byte)(((pixels[i]&0xFF)*aFP)>>>16);
374                    }
375                }
376                sp+=pixStride;
377            }
378        }
379    }
380    
381    
382}