VideoFrame.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/NonnullOwnPtr.h>
  7. #include <AK/OwnPtr.h>
  8. #include <LibVideo/Color/ColorConverter.h>
  9. #include "VideoFrame.h"
  10. namespace Video {
  11. ErrorOr<NonnullOwnPtr<SubsampledYUVFrame>> SubsampledYUVFrame::try_create(
  12. Gfx::Size<u32> size,
  13. u8 bit_depth, CodingIndependentCodePoints cicp,
  14. bool subsampling_horizontal, bool subsampling_vertical,
  15. Span<u16> plane_y, Span<u16> plane_u, Span<u16> plane_v)
  16. {
  17. auto plane_y_array = TRY(FixedArray<u16>::create(plane_y));
  18. auto plane_u_array = TRY(FixedArray<u16>::create(plane_u));
  19. auto plane_v_array = TRY(FixedArray<u16>::create(plane_v));
  20. return adopt_nonnull_own_or_enomem(new (nothrow) SubsampledYUVFrame(size, bit_depth, cicp, subsampling_horizontal, subsampling_vertical, plane_y_array, plane_u_array, plane_v_array));
  21. }
  22. template<u32 subsampling_horizontal>
  23. ALWAYS_INLINE void interpolate_row(u32 const row, u32 const width, u16 const* plane_u, u16 const* plane_v, u16* __restrict__ u_row, u16* __restrict__ v_row)
  24. {
  25. // OPTIMIZATION: __restrict__ allows some load eliminations because the planes and the rows will not alias.
  26. constexpr auto horizontal_step = 1u << subsampling_horizontal;
  27. auto const uv_width = (width + subsampling_horizontal) >> subsampling_horizontal;
  28. // Set the first column to the first chroma samples.
  29. u_row[0] = plane_u[row * uv_width];
  30. v_row[0] = plane_v[row * uv_width];
  31. auto const columns_end = width - subsampling_horizontal;
  32. // Interpolate the inner chroma columns.
  33. for (u32 column = 1; column < columns_end; column += horizontal_step) {
  34. auto uv_column = column >> subsampling_horizontal;
  35. u_row[column] = plane_u[row * uv_width + uv_column];
  36. v_row[column] = plane_v[row * uv_width + uv_column];
  37. if constexpr (subsampling_horizontal != 0) {
  38. u_row[column + 1] = (plane_u[row * uv_width + uv_column] + plane_u[row * uv_width + uv_column + 1]) >> 1;
  39. v_row[column + 1] = (plane_v[row * uv_width + uv_column] + plane_v[row * uv_width + uv_column + 1]) >> 1;
  40. }
  41. }
  42. // If there is a last chroma sample that hasn't been set above, set it now.
  43. if constexpr (subsampling_horizontal != 0) {
  44. if ((width & 1) == 0) {
  45. u_row[width - 1] = u_row[width - 2];
  46. v_row[width - 1] = v_row[width - 2];
  47. }
  48. }
  49. }
  50. template<u32 subsampling_horizontal, u32 subsampling_vertical, typename Convert>
  51. ALWAYS_INLINE DecoderErrorOr<void> convert_to_bitmap_subsampled(Convert convert, u32 const width, u32 const height, FixedArray<u16> const& plane_y, FixedArray<u16> const& plane_u, FixedArray<u16> const& plane_v, Gfx::Bitmap& bitmap)
  52. {
  53. VERIFY(bitmap.width() >= 0 && static_cast<u32>(bitmap.width()) == width);
  54. VERIFY(bitmap.height() >= 0 && static_cast<u32>(bitmap.height()) == height);
  55. auto temporary_buffer = DECODER_TRY_ALLOC(FixedArray<u16>::create(static_cast<size_t>(width) * 4));
  56. // Above rows
  57. auto* u_row_a = temporary_buffer.span().slice(static_cast<size_t>(width) * 0, width).data();
  58. auto* v_row_a = temporary_buffer.span().slice(static_cast<size_t>(width) * 1, width).data();
  59. // Below rows
  60. auto* u_row_b = temporary_buffer.span().slice(static_cast<size_t>(width) * 2, width).data();
  61. auto* v_row_b = temporary_buffer.span().slice(static_cast<size_t>(width) * 3, width).data();
  62. u32 const vertical_step = 1 << subsampling_vertical;
  63. interpolate_row<subsampling_horizontal>(0, width, plane_u.data(), plane_v.data(), u_row_a, v_row_a);
  64. // Do interpolation for all inner rows.
  65. const u32 rows_end = height - subsampling_vertical;
  66. for (u32 row = 0; row < rows_end; row += vertical_step) {
  67. // Horizontally scale the row if subsampled.
  68. auto uv_row = row >> subsampling_vertical;
  69. interpolate_row<subsampling_horizontal>(uv_row, width, plane_u.data(), plane_v.data(), u_row_b, v_row_b);
  70. // If subsampled vertically, vertically interpolate the middle row between the above and below rows.
  71. if constexpr (subsampling_vertical != 0) {
  72. // OPTIMIZATION: Splitting these two lines into separate loops enables vectorization.
  73. for (u32 column = 0; column < width; column++) {
  74. u_row_a[column] = (u_row_a[column] + u_row_b[column]) >> 1;
  75. }
  76. for (u32 column = 0; column < width; column++) {
  77. v_row_a[column] = (v_row_a[column] + v_row_b[column]) >> 1;
  78. }
  79. }
  80. auto const* y_row_a = &plane_y[static_cast<size_t>(row) * width];
  81. auto* scan_line_a = bitmap.scanline(static_cast<int>(row));
  82. for (size_t column = 0; column < width; column++) {
  83. scan_line_a[column] = convert(y_row_a[column], u_row_a[column], v_row_a[column]).value();
  84. }
  85. if constexpr (subsampling_vertical != 0) {
  86. auto const* y_row_b = &plane_y[static_cast<size_t>(row + 1) * width];
  87. auto* scan_line_b = bitmap.scanline(static_cast<int>(row + 1));
  88. for (size_t column = 0; column < width; column++) {
  89. scan_line_b[column] = convert(y_row_b[column], u_row_b[column], v_row_b[column]).value();
  90. }
  91. }
  92. AK::TypedTransfer<RemoveReference<decltype(*u_row_a)>>::move(u_row_a, u_row_b, width);
  93. AK::TypedTransfer<RemoveReference<decltype(*u_row_a)>>::move(v_row_a, v_row_b, width);
  94. }
  95. if constexpr (subsampling_vertical != 0) {
  96. // If there is a final row that hasn't been set above, convert it now.
  97. if ((height & 1) == 0) {
  98. auto const* y_row = &plane_y[static_cast<size_t>(height - 1) * width];
  99. auto* scan_line = bitmap.scanline(static_cast<int>(height - 1));
  100. for (size_t column = 0; column < width; column++) {
  101. scan_line[column] = convert(y_row[column], u_row_a[column], v_row_a[column]).value();
  102. }
  103. }
  104. }
  105. return {};
  106. }
  107. template<u32 subsampling_horizontal, u32 subsampling_vertical>
  108. static ALWAYS_INLINE DecoderErrorOr<void> convert_to_bitmap_selecting_converter(CodingIndependentCodePoints cicp, u8 bit_depth, u32 const width, u32 const height, FixedArray<u16> const& plane_y, FixedArray<u16> const& plane_u, FixedArray<u16> const& plane_v, Gfx::Bitmap& bitmap)
  109. {
  110. constexpr auto output_cicp = CodingIndependentCodePoints(ColorPrimaries::BT709, TransferCharacteristics::SRGB, MatrixCoefficients::BT709, VideoFullRangeFlag::Full);
  111. if (bit_depth == 8 && cicp.transfer_characteristics() == output_cicp.transfer_characteristics() && cicp.color_primaries() == output_cicp.color_primaries() && cicp.video_full_range_flag() == VideoFullRangeFlag::Studio) {
  112. switch (cicp.matrix_coefficients()) {
  113. case MatrixCoefficients::BT709:
  114. return convert_to_bitmap_subsampled<subsampling_horizontal, subsampling_vertical>([](u16 y, u16 u, u16 v) { return ColorConverter::convert_simple_yuv_to_rgb<MatrixCoefficients::BT709, VideoFullRangeFlag::Studio>(y, u, v); }, width, height, plane_y, plane_u, plane_v, bitmap);
  115. case MatrixCoefficients::BT601:
  116. return convert_to_bitmap_subsampled<subsampling_horizontal, subsampling_vertical>([](u16 y, u16 u, u16 v) { return ColorConverter::convert_simple_yuv_to_rgb<MatrixCoefficients::BT601, VideoFullRangeFlag::Studio>(y, u, v); }, width, height, plane_y, plane_u, plane_v, bitmap);
  117. case MatrixCoefficients::BT2020ConstantLuminance:
  118. case MatrixCoefficients::BT2020NonConstantLuminance:
  119. return convert_to_bitmap_subsampled<subsampling_horizontal, subsampling_vertical>([](u16 y, u16 u, u16 v) { return ColorConverter::convert_simple_yuv_to_rgb<MatrixCoefficients::BT2020ConstantLuminance, VideoFullRangeFlag::Studio>(y, u, v); }, width, height, plane_y, plane_u, plane_v, bitmap);
  120. default:
  121. VERIFY_NOT_REACHED();
  122. }
  123. }
  124. auto converter = TRY(ColorConverter::create(bit_depth, cicp, output_cicp));
  125. return convert_to_bitmap_subsampled<subsampling_horizontal, subsampling_vertical>([&](u16 y, u16 u, u16 v) { return converter.convert_yuv(y, u, v); }, width, height, plane_y, plane_u, plane_v, bitmap);
  126. }
  127. static DecoderErrorOr<void> convert_to_bitmap_selecting_subsampling(bool subsampling_horizontal, bool subsampling_vertical, CodingIndependentCodePoints cicp, u8 bit_depth, u32 const width, u32 const height, FixedArray<u16> const& plane_y, FixedArray<u16> const& plane_u, FixedArray<u16> const& plane_v, Gfx::Bitmap& bitmap)
  128. {
  129. if (subsampling_horizontal && subsampling_vertical) {
  130. return convert_to_bitmap_selecting_converter<true, true>(cicp, bit_depth, width, height, plane_y, plane_u, plane_v, bitmap);
  131. }
  132. if (subsampling_horizontal && !subsampling_vertical) {
  133. return convert_to_bitmap_selecting_converter<true, false>(cicp, bit_depth, width, height, plane_y, plane_u, plane_v, bitmap);
  134. }
  135. if (!subsampling_horizontal && subsampling_vertical) {
  136. return convert_to_bitmap_selecting_converter<false, true>(cicp, bit_depth, width, height, plane_y, plane_u, plane_v, bitmap);
  137. }
  138. return convert_to_bitmap_selecting_converter<false, false>(cicp, bit_depth, width, height, plane_y, plane_u, plane_v, bitmap);
  139. }
  140. DecoderErrorOr<void> SubsampledYUVFrame::output_to_bitmap(Gfx::Bitmap& bitmap)
  141. {
  142. return convert_to_bitmap_selecting_subsampling(m_subsampling_horizontal, m_subsampling_vertical, cicp(), bit_depth(), width(), height(), m_plane_y, m_plane_u, m_plane_v, bitmap);
  143. }
  144. }