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