001    /*
002    *
003    
004    Licensed under the Apache License, Version 2.0 (the "License");
005    you may not use this file except in compliance with the License.
006    You may obtain a copy of the License at
007    
008       http://www.apache.org/licenses/LICENSE-2.0
009    
010    Unless required by applicable law or agreed to in writing, software
011    distributed under the License is distributed on an "AS IS" BASIS,
012    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013    See the License for the specific language governing permissions and
014    limitations under the License.
015    */
016    
017    package railo.runtime.img.filter;import java.awt.Rectangle;
018    import java.awt.image.BufferedImage;
019    import java.util.Date;
020    import java.util.Random;
021    
022    import railo.runtime.engine.ThreadLocalPageContext;
023    import railo.runtime.exp.FunctionException;
024    import railo.runtime.exp.PageException;
025    import railo.runtime.img.ImageUtil;
026    import railo.runtime.type.KeyImpl;
027    import railo.runtime.type.List;
028    import railo.runtime.type.Struct;
029    
030    public class PlasmaFilter extends WholeImageFilter  implements DynFiltering {
031            
032            public float turbulence = 1.0f;
033            private float scaling = 0.0f;
034            private Colormap colormap = new LinearColormap();
035            private Random randomGenerator;
036            private long seed = 567;
037            private boolean useColormap = false;
038            private boolean useImageColors = false;
039    
040            public PlasmaFilter() {
041                    randomGenerator = new Random();
042            }
043    
044            /**
045         * Specifies the turbulence of the texture.
046         * @param turbulence the turbulence of the texture.
047         * @min-value 0
048         * @max-value 10
049         * @see #getTurbulence
050         */
051            public void setTurbulence(float turbulence) {
052                    this.turbulence = turbulence;
053            }
054    
055            /**
056         * Returns the turbulence of the effect.
057         * @return the turbulence of the effect.
058         * @see #setTurbulence
059         */
060            public float getTurbulence() {
061                    return turbulence;
062            }
063    
064            public void setScaling(float scaling) {
065                    this.scaling = scaling;
066            }
067    
068            public float getScaling() {
069                    return scaling;
070            }
071    
072        /**
073         * Set the colormap to be used for the filter.
074         * @param colormap the colormap
075         * @see #getColormap
076         */
077            public void setColormap(Colormap colormap) {
078                    this.colormap = colormap;
079            }
080            
081        /**
082         * Get the colormap to be used for the filter.
083         * @return the colormap
084         * @see #setColormap
085         */
086            public Colormap getColormap() {
087                    return colormap;
088            }
089            
090            public void setUseColormap(boolean useColormap) {
091                    this.useColormap = useColormap;
092            }
093    
094            public boolean getUseColormap() {
095                    return useColormap;
096            }
097    
098            public void setUseImageColors(boolean useImageColors) {
099                    this.useImageColors = useImageColors;
100            }
101    
102            public boolean getUseImageColors() {
103                    return useImageColors;
104            }
105    
106            public void setSeed(int seed) {
107                    this.seed = seed;
108            }
109    
110            public int getSeed() {
111                    return (int)seed;
112            }
113    
114            public void randomize() {
115                    seed = new Date().getTime();
116            }
117            
118            private int randomRGB(int[] inPixels, int x, int y) {
119                    if (useImageColors) {
120                            return inPixels[y*originalSpace.width+x];
121                    } 
122                    int r = (int)(255 * randomGenerator.nextFloat());
123                    int g = (int)(255 * randomGenerator.nextFloat());
124                    int b = (int)(255 * randomGenerator.nextFloat());
125                    return 0xff000000 | (r << 16) | (g << 8) | b;
126            }
127    
128            private int displace(int rgb, float amount) {
129                    int r = (rgb >> 16) & 0xff;
130                    int g = (rgb >> 8) & 0xff;
131                    int b = rgb & 0xff;
132                    r = PixelUtils.clamp(r + (int)(amount * (randomGenerator.nextFloat()-0.5)));
133                    g = PixelUtils.clamp(g + (int)(amount * (randomGenerator.nextFloat()-0.5)));
134                    b = PixelUtils.clamp(b + (int)(amount * (randomGenerator.nextFloat()-0.5)));
135                    return 0xff000000 | (r << 16) | (g << 8) | b;
136            }
137    
138            private int average(int rgb1, int rgb2) {
139                    return PixelUtils.combinePixels(rgb1, rgb2, PixelUtils.AVERAGE);
140            }
141    
142            private int getPixel(int x, int y, int[] pixels, int stride) {
143                    return pixels[y*stride+x];
144            }
145            
146            private void putPixel(int x, int y, int rgb, int[] pixels, int stride) {
147                    pixels[y*stride+x] = rgb;
148            }
149            
150            private boolean doPixel(int x1, int y1, int x2, int y2, int[] pixels, int stride, int depth, int scale) {
151                    int mx, my;
152    
153                    if (depth == 0) {
154                            int ml, mr, mt, mb, mm, t;
155    
156                            int tl = getPixel(x1, y1, pixels, stride);
157                            int bl = getPixel(x1, y2, pixels, stride);
158                            int tr = getPixel(x2, y1, pixels, stride);
159                            int br = getPixel(x2, y2, pixels, stride);
160    
161                            float amount = (256.0f / (2.0f * scale)) * turbulence;
162    
163                            mx = (x1 + x2) / 2;
164                            my = (y1 + y2) / 2;
165    
166                            if (mx == x1 && mx == x2 && my == y1 && my == y2)
167                                    return true;
168    
169                            if (mx != x1 || mx != x2) {
170                                    ml = average(tl, bl);
171                                    ml = displace(ml, amount);
172                                    putPixel(x1, my, ml, pixels, stride);
173    
174                                    if (x1 != x2){
175                                            mr = average(tr, br);
176                                            mr = displace(mr, amount);
177                                            putPixel(x2, my, mr, pixels, stride);
178                                    }
179                            }
180    
181                            if (my != y1 || my != y2){
182                                    if (x1 != mx || my != y2){
183                                            mb = average(bl, br);
184                                            mb = displace(mb, amount);
185                                            putPixel(mx, y2, mb, pixels, stride);
186                                    }
187    
188                                    if (y1 != y2){
189                                            mt = average(tl, tr);
190                                            mt = displace(mt, amount);
191                                            putPixel(mx, y1, mt, pixels, stride);
192                                    }
193                            }
194    
195                            if (y1 != y2 || x1 != x2) {
196                                    mm = average(tl, br);
197                                    t = average(bl, tr);
198                                    mm = average(mm, t);
199                                    mm = displace(mm, amount);
200                                    putPixel(mx, my, mm, pixels, stride);
201                            }
202    
203                            if (x2-x1 < 3 && y2-y1 < 3)
204                                    return false;
205                            return true;
206                    }
207    
208                    mx = (x1 + x2) / 2;
209                    my = (y1 + y2) / 2;
210    
211                    doPixel(x1, y1, mx, my, pixels, stride, depth-1, scale+1);
212                    doPixel(x1, my, mx ,y2, pixels, stride, depth-1, scale+1);
213                    doPixel(mx, y1, x2 , my, pixels, stride, depth-1, scale+1);
214                    return doPixel(mx, my, x2, y2, pixels, stride, depth-1, scale+1);
215            }
216            
217            protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
218                    int[] outPixels = new int[width * height];
219    
220                    randomGenerator.setSeed(seed);
221    
222                    int w1 = width-1;
223                    int h1 = height-1;
224                    putPixel(0, 0, randomRGB(inPixels, 0, 0), outPixels, width);
225                    putPixel(w1, 0, randomRGB(inPixels, w1, 0), outPixels, width);
226                    putPixel(0, h1, randomRGB(inPixels, 0, h1), outPixels, width);
227                    putPixel(w1, h1, randomRGB(inPixels, w1, h1), outPixels, width);
228                    putPixel(w1/2, h1/2, randomRGB(inPixels, w1/2, h1/2), outPixels, width);
229                    putPixel(0, h1/2, randomRGB(inPixels, 0, h1/2), outPixels, width);
230                    putPixel(w1, h1/2, randomRGB(inPixels, w1, h1/2), outPixels, width);
231                    putPixel(w1/2, 0, randomRGB(inPixels, w1/2, 0), outPixels, width);
232                    putPixel(w1/2, h1, randomRGB(inPixels, w1/2, h1), outPixels, width);
233    
234                    int depth = 1;
235                    while (doPixel(0, 0, width-1, height-1, outPixels, width, depth, 0))
236                            depth++;
237    
238                    if (useColormap && colormap != null) {
239                            int index = 0;
240                            for (int y = 0; y < height; y++) {
241                                    for (int x = 0; x < width; x++) {
242                                            outPixels[index] = colormap.getColor((outPixels[index] & 0xff)/255.0f);
243                                            index++;
244                                    }
245                            }
246                    }
247                    return outPixels;
248            }
249    
250            public String toString() {
251                    return "Texture/Plasma...";
252            }
253            
254            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
255                    Object o;
256                    if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap"));
257                    if((o=parameters.removeEL(KeyImpl.init("Turbulence")))!=null)setTurbulence(ImageFilterUtil.toFloatValue(o,"Turbulence"));
258                    if((o=parameters.removeEL(KeyImpl.init("Scaling")))!=null)setScaling(ImageFilterUtil.toFloatValue(o,"Scaling"));
259                    if((o=parameters.removeEL(KeyImpl.init("UseColormap")))!=null)setUseColormap(ImageFilterUtil.toBooleanValue(o,"UseColormap"));
260                    if((o=parameters.removeEL(KeyImpl.init("UseImageColors")))!=null)setUseImageColors(ImageFilterUtil.toBooleanValue(o,"UseImageColors"));
261                    if((o=parameters.removeEL(KeyImpl.init("Seed")))!=null)setSeed(ImageFilterUtil.toIntValue(o,"Seed"));
262    
263                    // check for arguments not supported
264                    if(parameters.size()>0) {
265                            throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [Colormap, Turbulence, Scaling, UseColormap, UseImageColors, Seed]");
266                    }
267    
268                    return filter(src, dst);
269            }
270    }