Add several more values to SkYUVColorSpace

Includes full-range Rec709 and BT2020, as well as more precise
matrices for 10 and 12 bit BT2020.

Bug: chromium:1108626
Change-Id: I28cbce982a00c082c8b510dbb7b144bdc1ce02e0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/305596
Reviewed-by: Dale Curtis <dalecurtis@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2020-07-23 14:22:22 -04:00 committed by Skia Commit-Bot
parent 131c5ad6f1
commit b107a11f88
3 changed files with 210 additions and 83 deletions

View File

@ -967,7 +967,9 @@ static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
} }
static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) { static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "2020", "Identity" }; static const char* kYUVColorSpaceNames[] = {"JPEG", "601", "709F", "709L",
"2020_8F", "2020_8L", "2020_10F", "2020_10L",
"2020_12F", "2020_12L", "Identity"};
static_assert(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace + 1); static_assert(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace + 1);
SkPaint paint; SkPaint paint;

View File

@ -139,20 +139,32 @@ SK_API bool SkColorTypeValidateAlphaType(SkColorType colorType, SkAlphaType alph
YUV to RGB. YUV to RGB.
JPEG YUV values encode the full range of 0 to 255 for all three components. JPEG YUV values encode the full range of 0 to 255 for all three components.
Video YUV values range from 16 to 235 for Y and from 16 to 240 for U and V. Video YUV values often range from 16 to 235 for Y and from 16 to 240 for U and V (limited).
Details of encoding and conversion to RGB are described in YCbCr color space. Details of encoding and conversion to RGB are described in YCbCr color space.
The identity colorspace exists to provide a utility mapping from Y to R, U to G and V to B. The identity colorspace exists to provide a utility mapping from Y to R, U to G and V to B.
It can be used to visualize the YUV planes or to explicitly post process the YUV channels. It can be used to visualize the YUV planes or to explicitly post process the YUV channels.
*/ */
enum SkYUVColorSpace { enum SkYUVColorSpace {
kJPEG_SkYUVColorSpace, //!< describes full range kJPEG_Full_SkYUVColorSpace, //!< describes full range
kRec601_SkYUVColorSpace, //!< describes SDTV range kRec601_Limited_SkYUVColorSpace, //!< describes SDTV range
kRec709_SkYUVColorSpace, //!< describes HDTV range kRec709_Full_SkYUVColorSpace, //!< describes HDTV range
kBT2020_SkYUVColorSpace, //!< describes UHDTV range, non-constant-luminance kRec709_Limited_SkYUVColorSpace,
kBT2020_8bit_Full_SkYUVColorSpace, //!< describes UHDTV range, non-constant-luminance
kBT2020_8bit_Limited_SkYUVColorSpace,
kBT2020_10bit_Full_SkYUVColorSpace,
kBT2020_10bit_Limited_SkYUVColorSpace,
kBT2020_12bit_Full_SkYUVColorSpace,
kBT2020_12bit_Limited_SkYUVColorSpace,
kIdentity_SkYUVColorSpace, //!< maps Y->R, U->G, V->B kIdentity_SkYUVColorSpace, //!< maps Y->R, U->G, V->B
kLastEnum_SkYUVColorSpace = kIdentity_SkYUVColorSpace, //!< last valid value kLastEnum_SkYUVColorSpace = kIdentity_SkYUVColorSpace, //!< last valid value
// Legacy (deprecated) names:
kJPEG_SkYUVColorSpace = kJPEG_Full_SkYUVColorSpace,
kRec601_SkYUVColorSpace = kRec601_Limited_SkYUVColorSpace,
kRec709_SkYUVColorSpace = kRec709_Limited_SkYUVColorSpace,
kBT2020_SkYUVColorSpace = kBT2020_8bit_Limited_SkYUVColorSpace,
}; };
/** \struct SkColorInfo /** \struct SkColorInfo

View File

@ -10,73 +10,162 @@
// in SkColorMatrix order (row-major) // in SkColorMatrix order (row-major)
// Created by running SkColorMatrix_DumpYUVMatrixTables() // Created by running SkColorMatrix_DumpYUVMatrixTables()
const float JPEG_full_rgb_to_yuv[] = {
const float Rec709_rgb_to_yuv[] = {
0.182586f, 0.614231f, 0.062007f, 0.000000f, 0.062745f,
-0.100644f, -0.338572f, 0.439216f, 0.000000f, 0.501961f,
0.439216f, -0.398942f, -0.040274f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec709_yuv_to_rgb[] = {
1.164384f, 0.000000f, 1.792741f, 0.000000f, -0.972945f,
1.164384f, -0.213249f, -0.532909f, 0.000000f, 0.301483f,
1.164384f, 2.112402f, 0.000000f, 0.000000f, -1.133402f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec601_rgb_to_yuv[] = {
0.256788f, 0.504129f, 0.097906f, 0.000000f, 0.062745f,
-0.148223f, -0.290993f, 0.439216f, 0.000000f, 0.501961f,
0.439216f, -0.367788f, -0.071427f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec601_yuv_to_rgb[] = {
1.164384f, 0.000000f, 1.596027f, 0.000000f, -0.874202f,
1.164384f, -0.391762f, -0.812968f, 0.000000f, 0.531668f,
1.164384f, 2.017232f, 0.000000f, 0.000000f, -1.085631f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float JPEG_rgb_to_yuv[] = {
0.299000f, 0.587000f, 0.114000f, 0.000000f, 0.000000f, 0.299000f, 0.587000f, 0.114000f, 0.000000f, 0.000000f,
-0.168736f, -0.331264f, 0.500000f, 0.000000f, 0.501961f, -0.168736f, -0.331264f, 0.500000f, 0.000000f, 0.501961f,
0.500000f, -0.418688f, -0.081312f, 0.000000f, 0.501961f, 0.500000f, -0.418688f, -0.081312f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
}; };
const float JPEG_yuv_to_rgb[] = { const float JPEG_full_yuv_to_rgb[] = {
1.000000f, 0.000000f, 1.402000f, 0.000000f, -0.703749f, 1.000000f, -0.000000f, 1.402000f, 0.000000f, -0.703749f,
1.000000f, -0.344136f, -0.714136f, 0.000000f, 0.531211f, 1.000000f, -0.344136f, -0.714136f, 0.000000f, 0.531211f,
1.000000f, 1.772000f, 0.000000f, 0.000000f, -0.889475f, 1.000000f, 1.772000f, 0.000000f, 0.000000f, -0.889475f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
}; };
const float BT2020_rgb_to_yuv[] = { const float Rec601_limited_rgb_to_yuv[] = {
0.256788f, 0.504129f, 0.097906f, 0.000000f, 0.062745f,
-0.148223f, -0.290993f, 0.439216f, 0.000000f, 0.501961f,
0.439216f, -0.367788f, -0.071427f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec601_limited_yuv_to_rgb[] = {
1.164384f, -0.000000f, 1.596027f, 0.000000f, -0.874202f,
1.164384f, -0.391762f, -0.812968f, 0.000000f, 0.531668f,
1.164384f, 2.017232f, 0.000000f, 0.000000f, -1.085631f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec709_full_rgb_to_yuv[] = {
0.212600f, 0.715200f, 0.072200f, 0.000000f, 0.000000f,
-0.114572f, -0.385428f, 0.500000f, 0.000000f, 0.501961f,
0.500000f, -0.454153f, -0.045847f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec709_full_yuv_to_rgb[] = {
1.000000f, -0.000000f, 1.574800f, 0.000000f, -0.790488f,
1.000000f, -0.187324f, -0.468124f, 0.000000f, 0.329010f,
1.000000f, 1.855600f, -0.000000f, 0.000000f, -0.931439f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec709_limited_rgb_to_yuv[] = {
0.182586f, 0.614231f, 0.062007f, 0.000000f, 0.062745f,
-0.100644f, -0.338572f, 0.439216f, 0.000000f, 0.501961f,
0.439216f, -0.398942f, -0.040274f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float Rec709_limited_yuv_to_rgb[] = {
1.164384f, -0.000000f, 1.792741f, 0.000000f, -0.972945f,
1.164384f, -0.213249f, -0.532909f, 0.000000f, 0.301483f,
1.164384f, 2.112402f, -0.000000f, 0.000000f, -1.133402f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_8bit_full_rgb_to_yuv[] = {
0.262700f, 0.678000f, 0.059300f, 0.000000f, 0.000000f,
-0.139630f, -0.360370f, 0.500000f, 0.000000f, 0.501961f,
0.500000f, -0.459786f, -0.040214f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_8bit_full_yuv_to_rgb[] = {
1.000000f, -0.000000f, 1.474600f, 0.000000f, -0.740191f,
1.000000f, -0.164553f, -0.571353f, 0.000000f, 0.369396f,
1.000000f, 1.881400f, -0.000000f, 0.000000f, -0.944389f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_8bit_limited_rgb_to_yuv[] = {
0.225613f, 0.582282f, 0.050928f, 0.000000f, 0.062745f, 0.225613f, 0.582282f, 0.050928f, 0.000000f, 0.062745f,
-0.122655f, -0.316560f, 0.439216f, 0.000000f, 0.501961f, -0.122655f, -0.316560f, 0.439216f, 0.000000f, 0.501961f,
0.439216f, -0.403890f, -0.035326f, 0.000000f, 0.501961f, 0.439216f, -0.403890f, -0.035326f, 0.000000f, 0.501961f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
}; };
const float BT2020_yuv_to_rgb[] = { const float BT2020_8bit_limited_yuv_to_rgb[] = {
1.164384f, 0.000000f, 1.678674f, 0.000000f, -0.915688f, 1.164384f, -0.000000f, 1.678674f, 0.000000f, -0.915688f,
1.164384f, -0.187326f, -0.650424f, 0.000000f, 0.347458f, 1.164384f, -0.187326f, -0.650424f, 0.000000f, 0.347458f,
1.164384f, 2.141772f, 0.000000f, 0.000000f, -1.148145f, 1.164384f, 2.141772f, -0.000000f, 0.000000f, -1.148145f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_10bit_full_rgb_to_yuv[] = {
0.262700f, 0.678000f, 0.059300f, 0.000000f, 0.000000f,
-0.139630f, -0.360370f, 0.500000f, 0.000000f, 0.500489f,
0.500000f, -0.459786f, -0.040214f, 0.000000f, 0.500489f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_10bit_full_yuv_to_rgb[] = {
1.000000f, -0.000000f, 1.474600f, 0.000000f, -0.738021f,
1.000000f, -0.164553f, -0.571353f, 0.000000f, 0.368313f,
1.000000f, 1.881400f, -0.000000f, 0.000000f, -0.941620f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_10bit_limited_rgb_to_yuv[] = {
0.224951f, 0.580575f, 0.050779f, 0.000000f, 0.062561f,
-0.122296f, -0.315632f, 0.437928f, 0.000000f, 0.500489f,
0.437928f, -0.402706f, -0.035222f, 0.000000f, 0.500489f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_10bit_limited_yuv_to_rgb[] = {
1.167808f, -0.000000f, 1.683611f, 0.000000f, -0.915688f,
1.167808f, -0.187877f, -0.652337f, 0.000000f, 0.347458f,
1.167808f, 2.148072f, -0.000000f, 0.000000f, -1.148145f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_12bit_full_rgb_to_yuv[] = {
0.262700f, 0.678000f, 0.059300f, 0.000000f, 0.000000f,
-0.139630f, -0.360370f, 0.500000f, 0.000000f, 0.500122f,
0.500000f, -0.459786f, -0.040214f, 0.000000f, 0.500122f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_12bit_full_yuv_to_rgb[] = {
1.000000f, -0.000000f, 1.474600f, 0.000000f, -0.737480f,
1.000000f, -0.164553f, -0.571353f, 0.000000f, 0.368043f,
1.000000f, 1.881400f, -0.000000f, 0.000000f, -0.940930f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_12bit_limited_rgb_to_yuv[] = {
0.224787f, 0.580149f, 0.050742f, 0.000000f, 0.062515f,
-0.122206f, -0.315401f, 0.437607f, 0.000000f, 0.500122f,
0.437607f, -0.402411f, -0.035196f, 0.000000f, 0.500122f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
};
const float BT2020_12bit_limited_yuv_to_rgb[] = {
1.168664f, -0.000000f, 1.684846f, 0.000000f, -0.915688f,
1.168664f, -0.188015f, -0.652816f, 0.000000f, 0.347458f,
1.168664f, 2.149647f, -0.000000f, 0.000000f, -1.148145f,
0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 1.000000f, 0.000000f,
}; };
static_assert(kJPEG_SkYUVColorSpace == 0, ""); static_assert(kJPEG_Full_SkYUVColorSpace == 0, "");
static_assert(kRec601_SkYUVColorSpace == 1, ""); static_assert(kRec601_Limited_SkYUVColorSpace == 1, "");
static_assert(kRec709_SkYUVColorSpace == 2, ""); static_assert(kRec709_Full_SkYUVColorSpace == 2, "");
static_assert(kBT2020_SkYUVColorSpace == 3, ""); static_assert(kRec709_Limited_SkYUVColorSpace == 3, "");
static_assert(kBT2020_8bit_Full_SkYUVColorSpace == 4, "");
static_assert(kBT2020_8bit_Limited_SkYUVColorSpace == 5, "");
static_assert(kBT2020_10bit_Full_SkYUVColorSpace == 6, "");
static_assert(kBT2020_10bit_Limited_SkYUVColorSpace == 7, "");
static_assert(kBT2020_12bit_Full_SkYUVColorSpace == 8, "");
static_assert(kBT2020_12bit_Limited_SkYUVColorSpace == 9, "");
const float* yuv_to_rgb_array[] = { const float* yuv_to_rgb_array[] = {
JPEG_yuv_to_rgb, JPEG_full_yuv_to_rgb,
Rec601_yuv_to_rgb, Rec601_limited_yuv_to_rgb,
Rec709_yuv_to_rgb, Rec709_full_yuv_to_rgb,
BT2020_yuv_to_rgb, Rec709_limited_yuv_to_rgb,
BT2020_8bit_full_yuv_to_rgb,
BT2020_8bit_limited_yuv_to_rgb,
BT2020_10bit_full_yuv_to_rgb,
BT2020_10bit_limited_yuv_to_rgb,
BT2020_12bit_full_yuv_to_rgb,
BT2020_12bit_limited_yuv_to_rgb,
}; };
const float* rgb_to_yuv_array[] = { const float* rgb_to_yuv_array[] = {
JPEG_rgb_to_yuv, JPEG_full_rgb_to_yuv,
Rec601_rgb_to_yuv, Rec601_limited_rgb_to_yuv,
Rec709_rgb_to_yuv, Rec709_full_rgb_to_yuv,
BT2020_rgb_to_yuv, Rec709_limited_rgb_to_yuv,
BT2020_8bit_full_rgb_to_yuv,
BT2020_8bit_limited_rgb_to_yuv,
BT2020_10bit_full_rgb_to_yuv,
BT2020_10bit_limited_rgb_to_yuv,
BT2020_12bit_full_rgb_to_yuv,
BT2020_12bit_limited_rgb_to_yuv,
}; };
constexpr size_t kSizeOfColorMatrix = 20 * sizeof(float); constexpr size_t kSizeOfColorMatrix = 20 * sizeof(float);
@ -145,49 +234,73 @@ static void scale3(float m[], float s) {
} }
namespace { namespace {
enum Range { kFull, kLimited };
struct YUVCoeff { struct YUVCoeff {
float Kr, Kb; float Kr, Kb;
float scaleY, addY; int bits;
float scaleUV; Range range;
}; };
} // namespace
const YUVCoeff gCoeff[] = { const YUVCoeff gCoeff[] = {
// kJPEG_SkYUVColorSpace { 0.2990f, 0.1140f, 8, kFull }, // kJPEG_Full_SkYUVColorSpace
{ 0.299f, 0.114f, 1, 0, 1, }, { 0.2990f, 0.1140f, 8, kLimited }, // kRec601_Limited_SkYUVColorSpace
{ 0.2126f, 0.0722f, 8, kFull }, // kRec709_Full_SkYUVColorSpace
// kRec601_SkYUVColorSpace { 0.2126f, 0.0722f, 8, kLimited }, // kRec709_Limited_SkYUVColorSpace
{ 0.299f, 0.114f, 219/255.f, 16/255.f, 224/255.f, }, { 0.2627f, 0.0593f, 8, kFull }, // kBT2020_8bit_Full_SkYUVColorSpace
{ 0.2627f, 0.0593f, 8, kLimited }, // kBT2020_8bit_Limited_SkYUVColorSpace
// kRec709_SkYUVColorSpace { 0.2627f, 0.0593f, 10, kFull }, // kBT2020_10bit_Full_SkYUVColorSpace
{ 0.2126f, 0.0722f, 219/255.f, 16/255.f, 224/255.f, }, { 0.2627f, 0.0593f, 10, kLimited }, // kBT2020_10bit_Limited_SkYUVColorSpace
{ 0.2627f, 0.0593f, 12, kFull }, // kBT2020_12bit_Full_SkYUVColorSpace
// kBT2020_SkYUVColorSpace { 0.2627f, 0.0593f, 12, kLimited }, // kBT2020_12bit_Limited_SkYUVColorSpace
{ 0.2627f, 0.0593f, 219/255.f, 16/255.f, 224/255.f, },
}; };
}
static void make_rgb_to_yuv_matrix(float mx[20], const YUVCoeff& c) { static void make_rgb_to_yuv_matrix(float mx[20], const YUVCoeff& c) {
SkASSERT(c.bits >= 8);
const float Kr = c.Kr; const float Kr = c.Kr;
const float Kb = c.Kb; const float Kb = c.Kb;
const float Kg = 1.0f - Kr - Kb; const float Kg = 1.0f - Kr - Kb;
const float Cr = 0.5f / (1.0f - Kb); const float Cr = 0.5f / (1.0f - Kb);
const float Cb = 0.5f / (1.0f - Kr); const float Cb = 0.5f / (1.0f - Kr);
const int shift = c.bits - 8;
const float denom = static_cast<float>((1 << c.bits) - 1);
float scaleY = 1.0f,
addY = 0.0f,
scaleUV = 1.0f,
addUV = (128 << shift) / denom;
if (c.range == kLimited) {
scaleY = (219 << shift) / denom;
addY = ( 16 << shift) / denom;
scaleUV = (224 << shift) / denom;
}
float m[20] = { float m[20] = {
Kr, Kg, Kb, 0, c.addY, Kr, Kg, Kb, 0, addY,
-Kr, -Kg, 1-Kb, 0, 128/255.f, -Kr, -Kg, 1-Kb, 0, addUV,
1-Kr, -Kg, -Kb, 0, 128/255.f, 1-Kr, -Kg, -Kb, 0, addUV,
0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
}; };
memcpy(mx, m, sizeof(m)); memcpy(mx, m, sizeof(m));
scale3(mx + 0, c.scaleY); scale3(mx + 0, scaleY );
scale3(mx + 5, Cr * c.scaleUV); scale3(mx + 5, Cr * scaleUV);
scale3(mx + 10, Cb * c.scaleUV); scale3(mx + 10, Cb * scaleUV);
} }
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv) { static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv) {
const char* names[] = { const char* names[] = {
"JPEG", "Rec601", "Rec709", "BT2020", "JPEG_full",
"Rec601_limited",
"Rec709_full",
"Rec709_limited",
"BT2020_8bit_full",
"BT2020_8bit_limited",
"BT2020_10bit_full",
"BT2020_10bit_limited",
"BT2020_12bit_full",
"BT2020_12bit_limited",
}; };
const char* dirnames[] = { const char* dirnames[] = {
"yuv_to_rgb", "rgb_to_yuv", "yuv_to_rgb", "rgb_to_yuv",
@ -206,8 +319,8 @@ static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv) {
// Used to create the prebuilt tables for each colorspace. // Used to create the prebuilt tables for each colorspace.
// Don't remove this function, in case we want to recompute those tables in the future. // Don't remove this function, in case we want to recompute those tables in the future.
void SkColorMatrix_DumpYUVMatrixTables() { void SkColorMatrix_DumpYUVMatrixTables() {
for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace, for (int i = 0; i < kLastEnum_SkYUVColorSpace; ++i) {
kBT2020_SkYUVColorSpace}) { SkYUVColorSpace cs = static_cast<SkYUVColorSpace>(i);
float m[20]; float m[20];
make_rgb_to_yuv_matrix(m, gCoeff[(unsigned)cs]); make_rgb_to_yuv_matrix(m, gCoeff[(unsigned)cs]);
dump(m, cs, true); dump(m, cs, true);