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.image.BufferedImage;
036
037import lucee.runtime.engine.ThreadLocalPageContext;
038import lucee.runtime.exp.FunctionException;
039import lucee.runtime.exp.PageException;
040import lucee.runtime.img.ImageUtil;
041import lucee.runtime.type.KeyImpl;
042import lucee.runtime.type.Struct;
043import lucee.runtime.type.util.CollectionUtil;
044
045
046
047/**
048 * A filter which applies a crystallizing effect to an image, by producing Voronoi cells filled with colours from the image.
049 */
050public class CrystallizeFilter extends CellularFilter  implements DynFiltering {
051
052        private float edgeThickness = 0.4f;
053        private boolean fadeEdges = false;
054        private int edgeColor = 0xff000000;
055
056        public CrystallizeFilter() {
057                setScale(16);
058                setRandomness(0.0f);
059        }
060        
061        public void setEdgeThickness(float edgeThickness) {
062                this.edgeThickness = edgeThickness;
063        }
064
065        public float getEdgeThickness() {
066                return edgeThickness;
067        }
068
069        public void setFadeEdges(boolean fadeEdges) {
070                this.fadeEdges = fadeEdges;
071        }
072
073        public boolean getFadeEdges() {
074                return fadeEdges;
075        }
076
077        public void setEdgeColor(int edgeColor) {
078                this.edgeColor = edgeColor;
079        }
080
081        public int getEdgeColor() {
082                return edgeColor;
083        }
084
085        public int getPixel(int x, int y, int[] inPixels, int width, int height) {
086                float nx = m00*x + m01*y;
087                float ny = m10*x + m11*y;
088                nx /= scale;
089                ny /= scale * stretch;
090                nx += 1000;
091                ny += 1000;     // Reduce artifacts around 0,0
092                float f = evaluate(nx, ny);
093
094                float f1 = results[0].distance;
095                float f2 = results[1].distance;
096                int srcx = ImageMath.clamp((int)((results[0].x-1000)*scale), 0, width-1);
097                int srcy = ImageMath.clamp((int)((results[0].y-1000)*scale), 0, height-1);
098                int v = inPixels[srcy * width + srcx];
099                f = (f2 - f1) / edgeThickness;
100                f = ImageMath.smoothStep(0, edgeThickness, f);
101                if (fadeEdges) {
102                        srcx = ImageMath.clamp((int)((results[1].x-1000)*scale), 0, width-1);
103                        srcy = ImageMath.clamp((int)((results[1].y-1000)*scale), 0, height-1);
104                        int v2 = inPixels[srcy * width + srcx];
105                        v2 = ImageMath.mixColors(0.5f, v2, v);
106                        v = ImageMath.mixColors(f, v2, v);
107                } else
108                        v = ImageMath.mixColors(f, edgeColor, v);
109                return v;
110        }
111
112        public String toString() {
113                return "Stylize/Crystallize...";
114        }
115        
116        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
117                Object o;
118                if((o=parameters.removeEL(KeyImpl.init("EdgeThickness")))!=null)setEdgeThickness(ImageFilterUtil.toFloatValue(o,"EdgeThickness"));
119                if((o=parameters.removeEL(KeyImpl.init("FadeEdges")))!=null)setFadeEdges(ImageFilterUtil.toBooleanValue(o,"FadeEdges"));
120                if((o=parameters.removeEL(KeyImpl.init("EdgeColor")))!=null)setEdgeColor(ImageFilterUtil.toColorRGB(o,"EdgeColor"));
121                if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap"));
122                if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount"));
123                if((o=parameters.removeEL(KeyImpl.init("Turbulence")))!=null)setTurbulence(ImageFilterUtil.toFloatValue(o,"Turbulence"));
124                if((o=parameters.removeEL(KeyImpl.init("Stretch")))!=null)setStretch(ImageFilterUtil.toFloatValue(o,"Stretch"));
125                if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle"));
126                if((o=parameters.removeEL(KeyImpl.init("AngleCoefficient")))!=null)setAngleCoefficient(ImageFilterUtil.toFloatValue(o,"AngleCoefficient"));
127                if((o=parameters.removeEL(KeyImpl.init("GradientCoefficient")))!=null)setGradientCoefficient(ImageFilterUtil.toFloatValue(o,"GradientCoefficient"));
128                if((o=parameters.removeEL(KeyImpl.init("F1")))!=null)setF1(ImageFilterUtil.toFloatValue(o,"F1"));
129                if((o=parameters.removeEL(KeyImpl.init("F2")))!=null)setF2(ImageFilterUtil.toFloatValue(o,"F2"));
130                if((o=parameters.removeEL(KeyImpl.init("F3")))!=null)setF3(ImageFilterUtil.toFloatValue(o,"F3"));
131                if((o=parameters.removeEL(KeyImpl.init("F4")))!=null)setF4(ImageFilterUtil.toFloatValue(o,"F4"));
132                if((o=parameters.removeEL(KeyImpl.init("Randomness")))!=null)setRandomness(ImageFilterUtil.toFloatValue(o,"Randomness"));
133                if((o=parameters.removeEL(KeyImpl.init("GridType")))!=null)setGridType(ImageFilterUtil.toString(o,"GridType"));
134                if((o=parameters.removeEL(KeyImpl.init("DistancePower")))!=null)setDistancePower(ImageFilterUtil.toFloatValue(o,"DistancePower"));
135                if((o=parameters.removeEL(KeyImpl.init("Scale")))!=null)setScale(ImageFilterUtil.toFloatValue(o,"Scale"));
136
137                // check for arguments not supported
138                if(parameters.size()>0) {
139                        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 [EdgeThickness, FadeEdges, EdgeColor, Colormap, Amount, Turbulence, Stretch, Angle, Coefficient, AngleCoefficient, GradientCoefficient, F1, F2, F3, F4, Randomness, GridType, DistancePower, Scale]");
140                }
141
142                return filter(src, dst);
143        }
144}