1 /*
2  * Copyright 2019 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkM44.h"
9 #include "src/core/SkYUVMath.h"
10 
11 // in SkColorMatrix order (row-major)
12 // Created by running SkColorMatrix_DumpYUVMatrixTables()
13 const float JPEG_full_rgb_to_yuv[] = {
14       0.299000f,  0.587000f,  0.114000f,  0.000000f,  0.000000f,
15      -0.168736f, -0.331264f,  0.500000f,  0.000000f,  0.501961f,
16       0.500000f, -0.418688f, -0.081312f,  0.000000f,  0.501961f,
17       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
18 };
19 const float JPEG_full_yuv_to_rgb[] = {
20       1.000000f, -0.000000f,  1.402000f,  0.000000f, -0.703749f,
21       1.000000f, -0.344136f, -0.714136f,  0.000000f,  0.531211f,
22       1.000000f,  1.772000f,  0.000000f,  0.000000f, -0.889475f,
23       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
24 };
25 const float Rec601_limited_rgb_to_yuv[] = {
26       0.256788f,  0.504129f,  0.097906f,  0.000000f,  0.062745f,
27      -0.148223f, -0.290993f,  0.439216f,  0.000000f,  0.501961f,
28       0.439216f, -0.367788f, -0.071427f,  0.000000f,  0.501961f,
29       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
30 };
31 const float Rec601_limited_yuv_to_rgb[] = {
32       1.164384f, -0.000000f,  1.596027f,  0.000000f, -0.874202f,
33       1.164384f, -0.391762f, -0.812968f,  0.000000f,  0.531668f,
34       1.164384f,  2.017232f,  0.000000f,  0.000000f, -1.085631f,
35       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
36 };
37 const float Rec709_full_rgb_to_yuv[] = {
38       0.212600f,  0.715200f,  0.072200f,  0.000000f,  0.000000f,
39      -0.114572f, -0.385428f,  0.500000f,  0.000000f,  0.501961f,
40       0.500000f, -0.454153f, -0.045847f,  0.000000f,  0.501961f,
41       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
42 };
43 const float Rec709_full_yuv_to_rgb[] = {
44       1.000000f, -0.000000f,  1.574800f,  0.000000f, -0.790488f,
45       1.000000f, -0.187324f, -0.468124f,  0.000000f,  0.329010f,
46       1.000000f,  1.855600f, -0.000000f,  0.000000f, -0.931439f,
47       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
48 };
49 const float Rec709_limited_rgb_to_yuv[] = {
50       0.182586f,  0.614231f,  0.062007f,  0.000000f,  0.062745f,
51      -0.100644f, -0.338572f,  0.439216f,  0.000000f,  0.501961f,
52       0.439216f, -0.398942f, -0.040274f,  0.000000f,  0.501961f,
53       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
54 };
55 const float Rec709_limited_yuv_to_rgb[] = {
56       1.164384f, -0.000000f,  1.792741f,  0.000000f, -0.972945f,
57       1.164384f, -0.213249f, -0.532909f,  0.000000f,  0.301483f,
58       1.164384f,  2.112402f, -0.000000f,  0.000000f, -1.133402f,
59       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
60 };
61 const float BT2020_8bit_full_rgb_to_yuv[] = {
62       0.262700f,  0.678000f,  0.059300f,  0.000000f,  0.000000f,
63      -0.139630f, -0.360370f,  0.500000f,  0.000000f,  0.501961f,
64       0.500000f, -0.459786f, -0.040214f,  0.000000f,  0.501961f,
65       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
66 };
67 const float BT2020_8bit_full_yuv_to_rgb[] = {
68       1.000000f, -0.000000f,  1.474600f,  0.000000f, -0.740191f,
69       1.000000f, -0.164553f, -0.571353f,  0.000000f,  0.369396f,
70       1.000000f,  1.881400f, -0.000000f,  0.000000f, -0.944389f,
71       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
72 };
73 const float BT2020_8bit_limited_rgb_to_yuv[] = {
74       0.225613f,  0.582282f,  0.050928f,  0.000000f,  0.062745f,
75      -0.122655f, -0.316560f,  0.439216f,  0.000000f,  0.501961f,
76       0.439216f, -0.403890f, -0.035326f,  0.000000f,  0.501961f,
77       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
78 };
79 const float BT2020_8bit_limited_yuv_to_rgb[] = {
80       1.164384f, -0.000000f,  1.678674f,  0.000000f, -0.915688f,
81       1.164384f, -0.187326f, -0.650424f,  0.000000f,  0.347458f,
82       1.164384f,  2.141772f, -0.000000f,  0.000000f, -1.148145f,
83       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
84 };
85 const float BT2020_10bit_full_rgb_to_yuv[] = {
86       0.262700f,  0.678000f,  0.059300f,  0.000000f,  0.000000f,
87      -0.139630f, -0.360370f,  0.500000f,  0.000000f,  0.500489f,
88       0.500000f, -0.459786f, -0.040214f,  0.000000f,  0.500489f,
89       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
90 };
91 const float BT2020_10bit_full_yuv_to_rgb[] = {
92       1.000000f, -0.000000f,  1.474600f,  0.000000f, -0.738021f,
93       1.000000f, -0.164553f, -0.571353f,  0.000000f,  0.368313f,
94       1.000000f,  1.881400f, -0.000000f,  0.000000f, -0.941620f,
95       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
96 };
97 const float BT2020_10bit_limited_rgb_to_yuv[] = {
98       0.224951f,  0.580575f,  0.050779f,  0.000000f,  0.062561f,
99      -0.122296f, -0.315632f,  0.437928f,  0.000000f,  0.500489f,
100       0.437928f, -0.402706f, -0.035222f,  0.000000f,  0.500489f,
101       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
102 };
103 const float BT2020_10bit_limited_yuv_to_rgb[] = {
104       1.167808f, -0.000000f,  1.683611f,  0.000000f, -0.915688f,
105       1.167808f, -0.187877f, -0.652337f,  0.000000f,  0.347458f,
106       1.167808f,  2.148072f, -0.000000f,  0.000000f, -1.148145f,
107       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
108 };
109 const float BT2020_12bit_full_rgb_to_yuv[] = {
110       0.262700f,  0.678000f,  0.059300f,  0.000000f,  0.000000f,
111      -0.139630f, -0.360370f,  0.500000f,  0.000000f,  0.500122f,
112       0.500000f, -0.459786f, -0.040214f,  0.000000f,  0.500122f,
113       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
114 };
115 const float BT2020_12bit_full_yuv_to_rgb[] = {
116       1.000000f, -0.000000f,  1.474600f,  0.000000f, -0.737480f,
117       1.000000f, -0.164553f, -0.571353f,  0.000000f,  0.368043f,
118       1.000000f,  1.881400f, -0.000000f,  0.000000f, -0.940930f,
119       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
120 };
121 const float BT2020_12bit_limited_rgb_to_yuv[] = {
122       0.224787f,  0.580149f,  0.050742f,  0.000000f,  0.062515f,
123      -0.122206f, -0.315401f,  0.437607f,  0.000000f,  0.500122f,
124       0.437607f, -0.402411f, -0.035196f,  0.000000f,  0.500122f,
125       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
126 };
127 const float BT2020_12bit_limited_yuv_to_rgb[] = {
128       1.168664f, -0.000000f,  1.684846f,  0.000000f, -0.915688f,
129       1.168664f, -0.188015f, -0.652816f,  0.000000f,  0.347458f,
130       1.168664f,  2.149647f, -0.000000f,  0.000000f, -1.148145f,
131       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
132 };
133 
134 static_assert(kJPEG_Full_SkYUVColorSpace            == 0, "");
135 static_assert(kRec601_Limited_SkYUVColorSpace       == 1, "");
136 static_assert(kRec709_Full_SkYUVColorSpace          == 2, "");
137 static_assert(kRec709_Limited_SkYUVColorSpace       == 3, "");
138 static_assert(kBT2020_8bit_Full_SkYUVColorSpace     == 4, "");
139 static_assert(kBT2020_8bit_Limited_SkYUVColorSpace  == 5, "");
140 static_assert(kBT2020_10bit_Full_SkYUVColorSpace    == 6, "");
141 static_assert(kBT2020_10bit_Limited_SkYUVColorSpace == 7, "");
142 static_assert(kBT2020_12bit_Full_SkYUVColorSpace    == 8, "");
143 static_assert(kBT2020_12bit_Limited_SkYUVColorSpace == 9, "");
144 
145 const float* yuv_to_rgb_array[] = {
146     JPEG_full_yuv_to_rgb,
147     Rec601_limited_yuv_to_rgb,
148     Rec709_full_yuv_to_rgb,
149     Rec709_limited_yuv_to_rgb,
150     BT2020_8bit_full_yuv_to_rgb,
151     BT2020_8bit_limited_yuv_to_rgb,
152     BT2020_10bit_full_yuv_to_rgb,
153     BT2020_10bit_limited_yuv_to_rgb,
154     BT2020_12bit_full_yuv_to_rgb,
155     BT2020_12bit_limited_yuv_to_rgb,
156 };
157 
158 const float* rgb_to_yuv_array[] = {
159     JPEG_full_rgb_to_yuv,
160     Rec601_limited_rgb_to_yuv,
161     Rec709_full_rgb_to_yuv,
162     Rec709_limited_rgb_to_yuv,
163     BT2020_8bit_full_rgb_to_yuv,
164     BT2020_8bit_limited_rgb_to_yuv,
165     BT2020_10bit_full_rgb_to_yuv,
166     BT2020_10bit_limited_rgb_to_yuv,
167     BT2020_12bit_full_rgb_to_yuv,
168     BT2020_12bit_limited_rgb_to_yuv,
169 };
170 
171 constexpr size_t kSizeOfColorMatrix = 20 * sizeof(float);
172 
SkColorMatrix_RGB2YUV(SkYUVColorSpace cs,float m[20])173 void SkColorMatrix_RGB2YUV(SkYUVColorSpace cs, float m[20]) {
174     if ((unsigned)cs < (unsigned)kIdentity_SkYUVColorSpace) {
175         memcpy(m, rgb_to_yuv_array[(unsigned)cs], kSizeOfColorMatrix);
176     } else {
177         memset(m, 0, kSizeOfColorMatrix);
178         m[0] = m[6] = m[12] = m[18] = 1;
179     }
180 }
181 
SkColorMatrix_YUV2RGB(SkYUVColorSpace cs,float m[20])182 void SkColorMatrix_YUV2RGB(SkYUVColorSpace cs, float m[20]) {
183     if ((unsigned)cs < (unsigned)kIdentity_SkYUVColorSpace) {
184         memcpy(m, yuv_to_rgb_array[(unsigned)cs], kSizeOfColorMatrix);
185     } else {
186         memset(m, 0, kSizeOfColorMatrix);
187         m[0] = m[6] = m[12] = m[18] = 1;
188     }
189 }
190 
191 ///////////////////////////////////////////////////////////////////////////////////////////////////
192 
193 // we just drop the alpha rol/col from the colormatrix
194 // output is |        tr |
195 //           |  3x3   tg |
196 //           |        tb |
197 //           | 0 0 0  1  |
colormatrix_to_matrix44(const float src[20],SkM44 * dst)198 static void colormatrix_to_matrix44(const float src[20], SkM44* dst) {
199     *dst = SkM44(src[ 0], src[ 1], src[ 2], src[ 4],
200                  src[ 5], src[ 6], src[ 7], src[ 9],
201                  src[10], src[11], src[12], src[14],
202                        0,       0,       0,       1);
203 }
204 
205 // input: ignore the bottom row
206 // output: inject identity row/column for alpha
matrix44_to_colormatrix(const SkM44 & src,float dst[20])207 static void matrix44_to_colormatrix(const SkM44& src, float dst[20]) {
208     dst[0] = src.rc(0,0);
209     dst[1] = src.rc(0,1);
210     dst[2] = src.rc(0,2);
211     dst[3] = 0;
212     dst[4] = src.rc(0,3);    // tx
213 
214     dst[5] = src.rc(1,0);
215     dst[6] = src.rc(1,1);
216     dst[7] = src.rc(1,2);
217     dst[8] = 0;
218     dst[9] = src.rc(1,3);    // ty
219 
220     dst[10] = src.rc(2,0);
221     dst[11] = src.rc(2,1);
222     dst[12] = src.rc(2,2);
223     dst[13] = 0;
224     dst[14] = src.rc(2,3);   // tz
225 
226     dst[15] = dst[16] = dst[17] = dst[19] = 0;
227     dst[18] = 1;
228 }
229 
scale3(float m[],float s)230 static void scale3(float m[], float s) {
231     for (int i = 0; i < 3; ++i) {
232         m[i] *= s;
233     }
234 }
235 
236 namespace {
237 enum Range { kFull, kLimited };
238 struct YUVCoeff {
239     float   Kr, Kb;
240     int     bits;
241     Range   range;
242 };
243 
244 const YUVCoeff gCoeff[] = {
245     { 0.2990f, 0.1140f,  8, kFull    }, // kJPEG_Full_SkYUVColorSpace
246     { 0.2990f, 0.1140f,  8, kLimited }, // kRec601_Limited_SkYUVColorSpace
247     { 0.2126f, 0.0722f,  8, kFull    }, // kRec709_Full_SkYUVColorSpace
248     { 0.2126f, 0.0722f,  8, kLimited }, // kRec709_Limited_SkYUVColorSpace
249     { 0.2627f, 0.0593f,  8, kFull    }, // kBT2020_8bit_Full_SkYUVColorSpace
250     { 0.2627f, 0.0593f,  8, kLimited }, // kBT2020_8bit_Limited_SkYUVColorSpace
251     { 0.2627f, 0.0593f, 10, kFull    }, // kBT2020_10bit_Full_SkYUVColorSpace
252     { 0.2627f, 0.0593f, 10, kLimited }, // kBT2020_10bit_Limited_SkYUVColorSpace
253     { 0.2627f, 0.0593f, 12, kFull    }, // kBT2020_12bit_Full_SkYUVColorSpace
254     { 0.2627f, 0.0593f, 12, kLimited }, // kBT2020_12bit_Limited_SkYUVColorSpace
255 };
256 }  // namespace
257 
make_rgb_to_yuv_matrix(float mx[20],const YUVCoeff & c)258 static void make_rgb_to_yuv_matrix(float mx[20], const YUVCoeff& c) {
259     SkASSERT(c.bits >= 8);
260     const float Kr = c.Kr;
261     const float Kb = c.Kb;
262     const float Kg = 1.0f - Kr - Kb;
263     const float Cr = 0.5f / (1.0f - Kb);
264     const float Cb = 0.5f / (1.0f - Kr);
265 
266     const int shift = c.bits - 8;
267 
268     const float denom = static_cast<float>((1 << c.bits) - 1);
269     float scaleY  = 1.0f,
270           addY    = 0.0f,
271           scaleUV = 1.0f,
272           addUV   = (128 << shift) / denom;
273 
274     if (c.range == kLimited) {
275         scaleY  = (219 << shift) / denom;
276         addY    = ( 16 << shift) / denom;
277         scaleUV = (224 << shift) / denom;
278     }
279 
280     float m[20] = {
281           Kr,  Kg,   Kb,  0,  addY,
282          -Kr, -Kg, 1-Kb,  0, addUV,
283         1-Kr, -Kg,  -Kb,  0, addUV,
284            0,   0,    0,  1,     0,
285     };
286     memcpy(mx, m, sizeof(m));
287     scale3(mx +  0,      scaleY );
288     scale3(mx +  5, Cr * scaleUV);
289     scale3(mx + 10, Cb * scaleUV);
290 }
291 
dump(const float m[20],SkYUVColorSpace cs,bool rgb2yuv)292 static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv) {
293     const char* names[] = {
294         "JPEG_full",
295         "Rec601_limited",
296         "Rec709_full",
297         "Rec709_limited",
298         "BT2020_8bit_full",
299         "BT2020_8bit_limited",
300         "BT2020_10bit_full",
301         "BT2020_10bit_limited",
302         "BT2020_12bit_full",
303         "BT2020_12bit_limited",
304     };
305     const char* dirnames[] = {
306         "yuv_to_rgb", "rgb_to_yuv",
307     };
308     SkDebugf("const float %s_%s[] = {\n", names[cs], dirnames[rgb2yuv]);
309     for (int i = 0; i < 4; ++i) {
310         SkDebugf("    ");
311         for (int j = 0; j < 5; ++j) {
312             SkDebugf(" %9.6ff,", m[i * 5 + j]);
313         }
314         SkDebugf("\n");
315     }
316     SkDebugf("};\n");
317 }
318 
319 // Used to create the prebuilt tables for each colorspace.
320 // Don't remove this function, in case we want to recompute those tables in the future.
SkColorMatrix_DumpYUVMatrixTables()321 void SkColorMatrix_DumpYUVMatrixTables() {
322     for (int i = 0; i < kLastEnum_SkYUVColorSpace; ++i) {
323         SkYUVColorSpace cs = static_cast<SkYUVColorSpace>(i);
324         float m[20];
325         make_rgb_to_yuv_matrix(m, gCoeff[(unsigned)cs]);
326         dump(m, cs, true);
327         SkM44 m44, im44;
328         colormatrix_to_matrix44(m, &m44);
329         float im[20];
330 #ifdef SK_DEBUG
331         // be sure our coversion between matrix44 and colormatrix is perfect
332         matrix44_to_colormatrix(m44, im);
333         SkASSERT(memcmp(m, im, sizeof(im)) == 0);
334 #endif
335         SkAssertResult(m44.invert(&im44));
336         matrix44_to_colormatrix(im44, im);
337         dump(im, cs, false);
338     }
339 }
340