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