1 // Copyright (c) 2022-2024 Advanced Micro Devices, Inc. All rights reserved.
2
3 #include <string.h>
4 #include <math.h>
5 #include "color_bg.h"
6 #include "vpe_priv.h"
7
8 struct csc_vector {
9 float x;
10 float y;
11 float z;
12 };
13
14 struct csc_table {
15 struct csc_vector rgb_offset; // RGB offset
16 struct csc_vector red_coef; // RED coefficient
17 struct csc_vector green_coef; // GREEN coefficient
18 struct csc_vector blue_coef; // BLUE coefficient
19 };
20
21
22 const double bt_709_rgb_xyz_matrix[] = {
23 0.135676572958501, 0.117645247657296, 0.059378179384203,
24 0.069958232931727, 0.235290495314592, 0.023751271753681,
25 0.006359839357430, 0.039215082552432, 0.312725078090138
26 };
27
28 const double bt_601_rgb_xyz_matrix[] = {
29 0.129468377303939, 0.120169907240092, 0.063061715455969,
30 0.069871822671967, 0.230648692928563, 0.028479484399470,
31 0.006165160823997, 0.036826261896157, 0.315308577279846
32 };
33
34 const double bt_2020_rgb_xyz_matrix[] = {
35 0.209559197891125, 0.047578961279863, 0.055561840829013,
36 0.086428369751707, 0.223061365529709, 0.019510264718585,
37 0.000000000000000, 0.009235916013150, 0.349064083986850
38 };
39
40 const double bt_709_xyz_rgb_matrix[] = {
41 9.850972467794900, -4.672897196261683, -1.515534225814599,
42 -2.946029289607537, 5.702028879962675, 0.126307165371354,
43 0.169088388136759, -0.619990756501448, 3.212679374598414
44 };
45
46 const double bt_601_xyz_rgb_matrix[] = {
47 10.656544932293809, -5.288117709127149, -1.653672548215019,
48 -3.249384680406732, 6.011485965740993, 0.106904010143450,
49 0.171144655726832, -0.598710197023623, 3.191344462670923
50 };
51
52 const double bt_2020_xyz_rgb_matrix[] = {
53 5.217784765870115, -1.081066212086299, -0.770110277731489,
54 -2.026396206177778, 4.913316828677627, 0.047928710680581,
55 0.053616587979668, -0.130001864005497, 2.863535322904176
56 };
57
58 static struct csc_table bgcolor_to_rgbfull_table[COLOR_SPACE_MAX] = {
59 [COLOR_SPACE_YCBCR601] =
60 {
61 {0.0f, -0.5f, -0.5f},
62 {1.0f, 0.0f, 1.402f},
63 {1.0f, -0.344136286f, -0.714136286f},
64 {1.0f, 1.772f, 0.0f},
65 },
66 [COLOR_SPACE_YCBCR709] =
67 {
68 {0.0f, -0.5f, -0.5f},
69 {1.0f, 0.0f, 1.5748f},
70 {1.0f, -0.187324273f, -0.468124273f},
71 {1.0f, 1.8556f, 0.0f},
72 },
73 [COLOR_SPACE_YCBCR601_LIMITED] =
74 {
75 {-0.0625f, -0.5f, -0.5f},
76 {1.164383562f, 0.0f, 1.596026786f},
77 {1.164383562f, -0.39176229f, -0.812967647f},
78 {1.164383562f, 2.017232143f, 0.0f},
79 },
80 [COLOR_SPACE_YCBCR709_LIMITED] =
81 {
82 {-0.0625f, -0.5f, -0.5f},
83 {1.164383562f, 0.0f, 1.792741071f},
84 {1.164383562f, -0.213248614f, -0.532909329f},
85 {1.164383562f, 2.112401786f, 0.0f},
86 },
87 [COLOR_SPACE_2020_YCBCR] =
88 {
89 {0.0f, -512.f / 1023.f, -512.f / 1023.f},
90 {1.0f, 0.0f, 1.4746f},
91 {1.0f, -0.164553127f, -0.571353127f},
92 {1.0f, 1.8814f, 0.0f},
93 },
94 [COLOR_SPACE_2020_YCBCR_LIMITED] =
95 {
96 {-0.0625f, -0.5f, -0.5f},
97 {1.167808219f, 0.0f, 1.683611384f},
98 {1.167808219f, -0.187877063f, -0.652337331f},
99 {1.167808219f, 2.148071652f, 0.0f},
100 },
101 [COLOR_SPACE_SRGB_LIMITED] =
102 {
103 {-0.0626221f, -0.0626221f, -0.0626221f},
104 {1.167783652f, 0.0f, 0.0f},
105 {0.0f, 1.167783652f, 0.0f},
106 {0.0f, 0.0, 1.167783652f},
107 },
108 [COLOR_SPACE_2020_RGB_LIMITEDRANGE] = {
109 {-0.0626221f, -0.0626221f, -0.0626221f},
110 {1.167783652f, 0.0f, 0.0f},
111 {0.0f, 1.167783652f, 0.0f},
112 {0.0f, 0.0, 1.167783652f},
113 }};
114
clip_double(double x)115 static double clip_double(double x)
116 {
117 if (x < 0.0)
118 return 0.0;
119 else if (x > 1.0)
120 return 1.0;
121 else
122 return x;
123 }
124
clip_float(float x)125 static float clip_float(float x)
126 {
127 if (x < 0.0f)
128 return 0.0f;
129 else if (x > 1.0f)
130 return 1.0f;
131 else
132 return x;
133 }
134
color_multiply_matrices_double(double * mResult,double * M1,double * M2,unsigned int Rows1,unsigned int Cols1,unsigned int Cols2)135 static void color_multiply_matrices_double(double *mResult, double *M1,
136 double *M2, unsigned int Rows1, unsigned int Cols1, unsigned int Cols2)
137 {
138 unsigned int i, j, k;
139
140 for (i = 0; i < Rows1; i++) {
141 for (j = 0; j < Cols2; j++) {
142 mResult[(i * Cols2) + j] = 0.0;
143 for (k = 0; k < Cols1; k++)
144 mResult[(i * Cols2) + j] = mResult[(i * Cols2) + j] +
145 M1[(i * Cols1) + k] * M2[(k * Cols2) + j];
146 }
147 }
148 }
149
set_gamut_remap_matrix(double * res,enum color_space src_cs,enum color_space dst_cs)150 static void set_gamut_remap_matrix(double* res, enum color_space src_cs, enum color_space dst_cs) {
151
152 double rgb_to_xyz[9] = { 0.0 };
153 double xyz_to_rgb[9] = { 0.0 };
154
155 switch (src_cs)
156 {
157 case COLOR_SPACE_SRGB:
158 case COLOR_SPACE_SRGB_LIMITED:
159 case COLOR_SPACE_MSREF_SCRGB:
160 case COLOR_SPACE_YCBCR709_LIMITED:
161 case COLOR_SPACE_YCBCR709:
162 case COLOR_SPACE_YCBCR_JFIF:
163 case COLOR_SPACE_RGB_JFIF:
164 memcpy(rgb_to_xyz, bt_709_rgb_xyz_matrix, 9 * sizeof(double));
165 break;
166 case COLOR_SPACE_YCBCR601:
167 case COLOR_SPACE_YCBCR601_LIMITED:
168 memcpy(rgb_to_xyz, bt_601_rgb_xyz_matrix, 9 * sizeof(double));
169 break;
170 case COLOR_SPACE_2020_RGB_FULLRANGE:
171 case COLOR_SPACE_2020_RGB_LIMITEDRANGE:
172 case COLOR_SPACE_2020_YCBCR:
173 case COLOR_SPACE_2020_YCBCR_LIMITED:
174 memcpy(rgb_to_xyz, bt_2020_rgb_xyz_matrix, 9 * sizeof(double));
175 break;
176 default:
177 VPE_ASSERT(0);
178 break;
179 }
180
181 switch (dst_cs)
182 {
183 case COLOR_SPACE_SRGB:
184 case COLOR_SPACE_SRGB_LIMITED:
185 case COLOR_SPACE_MSREF_SCRGB:
186 case COLOR_SPACE_YCBCR709_LIMITED:
187 case COLOR_SPACE_YCBCR709:
188 case COLOR_SPACE_YCBCR_JFIF:
189 case COLOR_SPACE_RGB_JFIF:
190 memcpy(xyz_to_rgb, bt_709_xyz_rgb_matrix, 9 * sizeof(double));
191 break;
192 case COLOR_SPACE_YCBCR601:
193 case COLOR_SPACE_YCBCR601_LIMITED:
194 memcpy(xyz_to_rgb, bt_601_xyz_rgb_matrix, 9 * sizeof(double));
195 break;
196 case COLOR_SPACE_2020_RGB_FULLRANGE:
197 case COLOR_SPACE_2020_RGB_LIMITEDRANGE:
198 case COLOR_SPACE_2020_YCBCR:
199 case COLOR_SPACE_2020_YCBCR_LIMITED:
200 memcpy(xyz_to_rgb, bt_2020_xyz_rgb_matrix, 9 * sizeof(double));
201 break;
202 default:
203 VPE_ASSERT(0);
204 break;
205 }
206
207 color_multiply_matrices_double(res, xyz_to_rgb, rgb_to_xyz, 3, 3, 3);
208
209 }
210
vpe_bg_csc(struct vpe_color * bg_color,enum color_space cs)211 bool vpe_bg_csc(struct vpe_color *bg_color, enum color_space cs)
212 {
213 struct csc_table *entry = &bgcolor_to_rgbfull_table[cs];
214 float csc_final[3] = {0};
215 float csc_mm[3][4] = {0};
216 bool output_is_clipped = false;
217
218 memcpy(&csc_mm[0][0], &entry->red_coef, sizeof(struct csc_vector));
219 memcpy(&csc_mm[1][0], &entry->green_coef, sizeof(struct csc_vector));
220 memcpy(&csc_mm[2][0], &entry->blue_coef, sizeof(struct csc_vector));
221
222 csc_mm[0][3] = entry->rgb_offset.x * csc_mm[0][0] + entry->rgb_offset.y * csc_mm[0][1] +
223 entry->rgb_offset.z * csc_mm[0][2];
224
225 csc_mm[1][3] = entry->rgb_offset.x * csc_mm[1][0] + entry->rgb_offset.y * csc_mm[1][1] +
226 entry->rgb_offset.z * csc_mm[1][2];
227
228 csc_mm[2][3] = entry->rgb_offset.x * csc_mm[2][0] + entry->rgb_offset.y * csc_mm[2][1] +
229 entry->rgb_offset.z * csc_mm[2][2];
230
231 csc_final[0] = csc_mm[0][0] * bg_color->ycbcra.y + csc_mm[0][1] * bg_color->ycbcra.cb +
232 csc_mm[0][2] * bg_color->ycbcra.cr + csc_mm[0][3];
233
234 csc_final[1] = csc_mm[1][0] * bg_color->ycbcra.y + csc_mm[1][1] * bg_color->ycbcra.cb +
235 csc_mm[1][2] * bg_color->ycbcra.cr + csc_mm[1][3];
236
237 csc_final[2] = csc_mm[2][0] * bg_color->ycbcra.y + csc_mm[2][1] * bg_color->ycbcra.cb +
238 csc_mm[2][2] * bg_color->ycbcra.cr + csc_mm[2][3];
239
240 // switch to RGB components
241 bg_color->rgba.a = bg_color->ycbcra.a;
242 bg_color->rgba.r = clip_float(csc_final[0]);
243 bg_color->rgba.g = clip_float(csc_final[1]);
244 bg_color->rgba.b = clip_float(csc_final[2]);
245 if ((bg_color->rgba.r != csc_final[0]) || (bg_color->rgba.g != csc_final[1]) ||
246 (bg_color->rgba.b != csc_final[2])) {
247 output_is_clipped = true;
248 }
249 bg_color->is_ycbcr = false;
250 return output_is_clipped;
251 }
252
vpe_is_global_bg_blend_applied(struct stream_ctx * stream_ctx)253 bool vpe_is_global_bg_blend_applied(struct stream_ctx *stream_ctx)
254 {
255
256 return (stream_ctx->stream.blend_info.blending) &&
257 (stream_ctx->stream.blend_info.global_alpha) &&
258 (stream_ctx->stream.blend_info.global_alpha_value != 1.0);
259 }
260
261 struct gamma_coefs {
262 float a0;
263 float a1;
264 float a2;
265 float a3;
266 float user_gamma;
267 float user_contrast;
268 float user_brightness;
269 };
270
271 // srgb, 709, G24
272 static const int32_t numerator01[] = {31308, 180000, 0};
273 static const int32_t numerator02[] = {12920, 4500, 0};
274 static const int32_t numerator03[] = {55, 99, 0};
275 static const int32_t numerator04[] = {55, 99, 0};
276 static const int32_t numerator05[] = {2400, 2222, 2400};
277
build_coefficients(struct gamma_coefs * coefficients,enum color_transfer_func type)278 static bool build_coefficients(struct gamma_coefs *coefficients, enum color_transfer_func type)
279 {
280 uint32_t index = 0;
281 bool ret = true;
282
283 if (type == TRANSFER_FUNC_SRGB)
284 index = 0;
285 else if (type == TRANSFER_FUNC_BT709)
286 index = 1;
287 else if (type == TRANSFER_FUNC_BT1886)
288 index = 2;
289 else {
290 ret = false;
291 goto release;
292 }
293
294 coefficients->a0 = (float)numerator01[index] / 10000000.0f;
295 coefficients->a1 = (float)numerator02[index] / 1000.0f;
296 coefficients->a2 = (float)numerator03[index] / 1000.0f;
297 coefficients->a3 = (float)numerator04[index] / 1000.0f;
298 coefficients->user_gamma = (float)numerator05[index] / 1000.0f;
299
300 release:
301 return ret;
302 }
303
translate_to_linear_space(double arg,double a0,double a1,double a2,double a3,double gamma)304 static double translate_to_linear_space(
305 double arg, double a0, double a1, double a2, double a3, double gamma)
306 {
307 double linear;
308 double base;
309
310 a0 *= a1;
311 if (arg <= -a0) {
312 base = (a2 - arg) / (1.0 + a3);
313 linear = -pow(base, gamma);
314 } else if ((-a0 <= arg) && (arg <= a0))
315 linear = arg / a1;
316 else {
317 base = (a2 + arg) / (1.0 + a3);
318 linear = pow(base, gamma);
319 }
320
321 return linear;
322 }
323
324 // for 709 & sRGB
compute_degam(enum color_transfer_func tf,double inY,double * outX,bool clip)325 static void compute_degam(enum color_transfer_func tf, double inY, double *outX, bool clip)
326 {
327 double ret;
328 struct gamma_coefs coefs = {0};
329
330 build_coefficients(&coefs, tf);
331
332 ret = translate_to_linear_space(inY, (double)coefs.a0, (double)coefs.a1, (double)coefs.a2,
333 (double)coefs.a3, (double)coefs.user_gamma);
334
335 if (clip) {
336 ret = clip_double(ret);
337 }
338 *outX = ret;
339 }
340
get_maximum_fp(double a,double b)341 static double get_maximum_fp(double a, double b)
342 {
343 if (a > b)
344 return a;
345 return b;
346 }
347
compute_depq(double inY,double * outX,bool clip)348 static void compute_depq(double inY, double *outX, bool clip)
349 {
350 double M1 = 0.159301758;
351 double M2 = 78.84375;
352 double C1 = 0.8359375;
353 double C2 = 18.8515625;
354 double C3 = 18.6875;
355
356 double nPowM2;
357 double base;
358 double one = 1.0;
359 double zero = 0.0;
360 bool negative = false;
361 double ret;
362
363 if (inY < zero) {
364 inY = -inY;
365 negative = true;
366 }
367 nPowM2 = pow(inY, one / M2);
368 base = get_maximum_fp(nPowM2 - C1, zero) / (C2 - C3 * nPowM2);
369 ret = pow(base, one / M1);
370 if (clip) {
371 ret = clip_double(ret);
372 }
373 if (negative)
374 ret = -ret;
375
376 *outX = ret;
377 }
378
is_limited_cs(enum color_space cs)379 static bool is_limited_cs(enum color_space cs)
380 {
381 bool is_limited = false;
382
383 switch (cs)
384 {
385 case COLOR_SPACE_SRGB:
386 case COLOR_SPACE_2020_RGB_FULLRANGE:
387 case COLOR_SPACE_MSREF_SCRGB:
388 case COLOR_SPACE_YCBCR601:
389 case COLOR_SPACE_YCBCR709:
390 case COLOR_SPACE_YCBCR_JFIF:
391 case COLOR_SPACE_RGB_JFIF:
392 case COLOR_SPACE_2020_YCBCR:
393 is_limited = false;
394 break;
395 case COLOR_SPACE_SRGB_LIMITED:
396 case COLOR_SPACE_YCBCR601_LIMITED:
397 case COLOR_SPACE_YCBCR709_LIMITED:
398 case COLOR_SPACE_2020_RGB_LIMITEDRANGE:
399 case COLOR_SPACE_2020_YCBCR_LIMITED:
400 is_limited = true;
401 break;
402 default:
403 VPE_ASSERT(0);
404 is_limited = false;
405 break;
406 }
407 return is_limited;
408 }
409
vpe_bg_degam(struct transfer_func * output_tf,struct vpe_color * bg_color)410 static void vpe_bg_degam(
411 struct transfer_func *output_tf, struct vpe_color *bg_color) {
412
413 double degam_r = (double)bg_color->rgba.r;
414 double degam_g = (double)bg_color->rgba.g;
415 double degam_b = (double)bg_color->rgba.b;
416
417 // de-gam
418 switch (output_tf->tf) {
419
420 case TRANSFER_FUNC_PQ2084:
421 compute_depq((double)bg_color->rgba.r, °am_r, true);
422 compute_depq((double)bg_color->rgba.g, °am_g, true);
423 compute_depq((double)bg_color->rgba.b, °am_b, true);
424 break;
425 case TRANSFER_FUNC_SRGB:
426 case TRANSFER_FUNC_BT709:
427 case TRANSFER_FUNC_BT1886:
428 compute_degam(output_tf->tf, (double)bg_color->rgba.r, °am_r, true);
429 compute_degam(output_tf->tf, (double)bg_color->rgba.g, °am_g, true);
430 compute_degam(output_tf->tf, (double)bg_color->rgba.b, °am_b, true);
431 break;
432 case TRANSFER_FUNC_LINEAR:
433 break;
434 default:
435 VPE_ASSERT(0);
436 break;
437 }
438 bg_color->rgba.r = (float)degam_r;
439 bg_color->rgba.g = (float)degam_g;
440 bg_color->rgba.b = (float)degam_b;
441
442 }
443
vpe_bg_inverse_gamut_remap(enum color_space output_cs,struct transfer_func * output_tf,struct vpe_color * bg_color)444 static void vpe_bg_inverse_gamut_remap(enum color_space output_cs,
445 struct transfer_func *output_tf, struct vpe_color *bg_color)
446 {
447
448 double bg_rgb[3] = { 0.0 };
449 double final_bg_rgb[3] = { 0.0 };
450 double matrix[9] = { 0.0 };
451 bg_rgb[0] = (double)bg_color->rgba.r;
452 bg_rgb[1] = (double)bg_color->rgba.g;
453 bg_rgb[2] = (double)bg_color->rgba.b;
454
455 switch (output_tf->tf) {
456 case TRANSFER_FUNC_LINEAR:
457 /* Since linear output uses Bt709, and this conversion is only needed
458 * when the tone mapping is enabled on (Bt2020) input, it is needed to
459 * apply the reverse of Bt2020 -> Bt709 on the background color to
460 * cancel out the effect of Bt2020 -> Bt709 on the background color.
461 */
462 set_gamut_remap_matrix(matrix, COLOR_SPACE_SRGB, COLOR_SPACE_2020_RGB_FULLRANGE);
463 color_multiply_matrices_double(final_bg_rgb, matrix, bg_rgb, 3, 3, 1);
464
465 bg_color->rgba.r = (float)clip_double(final_bg_rgb[0]);
466 bg_color->rgba.g = (float)clip_double(final_bg_rgb[1]);
467 bg_color->rgba.b = (float)clip_double(final_bg_rgb[2]);
468
469 break;
470 case TRANSFER_FUNC_PQ2084:
471 case TRANSFER_FUNC_SRGB:
472 case TRANSFER_FUNC_BT709:
473 case TRANSFER_FUNC_BT1886:
474 break;
475 default:
476 VPE_ASSERT(0);
477 break;
478 }
479
480 }
481
482 // To understand the logic for background color conversion,
483 // please refer to vpe_update_output_gamma_sequence in color.c
vpe_bg_color_convert(enum color_space output_cs,struct transfer_func * output_tf,enum vpe_surface_pixel_format pixel_format,struct vpe_color * mpc_bg_color,struct vpe_color * opp_bg_color,bool enable_3dlut)484 void vpe_bg_color_convert(enum color_space output_cs, struct transfer_func *output_tf,
485 enum vpe_surface_pixel_format pixel_format, struct vpe_color *mpc_bg_color,
486 struct vpe_color *opp_bg_color, bool enable_3dlut)
487 {
488 if (output_tf->type != TF_TYPE_BYPASS) {
489 // inverse degam
490 if (output_tf->tf == TRANSFER_FUNC_PQ2084 && !is_limited_cs(output_cs))
491 vpe_bg_degam(output_tf, mpc_bg_color);
492 // inverse gamut remap
493 if (enable_3dlut)
494 vpe_bg_inverse_gamut_remap(output_cs, output_tf, mpc_bg_color);
495 }
496 // for TF_TYPE_BYPASS, bg color should be programmed to mpc as linear
497 }
498