001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.functions.image; 020 021import java.awt.Composite; 022import java.awt.Font; 023import java.awt.Paint; 024import java.awt.Point; 025import java.awt.geom.AffineTransform; 026import java.awt.geom.Point2D; 027import java.awt.image.BufferedImage; 028import java.awt.image.Kernel; 029import java.lang.reflect.Method; 030import java.util.ArrayList; 031import java.util.Arrays; 032import java.util.HashMap; 033import java.util.Map; 034 035import lucee.commons.lang.StringUtil; 036import lucee.runtime.PageContext; 037import lucee.runtime.exp.FunctionException; 038import lucee.runtime.exp.PageException; 039import lucee.runtime.img.Image; 040import lucee.runtime.img.filter.*; 041import lucee.runtime.img.math.BinaryFunction; 042import lucee.runtime.img.math.Function2D; 043import lucee.runtime.op.Caster; 044import lucee.runtime.type.Struct; 045import lucee.runtime.type.StructImpl; 046import lucee.runtime.type.util.ListUtil; 047 048public class ImageFilter { 049 private static final Struct EMPTY_STRUCT = new StructImpl(); 050 private static Map<String,Class> filters=new HashMap<String, Class>(); 051 static { 052 filters.put("applymask",ApplyMaskFilter.class); 053 filters.put("average",AverageFilter.class); 054 filters.put("bicubicscale",BicubicScaleFilter.class); 055 filters.put("block",BlockFilter.class); 056 filters.put("blur",BlurFilter.class); 057 filters.put("border",BorderFilter.class); 058 filters.put("boxblur",BoxBlurFilter.class); 059 filters.put("brushedmetal",BrushedMetalFilter.class); 060 filters.put("bump",BumpFilter.class); 061 filters.put("caustics",CausticsFilter.class); 062 filters.put("cellular",CellularFilter.class); 063 filters.put("channelmix",ChannelMixFilter.class); 064 filters.put("check",CheckFilter.class); 065 filters.put("chromakey",ChromaKeyFilter.class); 066 filters.put("chrome",ChromeFilter.class); 067 filters.put("circle",CircleFilter.class); 068 ////////filters.put("composite",CompositeFilter.class); 069 //filters.put("compound",CompoundFilter.class); 070 filters.put("contour",ContourFilter.class); 071 filters.put("contrast",ContrastFilter.class); 072 filters.put("convolve",ConvolveFilter.class); 073 filters.put("crop",CropFilter.class); 074 filters.put("crystallize",CrystallizeFilter.class); 075 filters.put("curl",CurlFilter.class); 076 filters.put("curves",CurvesFilter.class); 077 filters.put("despeckle",DespeckleFilter.class); 078 filters.put("diffuse",DiffuseFilter.class); 079 filters.put("diffusion",DiffusionFilter.class); 080 filters.put("dilate",DilateFilter.class); 081 filters.put("displace",DisplaceFilter.class); 082 filters.put("dissolve",DissolveFilter.class); 083 filters.put("dither",DitherFilter.class); 084 filters.put("edge",EdgeFilter.class); 085 filters.put("emboss",EmbossFilter.class); 086 filters.put("equalize",EqualizeFilter.class); 087 filters.put("erodealpha",ErodeAlphaFilter.class); 088 filters.put("erode",ErodeFilter.class); 089 filters.put("exposure",ExposureFilter.class); 090 filters.put("fade",FadeFilter.class); 091 filters.put("fbm",FBMFilter.class); 092 filters.put("feedback",FeedbackFilter.class); 093 filters.put("fieldwarp",FieldWarpFilter.class); 094 filters.put("fill",FillFilter.class); 095 filters.put("flare",FlareFilter.class); 096 filters.put("flip",FlipFilter.class); 097 filters.put("flush3d",Flush3DFilter.class); 098 filters.put("fourcolor",FourColorFilter.class); 099 filters.put("gain",GainFilter.class); 100 filters.put("gamma",GammaFilter.class); 101 filters.put("gaussian",GaussianFilter.class); 102 filters.put("glint",GlintFilter.class); 103 filters.put("glow",GlowFilter.class); 104 filters.put("gradient",GradientFilter.class); 105 filters.put("gradientwipe",GradientWipeFilter.class); 106 filters.put("gray",GrayFilter.class); 107 filters.put("grayscale",GrayscaleFilter.class); 108 filters.put("halftone",HalftoneFilter.class); 109 filters.put("hsbadjust",HSBAdjustFilter.class); 110 filters.put("interpolate",InterpolateFilter.class); 111 filters.put("invertalpha",InvertAlphaFilter.class); 112 filters.put("invert",InvertFilter.class); 113 //filters.put("iterated",IteratedFilter.class); 114 filters.put("javalnf",JavaLnFFilter.class); 115 filters.put("kaleidoscope",KaleidoscopeFilter.class); 116 //filters.put("key",KeyFilter.class); 117 filters.put("lensblur",LensBlurFilter.class); 118 filters.put("levels",LevelsFilter.class); 119 filters.put("life",LifeFilter.class); 120 filters.put("light",LightFilter.class); 121 filters.put("lookup",LookupFilter.class); 122 filters.put("mapcolors",MapColorsFilter.class); 123 filters.put("map",MapFilter.class); 124 filters.put("marble",MarbleFilter.class); 125 filters.put("marbletex",MarbleTexFilter.class); 126 filters.put("mask",MaskFilter.class); 127 filters.put("maximum",MaximumFilter.class); 128 filters.put("median",MedianFilter.class); 129 filters.put("minimum",MinimumFilter.class); 130 filters.put("mirror",MirrorFilter.class); 131 filters.put("motionblur",MotionBlurFilter.class); 132 //filters.put("mutatable",MutatableFilter.class); 133 filters.put("noise",NoiseFilter.class); 134 filters.put("offset",OffsetFilter.class); 135 filters.put("oil",OilFilter.class); 136 filters.put("opacity",OpacityFilter.class); 137 filters.put("outline",OutlineFilter.class); 138 filters.put("perspective",PerspectiveFilter.class); 139 filters.put("pinch",PinchFilter.class); 140 filters.put("plasma",PlasmaFilter.class); 141 filters.put("pointillize",PointillizeFilter.class); 142 filters.put("polar",PolarFilter.class); 143 filters.put("posterize",PosterizeFilter.class); 144 //filters.put("premultiply",PremultiplyFilter.class); 145 filters.put("quantize",QuantizeFilter.class); 146 filters.put("quilt",QuiltFilter.class); 147 filters.put("rays",RaysFilter.class); 148 filters.put("reducenoise",ReduceNoiseFilter.class); 149 filters.put("rendertext",RenderTextFilter.class); 150 filters.put("rescale",RescaleFilter.class); 151 filters.put("rgbadjust",RGBAdjustFilter.class); 152 filters.put("ripple",RippleFilter.class); 153 filters.put("rotate",RotateFilter.class); 154 filters.put("saturation",SaturationFilter.class); 155 filters.put("scale",ScaleFilter.class); 156 filters.put("scratch",ScratchFilter.class); 157 filters.put("shade",ShadeFilter.class); 158 filters.put("shadow",ShadowFilter.class); 159 filters.put("shape",ShapeFilter.class); 160 filters.put("sharpen",SharpenFilter.class); 161 filters.put("shatter",ShatterFilter.class); 162 filters.put("shear",ShearFilter.class); 163 filters.put("shine",ShineFilter.class); 164 filters.put("skeleton",SkeletonFilter.class); 165 //filters.put("sky",SkyFilter.class); 166 filters.put("smartblur",SmartBlurFilter.class); 167 filters.put("smear",SmearFilter.class); 168 filters.put("solarize",SolarizeFilter.class); 169 filters.put("sparkle",SparkleFilter.class); 170 filters.put("sphere",SphereFilter.class); 171 filters.put("stamp",StampFilter.class); 172 filters.put("swim",SwimFilter.class); 173 filters.put("texture",TextureFilter.class); 174 filters.put("threshold",ThresholdFilter.class); 175 filters.put("tileimage",TileImageFilter.class); 176 //filters.put("transfer",TransferFilter.class); 177 //filters.put("transform",TransformFilter.class); 178 //filters.put("transition",TransitionFilter.class); 179 filters.put("twirl",TwirlFilter.class); 180 //filters.put("unpremultiply",UnpremultiplyFilter.class); 181 filters.put("unsharp",UnsharpFilter.class); 182 filters.put("variableblur",VariableBlurFilter.class); 183 //filters.put("warp",WarpFilter.class); 184 filters.put("water",WaterFilter.class); 185 filters.put("weave",WeaveFilter.class); 186 filters.put("wholeimage",WholeImageFilter.class); 187 filters.put("wood",WoodFilter.class); 188 } 189 190 191 192 public static String call(PageContext pc, Object name, String filterName) throws PageException { 193 return call(pc, name, filterName, EMPTY_STRUCT); 194 } 195 public static String call(PageContext pc, Object name, String filterName, Struct parameters) throws PageException { 196 //if(name instanceof String) name=pc.getVariable(Caster.toString(name)); 197 Image img = Image.toImage(pc,name); 198 String lcFilterName = filterName.trim().toLowerCase(); 199 200 // get filter class 201 Class clazz = filters.get(lcFilterName); 202 if(clazz==null) { 203 String[] keys = filters.keySet().toArray(new String[filters.size()]); 204 Arrays.sort(keys); 205 String list=ListUtil.arrayToList(keys, ", "); 206 207 String soundex = StringUtil.soundex(filterName); 208 java.util.List<String> similar=new ArrayList<String>(); 209 for(int i=0;i<keys.length;i++){ 210 if(StringUtil.soundex(keys[i]).equals(soundex)) 211 similar.add(keys[i]); 212 } 213 if(similar.size()>0) { 214 list=ListUtil.arrayToList(similar.toArray(new String[similar.size()]), ", "); 215 throw new FunctionException(pc, "ImageFilter", 2, "filtername", "invalid filter name ["+filterName+"], did you mean ["+list+"]"); 216 } 217 throw new FunctionException(pc, "ImageFilter", 2, "filtername", "invalid filter name ["+filterName+"], valid filter names are ["+list+"]"); 218 } 219 220 // load filter 221 DynFiltering filter=null; 222 try { 223 filter=(DynFiltering) clazz.newInstance(); 224 } catch (Throwable t) { 225 throw Caster.toPageException(t); 226 } 227 228 // execute filter 229 BufferedImage bi = img.getBufferedImage(); 230 //BufferedImage empty = bi;//ImageUtil.createBufferedImage(bi); 231 img.image(filter.filter(bi, parameters)); 232 233 return null; 234 } 235 236 private static void setters(String key, Class clazz, StringBuilder sb) { 237 238 //sb.append("Object o;\n"); 239 sb.append(" public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);\n"); 240 sb.append(" Object o;\n"); 241 242 Method[] methods = clazz.getMethods(); 243 Method method; 244 StringBuilder names=new StringBuilder(); 245 for(int i=0;i<methods.length;i++){ 246 method=methods[i]; 247 if(method.getName().startsWith("set") && !method.getName().equals("setRGB") && !method.getName().equals("setDestination")){ 248 String name=method.getName().substring(3); 249 args(key,name,method,sb,i); 250 if(names.length()>0) names.append(", "); 251 names.append(name); 252 253 } 254 } 255 sb.append("\n"); 256 sb.append(" // check for arguments not supported\n"); 257 sb.append(" if(parameters.size()>0) {\n"); 258 sb.append(" throw new FunctionException(ThreadLocalPageContext.get(), \"ImageFilter\", 3, \"parameters\", \"the parameter\"+(parameters.size()>1?\"s\":\"\")+\" [\"+List.arrayToList(parameters.keys(),\", \")+\"] \"+(parameters.size()>1?\"are\":\"is\")+\" not allowed, only the following parameters are supported ["+names+"]\");\n"); 259 sb.append(" }\n"); 260 sb.append("\n"); 261 262 263 264 265 266 267 sb.append(" return filter(src, dst);\n"); 268 sb.append(" }\n"); 269 270 271 } 272 private static void args(String className, String name, Method method, StringBuilder sb, int methodIndex) { 273 Class[] params = method.getParameterTypes(); 274 275 276 if(params.length==1){ 277 sb.append(" if((o=parameters.removeEL(KeyImpl.init(\""+name+"\")))!=null)"); 278 sb.append(method.getName()+"("); 279 arg(name, params[0], method,sb,methodIndex); 280 281 sb.append(");\n"); 282 } 283 else if(params.length==2 && name.equals("Dimensions")){ 284 sb.append(" if((o=parameters.removeEL(KeyImpl.init(\""+name+"\")))!=null){\n"); 285 sb.append(" int[] dim=ImageFilterUtil.toDimensions(o,\"Dimensions\");\n"); 286 sb.append(" "+method.getName()+"(dim[0],dim[1]"); 287 sb.append(");\n"); 288 sb.append(" }\n"); 289 } 290 //else print.e(className+"->"+method); 291 292 } 293 private static void arg(String name, Class param, Method method, StringBuilder sb, int methodIndex) { 294 295 296 sb.append("ImageFilterUtil."); 297 if(param==float.class) sb.append("toFloatValue"); 298 else if(param==boolean.class) sb.append("toBooleanValue"); 299 else if(param==int.class) sb.append("toIntValue"); 300 else if(param==Point2D.class) sb.append("toPoint2D"); 301 else if(param==WarpGrid.class) sb.append("toWarpGrid"); 302 else if(param==Kernel.class) sb.append("toKernel"); 303 else if(param==Colormap.class) sb.append("toColormap"); 304 else if(param==Function2D.class) sb.append("toFunction2D"); 305 else if(param==BufferedImage.class) sb.append("toBufferedImage"); 306 else if(param==BinaryFunction.class) sb.append("toBinaryFunction"); 307 else if(param==String.class) sb.append("toString"); 308 else if(param==Paint.class) sb.append("toPaint"); 309 else if(param==Font.class) sb.append("toFont"); 310 else if(param==AffineTransform.class) sb.append("toAffineTransform"); 311 else if(param==Composite.class) sb.append("toComposite"); 312 else if(param==LightFilter.Material.class) sb.append("toLightFilter$Material"); 313 //else if(param==FieldWarpFilter.Line.class) sb.append("toFieldWarpFilter$Line"); 314 else if(param==FieldWarpFilter.Line[].class) sb.append("toAFieldWarpFilter$Line"); 315 else if(param==CurvesFilter.Curve.class) sb.append("toCurvesFilter$Curve"); 316 else if(param==CurvesFilter.Curve[].class) sb.append("toACurvesFilter$Curve"); 317 else if(param==Point.class) sb.append("toPoint"); 318 319 320 else if(param==int[].class) sb.append("toAInt"); 321 else if(param==int[][].class) sb.append("toAAInt"); 322 else if(param==float[].class) sb.append("toAFloat"); 323 324 else { 325 new RuntimeException(name+"!!!!!!!!!!!!!"+param.getName()).printStackTrace(); 326 } 327 sb.append("(o,\""+name+"\")"); 328 329 } 330 331 332}