001    /*
002     * PCDYCbCrConversion
003     * 
004     * Copyright (c) 2001, 2002 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.color.conversion;
009    
010    import net.sourceforge.jiu.color.YCbCrIndex;
011    import net.sourceforge.jiu.data.RGBIndex;
012    
013    /**
014     * Convert from YCbCr color space (as used in Kodak PCD files) to
015     * RGB. Only works for 24 bits per pixel (8 bits per channel) image
016     * data.
017     *
018     * @author Marco Schmidt
019     */
020    public class PCDYCbCrConversion implements 
021            RGBIndex,
022            YCbCrIndex
023    {
024            // color conversion coefficients (YCbCr to RGB)
025            private static final float c11 =  0.0054980f * 256;
026            private static final float c12 =  0.0000000f * 256;
027            private static final float c13 =  0.0051681f * 256;
028            private static final float c21 =  0.0054980f * 256;
029            private static final float c22 = -0.0015446f * 256;
030            private static final float c23 = -0.0026325f * 256;
031            private static final float c31 =  0.0054980f * 256;
032            private static final float c32 =  0.0079533f * 256;
033            private static final float c33 =  0.0000000f * 256;
034    
035            private PCDYCbCrConversion()
036            {
037            }
038    
039            private static byte floatToByte(float f)
040            {
041                    if (f <= 0.0)
042                    {
043                            return 0;
044                    }
045                    if (f >= 255.0)
046                    {
047                            return (byte)255;
048                    }
049                    return (byte)((int)f);
050            }
051    
052            /* 
053             * Converts the color given by (y, cb, cr) to RGB color space.
054             * The three int variables y, cb and cr must be from the
055             * interval 0 to 255 (this is not checked).
056             * The rgb array will get the resulting RGB color, so it must
057             * have at least three entries.
058             * The three entries in that array will also be from 0 to 255 each.
059             *
060            public static void convertYCbCrToRgb(int y, int cb, int cr, int[] rgb)
061            {
062                    int cr137 = cr - 137;
063                    int cb156 = cb - 156;
064                    rgb[INDEX_RED] = floatToInt(c11 * y + c12 * (cb156) + c13 * (cr137));
065                    rgb[INDEX_GREEN] = floatToInt(c21 * y + c22 * (cb156) + c23 * (cr137));
066                    rgb[INDEX_BLUE] = floatToInt(c31 * y + c32 * (cb156) + c33 * (cr137));
067            }
068    
069            public static int[] convertToRgb(byte[][] data, int width, int height)
070                    throws IllegalArgumentException
071            {
072                    if (width < 1 || height < 1)
073                    {
074                            throw new IllegalArgumentException("Error -- width and height must be larger " +
075                                    "than 0 (width=" + width + ", height=" + height);
076                    }
077                    if (data == null)
078                    {
079                            throw new IllegalArgumentException("Error -- data array must not be null.");
080                    }
081                    if (data.length < 3)
082                    {
083                            throw new IllegalArgumentException("Error -- data array must have at least " +
084                                    "three items (has " + data.length);
085                    }
086                    int numPixels = width * height;
087                    int[] result = new int[numPixels];
088                    int[] rgb = new int[3];
089                    for (int i = 0; i < numPixels; i++)
090                    {
091                            int gray = data[INDEX_Y][i] & 0xff;
092                            int cb = data[INDEX_CB][i] & 0xff;
093                            int cr = data[INDEX_CR][i] & 0xff;
094                            convertYCbCrToRgb(gray, cb, cr, rgb);
095                            result[i] = 0xff000000 | rgb[0] << 16 | (rgb[1] << 8) | (rgb[2]);
096                    }
097                    return result;
098            }*/
099    
100            private static void checkArray(byte[] data, int offset, int num) throws IllegalArgumentException
101            {
102                    if (data == null)
103                    {
104                            throw new IllegalArgumentException("Data array must be initialized.");
105                    }
106                    if (offset < 0 || offset + num > data.length)
107                    {
108                            throw new IllegalArgumentException("Invalid combination of " +
109                                    "offset, number and array length: offset=" + offset +
110                                    ", num=" + num + ", data.length=" + data.length);
111                    }
112            }
113    
114            /**
115             * Converts pixels from YCbCr to RGB color space.
116             * Input pixels are given as three byte arrays for luminance and the 
117             * two chroma components.
118             * Same for output pixels, three other arrays for red, green and blue.
119             * Offset values can be specified separately for the YCbCr and the RGB
120             * arrays.
121             * @param y the array of gray source samples
122             * @param cb the array of chroma blue source samples
123             * @param cr the array of chroma red source samples
124             * @param yccOffset offset value into the arrays y, cb and cr; color 
125             *  conversion will be started at the yccOffset'th value of each array
126             * @param r the array of red destination samples
127             * @param g the array of green destination samples
128             * @param b the array of blue destination samples
129             * @param rgbOffset offset value into the arrays r, g and b; destination samples
130             *  will be written to the three arrays starting at the rgbOffset'th value of each array
131             * @param num the number of pixels to be converted
132             * @throws IllegalArgumentException if one of the int values is negative or one
133             *  of the arrays is null or too small
134             */
135            public static void convertYccToRgb(
136                    byte[] y, 
137                    byte[] cb, 
138                    byte[] cr, 
139                    int yccOffset, 
140                    byte[] r, 
141                    byte[] g, 
142                    byte[] b, 
143                    int rgbOffset, 
144                    int num)
145                    throws IllegalArgumentException
146            {
147                    if (num < 0)
148                    {
149                            throw new IllegalArgumentException("Negative number of pixels " +
150                                    "to be converted is invalid: " + num);
151                    }
152                    checkArray(y, yccOffset, num);
153                    checkArray(cb, yccOffset, num);
154                    checkArray(cr, yccOffset, num);
155                    checkArray(r, rgbOffset, num);
156                    checkArray(g, rgbOffset, num);                                  
157                    checkArray(b, rgbOffset, num);
158                    while (num-- > 0)
159                    {
160                            int gray = y[yccOffset] & 0xff;
161                            int chromaBlue = cb[yccOffset] & 0xff;
162                            int chromaRed = cr[yccOffset++] & 0xff;
163                            int cr137 = chromaRed - 137;
164                            int cb156 = chromaBlue - 156;
165                            r[rgbOffset] = floatToByte(c11 * gray + c12 * (cb156) + c13 * (cr137));
166                            g[rgbOffset] = floatToByte(c21 * gray + c22 * (cb156) + c23 * (cr137));
167                            b[rgbOffset++] = floatToByte(c31 * gray + c32 * (cb156) + c33 * (cr137));
168                    }
169            }
170    }