001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019/* 020* 021 022Licensed under the Apache License, Version 2.0 (the "License"); 023you may not use this file except in compliance with the License. 024You may obtain a copy of the License at 025 026 http://www.apache.org/licenses/LICENSE-2.0 027 028Unless required by applicable law or agreed to in writing, software 029distributed under the License is distributed on an "AS IS" BASIS, 030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 031See the License for the specific language governing permissions and 032limitations under the License. 033*/ 034 035package lucee.runtime.img.filter;import java.awt.AlphaComposite; 036import java.awt.Graphics2D; 037import java.awt.image.BufferedImage; 038 039import lucee.runtime.engine.ThreadLocalPageContext; 040import lucee.runtime.exp.FunctionException; 041import lucee.runtime.exp.PageException; 042import lucee.runtime.img.ImageUtil; 043import lucee.runtime.img.composite.MiscComposite; 044import lucee.runtime.type.KeyImpl; 045import lucee.runtime.type.Struct; 046import lucee.runtime.type.util.CollectionUtil; 047 048/** 049 * A filter which produces the effect of light rays shining out of an image. 050 */ 051public class RaysFilter extends MotionBlurOp implements DynFiltering { 052 053 private float opacity = 1.0f; 054 private float threshold = 0.0f; 055 private float strength = 0.5f; 056 private boolean raysOnly = false; 057 private Colormap colormap=new GrayscaleColormap(); 058 059 public RaysFilter() { 060 } 061 062 /** 063 * Set the opacity of the rays. 064 * @param opacity the opacity. 065 * @see #getOpacity 066 */ 067 public void setOpacity(float opacity) { 068 this.opacity = opacity; 069 } 070 071 /** 072 * Get the opacity of the rays. 073 * @return the opacity. 074 * @see #setOpacity 075 */ 076 public float getOpacity() { 077 return opacity; 078 } 079 080 /** 081 * Set the threshold value. 082 * @param threshold the threshold value 083 * @see #getThreshold 084 */ 085 public void setThreshold( float threshold ) { 086 this.threshold = threshold; 087 } 088 089 /** 090 * Get the threshold value. 091 * @return the threshold value 092 * @see #setThreshold 093 */ 094 public float getThreshold() { 095 return threshold; 096 } 097 098 /** 099 * Set the strength of the rays. 100 * @param strength the strength. 101 * @see #getStrength 102 */ 103 public void setStrength( float strength ) { 104 this.strength = strength; 105 } 106 107 /** 108 * Get the strength of the rays. 109 * @return the strength. 110 * @see #setStrength 111 */ 112 public float getStrength() { 113 return strength; 114 } 115 116 /** 117 * Set whether to render only the rays. 118 * @param raysOnly true to render rays only. 119 * @see #getRaysOnly 120 */ 121 public void setRaysOnly(boolean raysOnly) { 122 this.raysOnly = raysOnly; 123 } 124 125 /** 126 * Get whether to render only the rays. 127 * @return true to render rays only. 128 * @see #setRaysOnly 129 */ 130 public boolean getRaysOnly() { 131 return raysOnly; 132 } 133 134 /** 135 * Set the colormap to be used for the filter. 136 * @param colormap the colormap 137 * @see #getColormap 138 */ 139 public void setColormap(Colormap colormap) { 140 this.colormap = colormap; 141 } 142 143 /** 144 * Get the colormap to be used for the filter. 145 * @return the colormap 146 * @see #setColormap 147 */ 148 public Colormap getColormap() { 149 return colormap; 150 } 151 152 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 153 int width = src.getWidth(); 154 int height = src.getHeight(); 155 int[] pixels = new int[width]; 156 int[] srcPixels = new int[width]; 157 158 BufferedImage rays = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 159 160 int threshold3 = (int)(threshold*3*255); 161 for ( int y = 0; y < height; y++ ) { 162 getRGB( src, 0, y, width, 1, pixels ); 163 for ( int x = 0; x < width; x++ ) { 164 int rgb = pixels[x]; 165 int a = rgb & 0xff000000; 166 int r = (rgb >> 16) & 0xff; 167 int g = (rgb >> 8) & 0xff; 168 int b = rgb & 0xff; 169 int l = r + g + b; 170 if (l < threshold3) 171 pixels[x] = 0xff000000; 172 else { 173 l /= 3; 174 pixels[x] = a | (l << 16) | (l << 8) | l; 175 } 176 } 177 setRGB( rays, 0, y, width, 1, pixels ); 178 } 179 180 rays = super.filter( rays, null ); 181 182 for ( int y = 0; y < height; y++ ) { 183 getRGB( rays, 0, y, width, 1, pixels ); 184 getRGB( src, 0, y, width, 1, srcPixels ); 185 for ( int x = 0; x < width; x++ ) { 186 int rgb = pixels[x]; 187 int a = rgb & 0xff000000; 188 int r = (rgb >> 16) & 0xff; 189 int g = (rgb >> 8) & 0xff; 190 int b = rgb & 0xff; 191 192 if ( colormap != null ) { 193 int l = r + g + b; 194 rgb = colormap.getColor( l * strength * (1/3f) ); 195 } else { 196 r = PixelUtils.clamp((int)(r * strength)); 197 g = PixelUtils.clamp((int)(g * strength)); 198 b = PixelUtils.clamp((int)(b * strength)); 199 rgb = a | (r << 16) | (g << 8) | b; 200 } 201 202 pixels[x] = rgb; 203 } 204 setRGB( rays, 0, y, width, 1, pixels ); 205 } 206 207 if ( dst == null ) 208 dst = createCompatibleDestImage( src, null ); 209 210 Graphics2D g = dst.createGraphics(); 211 if ( !raysOnly ) { 212 g.setComposite( AlphaComposite.SrcOver ); 213 g.drawRenderedImage( src, null ); 214 } 215 g.setComposite( MiscComposite.getInstance( MiscComposite.ADD, opacity ) ); 216 g.drawRenderedImage( rays, null ); 217 g.dispose(); 218 219 return dst; 220 } 221 222 public String toString() { 223 return "Stylize/Rays..."; 224 } 225 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 226 Object o; 227 if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap")); 228 if((o=parameters.removeEL(KeyImpl.init("Strength")))!=null)setStrength(ImageFilterUtil.toFloatValue(o,"Strength")); 229 if((o=parameters.removeEL(KeyImpl.init("Opacity")))!=null)setOpacity(ImageFilterUtil.toFloatValue(o,"Opacity")); 230 if((o=parameters.removeEL(KeyImpl.init("RaysOnly")))!=null)setRaysOnly(ImageFilterUtil.toBooleanValue(o,"RaysOnly")); 231 if((o=parameters.removeEL(KeyImpl.init("Threshold")))!=null)setThreshold(ImageFilterUtil.toFloatValue(o,"Threshold")); 232 if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle")); 233 if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX")); 234 if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY")); 235 //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre")); 236 if((o=parameters.removeEL(KeyImpl.init("Distance")))!=null)setDistance(ImageFilterUtil.toFloatValue(o,"Distance")); 237 if((o=parameters.removeEL(KeyImpl.init("Rotation")))!=null)setRotation(ImageFilterUtil.toFloatValue(o,"Rotation")); 238 if((o=parameters.removeEL(KeyImpl.init("Zoom")))!=null)setZoom(ImageFilterUtil.toFloatValue(o,"Zoom")); 239 240 // check for arguments not supported 241 if(parameters.size()>0) { 242 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [Colormap, Strength, Opacity, RaysOnly, Threshold, Angle, CentreX, CentreY, Centre, Distance, Rotation, Zoom]"); 243 } 244 245 return filter(src, dst); 246 } 247}