/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * under the License. */ package org.apache.commons.imaging.formats.jpeg.decoder; public class YCbCrConverter { private static final int[] reds = new int[256 * 256]; private static final int[] blues = new int[256 * 256]; private static final int[] greens1 = new int[256 * 256]; private static final int[] greens2 = new int[256 * 512]; static { /* * 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 between adjacent pixels, so using it as the high order byte * gives higher locality of reference. */ for (int Y = 0; Y < 256; Y++) { for (int Cr = 0; Cr < 256; Cr++) { int r = Y + fastRound(1.402f * (Cr - 128)); if (r < 0) { r = 0; } if (r > 255) { r = 255; } reds[(Cr << 8) | Y] = r << 16; } } for (int Y = 0; Y < 256; Y++) { for (int Cb = 0; Cb < 256; Cb++) { int b = Y + fastRound(1.772f * (Cb - 128)); if (b < 0) { b = 0; } if (b > 255) { b = 255; } blues[(Cb << 8) | Y] = b; } } // green is the hardest // Math.round((float)(Y - 0.34414*(Cb-128) - 0.71414*(Cr-128))) // but Y is integral // = Y - Math.round((float)(0.34414*(Cb-128) + 0.71414*(Cr-128))) // = Y - Math.round(f(Cb, Cr)) // where // f(Cb, Cr) = 0.34414*(Cb-128) + 0.71414*(Cr-128) // Cb and Cr terms each vary from 255-128 = 127 to 0-128 = -128 // Linear function, so only examine endpoints: // Cb term Cr term Result // 127 127 134.4 // -128 -128 -135.4 // 127 -128 -47.7 // -128 127 46.6 // Thus with -135 being the minimum and 134 the maximum, // there is a range of 269 values, // and 135 needs to be added to make it zero-based. // As for Y - f(Cb, Cr) // the range becomes: // Y f(Cb, Cr) // 255 -135 // 255 134 // 0 -135 // 0 134 // thus the range is [-134,390] and has 524 values // but is clamped to [0, 255] for (int Cb = 0; Cb < 256; Cb++) { for (int Cr = 0; Cr < 256; Cr++) { final int value = fastRound(0.34414f * (Cb - 128) + 0.71414f * (Cr - 128)); greens1[(Cb << 8) | Cr] = value + 135; } } for (int Y = 0; Y < 256; Y++) { for (int value = 0; value < 270; value++) { int green = Y - (value - 135); if (green < 0) { green = 0; } else if (green > 255) { green = 255; } greens2[(value << 8) | Y] = green << 8; } } } private static int fastRound(final float x) { // Math.round() is very slow return (int) (x + 0.5f); } public static int convertYCbCrToRGB(final int Y, final int Cb, final int Cr) { final int r = reds[(Cr << 8) | Y]; final int g1 = greens1[(Cb << 8) | Cr]; final int g = greens2[(g1 << 8) | Y]; final int b = blues[(Cb << 8) | Y]; return r | g | b; } }