TransferCharacteristics.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /*
  2. * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Format.h>
  7. #include <AK/Math.h>
  8. #include <AK/StdLibExtras.h>
  9. #include "TransferCharacteristics.h"
  10. namespace Video {
  11. // SDR maximum luminance in candelas per meter squared
  12. constexpr float sdr_max_luminance = 120.0f;
  13. // sRGB
  14. constexpr float srgb_inverse_beta = 0.0031308f;
  15. constexpr float srgb_inverse_linear_coef = 12.92f;
  16. constexpr float srgb_gamma = 2.4f;
  17. constexpr float srgb_alpha = 1.055f;
  18. // BT.601/BT.709/BT.2020 constants
  19. constexpr float bt_601_beta = 0.018053968510807f;
  20. constexpr float bt_601_linear_coef = 4.5f;
  21. constexpr float bt_601_alpha = 1.0f + 5.5f * bt_601_beta;
  22. constexpr float bt_601_gamma = 0.45f;
  23. // Perceptual quantizer (SMPTE ST 2084) constants
  24. constexpr float pq_m1 = 2610.0f / 16384.0f;
  25. constexpr float pq_m2 = 128.0f * 2523.0f / 4096.0f;
  26. constexpr float pq_c1 = 3424.0f / 4096.0f;
  27. constexpr float pq_c2 = 32.0f * 2413.0f / 4096.0f;
  28. constexpr float pq_c3 = 32.0f * 2392.0f / 4096.0f;
  29. constexpr float pq_max_luminance = 10000.0f;
  30. // Hybrid log-gamma constants
  31. constexpr float hlg_a = 0.17883277f;
  32. constexpr float hlg_b = 0.28466892f;
  33. constexpr float hlg_c = 0.55991073f;
  34. float TransferCharacteristicsConversion::to_linear_luminance(float value, TransferCharacteristics transfer_function)
  35. {
  36. switch (transfer_function) {
  37. case TransferCharacteristics::BT709:
  38. case TransferCharacteristics::BT601:
  39. case TransferCharacteristics::BT2020BitDepth10:
  40. case TransferCharacteristics::BT2020BitDepth12:
  41. // https://en.wikipedia.org/wiki/Rec._601#Transfer_characteristics
  42. // https://en.wikipedia.org/wiki/Rec._709#Transfer_characteristics
  43. // https://en.wikipedia.org/wiki/Rec._2020#Transfer_characteristics
  44. // These three share identical OETFs.
  45. if (value < bt_601_beta * bt_601_linear_coef)
  46. return value / bt_601_linear_coef;
  47. return AK::pow((value + (bt_601_alpha - 1.0f)) / bt_601_alpha, 1.0f / bt_601_gamma);
  48. case TransferCharacteristics::SRGB:
  49. // https://color.org/sRGB.pdf
  50. if (value < srgb_inverse_linear_coef * srgb_inverse_beta)
  51. return value / srgb_inverse_linear_coef;
  52. return AK::pow((value + (srgb_alpha - 1.0f)) / srgb_alpha, srgb_gamma);
  53. case TransferCharacteristics::SMPTE2084: {
  54. // https://en.wikipedia.org/wiki/Perceptual_quantizer
  55. auto gamma_adjusted = AK::pow(value, 1.0f / pq_m2);
  56. auto numerator = max(gamma_adjusted - pq_c1, 0.0f);
  57. auto denominator = pq_c2 - pq_c3 * gamma_adjusted;
  58. return AK::pow(numerator / denominator, 1.0f / pq_m1) * (pq_max_luminance / sdr_max_luminance);
  59. }
  60. case TransferCharacteristics::HLG:
  61. // https://en.wikipedia.org/wiki/Hybrid_log-gamma
  62. if (value < 0.5f)
  63. return (value * value) / 3.0f;
  64. return (AK::exp((value - hlg_c) / hlg_a) + hlg_b) / 12.0f;
  65. default:
  66. dbgln("Unsupported transfer function {}", static_cast<u8>(transfer_function));
  67. VERIFY_NOT_REACHED();
  68. }
  69. }
  70. float TransferCharacteristicsConversion::to_non_linear_luminance(float value, TransferCharacteristics transfer_function)
  71. {
  72. switch (transfer_function) {
  73. case TransferCharacteristics::BT709:
  74. case TransferCharacteristics::BT601:
  75. case TransferCharacteristics::BT2020BitDepth10:
  76. case TransferCharacteristics::BT2020BitDepth12:
  77. // https://en.wikipedia.org/wiki/Rec._601#Transfer_characteristics
  78. // https://en.wikipedia.org/wiki/Rec._709#Transfer_characteristics
  79. // https://en.wikipedia.org/wiki/Rec._2020#Transfer_characteristics
  80. // These three share identical OETFs.
  81. if (value < bt_601_beta)
  82. return bt_601_linear_coef * value;
  83. return bt_601_alpha * AK::pow(value, bt_601_gamma) - (bt_601_alpha - 1.0f);
  84. case TransferCharacteristics::SRGB:
  85. // https://color.org/sRGB.pdf
  86. if (value < srgb_inverse_beta)
  87. return value * srgb_inverse_linear_coef;
  88. return srgb_alpha * AK::pow(value, 1.0f / srgb_gamma) - (srgb_alpha - 1.0f);
  89. case TransferCharacteristics::SMPTE2084: {
  90. // https://en.wikipedia.org/wiki/Perceptual_quantizer
  91. auto linear_value = AK::pow(value * (sdr_max_luminance / pq_max_luminance), pq_m1);
  92. auto numerator = pq_c1 + pq_c2 * linear_value;
  93. auto denominator = 1 + pq_c3 * linear_value;
  94. return AK::pow(numerator / denominator, pq_m2);
  95. }
  96. case TransferCharacteristics::HLG:
  97. // https://en.wikipedia.org/wiki/Hybrid_log-gamma
  98. if (value < 1.0f / 12.0f)
  99. return AK::sqrt(value * 3.0f);
  100. return hlg_a * AK::log(12.0f * value - hlg_b) + hlg_c;
  101. default:
  102. dbgln("Unsupported transfer function {}", static_cast<u8>(transfer_function));
  103. VERIFY_NOT_REACHED();
  104. }
  105. }
  106. FloatVector4 TransferCharacteristicsConversion::hlg_opto_optical_transfer_function(FloatVector4 const& vector, float gamma, float gain)
  107. {
  108. float luminance = (0.2627f * vector.x() + 0.6780f * vector.y() + 0.0593f * vector.z()) * 1000.0f;
  109. float coefficient = gain * AK::pow(luminance, gamma - 1.0f);
  110. return FloatVector4(vector.x() * coefficient, vector.y() * coefficient, vector.z() * coefficient, vector.w());
  111. }
  112. }