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.Color;
036import java.util.Random;
037
038/**
039 * Some more useful math functions for image processing.
040 * These are becoming obsolete as we move to Java2D. Use MiscComposite instead.
041 */
042public class PixelUtils {
043
044        public final static int REPLACE = 0;
045        public final static int NORMAL = 1;
046        public final static int MIN = 2;
047        public final static int MAX = 3;
048        public final static int ADD = 4;
049        public final static int SUBTRACT = 5;
050        public final static int DIFFERENCE = 6;
051        public final static int MULTIPLY = 7;
052        public final static int HUE = 8;
053        public final static int SATURATION = 9;
054        public final static int VALUE = 10;
055        public final static int COLOR = 11;
056        public final static int SCREEN = 12;
057        public final static int AVERAGE = 13;
058        public final static int OVERLAY = 14;
059        public final static int CLEAR = 15;
060        public final static int EXCHANGE = 16;
061        public final static int DISSOLVE = 17;
062        public final static int DST_IN = 18;
063        public final static int ALPHA = 19;
064        public final static int ALPHA_TO_GRAY = 20;
065
066        private static Random randomGenerator = new Random();
067
068        /**
069         * Clamp a value to the range 0..255
070         */
071        public static int clamp(int c) {
072                if (c < 0)
073                        return 0;
074                if (c > 255)
075                        return 255;
076                return c;
077        }
078
079        public static int interpolate(int v1, int v2, float f) {
080                return clamp((int)(v1+f*(v2-v1)));
081        }
082        
083        public static int brightness(int rgb) {
084                int r = (rgb >> 16) & 0xff;
085                int g = (rgb >> 8) & 0xff;
086                int b = rgb & 0xff;
087                return (r+g+b)/3;
088        }
089        
090        public static boolean nearColors(int rgb1, int rgb2, int tolerance) {
091                int r1 = (rgb1 >> 16) & 0xff;
092                int g1 = (rgb1 >> 8) & 0xff;
093                int b1 = rgb1 & 0xff;
094                int r2 = (rgb2 >> 16) & 0xff;
095                int g2 = (rgb2 >> 8) & 0xff;
096                int b2 = rgb2 & 0xff;
097                return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance;
098        }
099        
100        private final static float hsb1[] = new float[3];//FIXME-not thread safe
101        private final static float hsb2[] = new float[3];//FIXME-not thread safe
102        
103        // Return rgb1 painted onto rgb2
104        public static int combinePixels(int rgb1, int rgb2, int op) {
105                return combinePixels(rgb1, rgb2, op, 0xff);
106        }
107        
108        public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha, int channelMask) {
109                return (rgb2 & ~channelMask) | combinePixels(rgb1 & channelMask, rgb2, op, extraAlpha);
110        }
111        
112        public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha) {
113                if (op == REPLACE)
114                        return rgb1;
115                int a1 = (rgb1 >> 24) & 0xff;
116                int r1 = (rgb1 >> 16) & 0xff;
117                int g1 = (rgb1 >> 8) & 0xff;
118                int b1 = rgb1 & 0xff;
119                int a2 = (rgb2 >> 24) & 0xff;
120                int r2 = (rgb2 >> 16) & 0xff;
121                int g2 = (rgb2 >> 8) & 0xff;
122                int b2 = rgb2 & 0xff;
123
124                switch (op) {
125                case NORMAL:
126                        break;
127                case MIN:
128                        r1 = Math.min(r1, r2);
129                        g1 = Math.min(g1, g2);
130                        b1 = Math.min(b1, b2);
131                        break;
132                case MAX:
133                        r1 = Math.max(r1, r2);
134                        g1 = Math.max(g1, g2);
135                        b1 = Math.max(b1, b2);
136                        break;
137                case ADD:
138                        r1 = clamp(r1+r2);
139                        g1 = clamp(g1+g2);
140                        b1 = clamp(b1+b2);
141                        break;
142                case SUBTRACT:
143                        r1 = clamp(r2-r1);
144                        g1 = clamp(g2-g1);
145                        b1 = clamp(b2-b1);
146                        break;
147                case DIFFERENCE:
148                        r1 = clamp(Math.abs(r1-r2));
149                        g1 = clamp(Math.abs(g1-g2));
150                        b1 = clamp(Math.abs(b1-b2));
151                        break;
152                case MULTIPLY:
153                        r1 = clamp(r1*r2/255);
154                        g1 = clamp(g1*g2/255);
155                        b1 = clamp(b1*b2/255);
156                        break;
157                case DISSOLVE:
158                        if ((randomGenerator.nextInt() & 0xff) <= a1) {
159                                r1 = r2;
160                                g1 = g2;
161                                b1 = b2;
162                        }
163                        break;
164                case AVERAGE:
165                        r1 = (r1+r2)/2;
166                        g1 = (g1+g2)/2;
167                        b1 = (b1+b2)/2;
168                        break;
169                case HUE:
170                case SATURATION:
171                case VALUE:
172                case COLOR:
173                        Color.RGBtoHSB(r1, g1, b1, hsb1);
174                        Color.RGBtoHSB(r2, g2, b2, hsb2);
175                        switch (op) {
176                        case HUE:
177                                hsb2[0] = hsb1[0];
178                                break;
179                        case SATURATION:
180                                hsb2[1] = hsb1[1];
181                                break;
182                        case VALUE:
183                                hsb2[2] = hsb1[2];
184                                break;
185                        case COLOR:
186                                hsb2[0] = hsb1[0];
187                                hsb2[1] = hsb1[1];
188                                break;
189                        }
190                        rgb1 = Color.HSBtoRGB(hsb2[0], hsb2[1], hsb2[2]);
191                        r1 = (rgb1 >> 16) & 0xff;
192                        g1 = (rgb1 >> 8) & 0xff;
193                        b1 = rgb1 & 0xff;
194                        break;
195                case SCREEN:
196                        r1 = 255 - ((255 - r1) * (255 - r2)) / 255;
197                        g1 = 255 - ((255 - g1) * (255 - g2)) / 255;
198                        b1 = 255 - ((255 - b1) * (255 - b2)) / 255;
199                        break;
200                case OVERLAY:
201                        int m, s;
202                        s = 255 - ((255 - r1) * (255 - r2)) / 255;
203                        m = r1 * r2 / 255;
204                        r1 = (s * r1 + m * (255 - r1)) / 255;
205                        s = 255 - ((255 - g1) * (255 - g2)) / 255;
206                        m = g1 * g2 / 255;
207                        g1 = (s * g1 + m * (255 - g1)) / 255;
208                        s = 255 - ((255 - b1) * (255 - b2)) / 255;
209                        m = b1 * b2 / 255;
210                        b1 = (s * b1 + m * (255 - b1)) / 255;
211                        break;
212                case CLEAR:
213                        r1 = g1 = b1 = 0xff;
214                        break;
215                case DST_IN:
216                        r1 = clamp((r2*a1)/255);
217                        g1 = clamp((g2*a1)/255);
218                        b1 = clamp((b2*a1)/255);
219                        a1 = clamp((a2*a1)/255);
220                        return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
221                case ALPHA:
222                        a1 = a1*a2/255;
223                        return (a1 << 24) | (r2 << 16) | (g2 << 8) | b2;
224                case ALPHA_TO_GRAY:
225                        int na = 255-a1;
226                        return (a1 << 24) | (na << 16) | (na << 8) | na;
227                }
228                if (extraAlpha != 0xff || a1 != 0xff) {
229                        a1 = a1*extraAlpha/255;
230                        int a3 = (255-a1)*a2/255;
231                        r1 = clamp((r1*a1+r2*a3)/255);
232                        g1 = clamp((g1*a1+g2*a3)/255);
233                        b1 = clamp((b1*a1+b2*a3)/255);
234                        a1 = clamp(a1+a3);
235                }
236                return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
237        }
238
239}