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.image.BufferedImage; 018 019 import railo.runtime.engine.ThreadLocalPageContext; 020 import railo.runtime.exp.FunctionException; 021 import railo.runtime.exp.PageException; 022 import railo.runtime.img.ImageUtil; 023 import railo.runtime.type.KeyImpl; 024 import railo.runtime.type.List; 025 import railo.runtime.type.Struct; 026 027 028 029 /** 030 * A filter which performs ordered dithering on an image. 031 */ 032 public class DitherFilter extends PointFilter implements DynFiltering { 033 034 /** 035 * 2x2 magic square. 036 */ 037 protected final static int[] ditherMagic2x2Matrix = { 038 0, 2, 039 3, 1 040 }; 041 042 /** 043 * 4x4 magic square. 044 */ 045 protected final static int[] ditherMagic4x4Matrix = { 046 0, 14, 3, 13, 047 11, 5, 8, 6, 048 12, 2, 15, 1, 049 7, 9, 4, 10 050 }; 051 052 /** 053 * 4x4 ordered dither. 054 */ 055 public final static int[] ditherOrdered4x4Matrix = { 056 0, 8, 2, 10, 057 12, 4, 14, 6, 058 3, 11, 1, 9, 059 15, 7, 13, 5 060 }; 061 062 /** 063 * 4x4 lines. 064 */ 065 public final static int[] ditherLines4x4Matrix = { 066 0, 1, 2, 3, 067 4, 5, 6, 7, 068 8, 9, 10, 11, 069 12, 13, 14, 15 070 }; 071 072 /** 073 * 6x6 90 degree halftone. 074 */ 075 public final static int[] dither90Halftone6x6Matrix = { 076 29, 18, 12, 19, 30, 34, 077 17, 7, 4, 8, 20, 28, 078 11, 3, 0, 1, 9, 27, 079 16, 6, 2, 5, 13, 26, 080 25, 15, 10, 14, 21, 31, 081 33, 25, 24, 23, 33, 36 082 }; 083 084 /* 085 * The following dithering matrices are taken from "Digital Halftoning" 086 * by Robert Ulichney, MIT Press, ISBN 0-262-21009-6. 087 */ 088 089 /** 090 * Order-6 ordered dither. 091 */ 092 public final static int[] ditherOrdered6x6Matrix = { 093 1, 59, 15, 55, 2, 56, 12, 52, 094 33, 17, 47, 31, 34, 18, 44, 28, 095 9, 49, 5, 63, 10, 50, 6, 60, 096 41, 25, 37, 21, 42, 26, 38, 22, 097 3, 57, 13, 53, 0, 58, 14, 54, 098 35, 19, 45, 29, 32, 16, 46, 30, 099 11, 51, 7, 61, 8, 48, 4, 62, 100 43, 27, 39, 23, 40, 24, 36, 20 101 }; 102 103 /** 104 * Order-8 ordered dither. 105 */ 106 public final static int[] ditherOrdered8x8Matrix = { 107 1,235, 59,219, 15,231, 55,215, 2,232, 56,216, 12,228, 52,212, 108 129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116, 109 33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244, 110 161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92, 111 9,225, 49,209, 5,239, 63,223, 10,226, 50,210, 6,236, 60,220, 112 137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124, 113 41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252, 114 169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86, 115 3,233, 57,217, 13,229, 53,213, 0,234, 58,218, 14,230, 54,214, 116 131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118, 117 35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246, 118 163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94, 119 11,227, 51,211, 7,237, 61,221, 8,224, 48,208, 4,238, 62,222, 120 139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126, 121 43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254, 122 171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84 }; 123 124 /** 125 * Order-3 clustered dither. 126 */ 127 public final static int[] ditherCluster3Matrix = { 128 9,11,10, 8, 6, 7, 129 12,17,16, 5, 0, 1, 130 13,14,15, 4, 3, 2, 131 8, 6, 7, 9,11,10, 132 5, 0, 1,12,17,16, 133 4, 3, 2,13,14,15 }; 134 135 /** 136 * Order-4 clustered dither. 137 */ 138 public final static int[] ditherCluster4Matrix = { 139 18,20,19,16,13,11,12,15, 140 27,28,29,22, 4, 3, 2, 9, 141 26,31,30,21, 5, 0, 1,10, 142 23,25,24,17, 8, 6, 7,14, 143 13,11,12,15,18,20,19,16, 144 4, 3, 2, 9,27,28,29,22, 145 5, 0, 1,10,26,31,30,21, 146 8, 6, 7,14,23,25,24,17 }; 147 148 /** 149 * Order-8 clustered dither. 150 */ 151 public final static int[] ditherCluster8Matrix = { 152 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60, 153 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52, 154 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44, 155 88,110,123,124,125,118,107, 85, 39, 17, 4, 3, 2, 9, 20, 42, 156 89,111,122,127,126,117,106, 84, 38, 16, 5, 0, 1, 10, 21, 43, 157 79,102,119,121,120,113, 97, 82, 48, 25, 8, 6, 7, 14, 30, 45, 158 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53, 159 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61, 160 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67, 161 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75, 162 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83, 163 39, 17, 4, 3, 2, 9, 20, 42, 88,110,123,124,125,118,107, 85, 164 38, 16, 5, 0, 1, 10, 21, 43, 89,111,122,127,126,117,106, 84, 165 48, 25, 8, 6, 7, 14, 30, 45, 79,102,119,121,120,113, 97, 82, 166 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74, 167 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66 }; 168 169 private int[] matrix; 170 private int rows, cols, levels; 171 private int[] mod; 172 private int[] div; 173 private int[] map; 174 private boolean colorDither; 175 private boolean initialized = false; 176 177 /** 178 * Constuct a DitherFilter. 179 */ 180 public DitherFilter() { 181 rows = 2; 182 cols = 2; 183 matrix = ditherMagic4x4Matrix; 184 levels = 6; 185 colorDither = true; 186 } 187 188 /** 189 * Set the dither matrix. 190 * @param matrix the dither matrix 191 * @see #getMatrix 192 */ 193 public void setMatrix(int[] matrix) { 194 this.matrix = matrix; 195 } 196 197 /** 198 * Get the dither matrix. 199 * @return the dither matrix 200 * @see #setMatrix 201 */ 202 public int[] getMatrix() { 203 return matrix; 204 } 205 206 /** 207 * Set the number of dither levels. 208 * @param levels the number of levels 209 * @see #getLevels 210 */ 211 public void setLevels(int levels) { 212 this.levels = levels; 213 } 214 215 /** 216 * Get the number of dither levels. 217 * @return the number of levels 218 * @see #setLevels 219 */ 220 public int getLevels() { 221 return levels; 222 } 223 224 /** 225 * Set whether to use a color dither. 226 * @param colorDither whether to use a color dither 227 * @see #getColorDither 228 */ 229 public void setColorDither(boolean colorDither) { 230 this.colorDither = colorDither; 231 } 232 233 /** 234 * Get whether to use a color dither. 235 * @return whether to use a color dither 236 * @see #getColorDither 237 */ 238 public boolean getColorDither() { 239 return colorDither; 240 } 241 242 /** 243 * Initialize the filter. 244 */ 245 protected void initialize() { 246 rows = cols = (int)Math.sqrt(matrix.length); 247 map = new int[levels]; 248 for (int i = 0; i < levels; i++) { 249 int v = 255 * i / (levels-1); 250 map[i] = v; 251 } 252 div = new int[256]; 253 mod = new int[256]; 254 int rc = (rows*cols+1); 255 for (int i = 0; i < 256; i++) { 256 div[i] = (levels-1)*i / 256; 257 mod[i] = i*rc/256; 258 } 259 } 260 261 public int filterRGB(int x, int y, int rgb) { 262 if (!initialized) { 263 initialized = true; 264 initialize(); 265 } 266 int a = rgb & 0xff000000; 267 int r = (rgb >> 16) & 0xff; 268 int g = (rgb >> 8) & 0xff; 269 int b = rgb & 0xff; 270 int col = x % cols; 271 int row = y % rows; 272 int v = matrix[row*cols+col]; 273 if (colorDither) { 274 r = map[mod[r] > v ? div[r] + 1 : div[r]]; 275 g = map[mod[g] > v ? div[g] + 1 : div[g]]; 276 b = map[mod[b] > v ? div[b] + 1 : div[b]]; 277 } else { 278 int value = (r+g+b)/3; 279 r = g = b = map[mod[value] > v ? div[value] + 1 : div[value]]; 280 } 281 return a | (r << 16) | (g << 8) | b; 282 } 283 284 public String toString() { 285 return "Colors/Dither..."; 286 } 287 288 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 289 Object o; 290 if((o=parameters.removeEL(KeyImpl.init("Levels")))!=null)setLevels(ImageFilterUtil.toIntValue(o,"Levels")); 291 if((o=parameters.removeEL(KeyImpl.init("Matrix")))!=null)setMatrix(ImageFilterUtil.toAInt(o,"Matrix")); 292 if((o=parameters.removeEL(KeyImpl.init("ColorDither")))!=null)setColorDither(ImageFilterUtil.toBooleanValue(o,"ColorDither")); 293 if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){ 294 int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions"); 295 setDimensions(dim[0],dim[1]); 296 } 297 298 // check for arguments not supported 299 if(parameters.size()>0) { 300 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 [Levels, Matrix, ColorDither, Dimensions]"); 301 } 302 303 return filter(src, dst); 304 } 305 } 306