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