video-matrices.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /******************************************************************************
  2. Copyright (C) 2014 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "../util/bmem.h"
  15. #include "video-io.h"
  16. //#define COMPUTE_MATRICES
  17. #ifdef COMPUTE_MATRICES
  18. #include "../graphics/matrix3.h"
  19. #endif
  20. static struct {
  21. enum video_colorspace const color_space;
  22. float const Kb, Kr;
  23. int const range_min[3];
  24. int const range_max[3];
  25. int const black_levels[2][3];
  26. float float_range_min[3];
  27. float float_range_max[3];
  28. float matrix[2][16];
  29. } format_info[] = {
  30. {VIDEO_CS_601,
  31. 0.114f,
  32. 0.299f,
  33. {16, 16, 16},
  34. {235, 240, 240},
  35. {{16, 128, 128}, {0, 128, 128}},
  36. #ifndef COMPUTE_MATRICES
  37. {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f},
  38. {235.0f / 255.0f, 240.0f / 255.0f, 240.0f / 255.0f},
  39. {{1.164384f, 0.000000f, 1.596027f, -0.874202f, 1.164384f, -0.391762f,
  40. -0.812968f, 0.531668f, 1.164384f, 2.017232f, 0.000000f, -1.085631f,
  41. 0.000000f, 0.000000f, 0.000000f, 1.000000f},
  42. {1.000000f, 0.000000f, 1.407520f, -0.706520f, 1.000000f, -0.345491f,
  43. -0.716948f, 0.533303f, 1.000000f, 1.778976f, 0.000000f, -0.892976f,
  44. 0.000000f, 0.000000f, 0.000000f, 1.000000f}}
  45. #endif
  46. },
  47. {VIDEO_CS_709,
  48. 0.0722f,
  49. 0.2126f,
  50. {16, 16, 16},
  51. {235, 240, 240},
  52. {{16, 128, 128}, {0, 128, 128}},
  53. #ifndef COMPUTE_MATRICES
  54. {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f},
  55. {235.0f / 255.0f, 240.0f / 255.0f, 240.0f / 255.0f},
  56. {{1.164384f, 0.000000f, 1.792741f, -0.972945f, 1.164384f, -0.213249f,
  57. -0.532909f, 0.301483f, 1.164384f, 2.112402f, 0.000000f, -1.133402f,
  58. 0.000000f, 0.000000f, 0.000000f, 1.000000f},
  59. {1.000000f, 0.000000f, 1.581000f, -0.793600f, 1.000000f, -0.188062f,
  60. -0.469967f, 0.330305f, 1.000000f, 1.862906f, 0.000000f, -0.935106f,
  61. 0.000000f, 0.000000f, 0.000000f, 1.000000f}}
  62. #endif
  63. },
  64. {VIDEO_CS_2020_PQ,
  65. 0.0593f,
  66. 0.2627f,
  67. {64, 64, 64},
  68. {940, 960, 960},
  69. {{64, 512, 512}, {0, 512, 512}},
  70. #ifndef COMPUTE_MATRICES
  71. {64.0f / 1023.0f, 64.0f / 1023.0f, 64.0f / 1023.0f},
  72. {940.0f / 1023.0f, 960.0f / 1023.0f, 960.0f / 1023.0f},
  73. {{1.167808f, 0.000000f, 1.683611f, -0.915688f, 1.167808f, -0.187877f,
  74. -0.652337f, 0.347459f, 1.167808f, 2.148072f, 0.000000f, -1.148145f,
  75. 0.000000f, 0.000000f, 0.000000f, 1.000000f},
  76. {1.000000f, 0.000000f, 1.476043f, -0.738743f, 1.000000f, -0.164714f,
  77. -0.571912f, 0.368673f, 1.000000f, 1.883241f, 0.000000f, -0.942541f,
  78. 0.000000f, 0.000000f, 0.000000f, 1.000000f}}
  79. #endif
  80. },
  81. };
  82. #define NUM_FORMATS (sizeof(format_info) / sizeof(format_info[0]))
  83. #ifdef COMPUTE_MATRICES
  84. static void log_matrix(float const matrix[16])
  85. {
  86. blog(LOG_DEBUG,
  87. "\n% f, % f, % f, % f"
  88. "\n% f, % f, % f, % f"
  89. "\n% f, % f, % f, % f"
  90. "\n% f, % f, % f, % f",
  91. matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5],
  92. matrix[6], matrix[7], matrix[8], matrix[9], matrix[10], matrix[11],
  93. matrix[12], matrix[13], matrix[14], matrix[15]);
  94. }
  95. static void initialize_matrix(float const Kb, float const Kr,
  96. float bit_range_max, int const range_min[3],
  97. int const range_max[3], int const black_levels[3],
  98. float matrix[16])
  99. {
  100. struct matrix3 color_matrix;
  101. int yvals = range_max[0] - range_min[0];
  102. int uvals = (range_max[1] - range_min[1]) / 2;
  103. int vvals = (range_max[2] - range_min[2]) / 2;
  104. vec3_set(&color_matrix.x, bit_range_max / yvals, 0.,
  105. bit_range_max / vvals * (1.f - Kr));
  106. vec3_set(&color_matrix.y, bit_range_max / yvals,
  107. bit_range_max / uvals * (Kb - 1.f) * Kb / (1.f - Kb - Kr),
  108. bit_range_max / vvals * (Kr - 1.f) * Kr / (1.f - Kb - Kr));
  109. vec3_set(&color_matrix.z, bit_range_max / yvals,
  110. bit_range_max / uvals * (1.f - Kb), 0.);
  111. struct vec3 offsets, multiplied;
  112. vec3_set(&offsets, -black_levels[0] / bit_range_max,
  113. -black_levels[1] / bit_range_max,
  114. -black_levels[2] / bit_range_max);
  115. vec3_rotate(&multiplied, &offsets, &color_matrix);
  116. matrix[0] = color_matrix.x.x;
  117. matrix[1] = color_matrix.x.y;
  118. matrix[2] = color_matrix.x.z;
  119. matrix[3] = multiplied.x;
  120. matrix[4] = color_matrix.y.x;
  121. matrix[5] = color_matrix.y.y;
  122. matrix[6] = color_matrix.y.z;
  123. matrix[7] = multiplied.y;
  124. matrix[8] = color_matrix.z.x;
  125. matrix[9] = color_matrix.z.y;
  126. matrix[10] = color_matrix.z.z;
  127. matrix[11] = multiplied.z;
  128. matrix[12] = matrix[13] = matrix[14] = 0.;
  129. matrix[15] = 1.;
  130. log_matrix(matrix);
  131. }
  132. static void initialize_matrices()
  133. {
  134. static const int range_min[] = {0, 0, 0};
  135. static const int range_max_8bit[] = {255, 255, 255};
  136. static const int range_max_10bit[] = {1023, 1023, 1023};
  137. for (size_t i = 0; i < NUM_FORMATS; i++) {
  138. const int *range_max =
  139. (format_info[i].color_space == VIDEO_CS_2020_PQ)
  140. ? range_max_10bit
  141. : range_max_8bit;
  142. float f_r_max = (float)range_max[0];
  143. initialize_matrix(format_info[i].Kb, format_info[i].Kr, f_r_max,
  144. range_min, range_max,
  145. format_info[i].black_levels[1],
  146. format_info[i].matrix[1]);
  147. initialize_matrix(format_info[i].Kb, format_info[i].Kr, f_r_max,
  148. format_info[i].range_min,
  149. format_info[i].range_max,
  150. format_info[i].black_levels[0],
  151. format_info[i].matrix[0]);
  152. for (int j = 0; j < 3; j++) {
  153. format_info[i].float_range_min[j] =
  154. format_info[i].range_min[j] / f_r_max;
  155. format_info[i].float_range_max[j] =
  156. format_info[i].range_max[j] / f_r_max;
  157. }
  158. }
  159. }
  160. static bool matrices_initialized = false;
  161. #endif
  162. static const float full_min[3] = {0.0f, 0.0f, 0.0f};
  163. static const float full_max[3] = {1.0f, 1.0f, 1.0f};
  164. bool video_format_get_parameters(enum video_colorspace color_space,
  165. enum video_range_type range, float matrix[16],
  166. float range_min[3], float range_max[3])
  167. {
  168. #ifdef COMPUTE_MATRICES
  169. if (!matrices_initialized) {
  170. initialize_matrices();
  171. matrices_initialized = true;
  172. }
  173. #endif
  174. if ((color_space == VIDEO_CS_DEFAULT) || (color_space == VIDEO_CS_SRGB))
  175. color_space = VIDEO_CS_709;
  176. else if (color_space == VIDEO_CS_2020_HLG)
  177. color_space = VIDEO_CS_2020_PQ;
  178. for (size_t i = 0; i < NUM_FORMATS; i++) {
  179. if (format_info[i].color_space != color_space)
  180. continue;
  181. int full_range = range == VIDEO_RANGE_FULL ? 1 : 0;
  182. memcpy(matrix, format_info[i].matrix[full_range],
  183. sizeof(float) * 16);
  184. if (range == VIDEO_RANGE_FULL) {
  185. if (range_min)
  186. memcpy(range_min, full_min, sizeof(float) * 3);
  187. if (range_max)
  188. memcpy(range_max, full_max, sizeof(float) * 3);
  189. return true;
  190. }
  191. if (range_min)
  192. memcpy(range_min, format_info[i].float_range_min,
  193. sizeof(float) * 3);
  194. if (range_max)
  195. memcpy(range_max, format_info[i].float_range_max,
  196. sizeof(float) * 3);
  197. return true;
  198. }
  199. return false;
  200. }