001    package railo.runtime.img;
002    
003    import java.awt.AWTException;
004    import java.awt.AlphaComposite;
005    import java.awt.BasicStroke;
006    import java.awt.Color;
007    import java.awt.Composite;
008    import java.awt.Font;
009    import java.awt.Graphics;
010    import java.awt.Graphics2D;
011    import java.awt.GraphicsConfiguration;
012    import java.awt.GraphicsDevice;
013    import java.awt.GraphicsEnvironment;
014    import java.awt.HeadlessException;
015    import java.awt.Point;
016    import java.awt.RenderingHints;
017    import java.awt.Stroke;
018    import java.awt.Transparency;
019    import java.awt.color.ColorSpace;
020    import java.awt.font.TextAttribute;
021    import java.awt.geom.AffineTransform;
022    import java.awt.geom.CubicCurve2D;
023    import java.awt.geom.QuadCurve2D;
024    import java.awt.image.AffineTransformOp;
025    import java.awt.image.BufferedImage;
026    import java.awt.image.ColorModel;
027    import java.awt.image.ComponentColorModel;
028    import java.awt.image.DataBufferInt;
029    import java.awt.image.DirectColorModel;
030    import java.awt.image.IndexColorModel;
031    import java.awt.image.PackedColorModel;
032    import java.awt.image.PixelGrabber;
033    import java.awt.image.Raster;
034    import java.awt.image.SampleModel;
035    import java.awt.image.SinglePixelPackedSampleModel;
036    import java.awt.image.WritableRaster;
037    import java.awt.image.renderable.ParameterBlock;
038    import java.io.ByteArrayInputStream;
039    import java.io.ByteArrayOutputStream;
040    import java.io.File;
041    import java.io.IOException;
042    import java.io.InputStream;
043    import java.io.OutputStream;
044    import java.text.AttributedString;
045    import java.util.Iterator;
046    import java.util.Locale;
047    
048    import javax.imageio.IIOImage;
049    import javax.imageio.ImageIO;
050    import javax.imageio.ImageReader;
051    import javax.imageio.ImageTypeSpecifier;
052    import javax.imageio.ImageWriteParam;
053    import javax.imageio.ImageWriter;
054    import javax.imageio.metadata.IIOMetadata;
055    import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
056    import javax.imageio.stream.FileImageInputStream;
057    import javax.imageio.stream.ImageOutputStream;
058    import javax.imageio.stream.MemoryCacheImageInputStream;
059    import javax.media.jai.BorderExtender;
060    import javax.media.jai.BorderExtenderConstant;
061    import javax.media.jai.Interpolation;
062    import javax.media.jai.JAI;
063    import javax.media.jai.LookupTableJAI;
064    import javax.media.jai.operator.ShearDir;
065    import javax.media.jai.operator.TransposeType;
066    import javax.swing.ImageIcon;
067    
068    import org.apache.commons.codec.binary.Base64;
069    import org.w3c.dom.Attr;
070    import org.w3c.dom.Element;
071    import org.w3c.dom.NamedNodeMap;
072    import org.w3c.dom.Node;
073    import org.w3c.dom.NodeList;
074    
075    import railo.commons.io.IOUtil;
076    import railo.commons.io.res.Resource;
077    import railo.commons.lang.StringUtil;
078    import railo.commons.lang.font.FontUtil;
079    import railo.runtime.PageContext;
080    import railo.runtime.dump.DumpData;
081    import railo.runtime.dump.DumpProperties;
082    import railo.runtime.dump.DumpTable;
083    import railo.runtime.exp.CasterException;
084    import railo.runtime.exp.ExpressionException;
085    import railo.runtime.exp.PageException;
086    import railo.runtime.exp.PageRuntimeException;
087    import railo.runtime.img.filter.QuantizeFilter;
088    import railo.runtime.img.gif.GifEncoder;
089    import railo.runtime.op.Caster;
090    import railo.runtime.op.Constants;
091    import railo.runtime.op.Decision;
092    import railo.runtime.text.xml.XMLUtil;
093    import railo.runtime.type.Array;
094    import railo.runtime.type.ArrayImpl;
095    import railo.runtime.type.Collection;
096    import railo.runtime.type.List;
097    import railo.runtime.type.ObjectWrap;
098    import railo.runtime.type.Struct;
099    import railo.runtime.type.StructImpl;
100    import railo.runtime.type.dt.DateTime;
101    import railo.runtime.type.util.ArrayUtil;
102    import railo.runtime.type.util.StructSupport;
103    
104    public class Image extends StructSupport implements Cloneable,Struct {
105            private static final long serialVersionUID = -2370381932689749657L;
106    
107    
108            public static final int BORDER_TYPE_CONSTANT=-1;
109    
110            
111            public static final int INTERPOLATION_NONE=0;
112            public static final int INTERPOLATION_NEAREST=1;
113            public static final int INTERPOLATION_BILINEAR=2;
114            public static final int INTERPOLATION_BICUBIC=3;
115    
116            public static final int IP_NONE=0;
117            
118            public static final int IPC_NEAREST=1;
119            public static final int IPC_BILINEAR=2;
120            public static final int IPC_BICUBIC=3;
121            public static final int IPC_MAX=3;
122            
123            public static final int IP_HIGHESTQUALITY=100;
124            public static final int IP_HIGHQUALITY=101;
125            public static final int IP_MEDIUMQUALITY=102;
126            public static final int IP_HIGHESTPERFORMANCE=103;
127            public static final int IP_HIGHPERFORMANCE=104;
128            public static final int IP_MEDIUMPERFORMANCE=105;
129            
130            public static final int IP_BESSEL=109;
131            public static final int IP_BLACKMAN=110;
132            public static final int IP_HAMMING=111;
133            public static final int IP_HANNING=112;
134            public static final int IP_HERMITE=113;
135            public static final int IP_LANCZOS=114;
136            public static final int IP_MITCHELL=115;
137            public static final int IP_QUADRATIC=116;
138            public static final int IP_TRIANGLE=117;
139    
140            private static final int ANTI_ALIAS_NONE=0;
141            private static final int ANTI_ALIAS_ON=1;
142            private static final int ANTI_ALIAS_OFF=2;
143    
144    
145            private static final String FORMAT = "javax_imageio_1.0";
146            
147            private BufferedImage _image;
148            private Resource source=null;
149            private String format;
150    
151            private Graphics2D graphics;
152    
153            private Color bgColor;
154            private Color fgColor;
155            private Color xmColor;
156    
157            private float tranparency=-1;
158            private int antiAlias=ANTI_ALIAS_NONE;
159    
160            private Stroke stroke;
161    
162            private Struct sctInfo;
163    
164    
165            private float alpha=1;
166    
167    
168            private Composite composite;
169            private static Object sync=new Object();
170    
171            
172            static {
173                    ImageIO.scanForPlugins();
174            }
175    
176            public Image(byte[] binary) throws IOException {
177                    this(binary, ImageUtil.getFormat(binary,null)); 
178            }
179            
180            public Image(byte[] binary, String format) throws IOException {
181                    checkRestriction();
182                    this.format=format;
183                    _image=ImageUtil.toBufferedImage(binary,format);
184                    if(_image==null) throw new IOException("can not read in image");
185            }
186            
187            public Image(Resource res) throws IOException {
188                    checkRestriction();
189                    format=ImageUtil.getFormat(res);
190                    _image=ImageUtil.toBufferedImage(res,format);
191                    this.source=res;
192                    if(_image==null) throw new IOException("can not read in file "+res);
193            }
194    
195    
196            public Image(BufferedImage image) {
197                    checkRestriction();
198                    this._image=image;
199            }
200            
201    
202            public Image(String b64str) throws IOException, ExpressionException {
203                    this(ImageUtil.readBase64(b64str));
204            }
205    
206            public Image(int width, int height, int imageType, Color canvasColor) throws ExpressionException {
207                    checkRestriction();
208                    _image = new BufferedImage(width, height, imageType);
209                    if(!StringUtil.isEmpty(canvasColor)){
210                            
211                            setBackground(canvasColor);
212                            clearRect(0, 0, width, height);
213                    }
214            }
215    
216            public Image() {
217                    checkRestriction();
218            }
219    
220                    
221            
222    
223            /**
224             * add a border to image
225             * @param thickness
226             * @param color
227             * @param borderType 
228             */
229            public void addBorder(int thickness, Color color, int borderType)  throws ExpressionException{
230                    
231                    double colorArray[] = {color.getRed(), color.getGreen(), color.getBlue()};
232                    BorderExtender borderExtender = new BorderExtenderConstant(colorArray);
233                    
234                    ParameterBlock params = new ParameterBlock();
235                    params.addSource(image());
236                    params.add(thickness);
237                    params.add(thickness);
238                    params.add(thickness);
239                    params.add(thickness);
240                    if(BORDER_TYPE_CONSTANT==borderType)    params.add(borderExtender);
241                    else    params.add(BorderExtender.createInstance(borderType));
242                    //else if(BORDER_TYPE_WRAP==borderType)params.add(BorderExtender.createInstance(BorderExtender.BORDER_REFLECT));
243    
244                    image((JAI.create("border", params)).getAsBufferedImage());
245                    
246            }
247            
248            public void blur(int blurFactor)  throws ExpressionException{
249                ParameterBlock params = new ParameterBlock();
250                    params.addSource(image());
251                    params.add(blurFactor);
252                    RenderingHints hint= new RenderingHints(JAI.KEY_BORDER_EXTENDER,BorderExtender.createInstance(1));
253                    image(JAI.create("boxfilter", params, hint).getAsBufferedImage());
254            }
255    
256            public void clearRect(int x, int y, int width, int height)  throws ExpressionException{
257                    getGraphics().clearRect(x, y, width, height);
258            }
259            
260    
261            public Struct info()  throws ExpressionException{
262                    if(sctInfo!=null) return sctInfo;
263                    
264                    Struct sctInfo=new StructImpl(),sct;
265                    
266                    
267                    sctInfo.setEL("height",new Double(getHeight()));
268                    sctInfo.setEL("width",new Double(getWidth()));
269                    sctInfo.setEL("source",source==null?"":source.getAbsolutePath());
270                    //sct.setEL("mime_type",getMimeType());
271                    
272                    ColorModel cm = image().getColorModel();
273                    sct=new StructImpl();
274                    sctInfo.setEL("colormodel",sct);
275                    
276                    sct.setEL("alpha_channel_support",Caster.toBoolean(cm.hasAlpha()));
277                    sct.setEL("alpha_premultiplied",Caster.toBoolean(cm.isAlphaPremultiplied()));
278                    sct.setEL("transparency",toStringTransparency(cm.getTransparency()));
279                    sct.setEL("pixel_size",Caster.toDouble(cm.getPixelSize()));
280                    sct.setEL("num_components",Caster.toDouble(cm.getNumComponents()));
281                    sct.setEL("num_color_components",Caster.toDouble(cm.getNumColorComponents()));
282                    sct.setEL("colorspace",toStringColorSpace(cm.getColorSpace()));
283                    
284                //bits_component
285                    int[] bitspercomponent = cm.getComponentSize();
286                    Array arr=new ArrayImpl();
287                    Double value;
288                for (int i = 0; i < bitspercomponent.length; i++) {
289                    sct.setEL("bits_component_" + (i + 1),value=new Double(bitspercomponent[i]));
290                    arr.appendEL(value);
291                }
292                    sct.setEL("bits_component",arr);
293                    
294                // colormodel_type
295                    if (cm instanceof ComponentColorModel)          sct.setEL("colormodel_type", "ComponentColorModel");
296                    else if (cm instanceof IndexColorModel)         sct.setEL("colormodel_type", "IndexColorModel");
297                    else if (cm instanceof PackedColorModel)        sct.setEL("colormodel_type", "PackedColorModel");
298                    else sct.setEL("colormodel_type", List.last(cm.getClass().getName(), '.'));
299    
300                    
301                    getMetaData(sctInfo);
302                    
303                    ImageMeta.addInfo(format,source,sctInfo);
304                    
305                    this.sctInfo=sctInfo;
306                    return sctInfo;
307            }
308    
309            public IIOMetadata getMetaData(Struct parent) {
310            InputStream is=null;
311            javax.imageio.stream.ImageInputStreamImpl iis=null;
312            try {
313                    
314                    if(source instanceof File) { 
315                                    iis=new FileImageInputStream((File) source);
316                            }
317                            else if(source==null)iis=new MemoryCacheImageInputStream(new ByteArrayInputStream(getImageBytes(format,true)));
318                            else iis=new MemoryCacheImageInputStream(is=source.getInputStream());
319                            
320                Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
321                if (readers.hasNext()) {
322                    // pick the first available ImageReader
323                    ImageReader reader = readers.next();
324                    IIOMetadata meta=null;
325                    synchronized (sync) {
326                            // attach source to the reader
327                            reader.setInput(iis, true);
328            
329                            // read metadata of first image
330                            meta = reader.getImageMetadata(0);
331                            meta.setFromTree(FORMAT, meta.getAsTree(FORMAT));
332                            reader.reset();
333                    }
334                    // generating dump
335                    if(parent!=null){
336                            String[] formatNames = meta.getMetadataFormatNames();
337                                            for(int i=0;i<formatNames.length;i++) {
338                                                    Node root = meta.getAsTree(formatNames[i]);
339                                                    //print.out(XMLCaster.toString(root));
340                                                    addMetaddata(parent,"metadata",root);
341                                            }
342                    }
343                    return meta;
344                }
345            }
346            catch (Throwable t) {}
347            finally{
348                    ImageUtil.closeEL(iis);
349                            IOUtil.closeEL(is);
350            }
351            return null;
352        }
353    
354            private void addMetaddata(Struct parent, String name, Node node) {
355                    
356                    
357                    // attributes
358                    NamedNodeMap attrs = node.getAttributes();
359                    Attr attr;
360                    int len=attrs.getLength();
361                    if(len==1 && "value".equals(attrs.item(0).getNodeName())) {
362                            parent.setEL(name, attrs.item(0).getNodeValue());
363                    }
364                    else {
365                            Struct sct=metaGetChild(parent,name);
366                            for(int i=attrs.getLength()-1;i>=0;i--) {
367                                    attr=(Attr) attrs.item(i);
368                                    sct.setEL(attr.getName(), attr.getValue());
369                            }
370                    }
371                    
372                    
373                    // child nodes
374                    NodeList children = XMLUtil.getChildNodes(node, Node.ELEMENT_NODE);
375                    Element el;
376                    for(int i=children.getLength()-1;i>=0;i--) {
377                            el=(Element) children.item(i);
378                            Struct sct = metaGetChild(parent,name);
379                            addMetaddata(sct, el.getNodeName(),children.item(i));
380                    }
381            }
382    
383            private Struct metaGetChild(Struct parent, String name) {
384                    Object child=parent.get(name,null);
385                    if(child instanceof Struct) return (Struct) child;
386                    Struct sct=new StructImpl();
387                    parent.setEL(name, sct);
388                    return sct;
389            }
390    
391            public void sharpen(float gain)  throws ExpressionException{
392                    ParameterBlock params = new ParameterBlock();
393                    params.addSource(image());
394                    params.add((Object) null);
395                    params.add(new Float(gain));
396                    image(JAI.create("unsharpmask", params).getAsBufferedImage());
397            }
398            
399            public void setTranparency(float percent)  throws ExpressionException{
400                    if(percent==-1)return;
401                    tranparency=percent;
402                    AlphaComposite rule = AlphaComposite.getInstance(3, 1.0F-(percent/100.0F));
403                    getGraphics().setComposite(rule);
404            }
405    
406             public void invert()  throws ExpressionException{
407                    ParameterBlock params = new ParameterBlock();
408                    params.addSource(image());
409                    image(JAI.create("invert", params).getAsBufferedImage());
410            }
411    
412        public Image copy(float x, float y, float width, float height)  throws ExpressionException{
413            ParameterBlock params = new ParameterBlock();
414            params.addSource(image());
415            params.add(x);
416            params.add(y);
417            params.add(width);
418            params.add(height);
419            //image(JAI.create("crop", params).getAsBufferedImage());
420            return new Image(JAI.create("crop", params).getAsBufferedImage());
421        }
422        
423        public Image copy(float x, float y, float width, float height, float dx,float dy)  throws ExpressionException{
424            Image img = copy(x, y, width, height);
425                    img.getGraphics().copyArea((int)x, (int)y, (int)width, (int)height, (int)(dx-x), (int)(dy-y));
426                    return img;
427        }
428    
429        public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle, boolean filled)  throws ExpressionException{
430            if (filled)
431                    getGraphics().fillArc(x, y, width, height, startAngle, arcAngle);
432            else
433                    getGraphics().drawArc(x, y, width, height, startAngle, arcAngle);
434        }
435        
436    
437        public void draw3DRect(int x, int y, int width, int height, boolean raised, boolean filled)  throws ExpressionException{
438            if (filled)
439                    getGraphics().fill3DRect(x, y, width+1, height+1, raised);
440            else
441                    getGraphics().draw3DRect(x, y, width, height, raised);
442        }
443        
444        public void drawCubicCurve(double ctrlx1, double ctrly1, double ctrlx2, double ctrly2,double x1, double y1, double x2, double y2)  throws ExpressionException{
445            CubicCurve2D curve = new CubicCurve2D.Double(x1,y1,ctrlx1,ctrly1,ctrlx2,ctrly2,x2,y2);
446                    getGraphics().draw(curve);
447        }
448    
449        public void drawPoint(int x, int y) throws ExpressionException {
450            drawLine(x, y, x + 1, y);
451        }
452        
453        public void drawQuadraticCurve(double x1, double y1, double ctrlx, double ctrly, double x2, double y2)  throws ExpressionException {
454            QuadCurve2D curve = new QuadCurve2D.Double(x1, y1, ctrlx, ctrly, x2, y2);
455            getGraphics().draw(curve);
456        }
457        
458        public void drawRect(int x, int y, int width, int height, boolean filled)  throws ExpressionException {
459            if (filled)
460                getGraphics().fillRect(x, y, width + 1, height + 1);
461            else
462                    getGraphics().drawRect(x, y, width, height);
463        }
464        
465        public void drawRoundRect(int x, int y, int width, int height,int arcWidth, int arcHeight, boolean filled)  throws ExpressionException{
466            if (filled)
467                    getGraphics().fillRoundRect(x, y, width + 1, height + 1, arcWidth,arcHeight);
468            else
469                    getGraphics().drawRoundRect(x, y, width, height, arcWidth, arcHeight);
470        }
471        
472    
473    
474            public void drawLine(int x1, int y1, int x2, int y2)  throws ExpressionException{
475            getGraphics().drawLine(x1, y1, x2, y2);
476        }
477    
478            public void drawImage(Image img,int x, int y)  throws ExpressionException{
479            getGraphics().drawImage(img.image(), x, y,null);
480        }
481    
482            public void drawImage(Image img,int x, int y, int width, int height)  throws ExpressionException{
483            getGraphics().drawImage(img.image(), x, y,width,height,null);
484        }
485            
486        public void drawLines(int[] xcoords, int[] ycoords, boolean isPolygon,boolean filled)  throws ExpressionException{
487            if (isPolygon) {
488                    if (filled)     getGraphics().fillPolygon(xcoords, ycoords, xcoords.length);
489                    else            getGraphics().drawPolygon(xcoords, ycoords, xcoords.length);
490            } 
491            else {
492                                            getGraphics().drawPolyline(xcoords, ycoords, xcoords.length);
493            }
494        }
495        public void drawOval(int x, int y, int width, int height, boolean filled) throws ExpressionException {
496            if (filled)     getGraphics().fillOval(x, y, width, height);
497            else getGraphics().drawOval(x, y, width, height);
498            }
499        
500        public void drawString(String text, int x, int y, Struct attr) throws PageException {
501            
502            if (attr != null && attr.size()>0) {
503    
504             // font
505                    String font=StringUtil.toLowerCase(Caster.toString(attr.get("font",""))).trim();
506                if(!StringUtil.isEmpty(font)) {
507                        font=FontUtil.getFont(font).getFontName();
508                }
509                else font = "Serif";
510                
511             // alpha
512                    //float alpha=Caster.toFloatValue(attr.get("alpha",null),1F);
513                
514             // size
515                int size=Caster.toIntValue(attr.get("size", Constants.INTEGER_10));
516    
517             // style
518                int style=Font.PLAIN;
519                String strStyle=StringUtil.toLowerCase(Caster.toString(attr.get("style","")));
520                strStyle=StringUtil.removeWhiteSpace(strStyle);
521                if(!StringUtil.isEmpty(strStyle)) {
522                        if("plain".equals(strStyle)) style=Font.PLAIN;
523                        else if("bold".equals(strStyle)) style=Font.BOLD;
524                        else if("italic".equals(strStyle)) style=Font.ITALIC;
525                        else if("bolditalic".equals(strStyle)) style=Font.BOLD+Font.ITALIC;
526                        else if("bold,italic".equals(strStyle)) style=Font.BOLD+Font.ITALIC;
527                        else if("italicbold".equals(strStyle)) style=Font.BOLD+Font.ITALIC;
528                        else if("italic,bold".equals(strStyle)) style=Font.BOLD+Font.ITALIC;
529                        else throw new ExpressionException(
530                                    "key style of argument attributeCollection has a invalid value ["+strStyle+"], valid values are [plain,bold,italic,bolditalic]");
531                }
532    
533             // strikethrough
534                boolean strikethrough = Caster.toBooleanValue(attr.get("strikethrough",Boolean.FALSE));
535                
536             // underline
537                boolean underline = Caster.toBooleanValue(attr.get("underline",Boolean.FALSE));
538                
539                AttributedString as = new AttributedString(text);
540                as.addAttribute(TextAttribute.FONT, new Font(font, style, size));
541                if(strikethrough)   as.addAttribute(TextAttribute.STRIKETHROUGH,TextAttribute.STRIKETHROUGH_ON);
542                if(underline)               as.addAttribute(TextAttribute.UNDERLINE,TextAttribute.UNDERLINE_ON);
543                Graphics2D g = getGraphics();
544                //if(alpha!=1D) setAlpha(g,alpha);
545                
546                g.drawString(as.getIterator(), x, y);
547            } 
548            else getGraphics().drawString(text, x, y);
549            
550        }
551        
552        
553        /*private void setAlpha(Graphics2D graphics,float alpha) {
554            //Composite originalComposite = graphics.getComposite();
555            
556            AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
557            
558            graphics.setComposite(alphaComposite);
559            //graphics.setComposite(originalComposite);     
560            }*/
561    
562            public void setDrawingStroke(Struct attr) throws PageException {
563            
564            // empty 
565            if(attr==null || attr.size()==0) {
566                    setDrawingStroke(new BasicStroke());
567                    return;
568            }
569            
570            // width
571            float width=Caster.toFloatValue(attr.get("width",new Float(1F)));
572            if(width<0) throw new ExpressionException("key [width] should be a none negativ number");
573            
574            // endcaps
575            String strEndcaps=Caster.toString(attr.get("endcaps","square"));
576            strEndcaps=strEndcaps.trim().toLowerCase();
577            int endcaps;
578            if("square".equals(strEndcaps))         endcaps = BasicStroke.CAP_SQUARE;
579            else if("butt".equals(strEndcaps))      endcaps = BasicStroke.CAP_BUTT;
580            else if("round".equals(strEndcaps))     endcaps = BasicStroke.CAP_ROUND;
581            else throw new ExpressionException("key [endcaps] has a invalid value ["+strEndcaps+"], valid values are [square,round,butt]");
582            
583            // linejoins
584            String strLinejoins=Caster.toString(attr.get("linejoins","miter"));
585            strLinejoins=strLinejoins.trim().toLowerCase();
586            int linejoins;
587            if("bevel".equals(strLinejoins))                linejoins = BasicStroke.JOIN_BEVEL;
588            else if("miter".equals(strLinejoins))   linejoins = BasicStroke.JOIN_MITER;
589            else if("round".equals(strLinejoins))   linejoins = BasicStroke.JOIN_ROUND;
590            else throw new ExpressionException("key [linejoins] has a invalid value ["+strLinejoins+"], valid values are [bevel,miter,round]");
591            
592            // miterlimit
593            float miterlimit = 10.0F;
594            if(linejoins==BasicStroke.JOIN_MITER) {
595                    miterlimit=Caster.toFloatValue(attr.get("miterlimit",new Float(10F)));
596                    if(miterlimit<1F) throw new ExpressionException("key [miterlimit] should be greater or equal to 1");
597            }
598            
599            // dashArray
600            Object oDashArray=attr.get("dashArray",null);
601            float[] dashArray=null;
602            if(oDashArray!=null) {
603                    dashArray=ArrayUtil.toFloatArray(oDashArray);
604            }
605            
606            // dash_phase
607            float dash_phase=Caster.toFloatValue(attr.get("dash_phase",new Float(0F)));
608            
609            
610            
611            setDrawingStroke(width, endcaps, linejoins, miterlimit, dashArray, dash_phase);
612        }
613            
614        public void setDrawingStroke(float width, int endcaps, int linejoins,float miterlimit, float[] dash,float dash_phase)  throws ExpressionException {
615            setDrawingStroke(new BasicStroke(width, endcaps, linejoins, miterlimit, dash, dash_phase));
616            }
617                
618            public void setDrawingStroke(Stroke stroke) throws ExpressionException {
619                    if(stroke==null) return;
620                    this.stroke=stroke;
621                getGraphics().setStroke(stroke);
622            }
623        
624        
625        public void flip(TransposeType transpose) throws ExpressionException {
626            ParameterBlock params = new ParameterBlock();
627            params.addSource(image());
628            params.add(transpose);
629            image(JAI.create("transpose", params).getAsBufferedImage());
630        }
631    
632        public void grayscale() throws ExpressionException {
633            BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_BYTE_GRAY);
634            Graphics2D graphics = img.createGraphics();
635            graphics.drawImage(image(),new AffineTransformOp(AffineTransform.getTranslateInstance(0.0, 0.0),1),0, 0);
636            graphics.dispose();
637            image(img);
638        }
639    
640        public void rgb() throws ExpressionException {
641            BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
642            Graphics2D graphics = img.createGraphics();
643            graphics.drawImage(image(),new AffineTransformOp(AffineTransform.getTranslateInstance(0.0, 0.0),1),0, 0);
644            graphics.dispose();
645            image(img);
646            
647        }
648        public void threeBBger() throws ExpressionException {
649            BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
650            Graphics2D graphics = img.createGraphics();
651            graphics.drawImage(image(),new AffineTransformOp(AffineTransform.getTranslateInstance(0.0, 0.0),1),0, 0);
652            graphics.dispose();
653            image(img);
654        }
655        
656        public void overlay(Image topImage) throws ExpressionException {
657            ParameterBlock params = new ParameterBlock();
658            params.addSource(image());
659            params.addSource(topImage.image());
660            image(JAI.create("overlay", params).getAsBufferedImage());
661        }
662        
663        public void paste(Image topImage, int x, int y) throws ExpressionException {
664            RenderingHints interp = new RenderingHints(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC);
665            BorderExtender extender = BorderExtender.createInstance(1);
666            Graphics2D g = getGraphics();
667            g.addRenderingHints(new RenderingHints(JAI.KEY_BORDER_EXTENDER,extender));
668            g.drawImage(topImage.image(), (new AffineTransformOp(AffineTransform.getTranslateInstance(x,y),interp)), 0, 0);
669            
670        }
671        
672        public void setXorMode(Color color) throws ExpressionException {
673            if(color==null) return;
674            xmColor=color;
675            getGraphics().setXORMode(color);
676        }
677        
678    
679        public void translate(int xtrans, int ytrans, Object interpolation) throws ExpressionException {
680            
681            RenderingHints hints = new RenderingHints(RenderingHints.KEY_INTERPOLATION,interpolation);
682            if(interpolation!=RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) {
683                    hints.add(new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(1)));
684            }
685            
686            ParameterBlock pb = new ParameterBlock();
687            pb.addSource(image());
688            BufferedImage img = JAI.create("translate", pb).getAsBufferedImage();
689            Graphics2D graphics = img.createGraphics();
690            graphics.clearRect(0, 0, img.getWidth(), img.getHeight());
691            AffineTransform at = new AffineTransform();
692            at.setToIdentity();
693            graphics.drawImage(image(), new AffineTransformOp(at, hints), xtrans, ytrans);
694            graphics.dispose();
695            image(img);
696        }
697        
698        public void translateAxis(int x, int y) throws ExpressionException {
699            getGraphics().translate(x, y);
700        }
701    
702        public void rotateAxis(double angle) throws ExpressionException {
703            getGraphics().rotate(Math.toRadians(angle));
704        }
705            
706        public void rotateAxis(double angle, double x, double y) throws ExpressionException {
707            getGraphics().rotate(Math.toRadians(angle), x, y);
708        }
709            
710        public void shearAxis(double shx, double shy) throws ExpressionException {
711            getGraphics().shear(shx, shy);
712        }
713        
714        public void shear(float shear, ShearDir direction, Object interpolation) throws ExpressionException {
715            ParameterBlock params = new ParameterBlock();
716            params.addSource(image());
717            params.add(shear);
718            params.add(direction);
719            params.add(0.0F);
720            params.add(0.0F);
721            RenderingHints hints = null;
722            
723            if (interpolation==RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
724                params.add(Interpolation.getInstance(0));
725            else if (interpolation==RenderingHints.VALUE_INTERPOLATION_BILINEAR) {
726                params.add(Interpolation.getInstance(1));
727                BorderExtender extender = BorderExtender.createInstance(1);
728                hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender);
729            } 
730            else if (interpolation==RenderingHints.VALUE_INTERPOLATION_BICUBIC) {
731                params.add(Interpolation.getInstance(2));
732                BorderExtender extender = BorderExtender.createInstance(1);
733                hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender);
734            }
735            // TODO
736            Color bg = getGraphics().getBackground();
737            params.add(new double[]{bg.getRed(),bg.getGreen(),bg.getBlue()});
738            image(JAI.create("shear", params, hints).getAsBufferedImage());
739        }
740        
741            public BufferedImage getBufferedImage() throws ExpressionException {
742                    return image();
743            }
744            public BufferedImage image() throws ExpressionException {
745                    if(_image==null) throw (new ExpressionException("image is not initalized"));
746                    return _image;
747            }
748            public void image(BufferedImage image) {
749                    this._image=image;
750                    graphics=null;
751                    
752                    sctInfo=null;
753            }
754            
755        private Graphics2D getGraphics() throws ExpressionException {
756                    if(graphics==null) {
757                            graphics=image() .createGraphics();
758                            // reset all properties
759                            if(antiAlias!=ANTI_ALIAS_NONE)  setAntiAliasing(antiAlias==ANTI_ALIAS_ON);
760                            if(bgColor!=null)       setBackground(bgColor);
761                            if(fgColor!=null)       setColor(fgColor);
762                            if(alpha!=1)            setAlpha(alpha);
763                            if(tranparency!=-1)     setTranparency(tranparency);
764                            if(xmColor!=null)       setXorMode(xmColor);
765                            if(stroke!=null)        setDrawingStroke(stroke);
766                    }
767            return graphics;
768            }
769        
770        
771            private String toStringColorSpace(ColorSpace colorSpace) {
772                    switch (colorSpace.getType()) {
773                case 0: return "Any of the family of XYZ color spaces";
774                case 1: return "Any of the family of Lab color spaces";
775                case 2: return "Any of the family of Luv color spaces";
776                case 3: return "Any of the family of YCbCr color spaces";
777                case 4:return "Any of the family of Yxy color spaces";
778                case 5: return "Any of the family of RGB color spaces";
779                case 6: return "Any of the family of GRAY color spaces";
780                case 7: return "Any of the family of HSV color spaces";
781                case 8: return "Any of the family of HLS color spaces";
782                case 9: return "Any of the family of CMYK color spaces";
783                case 11: return "Any of the family of CMY color spaces";
784                case 12: return "Generic 2 component color space.";
785                case 13: return "Generic 3 component color space.";
786                case 14: return "Generic 4 component color space.";
787                case 15: return "Generic 5 component color space.";
788                case 16: return "Generic 6 component color space.";
789                case 17: return "Generic 7 component color space.";
790                case 18: return "Generic 8 component color space.";
791                case 19: return "Generic 9 component color space.";
792                case 20: return "Generic 10 component color space.";
793                case 21: return "Generic 11 component color space.";
794                case 22: return "Generic 12 component color space.";
795                case 23: return "Generic 13 component color space.";
796                case 24: return "Generic 14 component color space.";
797                case 25: return "Generic 15 component color space.";
798                case 1001: return "CIEXYZ";
799                case 1003: return "GRAY";
800                case 1004: return "LINEAR_RGB";
801                case 1002: return "PYCC";
802                case 1000: return "sRGB";
803                }
804                    
805                    return "Unknown ColorSpace" + colorSpace;
806            }
807    
808            private Object toStringTransparency(int transparency) {
809                    if(Transparency.OPAQUE==transparency)           return "OPAQUE";
810                    if(Transparency.BITMASK==transparency)          return "BITMASK";
811                    if(Transparency.TRANSLUCENT==transparency)      return "TRANSLUCENT";
812                    return "Unknown type of transparency";
813            }
814            
815            public String writeBase64(Resource destination, String format, boolean inHTMLFormat) throws PageException, IOException {
816                    // destination
817                    if(destination==null) {
818                            if(source!=null)destination=source;
819                            else throw new IOException("missing destination file");
820                    }
821                    
822                    String content = getBase64String(format);
823                    if(inHTMLFormat) content="data:image/" + format + ";base64,"+content;
824                    IOUtil.write(destination, content, null, false);
825                    return content;
826            }
827            
828            public String getBase64String(String format) throws PageException {
829                    byte[] imageBytes = getImageBytes(format);
830                    return new String(Base64.encodeBase64(imageBytes));
831            }
832            
833            public void writeOut(Resource destination, boolean overwrite, float quality) throws IOException, ExpressionException {
834                    String format = ImageUtil.getFormatFromExtension(destination,null);
835                    writeOut(destination, format, overwrite, quality);
836            }
837            
838            public void writeOut(Resource destination, String format,boolean overwrite, float quality) throws IOException, ExpressionException {
839                    if(destination==null) {
840                            if(source!=null)destination=source;
841                            else throw new IOException("missing destination file");
842                    }
843                    
844                    if(destination.exists()) {
845                            if(!overwrite)throw new IOException("can't overwrite existing image");
846                    }
847    
848            if(JAIUtil.isSupportedWriteFormat(format)){
849                    JAIUtil.write(getBufferedImage(),destination,format);
850                    return;
851            }
852                    OutputStream os=null;
853                    ImageOutputStream ios = null;
854                    try {
855                            os=destination.getOutputStream();
856                            ios = ImageIO.createImageOutputStream(os);
857                            _writeOut(ios, format, quality);
858                    }
859                    finally {
860                            ImageUtil.closeEL(ios);
861                            IOUtil.closeEL(os);
862                    }               
863            }
864    
865            
866            public static void writeOutGif(BufferedImage src, OutputStream os) throws IOException {
867                    BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_ARGB);
868                    QuantizeFilter filter=new QuantizeFilter();
869                    filter.setSerpentine(true);
870                    filter.setDither(true);
871                    //filter.setNumColors(8);
872                    filter.filter(src, dst);
873                    
874    
875                    //image(Quantizer.quantize(image(), 8));
876                    try {
877                            GifEncoder enc = new GifEncoder(dst);
878                            enc.Write(os);
879                            os.flush();
880                    } catch (AWTException e) {
881                            throw new IOException(e.getMessage());
882                    }
883            }
884            
885            public void writeOut(OutputStream os, String format,float quality, boolean closeStream) throws IOException, ExpressionException {
886                    ImageOutputStream ios = ImageIO.createImageOutputStream(os);
887                    try{
888                            _writeOut(ios, format, quality);
889                    }
890                    finally{
891                            IOUtil.closeEL(ios);
892                    }
893            }
894    
895            private void _writeOut(ImageOutputStream ios, String format,float quality) throws IOException, ExpressionException {
896                    _writeOut(ios, format, quality, false);
897            }
898            
899            private void _writeOut(ImageOutputStream ios, String format,float quality,boolean noMeta) throws IOException, ExpressionException {
900                    if(quality<0 || quality>1)
901                            throw new IOException("quality has a invalid value ["+quality+"], value has to be between 0 and 1");
902                    if(StringUtil.isEmpty(format))  format=this.format;
903                    if(StringUtil.isEmpty(format))  throw new IOException("missing format");
904                    
905                    BufferedImage im = image();
906                    
907                    //IIOMetadata meta = noMeta?null:metadata(format);
908                    IIOMetadata meta = noMeta?null:getMetaData(null);
909                    
910                    
911                    
912                    ImageWriter writer = null;
913            ImageTypeSpecifier type =ImageTypeSpecifier.createFromRenderedImage(im);
914            Iterator<ImageWriter> iter = ImageIO.getImageWriters(type, format);
915            
916            
917            if (iter.hasNext()) {
918                    writer = (ImageWriter)iter.next();
919            }
920            if (writer == null) throw new IOException("no writer for format ["+format+"] available, available writer formats are ["+List.arrayToList(ImageUtil.getWriterFormatNames(), ",")+"]");
921            
922            
923                    ImageWriteParam iwp=null;
924            if("jpg".equalsIgnoreCase(format)) {
925                    ColorModel cm = im.getColorModel();
926                    if(cm.hasAlpha())im=jpgImage(im);
927                    JPEGImageWriteParam jiwp = new JPEGImageWriteParam(Locale.getDefault());
928                    jiwp.setOptimizeHuffmanTables(true);
929                    iwp=jiwp;
930            }
931            else iwp = writer.getDefaultWriteParam();
932            
933                    setCompressionModeEL(iwp,ImageWriteParam.MODE_EXPLICIT);
934            setCompressionQualityEL(iwp,quality);
935            writer.setOutput(ios);
936            try {
937                    writer.write(meta, new IIOImage(im, null, meta), iwp);
938                    
939            } 
940            finally {
941                    writer.dispose();
942                    ios.flush();
943            }
944            }
945            
946            private BufferedImage jpgImage(BufferedImage src) {
947            int w = src.getWidth();
948            int h = src.getHeight();
949            SampleModel srcSM = src.getSampleModel();
950            WritableRaster srcWR = src.getRaster();
951            java.awt.image.DataBuffer srcDB = srcWR.getDataBuffer();
952            
953            ColorModel rgb = new DirectColorModel(32, 0xff0000, 65280, 255);
954            int[] bitMasks = new int[]{0xff0000, 65280, 255};
955            
956            SampleModel csm = new SinglePixelPackedSampleModel(3, w, h, bitMasks);
957            int data[] = new int[w * h];
958            for(int i = 0; i < h; i++) {
959                for(int j = 0; j < w; j++) {
960                    int pix[] = null;
961                    int sample[] = srcSM.getPixel(j, i, pix, srcDB);
962                    if(sample[3] == 0 && sample[2] == 0 && sample[1] == 0 && sample[0] == 0)
963                        data[i * w + j] = 0xffffff;
964                    else
965                        data[i * w + j] = sample[0] << 16 | sample[1] << 8 | sample[2];
966                }
967    
968            }
969    
970            java.awt.image.DataBuffer db = new DataBufferInt(data, w * h * 3);
971            WritableRaster wr = Raster.createWritableRaster(csm, db, new Point(0, 0));
972            return new BufferedImage(rgb, wr, false, null);
973        }
974    
975            private void setCompressionModeEL(ImageWriteParam iwp, int mode) {
976                    try {
977                            iwp.setCompressionMode(mode);
978                    }
979                    catch(Throwable t) {}
980            }
981    
982            private void setCompressionQualityEL(ImageWriteParam iwp, float quality) {
983                    try {
984                            iwp.setCompressionQuality(quality);
985                    }
986                    catch(Throwable t) {}
987            }
988    
989            public void convert(String format) {
990                    this.format=format;
991            }
992    
993            public void scaleToFit(String fitWidth, String fitHeight,String interpolation, double blurFactor) throws PageException {
994                    if (StringUtil.isEmpty(fitWidth) || StringUtil.isEmpty(fitHeight))      
995                            resize(fitWidth, fitHeight, interpolation, blurFactor);
996                    else {
997                            float width = Caster.toFloatValue(fitWidth) / getWidth();
998                            float height= Caster.toFloatValue(fitHeight) / getHeight();
999                            if (width < height)  resize(fitWidth, "", interpolation, blurFactor);
1000                            else                            resize("", fitHeight, interpolation, blurFactor);
1001                    }
1002            }
1003            
1004            
1005            
1006            /**
1007         * Convenience method that returns a scaled instance of the
1008         * provided {@code BufferedImage}.
1009         *
1010         * @param img the original image to be scaled
1011         * @param targetWidth the desired width of the scaled instance,
1012         *    in pixels
1013         * @param targetHeight the desired height of the scaled instance,
1014         *    in pixels
1015         * @param hint one of the rendering hints that corresponds to
1016         *    {@code RenderingHints.KEY_INTERPOLATION} (e.g.
1017         *    {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
1018         *    {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
1019         *    {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
1020         * @param higherQuality if true, this method will use a multi-step
1021         *    scaling technique that provides higher quality than the usual
1022         *    one-step technique (only useful in downscaling cases, where
1023         *    {@code targetWidth} or {@code targetHeight} is
1024         *    smaller than the original dimensions, and generally only when
1025         *    the {@code BILINEAR} hint is specified)
1026         * @return a scaled version of the original {@code BufferedImage}
1027         */
1028        private BufferedImage getScaledInstance(BufferedImage img,
1029                                               int targetWidth,
1030                                               int targetHeight,
1031                                               Object hint,
1032                                               boolean higherQuality)
1033        {
1034            // functionality not supported in java 1.4
1035            int transparency=Transparency.OPAQUE;
1036            try {
1037                            transparency=img.getTransparency();
1038                    } 
1039            catch (Throwable t) {}
1040            int type = (transparency == Transparency.OPAQUE) ?BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
1041            
1042            
1043            
1044            BufferedImage ret = img;
1045            int w, h;
1046            if (higherQuality) {
1047                // Use multi-step technique: start with original size, then
1048                // scale down in multiple passes with drawImage()
1049                // until the target size is reached
1050                w = img.getWidth();
1051                h = img.getHeight();
1052            } else {
1053                // Use one-step technique: scale directly from original
1054                // size to target size with a single drawImage() call
1055                w = targetWidth;
1056                h = targetHeight;
1057            }
1058            
1059            do {
1060                if (higherQuality && w > targetWidth) {
1061                    w /= 2;
1062                    if (w < targetWidth) {
1063                        w = targetWidth;
1064                    }
1065                }
1066    
1067                if (higherQuality && h > targetHeight) {
1068                    h /= 2;
1069                    if (h < targetHeight) {
1070                        h = targetHeight;
1071                    }
1072                }
1073    
1074                BufferedImage tmp = new BufferedImage(w, h, type);
1075                Graphics2D g2 = tmp.createGraphics();
1076                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
1077                g2.drawImage(ret, 0, 0, w, h, null);
1078                g2.dispose();
1079    
1080                ret = tmp;
1081            } while (w != targetWidth || h != targetHeight);
1082    
1083            return ret;
1084        }
1085    
1086            
1087            
1088            
1089            
1090        public void resize(int scale, String interpolation, double blurFactor) throws PageException {
1091            if (blurFactor <= 0.0 || blurFactor > 10.0)
1092                            throw new ExpressionException("blurFactor must be between 0 and 10");
1093            
1094                    float width=getWidth()/100F*scale;
1095                    float height=getHeight()/100F*scale;
1096                            
1097                    resize((int)width, (int)height, toInterpolation(interpolation), blurFactor);
1098        }
1099            
1100        public void resize(String strWidth, String strHeight, String interpolation, double blurFactor) throws PageException {
1101                    if (StringUtil.isEmpty(strWidth,true) && StringUtil.isEmpty(strHeight,true))
1102                            throw new ExpressionException("you have to define width or height");
1103                    if (blurFactor <= 0.0 || blurFactor > 10.0)
1104                            throw new ExpressionException("blurFactor must be between 0 and 10");
1105                    int w = getWidth();
1106                    int h = getHeight();
1107                    float height=resizeDimesion("height",strHeight, h);
1108                    float width=resizeDimesion("width",strWidth, w);
1109                    
1110                    if(height==-1)  height=h*(width/w);
1111                    if(width==-1)   width=w*(height/h);
1112                            
1113                    resize((int)width, (int)height, toInterpolation(interpolation), blurFactor);
1114        }
1115            
1116        public void resizeImage2(int width, int height) throws ExpressionException{
1117            image(getScaledInstance(image(),width,height,RenderingHints.VALUE_INTERPOLATION_BILINEAR,false));
1118        }
1119            
1120        public void resizeImage(int width, int height, int interpolation) throws ExpressionException{
1121            Object ip;
1122            if(interpolation==IPC_NEAREST)          ip=RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
1123            else if(interpolation==IPC_BICUBIC)     ip=RenderingHints.VALUE_INTERPOLATION_BICUBIC;
1124            else if(interpolation==IPC_BILINEAR)    ip=RenderingHints.VALUE_INTERPOLATION_BILINEAR;
1125            else throw new ExpressionException("invalid interpoltion definition");
1126            
1127            BufferedImage dst = new BufferedImage(width,height,image().getType());
1128            Graphics2D graphics = dst.createGraphics();
1129            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,ip); 
1130            graphics.drawImage(image(), 0, 0, width, height, null);
1131            graphics.dispose();
1132            image(dst);
1133            
1134        }
1135            
1136            private float resizeDimesion(String label,String strDimension, float originalDimension) throws PageException {
1137                    if (StringUtil.isEmpty(strDimension,true)) return -1;
1138                    strDimension=strDimension.trim();
1139                    
1140                    if (StringUtil.endsWith(strDimension, '%')) {
1141                            float p = Caster.toFloatValue(strDimension.substring(0,(strDimension.length()- 1))) / 100.0F;
1142                            return originalDimension*p;
1143                    }
1144                    float dimension = Caster.toFloatValue(strDimension);
1145                    if (dimension <= 0F)
1146                            throw new ExpressionException(label+" has to be a none negative number");
1147                    return dimension;
1148            }
1149            
1150            
1151    
1152        public void resize(int width, int height, int interpolation, double blurFactor) throws ExpressionException {
1153            
1154                    ColorModel cm = image().getColorModel();
1155                    
1156                if (interpolation==IP_HIGHESTPERFORMANCE)   {
1157                    interpolation = IPC_BICUBIC;
1158                }
1159                
1160                if (cm.getColorSpace().getType() == ColorSpace.TYPE_GRAY && cm.getComponentSize()[0] == 8) {
1161                    if (interpolation==IP_HIGHESTQUALITY || interpolation==IP_HIGHPERFORMANCE || interpolation==IP_HIGHQUALITY || interpolation==IP_MEDIUMPERFORMANCE || interpolation==IP_MEDIUMQUALITY)   {
1162                            interpolation = IPC_BICUBIC;
1163                    }
1164                    if (interpolation!=IPC_BICUBIC && interpolation!=IPC_BILINEAR && interpolation!=IPC_NEAREST)    {
1165                            throw new ExpressionException("invalid grayscale interpolation");
1166                    }
1167                }
1168                
1169                if (interpolation<=IPC_MAX)      {
1170                    resizeImage(width, height, interpolation);
1171                }
1172                else {
1173                    image(ImageResizer.resize(image(), width, height, interpolation, blurFactor));
1174                            
1175                }
1176            }
1177        
1178        
1179        
1180        /*private BufferedImage resizeImageWithJAI(float scaleWidth, float scaleHeight, int interpolation) throws ExpressionException {
1181            ParameterBlock params = new ParameterBlock();
1182            params.addSource(image());
1183                    params.add(scaleWidth);
1184                    params.add(scaleHeight);
1185                    params.add(0.0F);
1186                    params.add(0.0F);
1187                    RenderingHints hints = null;
1188                    if (interpolation != IP_NONE) {
1189                        if (interpolation==IP_NEAREST)      {
1190                            params.add(Interpolation.getInstance(0));
1191                        }
1192                        else if (interpolation==IP_BILINEAR) {
1193                            params.add(Interpolation.getInstance(1));
1194                            BorderExtender extender = BorderExtender.createInstance(1);
1195                            hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender);
1196                        } 
1197                        else if (interpolation==IP_BICUBIC) {
1198                            params.add(Interpolation.getInstance(2));
1199                            BorderExtender extender = BorderExtender.createInstance(1);
1200                            hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender);
1201                        } 
1202                        else        {
1203                            throw new ExpressionException("invalid interpolation definition");
1204                        }
1205                    }
1206                    return JAI.create("scale", params, hints).getAsBufferedImage();
1207        }*/
1208            
1209        private double toScale(int src, int dst) {
1210            double tmp = Math.round((int)((Caster.toDoubleValue(dst)/Caster.toDoubleValue(src))*100D));
1211            return tmp/100D;
1212            }
1213    
1214            public void rotate(float x, float y, float angle, int interpolation) throws ExpressionException {
1215            if(x==-1)x = (float)getWidth() / 2;
1216            if(y==-1)y = (float)getHeight() / 2;
1217                    
1218            angle = (float) Math.toRadians(angle);
1219            ColorModel cmSource = image().getColorModel();
1220            
1221            if (cmSource instanceof IndexColorModel && cmSource.hasAlpha() && !cmSource.isAlphaPremultiplied()) {
1222                    image(PaletteToARGB(image()));
1223                cmSource = image().getColorModel();
1224            }
1225            
1226            BufferedImage alpha = null;
1227            if (cmSource.hasAlpha() && !cmSource.isAlphaPremultiplied()) {
1228                alpha = getAlpha(image());
1229                image(removeAlpha(image()));
1230            }
1231            
1232            Interpolation interp = Interpolation.getInstance(0);
1233            if (INTERPOLATION_BICUBIC==interpolation)       interp = Interpolation.getInstance(1);
1234            else if (INTERPOLATION_BILINEAR==interpolation)         interp = Interpolation.getInstance(2);
1235            
1236            if (alpha != null) {
1237                ParameterBlock params = new ParameterBlock();
1238                params.addSource(alpha);
1239                params.add(x);
1240                params.add(y);
1241                params.add(angle);
1242                params.add(interp);
1243                params.add(new double[] { 0.0 });
1244                RenderingHints hints= new RenderingHints(RenderingHints.KEY_INTERPOLATION,(RenderingHints.VALUE_INTERPOLATION_BICUBIC));
1245                hints.add(new RenderingHints(JAI.KEY_BORDER_EXTENDER,new BorderExtenderConstant(new double[] { 255.0 })));
1246                hints.add(new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL,Boolean.TRUE));
1247                alpha = JAI.create("rotate", params, hints).getAsBufferedImage();
1248            }
1249            
1250            ParameterBlock params = new ParameterBlock();
1251            params.addSource(image());
1252            params.add(x);
1253            params.add(y);
1254            params.add(angle);
1255            params.add(interp);
1256            params.add(new double[] { 0.0 });
1257            BorderExtender extender= new BorderExtenderConstant(new double[] { 0.0 });
1258            RenderingHints hints= new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender);
1259            hints.add(new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE));
1260            image(JAI.create("rotate", params, hints).getAsBufferedImage());
1261            if (alpha != null)image(addAlpha(image(), alpha, 0, 0));
1262        }
1263        
1264        private static BufferedImage PaletteToARGB(BufferedImage src) {
1265            IndexColorModel icm = (IndexColorModel) src.getColorModel();
1266            int bands = icm.hasAlpha()?4:3;
1267            
1268            byte[][] data = new byte[bands][icm.getMapSize()];
1269            if (icm.hasAlpha()) icm.getAlphas(data[3]);
1270            icm.getReds(data[0]);
1271            icm.getGreens(data[1]);
1272            icm.getBlues(data[2]);
1273            LookupTableJAI rtable = new LookupTableJAI(data);
1274            return JAI.create("lookup", src, rtable).getAsBufferedImage();
1275        }
1276    
1277        
1278        private static BufferedImage getAlpha(BufferedImage src) {
1279            return JAI.create("bandselect", src, new int[] { 3 }).getAsBufferedImage();
1280        }
1281        
1282        private static BufferedImage removeAlpha(BufferedImage src) {
1283            return JAI.create("bandselect", src, new int[] { 0, 1, 2 }).getAsBufferedImage();
1284        }
1285        
1286        private static BufferedImage addAlpha(BufferedImage src, BufferedImage alpha, int x, int y) {
1287            int w = src.getWidth();
1288            int h = src.getHeight();
1289            BufferedImage bi = new BufferedImage(w, h, 2);
1290            WritableRaster wr = bi.getWritableTile(0, 0);
1291            WritableRaster wr3 = wr.createWritableChild(0, 0, w, h, 0, 0, new int[] { 0, 1, 2 });
1292            WritableRaster wr1 = wr.createWritableChild(0, 0, w, h, 0, 0, new int[] { 3 });
1293            wr3.setRect(src.getData());
1294            wr1.setRect(alpha.getData());
1295            bi.releaseWritableTile(0, 0);
1296            return bi;
1297        }
1298    
1299            
1300    
1301            public void _rotate(float x, float y, float angle, String interpolation) throws ExpressionException {
1302    
1303                    float radiansAngle = (float)Math.toRadians(angle);
1304    
1305                    // rotation center
1306                    float centerX = (float)getWidth() / 2;
1307                    float centerY = (float)getHeight() / 2;
1308                    
1309                    ParameterBlock pb = new ParameterBlock();
1310                    pb.addSource(image());
1311                    pb.add(centerX);
1312                    pb.add(centerY);
1313                    pb.add(radiansAngle);
1314                    pb.add(new javax.media.jai.InterpolationBicubic(10));
1315                    
1316                    // create a new, rotated image
1317                    image(JAI.create("rotate", pb).getAsBufferedImage());
1318    
1319            }
1320    
1321            public static Image toImage(Object obj) throws PageException {
1322                    if(obj instanceof Image) return (Image) obj;
1323                    if(obj instanceof ObjectWrap) return toImage(((ObjectWrap)obj).getEmbededObject());
1324                    throw new CasterException(obj,"Image");
1325            }
1326            
1327            public static boolean isImage(Object obj) {
1328                    if(obj instanceof Image) return true;
1329                    if(obj instanceof ObjectWrap) return isImage(((ObjectWrap)obj).getEmbededObject(""));
1330                    return false;
1331            }
1332    
1333            public static Image createImage(PageContext pc,Object obj, boolean check4Var, boolean clone, boolean checkAccess) throws PageException {
1334                    try {
1335                            if(obj instanceof String || obj instanceof Resource || obj instanceof File) {
1336                                    try {
1337                                            Resource res = Caster.toResource(obj);
1338                                            pc.getConfig().getSecurityManager().checkFileLocation(res);
1339                                            return new Image(res);
1340                                    } 
1341                                    catch (ExpressionException ee) {
1342                                            if(check4Var && Decision.isVariableName(Caster.toString(obj))) {
1343                                                    try {
1344                                                            return createImage(pc, pc.getVariable(Caster.toString(obj)), false,clone,checkAccess);
1345                                                    }
1346                                                    catch (Throwable t) {
1347                                                            throw ee;
1348                                                    }
1349                                            }
1350                                            try {
1351                                                    return new Image(Caster.toString(obj));
1352                                            }
1353                                            catch (Throwable t) {
1354                                                    throw ee;
1355                                            }
1356                                    }
1357                            }
1358                            if(obj instanceof Image)        {
1359                                    if(clone)return (Image) ((Image)obj).clone();
1360                                    return (Image)obj;
1361                            }
1362                            if(Decision.isBinary(obj))                      return new Image(Caster.toBinary(obj));
1363                            if(obj instanceof BufferedImage)        return new Image(((BufferedImage) obj));
1364                            if(obj instanceof java.awt.Image)       return new Image(toBufferedImage((java.awt.Image) obj));
1365                            
1366                    } catch (Throwable t) {
1367                            throw Caster.toPageException(t);
1368                    }
1369                    throw new CasterException(obj,"Image");
1370            }
1371    
1372            /**
1373             *
1374             * @see railo.runtime.type.Collection#duplicate(boolean)
1375             */
1376            public Collection duplicate(boolean deepCopy) {
1377                    try {
1378                            //if(_image!=null) return new Image(getBufferedImage());
1379                            return new Image(getImageBytes(null));
1380                            
1381                    } catch (Exception e) {
1382                            throw new PageRuntimeException(e.getMessage());
1383                    }
1384            }
1385            
1386            
1387            
1388    
1389            public ColorModel getColorModel() throws ExpressionException {
1390                    return image().getColorModel();
1391            }
1392        
1393        public void crop(float x, float y, float width, float height) throws ExpressionException {
1394            ParameterBlock params = new ParameterBlock();
1395            params.addSource(image());
1396            params.add(x);
1397            params.add(y);
1398    
1399            float w = getWidth();
1400            float h = getHeight();
1401            
1402            if (w < x + width) params.add(w - x);
1403            else params.add(width);
1404            
1405            if (h < y + height) params.add(h - y);
1406            else params.add(height);
1407            
1408            image(JAI.create("crop", params).getAsBufferedImage());
1409        }
1410        
1411        public int getWidth() throws ExpressionException {
1412            return image().getWidth();
1413        }
1414        
1415        public int getHeight() throws ExpressionException {
1416            return image().getHeight();
1417        }
1418    
1419            public String getFormat() {
1420                    return format;
1421            }
1422    
1423            public byte[] getImageBytes(String format) throws PageException{
1424                    return getImageBytes(format,false);
1425            }
1426            public byte[] getImageBytes(String format,boolean noMeta) throws PageException {
1427                    
1428                    
1429                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
1430                    
1431                    if(JAIUtil.isSupportedWriteFormat(format)){
1432                    try {
1433                                    JAIUtil.write(getBufferedImage(),baos,format);
1434                            }catch (IOException e) {
1435                                    throw Caster.toPageException(e);
1436                            }
1437            }
1438                    else {
1439                            ImageOutputStream ios = null;
1440                            try {
1441                                    ios = ImageIO.createImageOutputStream(baos);
1442                                    _writeOut(ios, format, 1,noMeta);
1443                            } catch (IOException e) {
1444                                    throw Caster.toPageException(e);
1445                            }
1446                            finally {
1447                                    IOUtil.closeEL(ios);
1448                            }
1449                    }
1450                    return baos.toByteArray();
1451            }
1452    
1453            public void setColor(Color color) throws ExpressionException {
1454                    if(color==null) return;
1455                    fgColor=color;
1456                    getGraphics().setColor(color);
1457            }
1458            
1459            public void setAlpha(float alpha) throws ExpressionException {
1460                    this.alpha=alpha;
1461                    Graphics2D g = getGraphics();
1462                    
1463                    Composite alphaComposite;
1464                    if(composite==null) {
1465                            if(alpha==1) return;
1466                            composite = g.getComposite();
1467                    }
1468                    if(alpha==1) alphaComposite=composite;
1469                    else alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
1470            
1471            g.setComposite(alphaComposite);
1472            //graphics.setComposite(originalComposite);     
1473            }       
1474            
1475            public void setBackground(Color color) throws ExpressionException {
1476                    if(color==null) return;
1477                    bgColor=color;
1478                    getGraphics().setBackground(color);
1479            }
1480            
1481            public void setAntiAliasing(boolean antiAlias) throws ExpressionException {
1482                    this.antiAlias=antiAlias?ANTI_ALIAS_ON:ANTI_ALIAS_OFF;
1483                    Graphics2D graphics = getGraphics();
1484                    if(antiAlias) {
1485                        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1486                        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
1487                    }
1488                    else {
1489                        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
1490                        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
1491                    } 
1492            }
1493            
1494            private Struct _info() {
1495                    try {
1496                            return info();
1497                    } catch (ExpressionException e) {
1498                            throw new PageRuntimeException(e);
1499                    }
1500            }
1501    
1502            /**
1503             *
1504             * @see railo.runtime.type.Collection#clear()
1505             */
1506            public void clear() {
1507                    throw new RuntimeException("can't clear struct, struct is readonly");
1508            }
1509    
1510            /**
1511             *
1512             * @see railo.runtime.type.Collection#containsKey(railo.runtime.type.Collection.Key)
1513             */
1514            public boolean containsKey(Key key) {
1515                    return _info().containsKey(key);
1516            }
1517    
1518            /**
1519             *
1520             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key)
1521             */
1522            public Object get(Key key) throws PageException {
1523                    return info().get(key);
1524            }
1525    
1526            /**
1527             *
1528             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key, java.lang.Object)
1529             */
1530            public Object get(Key key, Object defaultValue) {
1531                    return _info().get(key, defaultValue);
1532            }
1533    
1534            /**
1535             *
1536             * @see railo.runtime.type.Collection#keys()
1537             */
1538            public Key[] keys() {
1539                    return _info().keys();
1540            }
1541    
1542            /**
1543             *
1544             * @see railo.runtime.type.Collection#keysAsString()
1545             */
1546            public String[] keysAsString() {
1547                    return _info().keysAsString();
1548            }
1549    
1550            /**
1551             *
1552             * @see railo.runtime.type.Collection#remove(railo.runtime.type.Collection.Key)
1553             */
1554            public Object remove(Key key) throws PageException {
1555                    throw new ExpressionException("can't remove key ["+key.getString()+"] from struct, struct is readonly");
1556            }
1557    
1558            /**
1559             *
1560             * @see railo.runtime.type.Collection#removeEL(railo.runtime.type.Collection.Key)
1561             */
1562            public Object removeEL(Key key) {
1563                    throw new PageRuntimeException("can't remove key ["+key.getString()+"] from struct, struct is readonly");
1564            }
1565    
1566            /**
1567             *
1568             * @see railo.runtime.type.Collection#set(railo.runtime.type.Collection.Key, java.lang.Object)
1569             */
1570            public Object set(Key key, Object value) throws PageException {
1571                    throw new ExpressionException("can't set key ["+key.getString()+"] to struct, struct is readonly");
1572            }
1573    
1574            /**
1575             *
1576             * @see railo.runtime.type.Collection#setEL(railo.runtime.type.Collection.Key, java.lang.Object)
1577             */
1578            public Object setEL(Key key, Object value) {
1579                    throw new PageRuntimeException("can't set key ["+key.getString()+"] to struct, struct is readonly");
1580            }
1581    
1582            /**
1583             *
1584             * @see railo.runtime.type.Collection#size()
1585             */
1586            public int size() {
1587                    return _info().size();
1588            }
1589    
1590            /**
1591             *
1592             * @see railo.runtime.dump.Dumpable#toDumpData(railo.runtime.PageContext, int)
1593             */
1594            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
1595                    DumpData dd = _info().toDumpData(pageContext, maxlevel,dp);
1596                    if(dd instanceof DumpTable)((DumpTable)dd).setTitle("Struct (Image)");
1597                    return dd;
1598            }
1599    
1600            /**
1601             *
1602             * @see railo.runtime.type.Iteratorable#keyIterator()
1603             */
1604            public Iterator keyIterator() {
1605                    return _info().keyIterator();
1606            }
1607    
1608            /**
1609             *
1610             * @see railo.runtime.op.Castable#castToBooleanValue()
1611             */
1612            public boolean castToBooleanValue() throws PageException {
1613                    return info().castToBooleanValue();
1614            }
1615    
1616            /**
1617             * @see railo.runtime.type.util.StructSupport#castToBoolean(java.lang.Boolean)
1618             */
1619            public Boolean castToBoolean(Boolean defaultValue) {
1620                    try {
1621                            return info().castToBoolean(defaultValue);
1622                    } catch (ExpressionException e) {
1623                            return defaultValue;
1624                    }
1625            }
1626    
1627            /**
1628             *
1629             * @see railo.runtime.op.Castable#castToDateTime()
1630             */
1631            public DateTime castToDateTime() throws PageException {
1632                    return info().castToDateTime();
1633            }
1634        
1635        /**
1636         * @see railo.runtime.op.Castable#castToDateTime(railo.runtime.type.dt.DateTime)
1637         */
1638        public DateTime castToDateTime(DateTime defaultValue) {
1639            try {
1640                            return info().castToDateTime(defaultValue);
1641                    } catch (ExpressionException e) {
1642                            return defaultValue;
1643                    }
1644        }
1645    
1646            /**
1647             *
1648             * @see railo.runtime.op.Castable#castToDoubleValue()
1649             */
1650            public double castToDoubleValue() throws PageException {
1651                    return info().castToDoubleValue();
1652            }
1653        
1654        /**
1655         * @see railo.runtime.op.Castable#castToDoubleValue(double)
1656         */
1657        public double castToDoubleValue(double defaultValue) {
1658            try {
1659                            return info().castToDoubleValue(defaultValue);
1660                    } catch (ExpressionException e) {
1661                            return defaultValue;
1662                    }
1663        }
1664    
1665            /**
1666             *
1667             * @see railo.runtime.op.Castable#castToString()
1668             */
1669            public String castToString() throws PageException {
1670                    return info().castToString();
1671            }
1672            /**
1673             * @see railo.runtime.type.util.StructSupport#castToString(java.lang.String)
1674             */
1675            public String castToString(String defaultValue) {
1676                    try {
1677                            return info().castToString(defaultValue);
1678                    } catch (ExpressionException e) {
1679                            return defaultValue;
1680                    }
1681            }
1682    
1683            /**
1684             *
1685             * @see railo.runtime.op.Castable#compareTo(java.lang.String)
1686             */
1687            public int compareTo(String str) throws PageException {
1688                    return info().compareTo(str);
1689            }
1690    
1691            /**
1692             *
1693             * @see railo.runtime.op.Castable#compareTo(boolean)
1694             */
1695            public int compareTo(boolean b) throws PageException {
1696                    return info().compareTo(b);
1697            }
1698    
1699            /**
1700             *
1701             * @see railo.runtime.op.Castable#compareTo(double)
1702             */
1703            public int compareTo(double d) throws PageException {
1704                    return info().compareTo(d);
1705            }
1706    
1707            /**
1708             *
1709             * @see railo.runtime.op.Castable#compareTo(railo.runtime.type.dt.DateTime)
1710             */
1711            public int compareTo(DateTime dt) throws PageException {
1712                    return info().compareTo(dt);
1713            }
1714    
1715            private static void checkRestriction()  {
1716                    try {
1717                            if(JAI.class==null) return;
1718                    }
1719                    catch(Throwable t) {
1720                            throw new PageRuntimeException("the JAI extension is missing, please download [railo-x.x.x.xxx-jars.zip] on http://www.railo-technologies.com/en/download and copy it into the railo lib directory");
1721                    }       
1722            }
1723            
1724            public static int toInterpolation(String strInterpolation) throws ExpressionException {
1725                    if(StringUtil.isEmpty(strInterpolation))
1726                            throw new ExpressionException("interpolation definition is empty");
1727                    strInterpolation=strInterpolation.trim().toLowerCase();
1728                    
1729                    if("highestquality".equals(strInterpolation))                   return IP_HIGHESTQUALITY;
1730                    else if("highquality".equals(strInterpolation))                 return IP_HIGHQUALITY;
1731                    else if("mediumquality".equals(strInterpolation))               return IP_MEDIUMQUALITY;
1732                    else if("highestperformance".equals(strInterpolation))  return IP_HIGHESTPERFORMANCE;
1733                    else if("highperformance".equals(strInterpolation))     return IP_HIGHPERFORMANCE;
1734                    else if("mediumperformance".equals(strInterpolation))   return IP_MEDIUMPERFORMANCE;
1735                    else if("nearest".equals(strInterpolation))                     return IPC_NEAREST;
1736                    else if("bilinear".equals(strInterpolation))                    return IPC_BILINEAR;
1737                    else if("bicubic".equals(strInterpolation))                     return IPC_BICUBIC;
1738                    else if("bessel".equals(strInterpolation))                              return IP_BESSEL;
1739                    else if("blackman".equals(strInterpolation))                    return IP_BLACKMAN;
1740                    else if("hamming".equals(strInterpolation))                     return IP_HAMMING;
1741                    else if("hanning".equals(strInterpolation))                     return IP_HANNING;
1742                    else if("hermite".equals(strInterpolation))                     return IP_HERMITE;
1743                    else if("lanczos".equals(strInterpolation))                     return IP_LANCZOS;
1744                    else if("mitchell".equals(strInterpolation))                    return IP_MITCHELL;
1745                    else if("quadratic".equals(strInterpolation))                   return IP_QUADRATIC;
1746    
1747                    throw new ExpressionException("interpolation definition ["+strInterpolation+"] is invalid");
1748            }
1749    
1750            /**
1751             * @return the source
1752             */
1753            public Resource getSource() {
1754                    return source;
1755            }
1756    
1757            /**
1758             * @see java.util.Map#containsValue(java.lang.Object)
1759             */
1760            public boolean containsValue(Object value) {
1761                    try {
1762                            return info().containsValue(value);
1763                    } 
1764                    catch (ExpressionException e) {
1765                            return false;
1766                    }
1767            }
1768    
1769            /**
1770             * @see java.util.Map#values()
1771             */
1772            public java.util.Collection values() {
1773                    try {
1774                            return info().values();
1775                    } catch (ExpressionException e) {
1776                            throw new PageRuntimeException(e);
1777                    }
1778            }
1779    
1780            /**
1781             * This method returns true if the specified image has transparent pixels
1782             * @param image
1783             * @return
1784             */
1785            public static boolean hasAlpha(java.awt.Image image) {
1786                // If buffered image, the color model is readily available
1787                if (image instanceof BufferedImage) {
1788                    BufferedImage bimage = (BufferedImage)image;
1789                    return bimage.getColorModel().hasAlpha();
1790                }
1791    
1792                // Use a pixel grabber to retrieve the image's color model;
1793                // grabbing a single pixel is usually sufficient
1794                 PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
1795                try {
1796                    pg.grabPixels();
1797                } catch (InterruptedException e) {
1798                }
1799    
1800                // Get the image's color model
1801                ColorModel cm = pg.getColorModel();
1802                return cm.hasAlpha();
1803            }
1804            
1805            // This method returns a buffered image with the contents of an image
1806            public static BufferedImage toBufferedImage(java.awt.Image image) {
1807                if (image instanceof BufferedImage) {
1808                    return (BufferedImage)image;
1809                }
1810    
1811                // This code ensures that all the pixels in the image are loaded
1812                image = new ImageIcon(image).getImage();
1813    
1814                // Determine if the image has transparent pixels; for this method's
1815                boolean hasAlpha = hasAlpha(image);
1816    
1817                // Create a buffered image with a format that's compatible with the screen
1818                BufferedImage bimage = null;
1819                GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
1820                try {
1821                    // Determine the type of transparency of the new buffered image
1822                    int transparency = Transparency.OPAQUE;
1823                    if (hasAlpha) {
1824                        transparency = Transparency.BITMASK;
1825                    }
1826    
1827                    // Create the buffered image
1828                    GraphicsDevice gs = ge.getDefaultScreenDevice();
1829                    GraphicsConfiguration gc = gs.getDefaultConfiguration();
1830                    bimage = gc.createCompatibleImage(
1831                        image.getWidth(null), image.getHeight(null), transparency);
1832                } catch (HeadlessException e) {
1833                    // The system does not have a screen
1834                }
1835    
1836                if (bimage == null) {
1837                    // Create a buffered image using the default color model
1838                    int type = BufferedImage.TYPE_INT_RGB;
1839                    if (hasAlpha) {
1840                        type = BufferedImage.TYPE_INT_ARGB;
1841                    }
1842                    bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
1843                }
1844    
1845                // Copy image to buffered image
1846                Graphics g = bimage.createGraphics();
1847    
1848                // Paint the image onto the buffered image
1849                g.drawImage(image, 0, 0, null);
1850                g.dispose();
1851    
1852                return bimage;
1853            }
1854            
1855    }