001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009package imagingbook.common.color;
010
011import imagingbook.common.math.Matrix;
012
013import java.awt.Color;
014
015/**
016 * This class defines static methods for manipulating and converting RGB colors.
017 *
018 * @author WB
019 */
020public abstract class RgbUtils {
021        
022        private RgbUtils() {}
023        
024        /** ITU BR.601 weights for RGB to Y (luma) conversion. */
025        public static final double[] ITU601RgbWeights = {0.299, 0.587, 0.114}; 
026        
027        /** ITU BR.709 weights for RGB to Y (luma) conversion. */
028        public static final double[] ITU709RgbWeights = {0.2126, 0.7152, 0.0722}; 
029        
030        public static double[] getDefaultWeights() {
031                return ITU709RgbWeights.clone();
032        }
033
034        /**
035         * Converts the given integer-encoded 8-bit RGB color to a 3-element {@code int[]} with components red, green and
036         * blue.
037         *
038         * @param argb integer-encoded 8-bit RGB color in ARGB format
039         * @return {@code int[]} with R, G, B components in [0,255]
040         */
041        public static int[] intToRgb(int argb) {
042                int[] RGB = new int[3];
043                decodeIntToRgb(argb, RGB);
044                return RGB;
045        }
046
047        /**
048         * Converts the given 3-element {@code int[]} with components red, green and blue
049         * to an integer-encoded 8-bit RGB color.
050         *
051         * @param RGB {@code int[]} with R, G, B components in [0,255]
052         * @return integer-encoded 8-bit RGB color in ARGB format
053         * @see #encodeRgbToInt(int[])
054         */
055        public static int rgbToInt(int[] RGB) {
056                return encodeRgbToInt(RGB);
057        }
058
059        /**
060         * Converts the given integer-encoded 8-bit RGB color to a 3-element {@code int[]} with components red, green and
061         * blue. Fills the specified {@code int[]}, nothing is returned.
062         *
063         * @param argb integer-encoded 8-bit RGB color in ARGB format
064         * @param RGB {@code int[]} with R, G, B components
065         */
066        public static void decodeIntToRgb(int argb, int[] RGB) {
067                RGB[0] = ((argb >> 16) & 0xFF);
068                RGB[1] = ((argb >> 8) & 0xFF);
069                RGB[2] = (argb & 0xFF);
070        }
071
072        /**
073         * Encodes the given RGB component values into a single 32-bit {@code int} value in ARGB format (with transparency A
074         * set to zero).
075         *
076         * @param RGB {@code int[]} with R, G, B components
077         * @return integer-encoded 8-bit RGB color in ARGB format
078         */
079        public static int encodeRgbToInt(int[] RGB) {
080                return encodeRgbToInt(RGB[0], RGB[1], RGB[2]);
081        }
082
083        /**
084         * Encodes the given r, g, b component values into a single 32-bit {@code int} value in ARGB format (with
085         * transparency A set to zero).
086         *
087         * @param r red component value
088         * @param g breen component value
089         * @param b blue component value
090         * @return integer-encoded 8-bit RGB color in ARGB format
091         */
092        public static int encodeRgbToInt(int r, int g, int b) {
093                return ((r & 0xff)<<16) | ((g & 0xff)<<8) | b & 0xff;
094        }
095        
096        // -------------------------------------------------------------
097
098        /**
099         * Converts integer RGB values (with components assumed to be in [0,255]) to float values in [0,1].
100         *
101         * @param RGB a sequence of R,G,B values or {@code int[]}
102         * @return the RGB values normalized to [0,1]
103         */
104        public static float[] normalize(int[] RGB) {
105                return new float[] {RGB[0]/255f, RGB[1]/255f, RGB[2]/255f};
106        }
107        
108        public static double[] normalizeD(int[] RGB) {
109                return new double[] {RGB[0]/255.0, RGB[1]/255.0, RGB[2]/255.0};
110        }
111
112        /**
113         * Converts float RGB values (with components assumed to be in [0,1]) to integer values in [0,255].
114         *
115         * @param rgb RGB float values in [0,1]
116         * @return RGB integer values in [0,255]
117         */
118        public static int[] denormalize(float[] rgb) {
119                int[] RGB = new int[3];
120                for (int i = 0; i < 3; i++) {                   
121                        RGB[i] = Math.round(rgb[i] * 255f);
122                        if (RGB[i] < 0)
123                                RGB[i] = 0;
124                        else if (RGB[i] > 255)
125                                RGB[i] = 255;
126                }
127                return RGB;
128        }
129
130        /**
131         * Converts double RGB values (with components assumed to be in [0,1]) to integer values in [0,255].
132         *
133         * @param rgb RGB float values in [0,1]
134         * @return RGB integer values in [0,255]
135         */
136        public static int[] denormalizeD(double[] rgb) {
137                int[] RGB = new int[3];
138                for (int i = 0; i < 3; i++) {                   
139                        RGB[i] = (int) Math.round(rgb[i] * 255);
140                        if (RGB[i] < 0)
141                                RGB[i] = 0;
142                        else if (RGB[i] > 255)
143                                RGB[i] = 255;
144                }
145                return RGB;
146        }
147                
148        // -------------------------------------------------------------
149
150        /**
151         * Interpolates linearly between two specified colors.
152         * 
153         * @param ca first color (to be interpolated from)
154         * @param cb second color (to be interpolated to)
155         * @param t interpolation coefficient, must be in [0,1]
156         * @return the interpolated color
157         */
158        public static Color interpolate(Color ca, Color cb, double t) {
159                if (t < 0 || t > 1) {
160                        throw new IllegalArgumentException("interpolation coefficient must be in [0,1] but is " + t);
161                }
162                float[] a = ca.getRGBColorComponents(null);
163                float[] b = cb.getRGBColorComponents(null);
164                float[] c = Matrix.lerp(a, b, (float) t);
165                return new Color(c[0], c[1], c[2]);
166        }
167
168        /**
169         * Interpolates linearly between the colors in the specified color palette. The interpolation coefficient must be in
170         * [0,1]. If 0, the first palette color is returned, if 1 the last color.
171         *
172         * @param palette an array of colors (at least 2)
173         * @param t interpolation coefficient, must be in [0,1]
174         * @return the interpolated color
175         */
176        public static Color interpolate(Color[] palette, double t) {
177                if (palette.length < 2) {
178                        throw new IllegalArgumentException("length of color palette must be at least 2 but is " + palette.length);
179                }
180                if (t < 0 || t > 1) {
181                        throw new IllegalArgumentException("interpolation coefficient must be in [0,1] but is " + t);
182                }
183                final int n = palette.length;
184                double x = t * (n - 1);                 // x = 0,...,n-1 (float palette index)
185                int lo = (int) Math.floor(x);   // lower palette color index
186                int hi = (int) Math.ceil(x);    // upper palette color index
187                return interpolate(palette[lo], palette[hi], x - lo);
188        }
189
190}