Serenity Operating System
1/*
2 * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/NonnullOwnPtr.h>
8#include <AK/OwnPtr.h>
9#include <LibVideo/Color/ColorConverter.h>
10
11#include "VideoFrame.h"
12
13namespace Video {
14
15ErrorOr<NonnullOwnPtr<SubsampledYUVFrame>> SubsampledYUVFrame::try_create(
16 Gfx::IntSize size,
17 u8 bit_depth, CodingIndependentCodePoints cicp,
18 bool subsampling_horizontal, bool subsampling_vertical,
19 Span<u16> plane_y, Span<u16> plane_u, Span<u16> plane_v)
20{
21 auto plane_y_array = TRY(FixedArray<u16>::create(plane_y));
22 auto plane_u_array = TRY(FixedArray<u16>::create(plane_u));
23 auto plane_v_array = TRY(FixedArray<u16>::create(plane_v));
24 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));
25}
26
27DecoderErrorOr<void> SubsampledYUVFrame::output_to_bitmap(Gfx::Bitmap& bitmap)
28{
29 size_t width = this->width();
30 size_t height = this->height();
31 auto u_sample_row = DECODER_TRY_ALLOC(FixedArray<u16>::create(width));
32 auto v_sample_row = DECODER_TRY_ALLOC(FixedArray<u16>::create(width));
33 size_t uv_width = width >> m_subsampling_horizontal;
34
35 auto converter = TRY(ColorConverter::create(bit_depth(), cicp()));
36
37 for (size_t row = 0; row < height; row++) {
38 auto uv_row = row >> m_subsampling_vertical;
39
40 // Linearly interpolate the UV samples vertically first.
41 // This will write all UV samples that are located on the Y sample as well,
42 // so we only need to interpolate horizontally between UV samples in the next
43 // step.
44 if ((row & m_subsampling_vertical) == 0 || row == height - 1) {
45 for (size_t uv_column = 0; uv_column < uv_width; uv_column++) {
46 size_t column = uv_column << m_subsampling_horizontal;
47 size_t index = uv_row * uv_width + uv_column;
48 u_sample_row[column] = m_plane_u[index];
49 v_sample_row[column] = m_plane_v[index];
50 }
51 } else {
52 for (size_t uv_column = 0; uv_column < uv_width; uv_column++) {
53 size_t column = uv_column << m_subsampling_horizontal;
54 size_t index = (uv_row + 1) * uv_width + uv_column;
55 u_sample_row[column] = (u_sample_row[column] + m_plane_u[index]) >> 1;
56 v_sample_row[column] = (v_sample_row[column] + m_plane_v[index]) >> 1;
57 }
58 }
59 // Fill in the last pixel of the row which may not be applied by the above
60 // loops if the last pixel in each row is on an uneven index.
61 if ((width & 1) == 0) {
62 u_sample_row[width - 1] = u_sample_row[width - 2];
63 v_sample_row[width - 1] = v_sample_row[width - 2];
64 }
65
66 // Interpolate the samples horizontally.
67 if (m_subsampling_horizontal) {
68 for (size_t column = 1; column < width - 1; column += 2) {
69 u_sample_row[column] = (u_sample_row[column - 1] + u_sample_row[column + 1]) >> 1;
70 v_sample_row[column] = (v_sample_row[column - 1] + v_sample_row[column + 1]) >> 1;
71 }
72 }
73
74 for (size_t column = 0; column < width; column++) {
75 auto y_sample = m_plane_y[row * width + column];
76 auto u_sample = u_sample_row[column];
77 auto v_sample = v_sample_row[column];
78
79 bitmap.set_pixel(Gfx::IntPoint(column, row), converter.convert_yuv_to_full_range_rgb(y_sample, u_sample, v_sample));
80
81 /*auto r_float = clamp(y_sample + (v_sample - 128) * 219.0f / 224.0f * 1.5748f, 0, 255);
82 auto g_float = clamp(y_sample + (u_sample - 128) * 219.0f / 224.0f * -0.0722f * 1.8556f / 0.7152f + (v_sample - 128) * 219.0f / 224.0f * -0.2126f * 1.5748f / 0.7152f, 0, 255);
83 auto b_float = clamp(y_sample + (u_sample - 128) * 219.0f / 224.0f * 1.8556f, 0, 255);
84 auto r = static_cast<u8>(r_float);
85 auto g = static_cast<u8>(g_float);
86 auto b = static_cast<u8>(b_float);
87 bitmap.set_pixel(Gfx::IntPoint(column, row), Color(r, g, b));*/
88 }
89 }
90
91 return {};
92}
93
94}