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