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