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    
020    import railo.runtime.engine.ThreadLocalPageContext;
021    import railo.runtime.exp.FunctionException;
022    import railo.runtime.exp.PageException;
023    import railo.runtime.img.ImageUtil;
024    import railo.runtime.type.KeyImpl;
025    import railo.runtime.type.Struct;
026    import railo.runtime.type.util.CollectionUtil;
027    
028    public class ShapeFilter extends WholeImageFilter  implements DynFiltering {
029    
030            public final static int LINEAR = 0;
031            public final static int CIRCLE_UP = 1;
032            public final static int CIRCLE_DOWN = 2;
033            public final static int SMOOTH = 3;
034    
035            private float factor = 1.0f;
036            protected Colormap colormap;
037            private boolean useAlpha = true;
038            private boolean invert = false;
039            private boolean merge = false;
040            private int type;
041    
042            private final static int one   = 41;
043            private final static int sqrt2 = (int)(41*Math.sqrt(2));
044            private final static int sqrt5 = (int)(41*Math.sqrt(5));
045    
046            public ShapeFilter() {
047                    colormap = new LinearColormap();
048            }
049    
050            public void setFactor(float factor) {
051                    this.factor = factor;
052            }
053    
054            public float getFactor() {
055                    return factor;
056            }
057    
058        /**
059         * Set the colormap to be used for the filter.
060         * @param colormap the colormap
061         * @see #getColormap
062         */
063            public void setColormap(Colormap colormap) {
064                    this.colormap = colormap;
065            }
066    
067        /**
068         * Get the colormap to be used for the filter.
069         * @return the colormap
070         * @see #setColormap
071         */
072            public Colormap getColormap() {
073                    return colormap;
074            }
075    
076            public void setUseAlpha(boolean useAlpha) {
077                    this.useAlpha = useAlpha;
078            }
079    
080            public boolean getUseAlpha() {
081                    return useAlpha;
082            }
083    
084            public void setType(int type) {
085                    this.type = type;
086            }
087    
088            public int getType() {
089                    return type;
090            }
091    
092            public void setInvert(boolean invert) {
093                    this.invert = invert;
094            }
095    
096            public boolean getInvert() {
097                    return invert;
098            }
099    
100            public void setMerge(boolean merge) {
101                    this.merge = merge;
102            }
103    
104            public boolean getMerge() {
105                    return merge;
106            }
107    
108            protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
109                    int[] map = new int[width * height];
110                    makeMap(inPixels, map, width, height);
111                    int max = distanceMap(map, width, height);
112                    applyMap(map, inPixels, width, height, max);
113    
114                    return inPixels;
115            }
116    
117            public int distanceMap(int[] map, int width, int height) {
118                    int xmax = width - 3;
119                    int ymax = height - 3;
120                    int max = 0;
121                    int v;
122                    
123                    for (int y = 0; y < height; y++) {
124                            for (int x = 0; x < width; x++) {
125                                    int offset = x + y * width;
126                                    if (map[offset] > 0) {
127                                            if (x < 2 || x > xmax || y < 2 || y > ymax)
128                                                    v = setEdgeValue(x, y, map, width, offset, xmax, ymax);
129                                            else
130                                                    v = setValue(map, width, offset);
131                                            if (v > max)
132                                                    max = v;
133                                    }
134                            }
135                    }
136                    for (int y = height-1; y >= 0; y--) {
137                            for (int x = width-1; x >= 0; x--) {
138                                    int offset = x + y * width;
139                                    if (map[offset] > 0) {
140                                            if (x < 2 || x > xmax || y < 2 || y > ymax)
141                                                    v = setEdgeValue(x, y, map, width, offset, xmax, ymax);
142                                            else
143                                                    v = setValue(map, width, offset);
144                                            if (v > max)
145                                                    max = v;
146                                    }
147                            }
148                    }
149                    return max;
150            }
151    
152            private void makeMap(int[] pixels, int[] map, int width, int height) {
153                    for (int y = 0; y < height; y++) {
154                            for (int x = 0; x < width; x++) {
155                                    int offset = x + y * width;
156                                    int b = useAlpha ? (pixels[offset] >> 24) & 0xff : PixelUtils.brightness(pixels[offset]);
157    //                              map[offset] = b * one;
158                                    map[offset] = b * one / 10;
159                            }
160                    }
161            }
162    
163            private void applyMap(int[] map, int[] pixels, int width, int height, int max) {
164                    if (max == 0)
165                            max = 1;
166                    for (int y = 0; y < height; y++) {
167                            for (int x = 0; x < width; x++) {
168                                    int offset = x + y * width;
169                                    int m = map[offset];
170                                    float v = 0;
171                                    int sa = 0, sr = 0, sg = 0, sb = 0;
172    
173                                    if (m == 0) {
174                                            // default color
175                                            sa = sr = sg = sb = 0;
176                                            sa = (pixels[offset] >> 24) & 0xff;
177                                    } else {
178                                            // get V from map
179                                            v = ImageMath.clamp(factor * m / max, 0, 1);
180                                            switch (type) {
181                                            case CIRCLE_UP :
182                                                    v = (ImageMath.circleUp(v));
183                                                    break;
184                                            case CIRCLE_DOWN :
185                                                    v = (ImageMath.circleDown(v));
186                                                    break;
187                                            case SMOOTH :
188                                                    v = (ImageMath.smoothStep(0, 1, v));
189                                                    break;
190                                            }
191    
192                                            if (colormap == null) {
193                                                    sr = sg = sb = (int)(v*255);
194                                            } else {
195                                                    int c = (colormap.getColor(v));
196    
197                                                    sr = (c >> 16) & 0xFF;
198                                                    sg = (c >> 8) & 0xFF;
199                                                    sb = (c) & 0xFF;
200                                            }
201                                            
202                                            sa = useAlpha ? (pixels[offset] >> 24) & 0xff : PixelUtils.brightness(pixels[offset]);
203                                            
204                                            // invert v if necessary
205                                            if (invert) {
206                                                    sr = 255-sr;
207                                                    sg = 255-sg;
208                                                    sb = 255-sb;
209                                            }
210                                    }
211    
212                                    // write results
213                                    if (merge) {
214                                            // merge with source
215                                            int transp = 255;
216                                            int col = pixels[offset];
217    
218                                            int a = (col & 0xFF000000) >> 24;
219                                            int     r = (col & 0xFF0000) >> 16;
220                                            int     g = (col & 0xFF00) >> 8;
221                                            int     b = (col & 0xFF);
222    
223                                            r = ((sr*r/transp));
224                                            g = ((sg*g/transp));
225                                            b = ((sb*b/transp));
226                                    
227                                            // clip colors
228                                            if (r < 0)
229                                                    r = 0;
230                                            if (r > 255)
231                                                    r = 255; 
232                                            if (g < 0)
233                                                    g = 0;
234                                            if (g > 255)
235                                                    g = 255; 
236                                            if (b < 0)
237                                                    b = 0;
238                                            if (b > 255)
239                                                    b = 255;
240                                            
241                                            pixels[offset] = (a << 24) | (r << 16) | (g << 8) | b;
242                                    } else {
243                                            // write gray shades
244                                            pixels[offset] = (sa << 24) | (sr << 16) | (sg << 8) | sb;
245                                    }
246                            }
247                    }
248            }
249    
250            private int setEdgeValue(int x, int y, int[] map, int width, int offset, int xmax, int ymax) {
251                    int min, v;
252                    int r1, r2, r3, r4, r5;
253    
254                    r1 = offset - width - width - 2;
255                    r2 = r1 + width;
256                    r3 = r2 + width;
257                    r4 = r3 + width;
258                    r5 = r4 + width;
259    
260                    if (y == 0 || x == 0 || y == ymax+2 || x == xmax+2)
261                            return map[offset] = one;
262    
263                    v = map[r2 + 2] + one;
264                    min = v;
265                    
266                    v = map[r3 + 1] + one;
267                    if (v < min)
268                            min = v;
269                    
270                    v = map[r3 + 3] + one;
271                    if (v < min)
272                            min = v;
273                    
274                    v = map[r4 + 2] + one;
275                    if (v < min)
276                            min = v;
277                    
278                    v = map[r2 + 1] + sqrt2;
279                    if (v < min)
280                            min = v;
281                            
282                    v = map[r2 + 3] + sqrt2;
283                    if (v < min)
284                            min = v;
285                            
286                    v = map[r4 + 1] + sqrt2;
287                    if (v < min)
288                            min = v;
289                            
290                    v = map[r4 + 3] + sqrt2;
291                    if (v < min)
292                            min = v;
293                    
294                    if (y == 1 || x == 1 || y == ymax+1 || x == xmax+1)
295                            return map[offset] = min;
296    
297                    v = map[r1 + 1] + sqrt5;
298                    if (v < min)
299                            min = v;
300                            
301                    v = map[r1 + 3] + sqrt5;
302                    if (v < min)
303                            min = v;
304                            
305                    v = map[r2 + 4] + sqrt5;
306                    if (v < min)
307                            min = v;
308                            
309                    v = map[r4 + 4] + sqrt5;
310                    if (v < min)
311                            min = v;
312                            
313                    v = map[r5 + 3] + sqrt5;
314                    if (v < min)
315                            min = v;
316                            
317                    v = map[r5 + 1] + sqrt5;
318                    if (v < min)
319                            min = v;
320                            
321                    v = map[r4] + sqrt5;
322                    if (v < min)
323                            min = v;
324                            
325                    v = map[r2] + sqrt5;
326                    if (v < min)
327                            min = v;
328    
329                    return map[offset] = min;
330            }
331    
332            private int setValue(int[] map, int width, int offset) {
333                    int min, v;
334                    int r1, r2, r3, r4, r5;
335    
336                    r1 = offset - width - width - 2;
337                    r2 = r1 + width;
338                    r3 = r2 + width;
339                    r4 = r3 + width;
340                    r5 = r4 + width;
341    
342                    v = map[r2 + 2] + one;
343                    min = v;
344                    v = map[r3 + 1] + one;
345                    if (v < min)
346                            min = v;
347                    v = map[r3 + 3] + one;
348                    if (v < min)
349                            min = v;
350                    v = map[r4 + 2] + one;
351                    if (v < min)
352                            min = v;
353                    
354                    v = map[r2 + 1] + sqrt2;
355                    if (v < min)
356                            min = v;
357                    v = map[r2 + 3] + sqrt2;
358                    if (v < min)
359                            min = v;
360                    v = map[r4 + 1] + sqrt2;
361                    if (v < min)
362                            min = v;
363                    v = map[r4 + 3] + sqrt2;
364                    if (v < min)
365                            min = v;
366                    
367                    v = map[r1 + 1] + sqrt5;
368                    if (v < min)
369                            min = v;
370                    v = map[r1 + 3] + sqrt5;
371                    if (v < min)
372                            min = v;
373                    v = map[r2 + 4] + sqrt5;
374                    if (v < min)
375                            min = v;
376                    v = map[r4 + 4] + sqrt5;
377                    if (v < min)
378                            min = v;
379                    v = map[r5 + 3] + sqrt5;
380                    if (v < min)
381                            min = v;
382                    v = map[r5 + 1] + sqrt5;
383                    if (v < min)
384                            min = v;
385                    v = map[r4] + sqrt5;
386                    if (v < min)
387                            min = v;
388                    v = map[r2] + sqrt5;
389                    if (v < min)
390                            min = v;
391    
392                    return map[offset] = min;
393            }
394            
395            public String toString() {
396                    return "Stylize/Shapeburst...";
397            }
398    
399            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
400                    Object o;
401                    if((o=parameters.removeEL(KeyImpl.init("UseAlpha")))!=null)setUseAlpha(ImageFilterUtil.toBooleanValue(o,"UseAlpha"));
402                    if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap"));
403                    if((o=parameters.removeEL(KeyImpl.init("Invert")))!=null)setInvert(ImageFilterUtil.toBooleanValue(o,"Invert"));
404                    if((o=parameters.removeEL(KeyImpl.init("Factor")))!=null)setFactor(ImageFilterUtil.toFloatValue(o,"Factor"));
405                    if((o=parameters.removeEL(KeyImpl.init("Merge")))!=null)setMerge(ImageFilterUtil.toBooleanValue(o,"Merge"));
406                    if((o=parameters.removeEL(KeyImpl.init("Type")))!=null)setType(ImageFilterUtil.toIntValue(o,"Type"));
407    
408                    // check for arguments not supported
409                    if(parameters.size()>0) {
410                            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 [UseAlpha, Colormap, Invert, Factor, Merge, Type]");
411                    }
412    
413                    return filter(src, dst);
414            }
415    }