View Javadoc
1   /*
2    *  Licensed under the Apache License, Version 2.0 (the "License");
3    *  you may not use this file except in compliance with the License.
4    *  You may obtain a copy of the License at
5    *
6    *       http://www.apache.org/licenses/LICENSE-2.0
7    *
8    *  Unless required by applicable law or agreed to in writing, software
9    *  distributed under the License is distributed on an "AS IS" BASIS,
10   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   *  See the License for the specific language governing permissions and
12   *  limitations under the License.
13   *  under the License.
14   */
15  
16  package org.apache.commons.imaging.formats.jpeg.decoder;
17  
18  final class YCbCrConverter {
19      private static final int[] REDS = new int[256 * 256];
20      private static final int[] BLUES = new int[256 * 256];
21      private static final int[] GREENS1 = new int[256 * 256];
22      private static final int[] GREENS2 = new int[256 * 512];
23  
24      static {
25          /*
26           * Why use (Cr << 8) | Y and not (Y << 8) | Cr as the index? Y changes often, while Cb and Cr is usually subsampled less often and repeats itself
27           * between adjacent pixels, so using it as the high order byte gives higher locality of reference.
28           */
29          for (int y = 0; y < 256; y++) {
30              for (int cr = 0; cr < 256; cr++) {
31                  int r = y + fastRound(1.402f * (cr - 128));
32                  if (r < 0) {
33                      r = 0;
34                  }
35                  if (r > 255) {
36                      r = 255;
37                  }
38                  REDS[cr << 8 | y] = r << 16;
39              }
40          }
41          for (int y = 0; y < 256; y++) {
42              for (int cb = 0; cb < 256; cb++) {
43                  int b = y + fastRound(1.772f * (cb - 128));
44                  if (b < 0) {
45                      b = 0;
46                  }
47                  if (b > 255) {
48                      b = 255;
49                  }
50                  BLUES[cb << 8 | y] = b;
51              }
52          }
53          // green is the hardest
54          // Math.round((float) (Y - 0.34414*(Cb-128) - 0.71414*(Cr-128)))
55          // but Y is integral
56          // = Y - Math.round((float) (0.34414*(Cb-128) + 0.71414*(Cr-128)))
57          // = Y - Math.round(f(Cb, Cr))
58          // where
59          // f(Cb, Cr) = 0.34414*(Cb-128) + 0.71414*(Cr-128)
60          // Cb and Cr terms each vary from 255-128 = 127 to 0-128 = -128
61          // Linear function, so only examine endpoints:
62          // Cb term Cr term Result
63          // 127 127 134.4
64          // -128 -128 -135.4
65          // 127 -128 -47.7
66          // -128 127 46.6
67          // Thus with -135 being the minimum and 134 the maximum,
68          // there is a range of 269 values,
69          // and 135 needs to be added to make it zero-based.
70  
71          // As for Y - f(Cb, Cr)
72          // the range becomes:
73          // Y f(Cb, Cr)
74          // 255 -135
75          // 255 134
76          // 0 -135
77          // 0 134
78          // thus the range is [-134,390] and has 524 values
79          // but is clamped to [0, 255]
80          for (int cb = 0; cb < 256; cb++) {
81              for (int cr = 0; cr < 256; cr++) {
82                  final int value = fastRound(0.34414f * (cb - 128) + 0.71414f * (cr - 128));
83                  GREENS1[cb << 8 | cr] = value + 135;
84              }
85          }
86          for (int y = 0; y < 256; y++) {
87              for (int value = 0; value < 270; value++) {
88                  int green = y - (value - 135);
89                  if (green < 0) {
90                      green = 0;
91                  } else if (green > 255) {
92                      green = 255;
93                  }
94                  GREENS2[value << 8 | y] = green << 8;
95              }
96          }
97      }
98  
99      public static int convertYCbCrToRgb(final int y, final int cb, final int cr) {
100         final int r = REDS[cr << 8 | y];
101         final int g1 = GREENS1[cb << 8 | cr];
102         final int g = GREENS2[g1 << 8 | y];
103         final int b = BLUES[cb << 8 | y];
104         return r | g | b;
105     }
106 
107     private static int fastRound(final float x) {
108         // Math.round() is very slow
109         return (int) (x + 0.5f);
110     }
111 
112     private YCbCrConverter() {
113     }
114 }