X-Git-Url: https://git.sesse.net/?p=movit;a=blobdiff_plain;f=ycbcr.cpp;h=eaa2ee8c3bb277903bef8ab7a8a33f6c137aecf1;hp=f0124eaab178048a2ae3a00c758355fa275668a2;hb=25162b5457057af3ebcc1649571eeeb923e90098;hpb=ba60914d4e5eda7b28af700bf43e9699b7aa720d diff --git a/ycbcr.cpp b/ycbcr.cpp index f0124ea..eaa2ee8 100644 --- a/ycbcr.cpp +++ b/ycbcr.cpp @@ -1,3 +1,6 @@ +// Note: These functions are tested in ycbcr_input_test.cpp; both through some +// direct matrix tests, but most of all through YCbCrInput's unit tests. + #include #include @@ -45,7 +48,12 @@ namespace movit { float compute_chroma_offset(float pos, unsigned subsampling_factor, unsigned resolution) { float local_chroma_pos = (0.5 + pos * (subsampling_factor - 1)) / subsampling_factor; - return (0.5 - local_chroma_pos) / resolution; + if (fabs(local_chroma_pos - 0.5) < 1e-10) { + // x + (-0) can be optimized away freely, as opposed to x + 0. + return -0.0; + } else { + return (0.5 - local_chroma_pos) / resolution; + } } // Given , compute the values needed to turn Y'CbCr into R'G'B'; @@ -81,23 +89,26 @@ void compute_ycbcr_matrix(YCbCrFormat ycbcr_format, float* offset, Matrix3d* ycb assert(false); } + const int num_levels = ycbcr_format.num_levels; if (ycbcr_format.full_range) { - offset[0] = 0.0 / 255.0; - offset[1] = 128.0 / 255.0; - offset[2] = 128.0 / 255.0; + offset[0] = 0.0 / (num_levels - 1); + offset[1] = double(num_levels / 2) / (num_levels - 1); // E.g. 128/255. + offset[2] = double(num_levels / 2) / (num_levels - 1); scale[0] = 1.0; scale[1] = 1.0; scale[2] = 1.0; } else { - // Rec. 601, page 4; Rec. 709, page 19; Rec. 2020, page 4. - offset[0] = 16.0 / 255.0; - offset[1] = 128.0 / 255.0; - offset[2] = 128.0 / 255.0; - - scale[0] = 255.0 / 219.0; - scale[1] = 255.0 / 224.0; - scale[2] = 255.0 / 224.0; + // Rec. 601, page 4; Rec. 709, page 19; Rec. 2020, page 5. + // Rec. 2020 contains the most generic formulas, which we use here. + const double s = num_levels / 256.0; // 2^(n-8) in Rec. 2020 parlance. + offset[0] = (s * 16.0) / (num_levels - 1); + offset[1] = (s * 128.0) / (num_levels - 1); + offset[2] = (s * 128.0) / (num_levels - 1); + + scale[0] = double(num_levels - 1) / (s * 219.0); + scale[1] = double(num_levels - 1) / (s * 224.0); + scale[2] = double(num_levels - 1) / (s * 224.0); } // Matrix to convert RGB to YCbCr. See e.g. Rec. 601. @@ -106,12 +117,12 @@ void compute_ycbcr_matrix(YCbCrFormat ycbcr_format, float* offset, Matrix3d* ycb rgb_to_ycbcr(0,1) = coeff[1]; rgb_to_ycbcr(0,2) = coeff[2]; - float cb_fac = (224.0 / 219.0) / (coeff[0] + coeff[1] + 1.0f - coeff[2]); + float cb_fac = 1.0 / (coeff[0] + coeff[1] + 1.0f - coeff[2]); rgb_to_ycbcr(1,0) = -coeff[0] * cb_fac; rgb_to_ycbcr(1,1) = -coeff[1] * cb_fac; rgb_to_ycbcr(1,2) = (1.0f - coeff[2]) * cb_fac; - float cr_fac = (224.0 / 219.0) / (1.0f - coeff[0] + coeff[1] + coeff[2]); + float cr_fac = 1.0 / (1.0f - coeff[0] + coeff[1] + coeff[2]); rgb_to_ycbcr(2,0) = (1.0f - coeff[0]) * cr_fac; rgb_to_ycbcr(2,1) = -coeff[1] * cr_fac; rgb_to_ycbcr(2,2) = -coeff[2] * cr_fac;