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}