001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.util.*;
020import java.awt.Color;
021
022/**
023 * Some more useful math functions for image processing.
024 * These are becoming obsolete as we move to Java2D. Use MiscComposite instead.
025 */
026public class PixelUtils {
027
028        public final static int REPLACE = 0;
029        public final static int NORMAL = 1;
030        public final static int MIN = 2;
031        public final static int MAX = 3;
032        public final static int ADD = 4;
033        public final static int SUBTRACT = 5;
034        public final static int DIFFERENCE = 6;
035        public final static int MULTIPLY = 7;
036        public final static int HUE = 8;
037        public final static int SATURATION = 9;
038        public final static int VALUE = 10;
039        public final static int COLOR = 11;
040        public final static int SCREEN = 12;
041        public final static int AVERAGE = 13;
042        public final static int OVERLAY = 14;
043        public final static int CLEAR = 15;
044        public final static int EXCHANGE = 16;
045        public final static int DISSOLVE = 17;
046        public final static int DST_IN = 18;
047        public final static int ALPHA = 19;
048        public final static int ALPHA_TO_GRAY = 20;
049
050        private static Random randomGenerator = new Random();
051
052        /**
053         * Clamp a value to the range 0..255
054         */
055        public static int clamp(int c) {
056                if (c < 0)
057                        return 0;
058                if (c > 255)
059                        return 255;
060                return c;
061        }
062
063        public static int interpolate(int v1, int v2, float f) {
064                return clamp((int)(v1+f*(v2-v1)));
065        }
066        
067        public static int brightness(int rgb) {
068                int r = (rgb >> 16) & 0xff;
069                int g = (rgb >> 8) & 0xff;
070                int b = rgb & 0xff;
071                return (r+g+b)/3;
072        }
073        
074        public static boolean nearColors(int rgb1, int rgb2, int tolerance) {
075                int r1 = (rgb1 >> 16) & 0xff;
076                int g1 = (rgb1 >> 8) & 0xff;
077                int b1 = rgb1 & 0xff;
078                int r2 = (rgb2 >> 16) & 0xff;
079                int g2 = (rgb2 >> 8) & 0xff;
080                int b2 = rgb2 & 0xff;
081                return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance;
082        }
083        
084        private final static float hsb1[] = new float[3];//FIXME-not thread safe
085        private final static float hsb2[] = new float[3];//FIXME-not thread safe
086        
087        // Return rgb1 painted onto rgb2
088        public static int combinePixels(int rgb1, int rgb2, int op) {
089                return combinePixels(rgb1, rgb2, op, 0xff);
090        }
091        
092        public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha, int channelMask) {
093                return (rgb2 & ~channelMask) | combinePixels(rgb1 & channelMask, rgb2, op, extraAlpha);
094        }
095        
096        public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha) {
097                if (op == REPLACE)
098                        return rgb1;
099                int a1 = (rgb1 >> 24) & 0xff;
100                int r1 = (rgb1 >> 16) & 0xff;
101                int g1 = (rgb1 >> 8) & 0xff;
102                int b1 = rgb1 & 0xff;
103                int a2 = (rgb2 >> 24) & 0xff;
104                int r2 = (rgb2 >> 16) & 0xff;
105                int g2 = (rgb2 >> 8) & 0xff;
106                int b2 = rgb2 & 0xff;
107
108                switch (op) {
109                case NORMAL:
110                        break;
111                case MIN:
112                        r1 = Math.min(r1, r2);
113                        g1 = Math.min(g1, g2);
114                        b1 = Math.min(b1, b2);
115                        break;
116                case MAX:
117                        r1 = Math.max(r1, r2);
118                        g1 = Math.max(g1, g2);
119                        b1 = Math.max(b1, b2);
120                        break;
121                case ADD:
122                        r1 = clamp(r1+r2);
123                        g1 = clamp(g1+g2);
124                        b1 = clamp(b1+b2);
125                        break;
126                case SUBTRACT:
127                        r1 = clamp(r2-r1);
128                        g1 = clamp(g2-g1);
129                        b1 = clamp(b2-b1);
130                        break;
131                case DIFFERENCE:
132                        r1 = clamp(Math.abs(r1-r2));
133                        g1 = clamp(Math.abs(g1-g2));
134                        b1 = clamp(Math.abs(b1-b2));
135                        break;
136                case MULTIPLY:
137                        r1 = clamp(r1*r2/255);
138                        g1 = clamp(g1*g2/255);
139                        b1 = clamp(b1*b2/255);
140                        break;
141                case DISSOLVE:
142                        if ((randomGenerator.nextInt() & 0xff) <= a1) {
143                                r1 = r2;
144                                g1 = g2;
145                                b1 = b2;
146                        }
147                        break;
148                case AVERAGE:
149                        r1 = (r1+r2)/2;
150                        g1 = (g1+g2)/2;
151                        b1 = (b1+b2)/2;
152                        break;
153                case HUE:
154                case SATURATION:
155                case VALUE:
156                case COLOR:
157                        Color.RGBtoHSB(r1, g1, b1, hsb1);
158                        Color.RGBtoHSB(r2, g2, b2, hsb2);
159                        switch (op) {
160                        case HUE:
161                                hsb2[0] = hsb1[0];
162                                break;
163                        case SATURATION:
164                                hsb2[1] = hsb1[1];
165                                break;
166                        case VALUE:
167                                hsb2[2] = hsb1[2];
168                                break;
169                        case COLOR:
170                                hsb2[0] = hsb1[0];
171                                hsb2[1] = hsb1[1];
172                                break;
173                        }
174                        rgb1 = Color.HSBtoRGB(hsb2[0], hsb2[1], hsb2[2]);
175                        r1 = (rgb1 >> 16) & 0xff;
176                        g1 = (rgb1 >> 8) & 0xff;
177                        b1 = rgb1 & 0xff;
178                        break;
179                case SCREEN:
180                        r1 = 255 - ((255 - r1) * (255 - r2)) / 255;
181                        g1 = 255 - ((255 - g1) * (255 - g2)) / 255;
182                        b1 = 255 - ((255 - b1) * (255 - b2)) / 255;
183                        break;
184                case OVERLAY:
185                        int m, s;
186                        s = 255 - ((255 - r1) * (255 - r2)) / 255;
187                        m = r1 * r2 / 255;
188                        r1 = (s * r1 + m * (255 - r1)) / 255;
189                        s = 255 - ((255 - g1) * (255 - g2)) / 255;
190                        m = g1 * g2 / 255;
191                        g1 = (s * g1 + m * (255 - g1)) / 255;
192                        s = 255 - ((255 - b1) * (255 - b2)) / 255;
193                        m = b1 * b2 / 255;
194                        b1 = (s * b1 + m * (255 - b1)) / 255;
195                        break;
196                case CLEAR:
197                        r1 = g1 = b1 = 0xff;
198                        break;
199                case DST_IN:
200                        r1 = clamp((r2*a1)/255);
201                        g1 = clamp((g2*a1)/255);
202                        b1 = clamp((b2*a1)/255);
203                        a1 = clamp((a2*a1)/255);
204                        return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
205                case ALPHA:
206                        a1 = a1*a2/255;
207                        return (a1 << 24) | (r2 << 16) | (g2 << 8) | b2;
208                case ALPHA_TO_GRAY:
209                        int na = 255-a1;
210                        return (a1 << 24) | (na << 16) | (na << 8) | na;
211                }
212                if (extraAlpha != 0xff || a1 != 0xff) {
213                        a1 = a1*extraAlpha/255;
214                        int a3 = (255-a1)*a2/255;
215                        r1 = clamp((r1*a1+r2*a3)/255);
216                        g1 = clamp((g1*a1+g2*a3)/255);
217                        b1 = clamp((b1*a1+b2*a3)/255);
218                        a1 = clamp(a1+a3);
219                }
220                return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
221        }
222
223}