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.Struct;
025    import railo.runtime.type.util.CollectionUtil;
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":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(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