Java tutorial
/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.runtime.img; import java.awt.AWTException; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.font.TextAttribute; import java.awt.geom.AffineTransform; import java.awt.geom.CubicCurve2D; import java.awt.geom.QuadCurve2D; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBufferInt; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import java.awt.image.PackedColorModel; import java.awt.image.PixelGrabber; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.text.AttributedString; import java.util.Iterator; import java.util.Locale; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.metadata.IIOMetadata; import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import javax.imageio.stream.FileImageInputStream; import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import javax.media.jai.BorderExtender; import javax.media.jai.BorderExtenderConstant; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.LookupTableJAI; import javax.media.jai.operator.ShearDir; import javax.media.jai.operator.TransposeType; import javax.swing.ImageIcon; import lucee.commons.io.IOUtil; import lucee.commons.io.res.Resource; import lucee.commons.lang.CFTypes; import lucee.commons.lang.StringUtil; import lucee.commons.lang.font.FontUtil; import lucee.runtime.PageContext; import lucee.runtime.dump.DumpData; import lucee.runtime.dump.DumpProperties; import lucee.runtime.dump.DumpTable; import lucee.runtime.engine.ThreadLocalPageContext; import lucee.runtime.exp.CasterException; import lucee.runtime.exp.ExpressionException; import lucee.runtime.exp.PageException; import lucee.runtime.exp.PageRuntimeException; import lucee.runtime.img.filter.QuantizeFilter; import lucee.runtime.img.gif.GifEncoder; import lucee.runtime.interpreter.VariableInterpreter; import lucee.runtime.op.Caster; import lucee.runtime.op.Constants; import lucee.runtime.op.Decision; import lucee.runtime.text.xml.XMLUtil; import lucee.runtime.type.Array; import lucee.runtime.type.ArrayImpl; import lucee.runtime.type.Collection; import lucee.runtime.type.ObjectWrap; import lucee.runtime.type.Struct; import lucee.runtime.type.StructImpl; import lucee.runtime.type.UDFPlus; import lucee.runtime.type.dt.DateTime; import lucee.runtime.type.util.ArrayUtil; import lucee.runtime.type.util.ListUtil; import lucee.runtime.type.util.MemberUtil; import lucee.runtime.type.util.StructSupport; import org.apache.commons.codec.binary.Base64; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class Image extends StructSupport implements Cloneable, Struct { private static final long serialVersionUID = -2370381932689749657L; public static final int BORDER_TYPE_CONSTANT = -1; public static final int INTERPOLATION_NONE = 0; public static final int INTERPOLATION_NEAREST = 1; public static final int INTERPOLATION_BILINEAR = 2; public static final int INTERPOLATION_BICUBIC = 3; public static final int IP_NONE = 0; public static final int IPC_NEAREST = 1; public static final int IPC_BILINEAR = 2; public static final int IPC_BICUBIC = 3; public static final int IPC_MAX = 3; public static final int IP_HIGHESTQUALITY = 100; public static final int IP_HIGHQUALITY = 101; public static final int IP_MEDIUMQUALITY = 102; public static final int IP_HIGHESTPERFORMANCE = 103; public static final int IP_HIGHPERFORMANCE = 104; public static final int IP_MEDIUMPERFORMANCE = 105; public static final int IP_BESSEL = 109; public static final int IP_BLACKMAN = 110; public static final int IP_HAMMING = 111; public static final int IP_HANNING = 112; public static final int IP_HERMITE = 113; public static final int IP_LANCZOS = 114; public static final int IP_MITCHELL = 115; public static final int IP_QUADRATIC = 116; public static final int IP_TRIANGLE = 117; private static final int ANTI_ALIAS_NONE = 0; private static final int ANTI_ALIAS_ON = 1; private static final int ANTI_ALIAS_OFF = 2; private static final String FORMAT = "javax_imageio_1.0"; private BufferedImage _image; private Resource source = null; private String format; private Graphics2D graphics; private Color bgColor; private Color fgColor; private Color xmColor; private float tranparency = -1; private int antiAlias = ANTI_ALIAS_NONE; private Stroke stroke; private Struct sctInfo; private float alpha = 1; private Composite composite; private static Object sync = new Object(); static { ImageIO.scanForPlugins(); } public Image(byte[] binary) throws IOException { this(binary, null); } public Image(byte[] binary, String format) throws IOException { if (StringUtil.isEmpty(format)) format = ImageUtil.getFormat(binary, null); checkRestriction(); this.format = format; _image = ImageUtil.toBufferedImage(binary, format); if (_image == null) throw new IOException("can not read in image"); } public Image(Resource res) throws IOException { this(res, null); } public Image(Resource res, String format) throws IOException { if (StringUtil.isEmpty(format)) format = ImageUtil.getFormat(res); checkRestriction(); this.format = format; _image = ImageUtil.toBufferedImage(res, format); this.source = res; if (_image == null) throw new IOException("can not read in file " + res); } public Image(BufferedImage image) { checkRestriction(); this._image = image; } public Image(String b64str) throws IOException { this(b64str, null); } public Image(String b64str, String format) throws IOException { // load binary from base64 string and get format StringBuilder mimetype = new StringBuilder(); byte[] binary = ImageUtil.readBase64(b64str, mimetype); if (StringUtil.isEmpty(format) && !StringUtil.isEmpty(mimetype)) { format = ImageUtil.getFormatFromMimeType(mimetype.toString()); } if (StringUtil.isEmpty(format)) format = ImageUtil.getFormat(binary, null); checkRestriction(); this.format = format; _image = ImageUtil.toBufferedImage(binary, format); if (_image == null) throw new IOException("can not read in image"); } public Image(int width, int height, int imageType, Color canvasColor) throws ExpressionException { checkRestriction(); _image = new BufferedImage(width, height, imageType); if (!StringUtil.isEmpty(canvasColor)) { setBackground(canvasColor); clearRect(0, 0, width, height); } } public Image() { checkRestriction(); } /** * add a border to image * @param thickness * @param color * @param borderType */ public void addBorder(int thickness, Color color, int borderType) throws ExpressionException { double colorArray[] = { color.getRed(), color.getGreen(), color.getBlue() }; BorderExtender borderExtender = new BorderExtenderConstant(colorArray); ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(thickness); params.add(thickness); params.add(thickness); params.add(thickness); if (BORDER_TYPE_CONSTANT == borderType) params.add(borderExtender); else params.add(BorderExtender.createInstance(borderType)); //else if(BORDER_TYPE_WRAP==borderType)params.add(BorderExtender.createInstance(BorderExtender.BORDER_REFLECT)); image((JAI.create("border", params)).getAsBufferedImage()); } public void blur(int blurFactor) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(blurFactor); RenderingHints hint = new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(1)); image(JAI.create("boxfilter", params, hint).getAsBufferedImage()); } public void clearRect(int x, int y, int width, int height) throws ExpressionException { getGraphics().clearRect(x, y, width, height); } public Struct info() throws ExpressionException { if (sctInfo != null) return sctInfo; Struct sctInfo = new StructImpl(), sct; sctInfo.setEL("height", new Double(getHeight())); sctInfo.setEL("width", new Double(getWidth())); sctInfo.setEL("source", source == null ? "" : source.getAbsolutePath()); //sct.setEL("mime_type",getMimeType()); ColorModel cm = image().getColorModel(); sct = new StructImpl(); sctInfo.setEL("colormodel", sct); sct.setEL("alpha_channel_support", Caster.toBoolean(cm.hasAlpha())); sct.setEL("alpha_premultiplied", Caster.toBoolean(cm.isAlphaPremultiplied())); sct.setEL("transparency", toStringTransparency(cm.getTransparency())); sct.setEL("pixel_size", Caster.toDouble(cm.getPixelSize())); sct.setEL("num_components", Caster.toDouble(cm.getNumComponents())); sct.setEL("num_color_components", Caster.toDouble(cm.getNumColorComponents())); sct.setEL("colorspace", toStringColorSpace(cm.getColorSpace())); //bits_component int[] bitspercomponent = cm.getComponentSize(); Array arr = new ArrayImpl(); Double value; for (int i = 0; i < bitspercomponent.length; i++) { sct.setEL("bits_component_" + (i + 1), value = new Double(bitspercomponent[i])); arr.appendEL(value); } sct.setEL("bits_component", arr); // colormodel_type if (cm instanceof ComponentColorModel) sct.setEL("colormodel_type", "ComponentColorModel"); else if (cm instanceof IndexColorModel) sct.setEL("colormodel_type", "IndexColorModel"); else if (cm instanceof PackedColorModel) sct.setEL("colormodel_type", "PackedColorModel"); else sct.setEL("colormodel_type", ListUtil.last(cm.getClass().getName(), '.')); getMetaData(sctInfo); ImageMeta.addInfo(format, source, sctInfo); this.sctInfo = sctInfo; return sctInfo; } public IIOMetadata getMetaData(Struct parent) { InputStream is = null; javax.imageio.stream.ImageInputStreamImpl iis = null; try { if (source instanceof File) { iis = new FileImageInputStream((File) source); } else if (source == null) iis = new MemoryCacheImageInputStream(new ByteArrayInputStream(getImageBytes(format, true))); else iis = new MemoryCacheImageInputStream(is = source.getInputStream()); Iterator<ImageReader> readers = ImageIO.getImageReaders(iis); if (readers.hasNext()) { // pick the first available ImageReader ImageReader reader = readers.next(); IIOMetadata meta = null; synchronized (sync) { // attach source to the reader reader.setInput(iis, true); // read metadata of first image meta = reader.getImageMetadata(0); meta.setFromTree(FORMAT, meta.getAsTree(FORMAT)); reader.reset(); } // generating dump if (parent != null) { String[] formatNames = meta.getMetadataFormatNames(); for (int i = 0; i < formatNames.length; i++) { Node root = meta.getAsTree(formatNames[i]); //print.out(XMLCaster.toString(root)); addMetaddata(parent, "metadata", root); } } return meta; } } catch (Throwable t) { } finally { ImageUtil.closeEL(iis); IOUtil.closeEL(is); } return null; } private void addMetaddata(Struct parent, String name, Node node) { // attributes NamedNodeMap attrs = node.getAttributes(); Attr attr; int len = attrs.getLength(); if (len == 1 && "value".equals(attrs.item(0).getNodeName())) { parent.setEL(name, attrs.item(0).getNodeValue()); } else { Struct sct = metaGetChild(parent, name); for (int i = attrs.getLength() - 1; i >= 0; i--) { attr = (Attr) attrs.item(i); sct.setEL(attr.getName(), attr.getValue()); } } // child nodes NodeList children = XMLUtil.getChildNodes(node, Node.ELEMENT_NODE); Element el; for (int i = children.getLength() - 1; i >= 0; i--) { el = (Element) children.item(i); Struct sct = metaGetChild(parent, name); addMetaddata(sct, el.getNodeName(), children.item(i)); } } private Struct metaGetChild(Struct parent, String name) { Object child = parent.get(name, null); if (child instanceof Struct) return (Struct) child; Struct sct = new StructImpl(); parent.setEL(name, sct); return sct; } public void sharpen(float gain) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add((Object) null); params.add(new Float(gain)); image(JAI.create("unsharpmask", params).getAsBufferedImage()); } public void setTranparency(float percent) throws ExpressionException { if (percent == -1) return; tranparency = percent; AlphaComposite rule = AlphaComposite.getInstance(3, 1.0F - (percent / 100.0F)); getGraphics().setComposite(rule); } public void invert() throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); image(JAI.create("invert", params).getAsBufferedImage()); } public Image copy(float x, float y, float width, float height) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(x); params.add(y); params.add(width); params.add(height); //image(JAI.create("crop", params).getAsBufferedImage()); return new Image(JAI.create("crop", params).getAsBufferedImage()); } public Image copy(float x, float y, float width, float height, float dx, float dy) throws ExpressionException { Image img = copy(x, y, width, height); img.getGraphics().copyArea((int) x, (int) y, (int) width, (int) height, (int) (dx - x), (int) (dy - y)); return img; } public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle, boolean filled) throws ExpressionException { if (filled) getGraphics().fillArc(x, y, width, height, startAngle, arcAngle); else getGraphics().drawArc(x, y, width, height, startAngle, arcAngle); } public void draw3DRect(int x, int y, int width, int height, boolean raised, boolean filled) throws ExpressionException { if (filled) getGraphics().fill3DRect(x, y, width + 1, height + 1, raised); else getGraphics().draw3DRect(x, y, width, height, raised); } public void drawCubicCurve(double ctrlx1, double ctrly1, double ctrlx2, double ctrly2, double x1, double y1, double x2, double y2) throws ExpressionException { CubicCurve2D curve = new CubicCurve2D.Double(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); getGraphics().draw(curve); } public void drawPoint(int x, int y) throws ExpressionException { drawLine(x, y, x + 1, y); } public void drawQuadraticCurve(double x1, double y1, double ctrlx, double ctrly, double x2, double y2) throws ExpressionException { QuadCurve2D curve = new QuadCurve2D.Double(x1, y1, ctrlx, ctrly, x2, y2); getGraphics().draw(curve); } public void drawRect(int x, int y, int width, int height, boolean filled) throws ExpressionException { if (filled) getGraphics().fillRect(x, y, width + 1, height + 1); else getGraphics().drawRect(x, y, width, height); } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight, boolean filled) throws ExpressionException { if (filled) getGraphics().fillRoundRect(x, y, width + 1, height + 1, arcWidth, arcHeight); else getGraphics().drawRoundRect(x, y, width, height, arcWidth, arcHeight); } public void drawLine(int x1, int y1, int x2, int y2) throws ExpressionException { getGraphics().drawLine(x1, y1, x2, y2); } public void drawImage(Image img, int x, int y) throws ExpressionException { getGraphics().drawImage(img.image(), x, y, null); } public void drawImage(Image img, int x, int y, int width, int height) throws ExpressionException { getGraphics().drawImage(img.image(), x, y, width, height, null); } public void drawLines(int[] xcoords, int[] ycoords, boolean isPolygon, boolean filled) throws ExpressionException { if (isPolygon) { if (filled) getGraphics().fillPolygon(xcoords, ycoords, xcoords.length); else getGraphics().drawPolygon(xcoords, ycoords, xcoords.length); } else { getGraphics().drawPolyline(xcoords, ycoords, xcoords.length); } } public void drawOval(int x, int y, int width, int height, boolean filled) throws ExpressionException { if (filled) getGraphics().fillOval(x, y, width, height); else getGraphics().drawOval(x, y, width, height); } public void drawString(String text, int x, int y, Struct attr) throws PageException { if (attr != null && attr.size() > 0) { // font String font = StringUtil.toLowerCase(Caster.toString(attr.get("font", ""))).trim(); if (!StringUtil.isEmpty(font)) { font = FontUtil.getFont(font).getFontName(); } else font = "Serif"; // alpha //float alpha=Caster.toFloatValue(attr.get("alpha",null),1F); // size int size = Caster.toIntValue(attr.get("size", Constants.INTEGER_10)); // style int style = Font.PLAIN; String strStyle = StringUtil.toLowerCase(Caster.toString(attr.get("style", ""))); strStyle = StringUtil.removeWhiteSpace(strStyle); if (!StringUtil.isEmpty(strStyle)) { if ("plain".equals(strStyle)) style = Font.PLAIN; else if ("bold".equals(strStyle)) style = Font.BOLD; else if ("italic".equals(strStyle)) style = Font.ITALIC; else if ("bolditalic".equals(strStyle)) style = Font.BOLD + Font.ITALIC; else if ("bold,italic".equals(strStyle)) style = Font.BOLD + Font.ITALIC; else if ("italicbold".equals(strStyle)) style = Font.BOLD + Font.ITALIC; else if ("italic,bold".equals(strStyle)) style = Font.BOLD + Font.ITALIC; else throw new ExpressionException("key style of argument attributeCollection has an invalid value [" + strStyle + "], valid values are [plain,bold,italic,bolditalic]"); } // strikethrough boolean strikethrough = Caster.toBooleanValue(attr.get("strikethrough", Boolean.FALSE)); // underline boolean underline = Caster.toBooleanValue(attr.get("underline", Boolean.FALSE)); AttributedString as = new AttributedString(text); as.addAttribute(TextAttribute.FONT, new Font(font, style, size)); if (strikethrough) as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); if (underline) as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); Graphics2D g = getGraphics(); //if(alpha!=1D) setAlpha(g,alpha); g.drawString(as.getIterator(), x, y); } else getGraphics().drawString(text, x, y); } /*private void setAlpha(Graphics2D graphics,float alpha) { //Composite originalComposite = graphics.getComposite(); AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); graphics.setComposite(alphaComposite); //graphics.setComposite(originalComposite); }*/ public void setDrawingStroke(Struct attr) throws PageException { // empty if (attr == null || attr.size() == 0) { setDrawingStroke(new BasicStroke()); return; } // width float width = Caster.toFloatValue(attr.get("width", new Float(1F))); if (width < 0) throw new ExpressionException("key [width] should be a none negativ number"); // endcaps String strEndcaps = Caster.toString(attr.get("endcaps", "square")); strEndcaps = strEndcaps.trim().toLowerCase(); int endcaps; if ("square".equals(strEndcaps)) endcaps = BasicStroke.CAP_SQUARE; else if ("butt".equals(strEndcaps)) endcaps = BasicStroke.CAP_BUTT; else if ("round".equals(strEndcaps)) endcaps = BasicStroke.CAP_ROUND; else throw new ExpressionException("key [endcaps] has an invalid value [" + strEndcaps + "], valid values are [square,round,butt]"); // linejoins String strLinejoins = Caster.toString(attr.get("linejoins", "miter")); strLinejoins = strLinejoins.trim().toLowerCase(); int linejoins; if ("bevel".equals(strLinejoins)) linejoins = BasicStroke.JOIN_BEVEL; else if ("miter".equals(strLinejoins)) linejoins = BasicStroke.JOIN_MITER; else if ("round".equals(strLinejoins)) linejoins = BasicStroke.JOIN_ROUND; else throw new ExpressionException("key [linejoins] has an invalid value [" + strLinejoins + "], valid values are [bevel,miter,round]"); // miterlimit float miterlimit = 10.0F; if (linejoins == BasicStroke.JOIN_MITER) { miterlimit = Caster.toFloatValue(attr.get("miterlimit", new Float(10F))); if (miterlimit < 1F) throw new ExpressionException("key [miterlimit] should be greater or equal to 1"); } // dashArray Object oDashArray = attr.get("dashArray", null); float[] dashArray = null; if (oDashArray != null) { dashArray = ArrayUtil.toFloatArray(oDashArray); } // dash_phase float dash_phase = Caster.toFloatValue(attr.get("dash_phase", new Float(0F))); setDrawingStroke(width, endcaps, linejoins, miterlimit, dashArray, dash_phase); } public void setDrawingStroke(float width, int endcaps, int linejoins, float miterlimit, float[] dash, float dash_phase) throws ExpressionException { setDrawingStroke(new BasicStroke(width, endcaps, linejoins, miterlimit, dash, dash_phase)); } public void setDrawingStroke(Stroke stroke) throws ExpressionException { if (stroke == null) return; this.stroke = stroke; getGraphics().setStroke(stroke); } public void flip(TransposeType transpose) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(transpose); image(JAI.create("transpose", params).getAsBufferedImage()); } public void grayscale() throws ExpressionException { BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_BYTE_GRAY); Graphics2D graphics = img.createGraphics(); graphics.drawImage(image(), new AffineTransformOp(AffineTransform.getTranslateInstance(0.0, 0.0), 1), 0, 0); graphics.dispose(); image(img); } public void rgb() throws ExpressionException { BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_BYTE_INDEXED); Graphics2D graphics = img.createGraphics(); graphics.drawImage(image(), new AffineTransformOp(AffineTransform.getTranslateInstance(0.0, 0.0), 1), 0, 0); graphics.dispose(); image(img); } public void threeBBger() throws ExpressionException { BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR); Graphics2D graphics = img.createGraphics(); graphics.drawImage(image(), new AffineTransformOp(AffineTransform.getTranslateInstance(0.0, 0.0), 1), 0, 0); graphics.dispose(); image(img); } public void overlay(Image topImage) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.addSource(topImage.image()); image(JAI.create("overlay", params).getAsBufferedImage()); } public void paste(Image topImage, int x, int y) throws ExpressionException { RenderingHints interp = new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); BorderExtender extender = BorderExtender.createInstance(1); Graphics2D g = getGraphics(); g.addRenderingHints(new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender)); g.drawImage(topImage.image(), (new AffineTransformOp(AffineTransform.getTranslateInstance(x, y), interp)), 0, 0); } public void setXorMode(Color color) throws ExpressionException { if (color == null) return; xmColor = color; getGraphics().setXORMode(color); } public void translate(int xtrans, int ytrans, Object interpolation) throws ExpressionException { RenderingHints hints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, interpolation); if (interpolation != RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) { hints.add(new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(1))); } ParameterBlock pb = new ParameterBlock(); pb.addSource(image()); BufferedImage img = JAI.create("translate", pb).getAsBufferedImage(); Graphics2D graphics = img.createGraphics(); graphics.clearRect(0, 0, img.getWidth(), img.getHeight()); AffineTransform at = new AffineTransform(); at.setToIdentity(); graphics.drawImage(image(), new AffineTransformOp(at, hints), xtrans, ytrans); graphics.dispose(); image(img); } public void translateAxis(int x, int y) throws ExpressionException { getGraphics().translate(x, y); } public void rotateAxis(double angle) throws ExpressionException { getGraphics().rotate(Math.toRadians(angle)); } public void rotateAxis(double angle, double x, double y) throws ExpressionException { getGraphics().rotate(Math.toRadians(angle), x, y); } public void shearAxis(double shx, double shy) throws ExpressionException { getGraphics().shear(shx, shy); } public void shear(float shear, ShearDir direction, Object interpolation) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(shear); params.add(direction); params.add(0.0F); params.add(0.0F); RenderingHints hints = null; if (interpolation == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) params.add(Interpolation.getInstance(0)); else if (interpolation == RenderingHints.VALUE_INTERPOLATION_BILINEAR) { params.add(Interpolation.getInstance(1)); BorderExtender extender = BorderExtender.createInstance(1); hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender); } else if (interpolation == RenderingHints.VALUE_INTERPOLATION_BICUBIC) { params.add(Interpolation.getInstance(2)); BorderExtender extender = BorderExtender.createInstance(1); hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender); } // TODO Color bg = getGraphics().getBackground(); params.add(new double[] { bg.getRed(), bg.getGreen(), bg.getBlue() }); image(JAI.create("shear", params, hints).getAsBufferedImage()); } public BufferedImage getBufferedImage() throws ExpressionException { return image(); } public BufferedImage image() throws ExpressionException { if (_image == null) throw (new ExpressionException("image is not initalized")); return _image; } public void image(BufferedImage image) { this._image = image; graphics = null; sctInfo = null; } private Graphics2D getGraphics() throws ExpressionException { if (graphics == null) { graphics = image().createGraphics(); // reset all properties if (antiAlias != ANTI_ALIAS_NONE) setAntiAliasing(antiAlias == ANTI_ALIAS_ON); if (bgColor != null) setBackground(bgColor); if (fgColor != null) setColor(fgColor); if (alpha != 1) setAlpha(alpha); if (tranparency != -1) setTranparency(tranparency); if (xmColor != null) setXorMode(xmColor); if (stroke != null) setDrawingStroke(stroke); } return graphics; } private String toStringColorSpace(ColorSpace colorSpace) { switch (colorSpace.getType()) { case 0: return "Any of the family of XYZ color spaces"; case 1: return "Any of the family of Lab color spaces"; case 2: return "Any of the family of Luv color spaces"; case 3: return "Any of the family of YCbCr color spaces"; case 4: return "Any of the family of Yxy color spaces"; case 5: return "Any of the family of RGB color spaces"; case 6: return "Any of the family of GRAY color spaces"; case 7: return "Any of the family of HSV color spaces"; case 8: return "Any of the family of HLS color spaces"; case 9: return "Any of the family of CMYK color spaces"; case 11: return "Any of the family of CMY color spaces"; case 12: return "Generic 2 component color space."; case 13: return "Generic 3 component color space."; case 14: return "Generic 4 component color space."; case 15: return "Generic 5 component color space."; case 16: return "Generic 6 component color space."; case 17: return "Generic 7 component color space."; case 18: return "Generic 8 component color space."; case 19: return "Generic 9 component color space."; case 20: return "Generic 10 component color space."; case 21: return "Generic 11 component color space."; case 22: return "Generic 12 component color space."; case 23: return "Generic 13 component color space."; case 24: return "Generic 14 component color space."; case 25: return "Generic 15 component color space."; case 1001: return "CIEXYZ"; case 1003: return "GRAY"; case 1004: return "LINEAR_RGB"; case 1002: return "PYCC"; case 1000: return "sRGB"; } return "Unknown ColorSpace" + colorSpace; } private Object toStringTransparency(int transparency) { if (Transparency.OPAQUE == transparency) return "OPAQUE"; if (Transparency.BITMASK == transparency) return "BITMASK"; if (Transparency.TRANSLUCENT == transparency) return "TRANSLUCENT"; return "Unknown type of transparency"; } public String writeBase64(Resource destination, String format, boolean inHTMLFormat) throws PageException, IOException { // destination if (destination == null) { if (source != null) destination = source; else throw new IOException("missing destination file"); } String content = getBase64String(format); if (inHTMLFormat) content = "data:image/" + format + ";base64," + content; IOUtil.write(destination, content, (Charset) null, false); return content; } public String getBase64String(String format) throws PageException { byte[] imageBytes = getImageBytes(format); return new String(Base64.encodeBase64(imageBytes)); } public void writeOut(Resource destination, boolean overwrite, float quality) throws IOException, ExpressionException { String format = ImageUtil.getFormatFromExtension(destination, null); writeOut(destination, format, overwrite, quality); } public void writeOut(Resource destination, String format, boolean overwrite, float quality) throws IOException, ExpressionException { if (destination == null) { if (source != null) destination = source; else throw new IOException("missing destination file"); } if (destination.exists()) { if (!overwrite) throw new IOException("can't overwrite existing image"); } if (JAIUtil.isSupportedWriteFormat(format)) { JAIUtil.write(getBufferedImage(), destination, format); return; } OutputStream os = null; ImageOutputStream ios = null; try { os = destination.getOutputStream(); ios = ImageIO.createImageOutputStream(os); _writeOut(ios, format, quality); } finally { ImageUtil.closeEL(ios); IOUtil.closeEL(os); } } public static void writeOutGif(BufferedImage src, OutputStream os) throws IOException { BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_ARGB); QuantizeFilter filter = new QuantizeFilter(); filter.setSerpentine(true); filter.setDither(true); //filter.setNumColors(8); filter.filter(src, dst); //image(Quantizer.quantize(image(), 8)); try { GifEncoder enc = new GifEncoder(dst); enc.Write(os); os.flush(); } catch (AWTException e) { throw new IOException(e.getMessage()); } } public void writeOut(OutputStream os, String format, float quality, boolean closeStream) throws IOException, ExpressionException { ImageOutputStream ios = ImageIO.createImageOutputStream(os); try { _writeOut(ios, format, quality); } finally { IOUtil.closeEL(ios); } } private void _writeOut(ImageOutputStream ios, String format, float quality) throws IOException, ExpressionException { _writeOut(ios, format, quality, false); } private void _writeOut(ImageOutputStream ios, String format, float quality, boolean noMeta) throws IOException, ExpressionException { if (quality < 0 || quality > 1) throw new IOException( "quality has an invalid value [" + quality + "], value has to be between 0 and 1"); if (StringUtil.isEmpty(format)) format = this.format; if (StringUtil.isEmpty(format)) throw new IOException("missing format"); BufferedImage im = image(); //IIOMetadata meta = noMeta?null:metadata(format); IIOMetadata meta = noMeta ? null : getMetaData(null); ImageWriter writer = null; ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(im); Iterator<ImageWriter> iter = ImageIO.getImageWriters(type, format); if (iter.hasNext()) { writer = iter.next(); } if (writer == null) throw new IOException("no writer for format [" + format + "] available, available writer formats are [" + ListUtil.arrayToList(ImageUtil.getWriterFormatNames(), ",") + "]"); ImageWriteParam iwp = null; if ("jpg".equalsIgnoreCase(format)) { ColorModel cm = im.getColorModel(); if (cm.hasAlpha()) im = jpgImage(im); JPEGImageWriteParam jiwp = new JPEGImageWriteParam(Locale.getDefault()); jiwp.setOptimizeHuffmanTables(true); iwp = jiwp; } else iwp = writer.getDefaultWriteParam(); setCompressionModeEL(iwp, ImageWriteParam.MODE_EXPLICIT); setCompressionQualityEL(iwp, quality); writer.setOutput(ios); try { writer.write(meta, new IIOImage(im, null, meta), iwp); } finally { writer.dispose(); ios.flush(); } } private BufferedImage jpgImage(BufferedImage src) { int w = src.getWidth(); int h = src.getHeight(); SampleModel srcSM = src.getSampleModel(); WritableRaster srcWR = src.getRaster(); java.awt.image.DataBuffer srcDB = srcWR.getDataBuffer(); ColorModel rgb = new DirectColorModel(32, 0xff0000, 65280, 255); int[] bitMasks = new int[] { 0xff0000, 65280, 255 }; SampleModel csm = new SinglePixelPackedSampleModel(3, w, h, bitMasks); int data[] = new int[w * h]; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int pix[] = null; int sample[] = srcSM.getPixel(j, i, pix, srcDB); if (sample[3] == 0 && sample[2] == 0 && sample[1] == 0 && sample[0] == 0) data[i * w + j] = 0xffffff; else data[i * w + j] = sample[0] << 16 | sample[1] << 8 | sample[2]; } } java.awt.image.DataBuffer db = new DataBufferInt(data, w * h * 3); WritableRaster wr = Raster.createWritableRaster(csm, db, new Point(0, 0)); return new BufferedImage(rgb, wr, false, null); } private void setCompressionModeEL(ImageWriteParam iwp, int mode) { try { iwp.setCompressionMode(mode); } catch (Throwable t) { } } private void setCompressionQualityEL(ImageWriteParam iwp, float quality) { try { iwp.setCompressionQuality(quality); } catch (Throwable t) { } } public void convert(String format) { this.format = format; } public void scaleToFit(String fitWidth, String fitHeight, String interpolation, double blurFactor) throws PageException { if (StringUtil.isEmpty(fitWidth) || StringUtil.isEmpty(fitHeight)) resize(fitWidth, fitHeight, interpolation, blurFactor); else { float width = Caster.toFloatValue(fitWidth) / getWidth(); float height = Caster.toFloatValue(fitHeight) / getHeight(); if (width < height) resize(fitWidth, "", interpolation, blurFactor); else resize("", fitHeight, interpolation, blurFactor); } } /** * Convenience method that returns a scaled instance of the * provided {@code BufferedImage}. * * @param img the original image to be scaled * @param targetWidth the desired width of the scaled instance, * in pixels * @param targetHeight the desired height of the scaled instance, * in pixels * @param hint one of the rendering hints that corresponds to * {@code RenderingHints.KEY_INTERPOLATION} (e.g. * {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR}, * {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR}, * {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC}) * @param higherQuality if true, this method will use a multi-step * scaling technique that provides higher quality than the usual * one-step technique (only useful in downscaling cases, where * {@code targetWidth} or {@code targetHeight} is * smaller than the original dimensions, and generally only when * the {@code BILINEAR} hint is specified) * @return a scaled version of the original {@code BufferedImage} */ private BufferedImage getScaledInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint, boolean higherQuality) { // functionality not supported in java 1.4 int transparency = Transparency.OPAQUE; try { transparency = img.getTransparency(); } catch (Throwable t) { } int type = (transparency == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; BufferedImage ret = img; int w, h; if (higherQuality) { // Use multi-step technique: start with original size, then // scale down in multiple passes with drawImage() // until the target size is reached w = img.getWidth(); h = img.getHeight(); } else { // Use one-step technique: scale directly from original // size to target size with a single drawImage() call w = targetWidth; h = targetHeight; } do { if (higherQuality && w > targetWidth) { w /= 2; if (w < targetWidth) { w = targetWidth; } } if (higherQuality && h > targetHeight) { h /= 2; if (h < targetHeight) { h = targetHeight; } } BufferedImage tmp = new BufferedImage(w, h, type); Graphics2D g2 = tmp.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); g2.drawImage(ret, 0, 0, w, h, null); g2.dispose(); ret = tmp; } while (w != targetWidth || h != targetHeight); return ret; } public void resize(int scale, String interpolation, double blurFactor) throws PageException { if (blurFactor <= 0.0 || blurFactor > 10.0) throw new ExpressionException("blurFactor must be between 0 and 10"); float width = getWidth() / 100F * scale; float height = getHeight() / 100F * scale; resize((int) width, (int) height, toInterpolation(interpolation), blurFactor); } public void resize(String strWidth, String strHeight, String interpolation, double blurFactor) throws PageException { if (StringUtil.isEmpty(strWidth, true) && StringUtil.isEmpty(strHeight, true)) throw new ExpressionException("you have to define width or height"); if (blurFactor <= 0.0 || blurFactor > 10.0) throw new ExpressionException("blurFactor must be between 0 and 10"); int w = getWidth(); int h = getHeight(); float height = resizeDimesion("height", strHeight, h); float width = resizeDimesion("width", strWidth, w); if (height == -1) height = h * (width / w); if (width == -1) width = w * (height / h); resize((int) width, (int) height, toInterpolation(interpolation), blurFactor); } public void resizeImage2(int width, int height) throws ExpressionException { image(getScaledInstance(image(), width, height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false)); } public void resizeImage(int width, int height, int interpolation) throws ExpressionException { Object ip; if (interpolation == IPC_NEAREST) ip = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; else if (interpolation == IPC_BICUBIC) ip = RenderingHints.VALUE_INTERPOLATION_BICUBIC; else if (interpolation == IPC_BILINEAR) ip = RenderingHints.VALUE_INTERPOLATION_BILINEAR; else throw new ExpressionException("invalid interpoltion definition"); BufferedImage dst = new BufferedImage(width, height, image().getType()); Graphics2D graphics = dst.createGraphics(); graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, ip); graphics.drawImage(image(), 0, 0, width, height, null); graphics.dispose(); image(dst); } private float resizeDimesion(String label, String strDimension, float originalDimension) throws PageException { if (StringUtil.isEmpty(strDimension, true)) return -1; strDimension = strDimension.trim(); if (StringUtil.endsWith(strDimension, '%')) { float p = Caster.toFloatValue(strDimension.substring(0, (strDimension.length() - 1))) / 100.0F; return originalDimension * p; } float dimension = Caster.toFloatValue(strDimension); if (dimension <= 0F) throw new ExpressionException(label + " has to be a none negative number"); return dimension; } public void resize(int width, int height, int interpolation, double blurFactor) throws ExpressionException { ColorModel cm = image().getColorModel(); if (interpolation == IP_HIGHESTPERFORMANCE) { interpolation = IPC_BICUBIC; } if (cm.getColorSpace().getType() == ColorSpace.TYPE_GRAY && cm.getComponentSize()[0] == 8) { if (interpolation == IP_HIGHESTQUALITY || interpolation == IP_HIGHPERFORMANCE || interpolation == IP_HIGHQUALITY || interpolation == IP_MEDIUMPERFORMANCE || interpolation == IP_MEDIUMQUALITY) { interpolation = IPC_BICUBIC; } if (interpolation != IPC_BICUBIC && interpolation != IPC_BILINEAR && interpolation != IPC_NEAREST) { throw new ExpressionException("invalid grayscale interpolation"); } } if (interpolation <= IPC_MAX) { resizeImage(width, height, interpolation); } else { image(ImageResizer.resize(image(), width, height, interpolation, blurFactor)); } } /*private BufferedImage resizeImageWithJAI(float scaleWidth, float scaleHeight, int interpolation) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(scaleWidth); params.add(scaleHeight); params.add(0.0F); params.add(0.0F); RenderingHints hints = null; if (interpolation != IP_NONE) { if (interpolation==IP_NEAREST) { params.add(Interpolation.getInstance(0)); } else if (interpolation==IP_BILINEAR) { params.add(Interpolation.getInstance(1)); BorderExtender extender = BorderExtender.createInstance(1); hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender); } else if (interpolation==IP_BICUBIC) { params.add(Interpolation.getInstance(2)); BorderExtender extender = BorderExtender.createInstance(1); hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender); } else { throw new ExpressionException("invalid interpolation definition"); } } return JAI.create("scale", params, hints).getAsBufferedImage(); }*/ private double toScale(int src, int dst) { double tmp = Math.round((int) ((Caster.toDoubleValue(dst) / Caster.toDoubleValue(src)) * 100D)); return tmp / 100D; } public void rotate(float x, float y, float angle, int interpolation) throws ExpressionException { if (x == -1) x = (float) getWidth() / 2; if (y == -1) y = (float) getHeight() / 2; angle = (float) Math.toRadians(angle); ColorModel cmSource = image().getColorModel(); if (cmSource instanceof IndexColorModel && cmSource.hasAlpha() && !cmSource.isAlphaPremultiplied()) { image(PaletteToARGB(image())); cmSource = image().getColorModel(); } BufferedImage alpha = null; if (cmSource.hasAlpha() && !cmSource.isAlphaPremultiplied()) { alpha = getAlpha(image()); image(removeAlpha(image())); } Interpolation interp = Interpolation.getInstance(0); if (INTERPOLATION_BICUBIC == interpolation) interp = Interpolation.getInstance(1); else if (INTERPOLATION_BILINEAR == interpolation) interp = Interpolation.getInstance(2); if (alpha != null) { ParameterBlock params = new ParameterBlock(); params.addSource(alpha); params.add(x); params.add(y); params.add(angle); params.add(interp); params.add(new double[] { 0.0 }); RenderingHints hints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, (RenderingHints.VALUE_INTERPOLATION_BICUBIC)); hints.add(new RenderingHints(JAI.KEY_BORDER_EXTENDER, new BorderExtenderConstant(new double[] { 255.0 }))); hints.add(new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE)); alpha = JAI.create("rotate", params, hints).getAsBufferedImage(); } ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(x); params.add(y); params.add(angle); params.add(interp); params.add(new double[] { 0.0 }); BorderExtender extender = new BorderExtenderConstant(new double[] { 0.0 }); RenderingHints hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender); hints.add(new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE)); image(JAI.create("rotate", params, hints).getAsBufferedImage()); if (alpha != null) image(addAlpha(image(), alpha, 0, 0)); } private static BufferedImage PaletteToARGB(BufferedImage src) { IndexColorModel icm = (IndexColorModel) src.getColorModel(); int bands = icm.hasAlpha() ? 4 : 3; byte[][] data = new byte[bands][icm.getMapSize()]; if (icm.hasAlpha()) icm.getAlphas(data[3]); icm.getReds(data[0]); icm.getGreens(data[1]); icm.getBlues(data[2]); LookupTableJAI rtable = new LookupTableJAI(data); return JAI.create("lookup", src, rtable).getAsBufferedImage(); } private static BufferedImage getAlpha(BufferedImage src) { return JAI.create("bandselect", src, new int[] { 3 }).getAsBufferedImage(); } private static BufferedImage removeAlpha(BufferedImage src) { return JAI.create("bandselect", src, new int[] { 0, 1, 2 }).getAsBufferedImage(); } private static BufferedImage addAlpha(BufferedImage src, BufferedImage alpha, int x, int y) { int w = src.getWidth(); int h = src.getHeight(); BufferedImage bi = new BufferedImage(w, h, 2); WritableRaster wr = bi.getWritableTile(0, 0); WritableRaster wr3 = wr.createWritableChild(0, 0, w, h, 0, 0, new int[] { 0, 1, 2 }); WritableRaster wr1 = wr.createWritableChild(0, 0, w, h, 0, 0, new int[] { 3 }); wr3.setRect(src.getData()); wr1.setRect(alpha.getData()); bi.releaseWritableTile(0, 0); return bi; } public void _rotate(float x, float y, float angle, String interpolation) throws ExpressionException { float radiansAngle = (float) Math.toRadians(angle); // rotation center float centerX = (float) getWidth() / 2; float centerY = (float) getHeight() / 2; ParameterBlock pb = new ParameterBlock(); pb.addSource(image()); pb.add(centerX); pb.add(centerY); pb.add(radiansAngle); pb.add(new javax.media.jai.InterpolationBicubic(10)); // create a new, rotated image image(JAI.create("rotate", pb).getAsBufferedImage()); } public static Image toImage(Object obj) throws PageException { return toImage(ThreadLocalPageContext.get(), obj, true); } // used in bytecode public static Image toImage(Object obj, PageContext pc) throws PageException { return toImage(pc, obj, true); } public static Image toImage(PageContext pc, Object obj) throws PageException { return toImage(pc, obj, true); } public static Image toImage(PageContext pc, Object obj, boolean checkForVariables) throws PageException { if (obj instanceof Image) return (Image) obj; if (obj instanceof ObjectWrap) return toImage(pc, ((ObjectWrap) obj).getEmbededObject(), checkForVariables); // try to load from binary if (Decision.isBinary(obj)) { try { return new Image(Caster.toBinary(obj), null); } catch (IOException e) { throw Caster.toPageException(e); } } // try to load from String (base64) if (Decision.isString(obj)) { String str = Caster.toString(obj); if (checkForVariables && pc != null) { Object o = VariableInterpreter.getVariableEL(pc, str, null); if (o != null) return toImage(pc, o, false); } try { return new Image(str); } catch (IOException e) { throw Caster.toPageException(e); } } throw new CasterException(obj, "Image"); } public static Image toImage(PageContext pc, Object obj, boolean checkForVariables, Image defaultValue) { try { return toImage(pc, obj, checkForVariables); } catch (Throwable t) { return defaultValue; } } public static boolean isImage(Object obj) { if (obj instanceof Image) return true; if (obj instanceof ObjectWrap) return isImage(((ObjectWrap) obj).getEmbededObject("")); return false; } @Override public Object call(PageContext pc, Key methodName, Object[] args) throws PageException { Object obj = get(methodName, null); if (obj instanceof UDFPlus) { return ((UDFPlus) obj).call(pc, methodName, args, false); } return MemberUtil.call(pc, this, methodName, args, CFTypes.TYPE_IMAGE, "image"); } @Override public Object callWithNamedValues(PageContext pc, Key methodName, Struct args) throws PageException { Object obj = get(methodName, null); if (obj instanceof UDFPlus) { return ((UDFPlus) obj).callWithNamedValues(pc, methodName, args, false); } return MemberUtil.callWithNamedValues(pc, this, methodName, args, CFTypes.TYPE_IMAGE, "image"); } public static boolean isCastableToImage(PageContext pc, Object obj) { if (isImage(obj)) return true; return toImage(pc, obj, true, null) != null; } public static Image createImage(PageContext pc, Object obj, boolean check4Var, boolean clone, boolean checkAccess, String format) throws PageException { try { if (obj instanceof String || obj instanceof Resource || obj instanceof File) { try { Resource res = Caster.toResource(pc, obj, true); pc.getConfig().getSecurityManager().checkFileLocation(res); return new Image(res, format); } catch (ExpressionException ee) { if (check4Var && Decision.isVariableName(Caster.toString(obj))) { try { return createImage(pc, pc.getVariable(Caster.toString(obj)), false, clone, checkAccess, format); } catch (Throwable t) { throw ee; } } try { return new Image(Caster.toString(obj), format); } catch (Throwable t) { throw ee; } } } if (obj instanceof Image) { if (clone) return (Image) ((Image) obj).clone(); return (Image) obj; } if (Decision.isBinary(obj)) return new Image(Caster.toBinary(obj), format); if (obj instanceof BufferedImage) return new Image(((BufferedImage) obj)); if (obj instanceof java.awt.Image) return new Image(toBufferedImage((java.awt.Image) obj)); } catch (Throwable t) { throw Caster.toPageException(t); } throw new CasterException(obj, "Image"); } @Override public Collection duplicate(boolean deepCopy) { try { //if(_image!=null) return new Image(getBufferedImage()); return new Image(getImageBytes(null)); } catch (Exception e) { throw new PageRuntimeException(e.getMessage()); } } public ColorModel getColorModel() throws ExpressionException { return image().getColorModel(); } public void crop(float x, float y, float width, float height) throws ExpressionException { ParameterBlock params = new ParameterBlock(); params.addSource(image()); params.add(x); params.add(y); float w = getWidth(); float h = getHeight(); if (w < x + width) params.add(w - x); else params.add(width); if (h < y + height) params.add(h - y); else params.add(height); image(JAI.create("crop", params).getAsBufferedImage()); } public int getWidth() throws ExpressionException { return image().getWidth(); } public int getHeight() throws ExpressionException { return image().getHeight(); } public String getFormat() { return format; } public byte[] getImageBytes(String format) throws PageException { return getImageBytes(format, false); } public byte[] getImageBytes(String format, boolean noMeta) throws PageException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (JAIUtil.isSupportedWriteFormat(format)) { try { JAIUtil.write(getBufferedImage(), baos, format); } catch (IOException e) { throw Caster.toPageException(e); } } else { ImageOutputStream ios = null; try { ios = ImageIO.createImageOutputStream(baos); _writeOut(ios, format, 1, noMeta); } catch (IOException e) { throw Caster.toPageException(e); } finally { IOUtil.closeEL(ios); } } return baos.toByteArray(); } public void setColor(Color color) throws ExpressionException { if (color == null) return; fgColor = color; getGraphics().setColor(color); } public void setAlpha(float alpha) throws ExpressionException { this.alpha = alpha; Graphics2D g = getGraphics(); Composite alphaComposite; if (composite == null) { if (alpha == 1) return; composite = g.getComposite(); } if (alpha == 1) alphaComposite = composite; else alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); g.setComposite(alphaComposite); //graphics.setComposite(originalComposite); } public void setBackground(Color color) throws ExpressionException { if (color == null) return; bgColor = color; getGraphics().setBackground(color); } public void setAntiAliasing(boolean antiAlias) throws ExpressionException { this.antiAlias = antiAlias ? ANTI_ALIAS_ON : ANTI_ALIAS_OFF; Graphics2D graphics = getGraphics(); if (antiAlias) { graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } else { graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); } } private Struct _info() { try { return info(); } catch (ExpressionException e) { throw new PageRuntimeException(e); } } @Override public void clear() { throw new RuntimeException("can't clear struct, struct is readonly"); } @Override public boolean containsKey(Key key) { return _info().containsKey(key); } @Override public Object get(Key key) throws PageException { return info().get(key); } @Override public Object get(Key key, Object defaultValue) { return _info().get(key, defaultValue); } @Override public Key[] keys() { return _info().keys(); } @Override public Object remove(Key key) throws PageException { throw new ExpressionException("can't remove key [" + key.getString() + "] from struct, struct is readonly"); } @Override public Object removeEL(Key key) { throw new PageRuntimeException( "can't remove key [" + key.getString() + "] from struct, struct is readonly"); } @Override public Object set(Key key, Object value) throws PageException { throw new ExpressionException("can't set key [" + key.getString() + "] to struct, struct is readonly"); } @Override public Object setEL(Key key, Object value) { throw new PageRuntimeException("can't set key [" + key.getString() + "] to struct, struct is readonly"); } @Override public int size() { return _info().size(); } @Override public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { DumpData dd = _info().toDumpData(pageContext, maxlevel, dp); if (dd instanceof DumpTable) ((DumpTable) dd).setTitle("Struct (Image)"); return dd; } @Override public Iterator<Collection.Key> keyIterator() { return _info().keyIterator(); } @Override public Iterator<String> keysAsStringIterator() { return _info().keysAsStringIterator(); } @Override public Iterator<Entry<Key, Object>> entryIterator() { return _info().entryIterator(); } @Override public Iterator<Object> valueIterator() { return _info().valueIterator(); } @Override public boolean castToBooleanValue() throws PageException { return info().castToBooleanValue(); } @Override public Boolean castToBoolean(Boolean defaultValue) { try { return info().castToBoolean(defaultValue); } catch (ExpressionException e) { return defaultValue; } } @Override public DateTime castToDateTime() throws PageException { return info().castToDateTime(); } @Override public DateTime castToDateTime(DateTime defaultValue) { try { return info().castToDateTime(defaultValue); } catch (ExpressionException e) { return defaultValue; } } @Override public double castToDoubleValue() throws PageException { return info().castToDoubleValue(); } @Override public double castToDoubleValue(double defaultValue) { try { return info().castToDoubleValue(defaultValue); } catch (ExpressionException e) { return defaultValue; } } @Override public String castToString() throws PageException { return info().castToString(); } @Override public String castToString(String defaultValue) { try { return info().castToString(defaultValue); } catch (ExpressionException e) { return defaultValue; } } @Override public int compareTo(String str) throws PageException { return info().compareTo(str); } @Override public int compareTo(boolean b) throws PageException { return info().compareTo(b); } @Override public int compareTo(double d) throws PageException { return info().compareTo(d); } @Override public int compareTo(DateTime dt) throws PageException { return info().compareTo(dt); } private static void checkRestriction() { try { if (JAI.class == null) return; } catch (Throwable t) { 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"); } } public static int toInterpolation(String strInterpolation) throws ExpressionException { if (StringUtil.isEmpty(strInterpolation)) throw new ExpressionException("interpolation definition is empty"); strInterpolation = strInterpolation.trim().toLowerCase(); if ("highestquality".equals(strInterpolation)) return IP_HIGHESTQUALITY; else if ("highquality".equals(strInterpolation)) return IP_HIGHQUALITY; else if ("mediumquality".equals(strInterpolation)) return IP_MEDIUMQUALITY; else if ("highestperformance".equals(strInterpolation)) return IP_HIGHESTPERFORMANCE; else if ("highperformance".equals(strInterpolation)) return IP_HIGHPERFORMANCE; else if ("mediumperformance".equals(strInterpolation)) return IP_MEDIUMPERFORMANCE; else if ("nearest".equals(strInterpolation)) return IPC_NEAREST; else if ("bilinear".equals(strInterpolation)) return IPC_BILINEAR; else if ("bicubic".equals(strInterpolation)) return IPC_BICUBIC; else if ("bessel".equals(strInterpolation)) return IP_BESSEL; else if ("blackman".equals(strInterpolation)) return IP_BLACKMAN; else if ("hamming".equals(strInterpolation)) return IP_HAMMING; else if ("hanning".equals(strInterpolation)) return IP_HANNING; else if ("hermite".equals(strInterpolation)) return IP_HERMITE; else if ("lanczos".equals(strInterpolation)) return IP_LANCZOS; else if ("mitchell".equals(strInterpolation)) return IP_MITCHELL; else if ("quadratic".equals(strInterpolation)) return IP_QUADRATIC; throw new ExpressionException("interpolation definition [" + strInterpolation + "] is invalid"); } /** * @return the source */ public Resource getSource() { return source; } @Override public boolean containsValue(Object value) { try { return info().containsValue(value); } catch (ExpressionException e) { return false; } } @Override public java.util.Collection values() { try { return info().values(); } catch (ExpressionException e) { throw new PageRuntimeException(e); } } /** * This method returns true if the specified image has transparent pixels * @param image * @return */ public static boolean hasAlpha(java.awt.Image image) { // If buffered image, the color model is readily available if (image instanceof BufferedImage) { BufferedImage bimage = (BufferedImage) image; return bimage.getColorModel().hasAlpha(); } // Use a pixel grabber to retrieve the image's color model; // grabbing a single pixel is usually sufficient PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); try { pg.grabPixels(); } catch (InterruptedException e) { } // Get the image's color model ColorModel cm = pg.getColorModel(); return cm.hasAlpha(); } // This method returns a buffered image with the contents of an image public static BufferedImage toBufferedImage(java.awt.Image image) { if (image instanceof BufferedImage) { return (BufferedImage) image; } // This code ensures that all the pixels in the image are loaded image = new ImageIcon(image).getImage(); // Determine if the image has transparent pixels; for this method's boolean hasAlpha = hasAlpha(image); // Create a buffered image with a format that's compatible with the screen BufferedImage bimage = null; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); try { // Determine the type of transparency of the new buffered image int transparency = Transparency.OPAQUE; if (hasAlpha) { transparency = Transparency.BITMASK; } // Create the buffered image GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration(); bimage = gc.createCompatibleImage(image.getWidth(null), image.getHeight(null), transparency); } catch (HeadlessException e) { // The system does not have a screen } if (bimage == null) { // Create a buffered image using the default color model int type = BufferedImage.TYPE_INT_RGB; if (hasAlpha) { type = BufferedImage.TYPE_INT_ARGB; } bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type); } // Copy image to buffered image Graphics g = bimage.createGraphics(); // Paint the image onto the buffered image g.drawImage(image, 0, 0, null); g.dispose(); return bimage; } }