blob: 79176c4dfd945ab831b66534d9f853a4aec5c6bc [file] [log] [blame]
/*
* 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++)
{
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(float x)
{
// Math.round() is very slow
return (int) (x + 0.5f);
}
public static int convertYCbCrToRGB(int Y, int Cb, int Cr)
{
int r = reds[(Cr << 8) | Y];
int g1 = greens1[(Cb << 8) | Cr];
int g = greens2[(g1 << 8) | Y];
int b = blues[(Cb << 8) | Y];
return r | g | b;
}
}