]> git.sesse.net Git - movit/commitdiff
Prepare for better understanding of 10- and 12-bit Y'CbCr.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 8 Sep 2015 23:28:40 +0000 (01:28 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 8 Sep 2015 23:28:40 +0000 (01:28 +0200)
Seemingly there is trickiness in how to interpret the integer
values that is different from what you'll typically see in R'G'B'
(or just GPUs and TV standards differ on that point as well).
Add an explanatory comment, and add a data member to YCbCrFormat
to prepare for correct 10/12-bit level handlings. We'll stay 8-bit
only for now, though, to avoid an API break for existing clients
for no good reason (there's no 10-bit input, really).

ycbcr.cpp
ycbcr.h
ycbcr_422interleaved_input_test.cpp
ycbcr_input_test.cpp

index 277ea9c7b3a3de1c97cf95a51b1941661656bcbd..dc223e7441c845cf7c1449e439862c5732efc71f 100644 (file)
--- a/ycbcr.cpp
+++ b/ycbcr.cpp
@@ -90,6 +90,7 @@ void compute_ycbcr_matrix(YCbCrFormat ycbcr_format, float* offset, Matrix3d* ycb
        }
 
        if (ycbcr_format.full_range) {
        }
 
        if (ycbcr_format.full_range) {
+               // TODO: Use num_levels.
                offset[0] = 0.0 / 255.0;
                offset[1] = 128.0 / 255.0;
                offset[2] = 128.0 / 255.0;
                offset[0] = 0.0 / 255.0;
                offset[1] = 128.0 / 255.0;
                offset[2] = 128.0 / 255.0;
@@ -99,6 +100,7 @@ void compute_ycbcr_matrix(YCbCrFormat ycbcr_format, float* offset, Matrix3d* ycb
                scale[2] = 1.0;
        } else {
                // Rec. 601, page 4; Rec. 709, page 19; Rec. 2020, page 4.
                scale[2] = 1.0;
        } else {
                // Rec. 601, page 4; Rec. 709, page 19; Rec. 2020, page 4.
+               // TODO: Use num_levels.
                offset[0] = 16.0 / 255.0;
                offset[1] = 128.0 / 255.0;
                offset[2] = 128.0 / 255.0;
                offset[0] = 16.0 / 255.0;
                offset[1] = 128.0 / 255.0;
                offset[2] = 128.0 / 255.0;
diff --git a/ycbcr.h b/ycbcr.h
index 7e5891f741907b48c5b00c52fb733d0614d4b8ef..9179b1958a5cdcc73e1a5b74132a9b2bab45d3c8 100644 (file)
--- a/ycbcr.h
+++ b/ycbcr.h
@@ -2,6 +2,35 @@
 #define _MOVIT_YCBCR_H 1
 
 // Shared utility functions between YCbCrInput and YCbCr422InterleavedInput.
 #define _MOVIT_YCBCR_H 1
 
 // Shared utility functions between YCbCrInput and YCbCr422InterleavedInput.
+//
+// Conversion from integer to floating-point representation in case of
+// Y'CbCr is seemingly tricky:
+//
+// BT.601 page 8 has a table that says that for luma, black is at 16.00_d and
+// white is at 235.00_d. _d seemingly means “on a floating-point scale from 0
+// to 255.75”, see §2.4. The .75 is because BT.601 wants to support 10-bit,
+// but all values are scaled for 8-bit since that's the most common; it is
+// specified that conversion from 8-bit to 10-bit is done by inserting two
+// binary zeroes at the end (not repeating bits as one would often do
+// otherwise). It would seem that BT.601 lives in a world where the idealized
+// range is really [0,256), not [0,255].
+//
+// However, GPUs (and by extension Movit) don't work this way. For them,
+// typically 1.0 maps to the largest possible representable value in the
+// framebuffer, ie., the range [0.0,1.0] maps to [0,255] for 8-bit
+// and to [0,1023] (or [0_d,255.75_d] in BT.601 parlance) for 10-bit.
+//
+// BT.701 (page 5) seems to agree with BT.601; it specifies range 16–235 for
+// 8-bit luma, and 64–940 for 10-bit luma. This would indicate, for a GPU,
+// that that for 8-bit mode, the range would be 16/255 to 235/255
+// (0.06275 to 0.92157), while for 10-bit, it should be 64/1023 to 940/1023
+// (0.06256 to 0.91887). There's no good compromise here; if you select 8-bit
+// range, 10-bit goes out of range (white gets to 942), while if you select
+// 10-bit range, 8-bit gets only to 234, making true white impossible.
+//
+// We currently support the 8-bit ranges only, since all of our Y'CbCr
+// handling effects happen to support only 8-bit at the moment. We will need
+// to fix this eventually, though, with an added field to YCbCrFormat.
 
 #include "image_format.h"
 
 
 #include "image_format.h"
 
@@ -18,6 +47,10 @@ struct YCbCrFormat {
        // JPEG uses the Rec. 601 luma coefficients, but full range.
        bool full_range;
 
        // JPEG uses the Rec. 601 luma coefficients, but full range.
        bool full_range;
 
+       // Currently unused, but should be set to 256 for future expansion,
+       // indicating 8-bit interpretation (see file-level comment).
+       int num_levels;
+
        // Sampling factors for chroma components. For no subsampling (4:4:4),
        // set both to 1.
        unsigned chroma_subsampling_x, chroma_subsampling_y;
        // Sampling factors for chroma components. For no subsampling (4:4:4),
        // set both to 1.
        unsigned chroma_subsampling_x, chroma_subsampling_y;
index 290885e10156de4cf5f739a1700781a44f17fe6c..9a56fb1621285eecc89932a9f1dc94c4da8c98d5 100644 (file)
@@ -45,6 +45,7 @@ TEST(YCbCr422InterleavedInputTest, Simple422) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since Y is constant.
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since Y is constant.
@@ -90,6 +91,7 @@ TEST(YCbCr422InterleavedInputTest, LumaLinearInterpolation) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since U/V are constant.
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since U/V are constant.
@@ -150,6 +152,7 @@ TEST(YCbCr422InterleavedInputTest, DifferentCbAndCrPositioning) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;
@@ -208,6 +211,7 @@ TEST(YCbCr422InterleavedInputTest, PBO) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since Y is constant.
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since Y is constant.
index ee77e4c35e07314aee7fc21153efc4635613e78f..873a6c5f132c3b0e5a303e4192d5905f0c69c049 100644 (file)
@@ -44,6 +44,7 @@ TEST(YCbCrInputTest, Simple444) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
@@ -98,6 +99,7 @@ TEST(YCbCrInputTest, FullRangeRec601) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = true;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = true;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
@@ -151,6 +153,7 @@ TEST(YCbCrInputTest, Rec709) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_709;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_709;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
@@ -206,6 +209,7 @@ TEST(YCbCrInputTest, Rec2020) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_2020;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_2020;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
@@ -267,6 +271,7 @@ TEST(YCbCrInputTest, Subsampling420) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 2;
        ycbcr_format.cb_x_position = 0.5f;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 2;
        ycbcr_format.cb_x_position = 0.5f;
@@ -328,6 +333,7 @@ TEST(YCbCrInputTest, Subsampling420WithNonCenteredSamples) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 2;
        ycbcr_format.cb_x_position = 0.0f;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 2;
        ycbcr_format.cb_x_position = 0.0f;
@@ -397,6 +403,7 @@ TEST(YCbCrInputTest, DifferentCbAndCrPositioning) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;
        ycbcr_format.chroma_subsampling_x = 2;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.0f;
@@ -454,6 +461,7 @@ TEST(YCbCrInputTest, PBO) {
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
        YCbCrFormat ycbcr_format;
        ycbcr_format.luma_coefficients = YCBCR_REC_601;
        ycbcr_format.full_range = false;
+       ycbcr_format.num_levels = 256;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;
        ycbcr_format.chroma_subsampling_x = 1;
        ycbcr_format.chroma_subsampling_y = 1;
        ycbcr_format.cb_x_position = 0.5f;