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