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.Rectangle;
036import java.awt.image.BufferedImage;
037
038import lucee.runtime.engine.ThreadLocalPageContext;
039import lucee.runtime.exp.FunctionException;
040import lucee.runtime.exp.PageException;
041import lucee.runtime.img.ImageUtil;
042import lucee.runtime.type.KeyImpl;
043import lucee.runtime.type.Struct;
044import lucee.runtime.type.util.CollectionUtil;
045
046/**
047 * A filter which reduces a binary image to a skeleton.
048 *
049 * Based on an algorithm by Zhang and Suen (CACM, March 1984, 236-239).
050 */
051public class SkeletonFilter extends BinaryFilter  implements DynFiltering {
052
053        private final static byte[] skeletonTable = {
054                0, 0, 0, 1, 0, 0, 1, 3, 0, 0, 3, 1, 1, 0, 1, 3, 
055                0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 3, 0, 3, 3,
056                0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 
057                2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 2, 2,
058                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
059                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
060                2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 
061                3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 2, 0,
062                0, 1, 3, 1, 0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 
063                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
064                3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
065                2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
066                2, 3, 1, 3, 0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 
067                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
068                2, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
069                3, 3, 0, 1, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, 0
070        };
071        
072        public SkeletonFilter() {
073                newColor = 0xffffffff;
074        }
075
076        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
077                int[] outPixels = new int[width * height];
078
079                int count = 0;
080                int black = 0xff000000;
081                //int white = 0xffffffff;
082                for (int i = 0; i < iterations; i++) {
083                        count = 0;
084                        for (int pass = 0; pass < 2; pass++) {
085                                for (int y = 1; y < height-1; y++) {
086                                        int offset = y*width+1;
087                                        for (int x = 1; x < width-1; x++) {
088                                                int pixel = inPixels[offset];
089                                                if (pixel == black) {
090                                                        int tableIndex = 0;
091
092                                                        if (inPixels[offset-width-1] == black)
093                                                                tableIndex |= 1;
094                                                        if (inPixels[offset-width] == black)
095                                                                tableIndex |= 2;
096                                                        if (inPixels[offset-width+1] == black)
097                                                                tableIndex |= 4;
098                                                        if (inPixels[offset+1] == black)
099                                                                tableIndex |= 8;
100                                                        if (inPixels[offset+width+1] == black)
101                                                                tableIndex |= 16;
102                                                        if (inPixels[offset+width] == black)
103                                                                tableIndex |= 32;
104                                                        if (inPixels[offset+width-1] == black)
105                                                                tableIndex |= 64;
106                                                        if (inPixels[offset-1] == black)
107                                                                tableIndex |= 128;
108                                                        int code = skeletonTable[tableIndex];
109                                                        if (pass == 1) {
110                                                                if (code == 2 || code == 3) {
111                                                                        if (colormap != null)
112                                                                                pixel = colormap.getColor((float)i/iterations);
113                                                                        else
114                                                                                pixel = newColor;
115                                                                        count++;
116                                                                }
117                                                        } else {
118                                                                if (code == 1 || code == 3) {
119                                                                        if (colormap != null)
120                                                                                pixel = colormap.getColor((float)i/iterations);
121                                                                        else
122                                                                                pixel = newColor;
123                                                                        count++;
124                                                                }
125                                                        }
126                                                }
127                                                outPixels[offset++] = pixel;
128                                        }
129                                }
130                                if (pass == 0) {
131                                        inPixels = outPixels;
132                                        outPixels = new int[width * height];
133                                }
134                        }
135                        if (count == 0)
136                                break;
137                }
138                return outPixels;
139        }
140
141        public String toString() {
142                return "Binary/Skeletonize...";
143        }
144
145        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
146                Object o;
147                if((o=parameters.removeEL(KeyImpl.init("Iterations")))!=null)setIterations(ImageFilterUtil.toIntValue(o,"Iterations"));
148                if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap"));
149                if((o=parameters.removeEL(KeyImpl.init("NewColor")))!=null)setNewColor(ImageFilterUtil.toColorRGB(o,"NewColor"));
150                //if((o=parameters.removeEL(KeyImpl.init("BlackFunction")))!=null)setBlackFunction(ImageFilterUtil.toBinaryFunction(o,"BlackFunction"));
151
152                // check for arguments not supported
153                if(parameters.size()>0) {
154                        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 [Iterations, Colormap, NewColor, BlackFunction]");
155                }
156
157                return filter(src, dst);
158        }
159}
160