summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Applications/VideoPlayer/main.cpp25
-rw-r--r--Userland/Libraries/LibVideo/CMakeLists.txt3
-rw-r--r--Userland/Libraries/LibVideo/Color/CodingIndependentCodePoints.h228
-rw-r--r--Userland/Libraries/LibVideo/Color/ColorConverter.cpp263
-rw-r--r--Userland/Libraries/LibVideo/Color/ColorConverter.h93
-rw-r--r--Userland/Libraries/LibVideo/Color/ColorPrimaries.cpp95
-rw-r--r--Userland/Libraries/LibVideo/Color/ColorPrimaries.h17
-rw-r--r--Userland/Libraries/LibVideo/Color/TransferCharacteristics.cpp124
-rw-r--r--Userland/Libraries/LibVideo/Color/TransferCharacteristics.h25
-rw-r--r--Userland/Libraries/LibVideo/DecoderError.h2
-rw-r--r--Userland/Libraries/LibVideo/VP9/Decoder.cpp65
-rw-r--r--Userland/Libraries/LibVideo/VP9/Decoder.h3
-rw-r--r--Userland/Libraries/LibVideo/VP9/Enums.h5
-rw-r--r--Userland/Libraries/LibVideo/VP9/Parser.cpp6
-rw-r--r--Userland/Libraries/LibVideo/VP9/Parser.h1
15 files changed, 936 insertions, 19 deletions
diff --git a/Userland/Applications/VideoPlayer/main.cpp b/Userland/Applications/VideoPlayer/main.cpp
index 9e129e2c7f..fb56803a05 100644
--- a/Userland/Applications/VideoPlayer/main.cpp
+++ b/Userland/Applications/VideoPlayer/main.cpp
@@ -12,6 +12,7 @@
#include <LibGUI/Window.h>
#include <LibGfx/Bitmap.h>
#include <LibMain/Main.h>
+#include <LibVideo/Color/ColorConverter.h>
#include <LibVideo/MatroskaReader.h>
#include <LibVideo/VP9/Decoder.h>
@@ -91,6 +92,15 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto uv_subsampling_y = vp9_decoder.get_uv_subsampling_y();
auto uv_subsampling_x = vp9_decoder.get_uv_subsampling_x();
Gfx::IntSize uv_size { y_size.width() >> uv_subsampling_x, y_size.height() >> uv_subsampling_y };
+ auto cicp = vp9_decoder.get_cicp_color_space();
+ cicp.default_code_points_if_unspecified(Video::ColorPrimaries::BT709, Video::TransferCharacteristics::BT709, Video::MatrixCoefficients::BT709);
+
+ auto color_converter_result = Video::ColorConverter::create(vp9_decoder.get_bit_depth(), cicp);
+ if (color_converter_result.is_error()) {
+ outln("Cannot convert video colors: {}", color_converter_result.release_error().string_literal());
+ return;
+ }
+ auto color_converter = color_converter_result.release_value();
for (auto y_row = 0u; y_row < video_track.pixel_height; y_row++) {
auto uv_row = y_row >> uv_subsampling_y;
@@ -99,17 +109,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto uv_column = y_column >> uv_subsampling_x;
auto y = output_y[y_row * y_size.width() + y_column];
- auto cb = output_u[uv_row * uv_size.width() + uv_column];
- auto cr = output_v[uv_row * uv_size.width() + uv_column];
- // Convert from Rec.709 YCbCr to RGB.
- auto r_float = floorf(clamp(y + (cr - 128) * 219.0f / 224.0f * 1.5748f, 0, 255));
- auto g_float = floorf(clamp(y + (cb - 128) * 219.0f / 224.0f * -0.0722f * 1.8556f / 0.7152f + (cr - 128) * 219.0f / 224.0f * -0.2126f * 1.5748f / 0.7152f, 0, 255));
- auto b_float = floorf(clamp(y + (cb - 128) * 219.0f / 224.0f * 1.8556f, 0, 255));
- auto r = static_cast<u8>(r_float);
- auto g = static_cast<u8>(g_float);
- auto b = static_cast<u8>(b_float);
-
- image->set_pixel(y_column, y_row, Gfx::Color(r, g, b));
+ auto u = output_u[uv_row * uv_size.width() + uv_column];
+ auto v = output_v[uv_row * uv_size.width() + uv_column];
+
+ image->set_pixel(y_column, y_row, color_converter.convert_yuv_to_full_range_rgb(y, u, v));
}
}
diff --git a/Userland/Libraries/LibVideo/CMakeLists.txt b/Userland/Libraries/LibVideo/CMakeLists.txt
index 5da617f954..deffad34be 100644
--- a/Userland/Libraries/LibVideo/CMakeLists.txt
+++ b/Userland/Libraries/LibVideo/CMakeLists.txt
@@ -1,4 +1,7 @@
set(SOURCES
+ Color/ColorConverter.cpp
+ Color/ColorPrimaries.cpp
+ Color/TransferCharacteristics.cpp
MatroskaReader.cpp
VP9/BitStream.cpp
VP9/Decoder.cpp
diff --git a/Userland/Libraries/LibVideo/Color/CodingIndependentCodePoints.h b/Userland/Libraries/LibVideo/Color/CodingIndependentCodePoints.h
new file mode 100644
index 0000000000..e393406c91
--- /dev/null
+++ b/Userland/Libraries/LibVideo/Color/CodingIndependentCodePoints.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/StringView.h>
+
+namespace Video {
+
+// CICP is defined by H.273:
+// https://www.itu.int/rec/T-REC-H.273/en
+// See the Section 8.
+// Current edition is from 07/21.
+
+enum class ColorPrimaries : u8 {
+ Reserved = 0,
+ BT709 = 1,
+ Unspecified = 2, // Used by codecs to indicate that an alternative value may be used
+ BT470M = 4,
+ BT470BG = 5,
+ BT601 = 6,
+ SMPTE240 = 7,
+ GenericFilm = 8,
+ BT2020 = 9,
+ XYZ = 10,
+ SMPTE431 = 11,
+ SMPTE432 = 12,
+ EBU3213 = 22,
+ // All other values are also Reserved for later use.
+};
+
+enum class TransferCharacteristics : u8 {
+ Reserved = 0,
+ BT709 = 1,
+ Unspecified = 2, // Used by codecs to indicate that an alternative value may be used
+ BT470M = 4,
+ BT470BG = 5,
+ BT601 = 6, // BT.601 or Rec. 601
+ SMPTE240 = 7,
+ Linear = 8,
+ Log100 = 9,
+ Log100Sqrt10 = 10,
+ IEC61966 = 11,
+ BT1361 = 12,
+ SRGB = 13,
+ BT2020BitDepth10 = 14,
+ BT2020BitDepth12 = 15,
+ SMPTE2084 = 16, // Also known as PQ
+ SMPTE428 = 17,
+ HLG = 18,
+ // All other values are also Reserved for later use.
+};
+
+enum class MatrixCoefficients : u8 {
+ Identity = 0, // Applies no transformation to input values
+ BT709 = 1,
+ Unspecified = 2, // Used by codecs to indicate that an alternative value may be used
+ FCC = 4,
+ BT470BG = 5,
+ BT601 = 6,
+ SMPTE240 = 7,
+ YCgCo = 8,
+ BT2020NonConstantLuminance = 9,
+ BT2020ConstantLuminance = 10,
+ SMPTE2085 = 11,
+ ChromaticityDerivedNonConstantLuminance = 12,
+ ChromaticityDerivedConstantLuminance = 13,
+ ICtCp = 14,
+ // All other values are Reserved for later use.
+};
+
+enum class ColorRange : u8 {
+ Studio = 0, // Y range 16..235, UV range 16..240
+ Full = 1, // 0..255
+};
+
+// https://en.wikipedia.org/wiki/Coding-independent_code_points
+struct CodingIndependentCodePoints {
+public:
+ constexpr CodingIndependentCodePoints(ColorPrimaries color_primaries, TransferCharacteristics transfer_characteristics, MatrixCoefficients matrix_coefficients, ColorRange color_range)
+ : m_color_primaries(color_primaries)
+ , m_transfer_characteristics(transfer_characteristics)
+ , m_matrix_coefficients(matrix_coefficients)
+ , m_color_range(color_range)
+ {
+ }
+
+ constexpr ColorPrimaries color_primaries() const { return m_color_primaries; }
+ constexpr void set_color_primaries(ColorPrimaries value) { m_color_primaries = value; }
+ constexpr TransferCharacteristics transfer_characteristics() const { return m_transfer_characteristics; }
+ constexpr void set_transfer_characteristics(TransferCharacteristics value) { m_transfer_characteristics = value; }
+ constexpr MatrixCoefficients matrix_coefficients() const { return m_matrix_coefficients; }
+ constexpr void set_matrix_coefficients(MatrixCoefficients value) { m_matrix_coefficients = value; }
+ constexpr ColorRange color_range() const { return m_color_range; }
+ constexpr void set_color_range(ColorRange value) { m_color_range = value; }
+
+ constexpr void default_code_points_if_unspecified(ColorPrimaries cp, TransferCharacteristics tc, MatrixCoefficients mc)
+ {
+ if (color_primaries() == ColorPrimaries::Unspecified)
+ set_color_primaries(cp);
+ if (transfer_characteristics() == TransferCharacteristics::Unspecified)
+ set_transfer_characteristics(tc);
+ if (matrix_coefficients() == MatrixCoefficients::Unspecified)
+ set_matrix_coefficients(mc);
+ }
+
+private:
+ ColorPrimaries m_color_primaries;
+ TransferCharacteristics m_transfer_characteristics;
+ MatrixCoefficients m_matrix_coefficients;
+ ColorRange m_color_range;
+};
+
+constexpr StringView color_primaries_to_string(ColorPrimaries color_primaries)
+{
+ switch (color_primaries) {
+ case ColorPrimaries::Reserved:
+ return "Reserved"sv;
+ case ColorPrimaries::BT709:
+ return "BT.709"sv;
+ case ColorPrimaries::Unspecified:
+ return "Unspecified"sv;
+ case ColorPrimaries::BT470M:
+ return "BT.470 System M"sv;
+ case ColorPrimaries::BT470BG:
+ return "BT.470 System B, G"sv;
+ case ColorPrimaries::BT601:
+ return "BT.601"sv;
+ case ColorPrimaries::SMPTE240:
+ return "SMPTE ST 240"sv;
+ case ColorPrimaries::GenericFilm:
+ return "Generic film"sv;
+ case ColorPrimaries::BT2020:
+ return "BT.2020"sv;
+ case ColorPrimaries::XYZ:
+ return "CIE 1931 XYZ"sv;
+ case ColorPrimaries::SMPTE431:
+ return "SMPTE RP 431"sv;
+ case ColorPrimaries::SMPTE432:
+ return "SMPTE EG 432"sv;
+ case ColorPrimaries::EBU3213:
+ return "EBU Tech 3213"sv;
+ }
+ return "Reserved"sv;
+};
+
+constexpr StringView transfer_characteristics_to_string(TransferCharacteristics transfer_characteristics)
+{
+ switch (transfer_characteristics) {
+ case TransferCharacteristics::Reserved:
+ return "Reserved"sv;
+ case TransferCharacteristics::BT709:
+ return "BT.709"sv;
+ case TransferCharacteristics::Unspecified:
+ return "Unspecified"sv;
+ case TransferCharacteristics::BT470M:
+ return "BT.470 System M"sv;
+ case TransferCharacteristics::BT470BG:
+ return "BT.470 System B, G"sv;
+ case TransferCharacteristics::BT601:
+ return "BT.601"sv;
+ case TransferCharacteristics::SMPTE240:
+ return "SMPTE ST 240"sv;
+ case TransferCharacteristics::Linear:
+ return "Linear"sv;
+ case TransferCharacteristics::Log100:
+ return "Logarithmic (100:1 range)"sv;
+ case TransferCharacteristics::Log100Sqrt10:
+ return "Logarithmic (100xSqrt(10):1 range)"sv;
+ case TransferCharacteristics::IEC61966:
+ return "IEC 61966"sv;
+ case TransferCharacteristics::BT1361:
+ return "BT.1361"sv;
+ case TransferCharacteristics::SRGB:
+ return "sRGB"sv;
+ case TransferCharacteristics::BT2020BitDepth10:
+ return "BT.2020 (10-bit)"sv;
+ case TransferCharacteristics::BT2020BitDepth12:
+ return "BT.2020 (12-bit)"sv;
+ case TransferCharacteristics::SMPTE2084:
+ return "SMPTE ST 2084 (PQ)"sv;
+ case TransferCharacteristics::SMPTE428:
+ return "SMPTE ST 428"sv;
+ case TransferCharacteristics::HLG:
+ return "ARIB STD-B67 (HLG, BT.2100)"sv;
+ }
+ return "Reserved"sv;
+};
+
+constexpr StringView matrix_coefficients_to_string(MatrixCoefficients matrix_coefficients)
+{
+ switch (matrix_coefficients) {
+ case MatrixCoefficients::Identity:
+ return "Identity"sv;
+ case MatrixCoefficients::BT709:
+ return "BT.709"sv;
+ case MatrixCoefficients::Unspecified:
+ return "Unspecified"sv;
+ case MatrixCoefficients::FCC:
+ return "FCC (CFR 73.682)"sv;
+ case MatrixCoefficients::BT470BG:
+ return "BT.470 System B, G"sv;
+ case MatrixCoefficients::BT601:
+ return "BT.601"sv;
+ case MatrixCoefficients::SMPTE240:
+ return "SMPTE ST 240"sv;
+ case MatrixCoefficients::YCgCo:
+ return "YCgCo"sv;
+ case MatrixCoefficients::BT2020NonConstantLuminance:
+ return "BT.2020, non-constant luminance"sv;
+ case MatrixCoefficients::BT2020ConstantLuminance:
+ return "BT.2020, constant luminance"sv;
+ case MatrixCoefficients::SMPTE2085:
+ return "SMPTE ST 2085"sv;
+ case MatrixCoefficients::ChromaticityDerivedNonConstantLuminance:
+ return "Chromaticity-derived, non-constant luminance"sv;
+ case MatrixCoefficients::ChromaticityDerivedConstantLuminance:
+ return "Chromaticity-derived, constant luminance"sv;
+ case MatrixCoefficients::ICtCp:
+ return "BT.2100 ICtCp"sv;
+ }
+ return "Reserved"sv;
+};
+
+}
diff --git a/Userland/Libraries/LibVideo/Color/ColorConverter.cpp b/Userland/Libraries/LibVideo/Color/ColorConverter.cpp
new file mode 100644
index 0000000000..4a80b014d8
--- /dev/null
+++ b/Userland/Libraries/LibVideo/Color/ColorConverter.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Format.h>
+#include <AK/Math.h>
+#include <AK/StdLibExtras.h>
+#include <LibGfx/Matrix4x4.h>
+#include <LibVideo/Color/ColorPrimaries.h>
+#include <LibVideo/Color/TransferCharacteristics.h>
+
+#include "ColorConverter.h"
+
+namespace Video {
+
+// Tonemapping methods are outlined here:
+// https://64.github.io/tonemapping/
+
+template<typename T>
+ALWAYS_INLINE constexpr T scalar_to_color_vector(float value)
+{
+ if constexpr (IsSame<T, Gfx::VectorN<4, float>>) {
+ return Gfx::VectorN<4, float>(value, value, value, 1.0f);
+ } else if constexpr (IsSame<T, Gfx::VectorN<3, float>>) {
+ return Gfx::VectorN<3, float>(value, value, value);
+ } else {
+ static_assert(IsFloatingPoint<T>);
+ return static_cast<T>(value);
+ }
+}
+
+template<typename T>
+ALWAYS_INLINE constexpr T hable_tonemapping_partial(T value)
+{
+ constexpr auto a = scalar_to_color_vector<T>(0.15f);
+ constexpr auto b = scalar_to_color_vector<T>(0.5f);
+ constexpr auto c = scalar_to_color_vector<T>(0.1f);
+ constexpr auto d = scalar_to_color_vector<T>(0.2f);
+ constexpr auto e = scalar_to_color_vector<T>(0.02f);
+ constexpr auto f = scalar_to_color_vector<T>(0.3f);
+ return ((value * (a * value + c * b) + d * e) / (value * (a * value + b) + d * f)) - e / f;
+}
+
+template<typename T>
+ALWAYS_INLINE constexpr T hable_tonemapping(T value)
+{
+ constexpr auto exposure_bias = scalar_to_color_vector<T>(2.0f);
+ value = hable_tonemapping_partial<T>(value * exposure_bias);
+ constexpr auto scale = scalar_to_color_vector<T>(1.0f) / scalar_to_color_vector<T>(hable_tonemapping_partial(11.2f));
+ return value * scale;
+}
+
+DecoderErrorOr<ColorConverter> ColorConverter::create(u8 bit_depth, CodingIndependentCodePoints cicp)
+{
+ // We'll need to apply tonemapping for linear HDR values.
+ bool should_tonemap = false;
+ switch (cicp.transfer_characteristics()) {
+ case TransferCharacteristics::SMPTE2084:
+ should_tonemap = true;
+ break;
+ case TransferCharacteristics::HLG:
+ should_tonemap = true;
+ break;
+ default:
+ break;
+ }
+
+ // Conversion process:
+ // 1. Scale integer YUV values with maximum values of (1 << bit_depth) - 1 into
+ // float 0..1 range.
+ // This can be done with a 3x3 scaling matrix.
+ size_t maximum_value = (1u << bit_depth) - 1;
+ float scale = 1.0 / maximum_value;
+ FloatMatrix4x4 integer_scaling_matrix = {
+ scale, 0.0f, 0.0f, 0.0f, // y
+ 0.0f, scale, 0.0f, 0.0f, // u
+ 0.0f, 0.0f, scale, 0.0f, // v
+ 0.0f, 0.0f, 0.0f, 1.0f, // w
+ };
+
+ // 2. Scale YUV values into usable ranges.
+ // For studio range, Y range is 16..235, and UV is 16..240.
+ // UV values should be scaled to a range of -1..1.
+ // This can be done in a 4x4 matrix with translation and scaling.
+ float y_min;
+ float y_max;
+ float uv_min;
+ float uv_max;
+ if (cicp.color_range() == ColorRange::Studio) {
+ y_min = 16.0f / 255.0f;
+ y_max = 235.0f / 255.0f;
+ uv_min = y_min;
+ uv_max = 240.0f / 255.0f;
+ } else {
+ y_min = 0.0f;
+ y_max = 1.0f;
+ uv_min = 0.0f;
+ uv_max = 1.0f;
+ }
+ auto clip_y_scale = 1.0f / (y_max - y_min);
+ auto clip_uv_scale = 2.0f / (uv_max - uv_min);
+
+ FloatMatrix4x4 range_scaling_matrix = {
+ clip_y_scale, 0.0f, 0.0f, -y_min * clip_y_scale, // y
+ 0.0f, clip_uv_scale, 0.0f, -(uv_min * clip_uv_scale + 1.0f), // u
+ 0.0f, 0.0f, clip_uv_scale, -(uv_min * clip_uv_scale + 1.0f), // v
+ 0.0f, 0.0f, 0.0f, 1.0f, // w
+ };
+
+ // 3. Convert YUV values to RGB.
+ // This is done with coefficients that can be put into a 3x3 matrix
+ // and combined with the above 4x4 matrix to combine steps 1 and 2.
+ FloatMatrix4x4 color_conversion_matrix;
+
+ // https://kdashg.github.io/misc/colors/from-coeffs.html
+ switch (cicp.matrix_coefficients()) {
+ case MatrixCoefficients::BT709:
+ color_conversion_matrix = {
+ 1.0f, 0.0f, 0.78740f, 0.0f, // y
+ 1.0f, -0.09366f, -0.23406f, 0.0f, // u
+ 1.0f, 0.92780f, 0.0f, 0.0f, // v
+ 0.0f, 0.0f, 0.0f, 1.0f, // w
+ };
+ break;
+ case MatrixCoefficients::BT601:
+ color_conversion_matrix = {
+ 1.0f, 0.0f, 0.70100f, 0.0f, // y
+ 1.0f, -0.17207f, -0.35707f, 0.0f, // u
+ 1.0f, 0.88600f, 0.0f, 0.0f, // v
+ 0.0f, 0.0f, 0.0f, 1.0f, // w
+ };
+ break;
+ case MatrixCoefficients::BT2020ConstantLuminance:
+ case MatrixCoefficients::BT2020NonConstantLuminance:
+ color_conversion_matrix = {
+ 1.0f, 0.0f, 0.73730f, 0.0f, // y
+ 1.0f, -0.08228f, -0.28568f, 0.0f, // u
+ 1.0f, 0.94070f, 0.0f, 0.0f, // v
+ 0.0f, 0.0f, 0.0f, 1.0f, // w
+ };
+ break;
+ default:
+ return DecoderError::format(DecoderErrorCategory::Invalid, "Matrix coefficients {} not supported", matrix_coefficients_to_string(cicp.matrix_coefficients()));
+ }
+
+ // 4. Apply the inverse transfer function to convert RGB values to the
+ // linear color space.
+ // This will be turned into a lookup table and interpolated to speed
+ // up the conversion.
+ auto to_linear_lookup_table = InterpolatedLookupTable<to_linear_size>::create(
+ [&](float value) {
+ return TransferCharacteristicsConversion::to_linear_luminance(value, cicp.transfer_characteristics());
+ });
+
+ // 5. Convert the RGB color to CIE XYZ coordinates using the input color
+ // primaries and then to the output color primaries.
+ // This is done with two 3x3 matrices that can be combined into one
+ // matrix multiplication.
+ ColorPrimaries output_cp = ColorPrimaries::BT709;
+ FloatMatrix3x3 color_primaries_matrix = TRY(get_conversion_matrix(cicp.color_primaries(), output_cp));
+
+ // 6. Apply the output transfer function. For HDR color spaces, this
+ // should apply tonemapping as well.
+ // Use a lookup table as with step 3.
+ TransferCharacteristics output_tc = TransferCharacteristics::SRGB;
+ switch (cicp.transfer_characteristics()) {
+ case TransferCharacteristics::Unspecified:
+ break;
+ case TransferCharacteristics::BT709:
+ case TransferCharacteristics::BT601:
+ case TransferCharacteristics::BT2020BitDepth10:
+ case TransferCharacteristics::BT2020BitDepth12:
+ // BT.601, BT.709 and BT.2020 have a similar transfer function to sRGB, and other applications
+ // (Chromium, VLC) seem to keep video output in those transfer characteristics.
+ output_tc = TransferCharacteristics::BT709;
+ break;
+ default:
+ break;
+ }
+
+ auto to_non_linear_lookup_table = InterpolatedLookupTable<to_non_linear_size>::create(
+ [&](float value) {
+ return TransferCharacteristicsConversion::to_non_linear_luminance(value, output_tc);
+ });
+
+ // Expand color primaries matrix with identity elements.
+ FloatMatrix4x4 color_primaries_matrix_4x4 = {
+ color_primaries_matrix.elements()[0][0],
+ color_primaries_matrix.elements()[0][1],
+ color_primaries_matrix.elements()[0][2],
+ 0.0f, // y
+ color_primaries_matrix.elements()[1][0],
+ color_primaries_matrix.elements()[1][1],
+ color_primaries_matrix.elements()[1][2],
+ 0.0f, // u
+ color_primaries_matrix.elements()[2][0],
+ color_primaries_matrix.elements()[2][1],
+ color_primaries_matrix.elements()[2][2],
+ 0.0f, // v
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f, // w
+ };
+
+ bool should_skip_color_remapping = output_cp == cicp.color_primaries() && output_tc == cicp.transfer_characteristics();
+ FloatMatrix4x4 input_conversion_matrix = color_conversion_matrix * range_scaling_matrix * integer_scaling_matrix;
+
+ return ColorConverter(bit_depth, cicp, should_skip_color_remapping, should_tonemap, input_conversion_matrix, to_linear_lookup_table, color_primaries_matrix_4x4, to_non_linear_lookup_table);
+}
+
+ALWAYS_INLINE FloatVector4 max_zero(FloatVector4 vector)
+{
+ return { max(0.0f, vector.x()), max(0.0f, vector.y()), max(0.0f, vector.z()), vector.w() };
+}
+
+// Referencing https://en.wikipedia.org/wiki/YCbCr
+Gfx::Color ColorConverter::convert_yuv_to_full_range_rgb(u16 y, u16 u, u16 v)
+{
+ FloatVector4 color_vector = { static_cast<float>(y), static_cast<float>(u), static_cast<float>(v), 1.0f };
+ color_vector = m_input_conversion_matrix * color_vector;
+
+ if (m_should_skip_color_remapping) {
+ color_vector.clamp(0.0f, 1.0f);
+ } else {
+ color_vector = max_zero(color_vector);
+ color_vector = m_to_linear_lookup.do_lookup(color_vector);
+
+ if (m_cicp.transfer_characteristics() == TransferCharacteristics::HLG) {
+ static auto hlg_ootf_lookup_table = InterpolatedLookupTable<32, 1000>::create(
+ [](float value) {
+ return AK::pow(value, 1.2f - 1.0f);
+ });
+ // See: https://en.wikipedia.org/wiki/Hybrid_log-gamma under a bolded section "HLG reference OOTF"
+ float luminance = (0.2627f * color_vector.x() + 0.6780f * color_vector.y() + 0.0593f * color_vector.z()) * 1000.0f;
+ float coefficient = hlg_ootf_lookup_table.do_lookup(luminance);
+ color_vector = { color_vector.x() * coefficient, color_vector.y() * coefficient, color_vector.z() * coefficient, 1.0f };
+ }
+
+ // FIXME: We could implement gamut compression here:
+ // https://github.com/jedypod/gamut-compress/blob/master/docs/gamut-compress-algorithm.md
+ // This would allow the color values outside the output gamut to be
+ // preserved relative to values within the gamut instead of clipping. The
+ // downside is that this requires a pass over the image before conversion
+ // back into gamut is done to find the maximum color values to compress.
+ // The compression would have to be somewhat temporally consistent as well.
+ color_vector = m_color_space_conversion_matrix * color_vector;
+ color_vector = max_zero(color_vector);
+ if (m_should_tonemap)
+ color_vector = hable_tonemapping(color_vector);
+ color_vector = m_to_non_linear_lookup.do_lookup(color_vector);
+ color_vector = max_zero(color_vector);
+ }
+
+ u8 r = static_cast<u8>(color_vector.x() * 255.0f);
+ u8 g = static_cast<u8>(color_vector.y() * 255.0f);
+ u8 b = static_cast<u8>(color_vector.z() * 255.0f);
+ return Gfx::Color(r, g, b);
+}
+
+}
diff --git a/Userland/Libraries/LibVideo/Color/ColorConverter.h b/Userland/Libraries/LibVideo/Color/ColorConverter.h
new file mode 100644
index 0000000000..175e32496a
--- /dev/null
+++ b/Userland/Libraries/LibVideo/Color/ColorConverter.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Array.h>
+#include <AK/Function.h>
+#include <LibGfx/Color.h>
+#include <LibGfx/Matrix4x4.h>
+#include <LibVideo/Color/CodingIndependentCodePoints.h>
+#include <LibVideo/DecoderError.h>
+
+namespace Video {
+
+template<size_t N, size_t Scale = 1>
+struct InterpolatedLookupTable {
+public:
+ static InterpolatedLookupTable<N, Scale> create(Function<float(float)> transfer_function)
+ {
+ // We'll allocate one extra index to allow the values to reach 1.0.
+ InterpolatedLookupTable<N, Scale> lookup_table;
+ float index_to_value_mult = static_cast<float>(Scale) / maximum_value;
+ for (size_t i = 0; i < N; i++) {
+ float value = i * index_to_value_mult;
+ value = transfer_function(value);
+ lookup_table.m_lookup_table[i] = value;
+ }
+ return lookup_table;
+ }
+
+ float do_lookup(float value) const
+ {
+ float float_index = value * (maximum_value / static_cast<float>(Scale));
+ if (float_index > maximum_value) [[unlikely]]
+ float_index = maximum_value;
+ size_t index = static_cast<size_t>(float_index);
+ float partial_index = float_index - index;
+ value = m_lookup_table[index] * (1.0f - partial_index) + m_lookup_table[index + 1] * partial_index;
+ return value;
+ }
+
+ FloatVector4 do_lookup(FloatVector4 vector) const
+ {
+ return {
+ do_lookup(vector.x()),
+ do_lookup(vector.y()),
+ do_lookup(vector.z()),
+ vector.w()
+ };
+ }
+
+private:
+ static constexpr size_t maximum_value = N - 2;
+
+ Array<float, N> m_lookup_table;
+};
+
+class ColorConverter final {
+
+public:
+ static DecoderErrorOr<ColorConverter> create(u8 bit_depth, CodingIndependentCodePoints cicp);
+
+ Gfx::Color convert_yuv_to_full_range_rgb(u16 y, u16 u, u16 v);
+
+private:
+ static constexpr size_t to_linear_size = 64;
+ static constexpr size_t to_non_linear_size = 64;
+
+ ColorConverter(u8 bit_depth, CodingIndependentCodePoints cicp, bool should_skip_color_remapping, bool should_tonemap, FloatMatrix4x4 input_conversion_matrix, InterpolatedLookupTable<to_linear_size> to_linear_lookup, FloatMatrix4x4 color_space_conversion_matrix, InterpolatedLookupTable<to_non_linear_size> to_non_linear_lookup)
+ : m_bit_depth(bit_depth)
+ , m_cicp(cicp)
+ , m_should_skip_color_remapping(should_skip_color_remapping)
+ , m_should_tonemap(should_tonemap)
+ , m_input_conversion_matrix(input_conversion_matrix)
+ , m_to_linear_lookup(move(to_linear_lookup))
+ , m_color_space_conversion_matrix(color_space_conversion_matrix)
+ , m_to_non_linear_lookup(move(to_non_linear_lookup))
+ {
+ }
+ u8 m_bit_depth;
+ CodingIndependentCodePoints m_cicp;
+ bool m_should_skip_color_remapping;
+ bool m_should_tonemap;
+ FloatMatrix4x4 m_input_conversion_matrix;
+ InterpolatedLookupTable<to_linear_size> m_to_linear_lookup;
+ FloatMatrix4x4 m_color_space_conversion_matrix;
+ InterpolatedLookupTable<to_non_linear_size> m_to_non_linear_lookup;
+};
+
+}
diff --git a/Userland/Libraries/LibVideo/Color/ColorPrimaries.cpp b/Userland/Libraries/LibVideo/Color/ColorPrimaries.cpp
new file mode 100644
index 0000000000..f796507c69
--- /dev/null
+++ b/Userland/Libraries/LibVideo/Color/ColorPrimaries.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibGfx/Vector2.h>
+#include <LibGfx/Vector3.h>
+
+#include "ColorPrimaries.h"
+
+namespace Video {
+
+ALWAYS_INLINE constexpr FloatVector3 primaries_to_xyz(FloatVector2 primaries)
+{
+ // https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space
+ // Luminosity is set to 1.0, so the equations are simplified.
+ auto const x = primaries.x();
+ auto const y = primaries.y();
+ return {
+ x / y,
+ 1.0f,
+ (1.0f - x - y) / y
+ };
+}
+
+ALWAYS_INLINE constexpr FloatMatrix3x3 vectors_to_matrix(FloatVector3 a, FloatVector3 b, FloatVector3 c)
+{
+ return FloatMatrix3x3(
+ a.x(), a.y(), a.z(),
+ b.x(), b.y(), b.z(),
+ c.x(), c.y(), c.z());
+}
+
+ALWAYS_INLINE constexpr FloatMatrix3x3 primaries_matrix(FloatVector2 red, FloatVector2 green, FloatVector2 blue)
+{
+ return vectors_to_matrix(primaries_to_xyz(red), primaries_to_xyz(green), primaries_to_xyz(blue)).transpose();
+}
+
+ALWAYS_INLINE constexpr FloatVector3 matrix_row(FloatMatrix3x3 matrix, size_t row)
+{
+ return { matrix.elements()[row][0], matrix.elements()[row][1], matrix.elements()[row][2] };
+}
+
+ALWAYS_INLINE constexpr FloatMatrix3x3 generate_rgb_to_xyz_matrix(FloatVector2 red_xy, FloatVector2 green_xy, FloatVector2 blue_xy, FloatVector2 white_xy)
+{
+ // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+ const FloatMatrix3x3 matrix = primaries_matrix(red_xy, green_xy, blue_xy);
+ const FloatVector3 scale_vector = matrix.inverse() * primaries_to_xyz(white_xy);
+ return vectors_to_matrix(matrix_row(matrix, 0) * scale_vector, matrix_row(matrix, 1) * scale_vector, matrix_row(matrix, 2) * scale_vector);
+}
+
+constexpr FloatVector2 ILLUMINANT_D65 = { 0.3127f, 0.3290f };
+
+constexpr FloatVector2 BT_709_RED = { 0.64f, 0.33f };
+constexpr FloatVector2 BT_709_GREEN = { 0.30f, 0.60f };
+constexpr FloatVector2 BT_709_BLUE = { 0.15f, 0.06f };
+
+constexpr FloatVector2 BT_2020_RED = { 0.708f, 0.292f };
+constexpr FloatVector2 BT_2020_GREEN = { 0.170f, 0.797f };
+constexpr FloatVector2 BT_2020_BLUE = { 0.131f, 0.046f };
+
+constexpr FloatMatrix3x3 bt_2020_rgb_to_xyz = generate_rgb_to_xyz_matrix(BT_2020_RED, BT_2020_GREEN, BT_2020_BLUE, ILLUMINANT_D65);
+constexpr FloatMatrix3x3 bt_709_rgb_to_xyz = generate_rgb_to_xyz_matrix(BT_709_RED, BT_709_GREEN, BT_709_BLUE, ILLUMINANT_D65);
+
+DecoderErrorOr<FloatMatrix3x3> get_conversion_matrix(ColorPrimaries input_primaries, ColorPrimaries output_primaries)
+{
+ FloatMatrix3x3 input_conversion_matrix;
+ switch (input_primaries) {
+ case ColorPrimaries::BT709:
+ input_conversion_matrix = bt_709_rgb_to_xyz;
+ break;
+ case ColorPrimaries::BT2020:
+ input_conversion_matrix = bt_2020_rgb_to_xyz;
+ break;
+ default:
+ return DecoderError::format(DecoderErrorCategory::NotImplemented, "Conversion of primaries {} is not implemented", color_primaries_to_string(input_primaries));
+ }
+
+ FloatMatrix3x3 output_conversion_matrix;
+ switch (output_primaries) {
+ case ColorPrimaries::BT709:
+ output_conversion_matrix = bt_709_rgb_to_xyz.inverse();
+ break;
+ case ColorPrimaries::BT2020:
+ output_conversion_matrix = bt_2020_rgb_to_xyz.inverse();
+ break;
+ default:
+ return DecoderError::format(DecoderErrorCategory::NotImplemented, "Conversion of primaries {} is not implemented", color_primaries_to_string(output_primaries));
+ }
+
+ return output_conversion_matrix * input_conversion_matrix;
+}
+
+}
diff --git a/Userland/Libraries/LibVideo/Color/ColorPrimaries.h b/Userland/Libraries/LibVideo/Color/ColorPrimaries.h
new file mode 100644
index 0000000000..59693fbf73
--- /dev/null
+++ b/Userland/Libraries/LibVideo/Color/ColorPrimaries.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGfx/Matrix3x3.h>
+#include <LibVideo/Color/CodingIndependentCodePoints.h>
+#include <LibVideo/DecoderError.h>
+
+namespace Video {
+
+DecoderErrorOr<FloatMatrix3x3> get_conversion_matrix(ColorPrimaries input_primaries, ColorPrimaries output_primaries);
+
+}
diff --git a/Userland/Libraries/LibVideo/Color/TransferCharacteristics.cpp b/Userland/Libraries/LibVideo/Color/TransferCharacteristics.cpp
new file mode 100644
index 0000000000..d829b937ec
--- /dev/null
+++ b/Userland/Libraries/LibVideo/Color/TransferCharacteristics.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Format.h>
+#include <AK/Math.h>
+#include <AK/StdLibExtras.h>
+
+#include "TransferCharacteristics.h"
+
+namespace Video {
+
+// SDR maximum luminance in candelas per meter squared
+constexpr float sdr_max_luminance = 120.0f;
+
+// sRGB
+constexpr float srgb_inverse_beta = 0.0031308f;
+constexpr float srgb_inverse_linear_coef = 12.92f;
+constexpr float srgb_gamma = 2.4f;
+constexpr float srgb_alpha = 1.055f;
+
+// BT.601/BT.709/BT.2020 constants
+constexpr float bt_601_beta = 0.018053968510807f;
+constexpr float bt_601_linear_coef = 4.5f;
+constexpr float bt_601_alpha = 1.0f + 5.5f * bt_601_beta;
+constexpr float bt_601_gamma = 0.45f;
+
+// Perceptual quantizer (SMPTE ST 2084) constants
+constexpr float pq_m1 = 2610.0f / 16384.0f;
+constexpr float pq_m2 = 128.0f * 2523.0f / 4096.0f;
+constexpr float pq_c1 = 3424.0f / 4096.0f;
+constexpr float pq_c2 = 32.0f * 2413.0f / 4096.0f;
+constexpr float pq_c3 = 32.0f * 2392.0f / 4096.0f;
+constexpr float pq_max_luminance = 10000.0f;
+
+// Hybrid log-gamma constants
+constexpr float hlg_a = 0.17883277f;
+constexpr float hlg_b = 0.28466892f;
+constexpr float hlg_c = 0.55991073f;
+
+float TransferCharacteristicsConversion::to_linear_luminance(float value, TransferCharacteristics transfer_function)
+{
+ switch (transfer_function) {
+ case TransferCharacteristics::BT709:
+ case TransferCharacteristics::BT601:
+ case TransferCharacteristics::BT2020BitDepth10:
+ case TransferCharacteristics::BT2020BitDepth12:
+ // https://en.wikipedia.org/wiki/Rec._601#Transfer_characteristics
+ // https://en.wikipedia.org/wiki/Rec._709#Transfer_characteristics
+ // https://en.wikipedia.org/wiki/Rec._2020#Transfer_characteristics
+ // These three share identical OETFs.
+ if (value < bt_601_beta * bt_601_linear_coef)
+ return value / bt_601_linear_coef;
+ return AK::pow((value + (bt_601_alpha - 1.0f)) / bt_601_alpha, 1.0f / bt_601_gamma);
+ case TransferCharacteristics::SRGB:
+ // https://color.org/sRGB.pdf
+ if (value < srgb_inverse_linear_coef * srgb_inverse_beta)
+ return value / srgb_inverse_linear_coef;
+ return AK::pow((value + (srgb_alpha - 1.0f)) / srgb_alpha, srgb_gamma);
+ case TransferCharacteristics::SMPTE2084: {
+ // https://en.wikipedia.org/wiki/Perceptual_quantizer
+ auto gamma_adjusted = AK::pow(value, 1.0f / pq_m2);
+ auto numerator = max(gamma_adjusted - pq_c1, 0.0f);
+ auto denominator = pq_c2 - pq_c3 * gamma_adjusted;
+ return AK::pow(numerator / denominator, 1.0f / pq_m1) * (pq_max_luminance / sdr_max_luminance);
+ }
+ case TransferCharacteristics::HLG:
+ // https://en.wikipedia.org/wiki/Hybrid_log-gamma
+ if (value < 0.5f)
+ return (value * value) / 3.0f;
+ return (AK::exp((value - hlg_c) / hlg_a) + hlg_b) / 12.0f;
+ default:
+ dbgln("Unsupported transfer function {}", static_cast<u8>(transfer_function));
+ VERIFY_NOT_REACHED();
+ }
+}
+
+float TransferCharacteristicsConversion::to_non_linear_luminance(float value, TransferCharacteristics transfer_function)
+{
+ switch (transfer_function) {
+ case TransferCharacteristics::BT709:
+ case TransferCharacteristics::BT601:
+ case TransferCharacteristics::BT2020BitDepth10:
+ case TransferCharacteristics::BT2020BitDepth12:
+ // https://en.wikipedia.org/wiki/Rec._601#Transfer_characteristics
+ // https://en.wikipedia.org/wiki/Rec._709#Transfer_characteristics
+ // https://en.wikipedia.org/wiki/Rec._2020#Transfer_characteristics
+ // These three share identical OETFs.
+ if (value < bt_601_beta)
+ return bt_601_linear_coef * value;
+ return bt_601_alpha * AK::pow(value, bt_601_gamma) - (bt_601_alpha - 1.0f);
+ case TransferCharacteristics::SRGB:
+ // https://color.org/sRGB.pdf
+ if (value < srgb_inverse_beta)
+ return value * srgb_inverse_linear_coef;
+ return srgb_alpha * AK::pow(value, 1.0f / srgb_gamma) - (srgb_alpha - 1.0f);
+ case TransferCharacteristics::SMPTE2084: {
+ // https://en.wikipedia.org/wiki/Perceptual_quantizer
+ auto linear_value = AK::pow(value * (sdr_max_luminance / pq_max_luminance), pq_m1);
+ auto numerator = pq_c1 + pq_c2 * linear_value;
+ auto denominator = 1 + pq_c3 * linear_value;
+ return AK::pow(numerator / denominator, pq_m2);
+ }
+ case TransferCharacteristics::HLG:
+ // https://en.wikipedia.org/wiki/Hybrid_log-gamma
+ if (value < 1.0f / 12.0f)
+ return AK::sqrt(value * 3.0f);
+ return hlg_a * AK::log(12.0f * value - hlg_b) + hlg_c;
+ default:
+ dbgln("Unsupported transfer function {}", static_cast<u8>(transfer_function));
+ VERIFY_NOT_REACHED();
+ }
+}
+
+FloatVector4 TransferCharacteristicsConversion::hlg_opto_optical_transfer_function(FloatVector4 const& vector, float gamma, float gain)
+{
+ float luminance = (0.2627f * vector.x() + 0.6780f * vector.y() + 0.0593f * vector.z()) * 1000.0f;
+ float coefficient = gain * AK::pow(luminance, gamma - 1.0f);
+ return FloatVector4(vector.x() * coefficient, vector.y() * coefficient, vector.z() * coefficient, vector.w());
+}
+
+}
diff --git a/Userland/Libraries/LibVideo/Color/TransferCharacteristics.h b/Userland/Libraries/LibVideo/Color/TransferCharacteristics.h
new file mode 100644
index 0000000000..b8360d163e
--- /dev/null
+++ b/Userland/Libraries/LibVideo/Color/TransferCharacteristics.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGfx/Vector4.h>
+#include <LibVideo/Color/CodingIndependentCodePoints.h>
+
+namespace Video {
+
+class TransferCharacteristicsConversion {
+public:
+ static float to_linear_luminance(float value, TransferCharacteristics transfer_function);
+
+ static float to_non_linear_luminance(float value, TransferCharacteristics transfer_function);
+
+ // https://en.wikipedia.org/wiki/Hybrid_log-gamma
+ // See "HLG reference OOTF"
+ static FloatVector4 hlg_opto_optical_transfer_function(FloatVector4 const& vector, float gamma, float gain);
+};
+
+}
diff --git a/Userland/Libraries/LibVideo/DecoderError.h b/Userland/Libraries/LibVideo/DecoderError.h
index 02d5160233..0f72034628 100644
--- a/Userland/Libraries/LibVideo/DecoderError.h
+++ b/Userland/Libraries/LibVideo/DecoderError.h
@@ -25,6 +25,8 @@ enum class DecoderErrorCategory : u32 {
Memory,
// The input is corrupted.
Corrupted,
+ // Invalid call.
+ Invalid,
// The input uses features that are not yet implemented.
NotImplemented,
};
diff --git a/Userland/Libraries/LibVideo/VP9/Decoder.cpp b/Userland/Libraries/LibVideo/VP9/Decoder.cpp
index 41a2d47f88..4d8b9fa950 100644
--- a/Userland/Libraries/LibVideo/VP9/Decoder.cpp
+++ b/Userland/Libraries/LibVideo/VP9/Decoder.cpp
@@ -7,6 +7,7 @@
#include <AK/IntegralMath.h>
#include <LibGfx/Size.h>
+#include <LibVideo/Color/CodingIndependentCodePoints.h>
#include "Decoder.h"
#include "Utilities.h"
@@ -149,6 +150,70 @@ bool Decoder::get_uv_subsampling_x()
return m_parser->m_subsampling_x;
}
+CodingIndependentCodePoints Decoder::get_cicp_color_space()
+{
+ ColorPrimaries color_primaries;
+ TransferCharacteristics transfer_characteristics;
+ MatrixCoefficients matrix_coefficients;
+
+ switch (m_parser->m_color_space) {
+ case ColorSpace::Unknown:
+ color_primaries = ColorPrimaries::Unspecified;
+ transfer_characteristics = TransferCharacteristics::Unspecified;
+ matrix_coefficients = MatrixCoefficients::Unspecified;
+ break;
+ case ColorSpace::Bt601:
+ color_primaries = ColorPrimaries::BT601;
+ transfer_characteristics = TransferCharacteristics::BT601;
+ matrix_coefficients = MatrixCoefficients::BT601;
+ break;
+ case ColorSpace::Bt709:
+ color_primaries = ColorPrimaries::BT709;
+ transfer_characteristics = TransferCharacteristics::BT709;
+ matrix_coefficients = MatrixCoefficients::BT709;
+ break;
+ case ColorSpace::Smpte170:
+ // https://www.kernel.org/doc/html/v4.9/media/uapi/v4l/pixfmt-007.html#colorspace-smpte-170m-v4l2-colorspace-smpte170m
+ color_primaries = ColorPrimaries::BT601;
+ transfer_characteristics = TransferCharacteristics::BT709;
+ matrix_coefficients = MatrixCoefficients::BT601;
+ break;
+ case ColorSpace::Smpte240:
+ color_primaries = ColorPrimaries::SMPTE240;
+ transfer_characteristics = TransferCharacteristics::SMPTE240;
+ matrix_coefficients = MatrixCoefficients::SMPTE240;
+ break;
+ case ColorSpace::Bt2020:
+ color_primaries = ColorPrimaries::BT2020;
+ // Bit depth doesn't actually matter to our transfer functions since we
+ // convert in floats of range 0-1 (for now?), but just for correctness set
+ // the TC to match the bit depth here.
+ if (m_parser->m_bit_depth == 12)
+ transfer_characteristics = TransferCharacteristics::BT2020BitDepth12;
+ else if (m_parser->m_bit_depth == 10)
+ transfer_characteristics = TransferCharacteristics::BT2020BitDepth10;
+ else
+ transfer_characteristics = TransferCharacteristics::BT709;
+ matrix_coefficients = MatrixCoefficients::BT2020NonConstantLuminance;
+ break;
+ case ColorSpace::RGB:
+ color_primaries = ColorPrimaries::BT709;
+ transfer_characteristics = TransferCharacteristics::Linear;
+ matrix_coefficients = MatrixCoefficients::Identity;
+ break;
+ case ColorSpace::Reserved:
+ VERIFY_NOT_REACHED();
+ break;
+ }
+
+ return { color_primaries, transfer_characteristics, matrix_coefficients, m_parser->m_color_range };
+}
+
+u8 Decoder::get_bit_depth()
+{
+ return m_parser->m_bit_depth;
+}
+
u8 Decoder::merge_prob(u8 pre_prob, u8 count_0, u8 count_1, u8 count_sat, u8 max_update_factor)
{
auto total_decode_count = count_0 + count_1;
diff --git a/Userland/Libraries/LibVideo/VP9/Decoder.h b/Userland/Libraries/LibVideo/VP9/Decoder.h
index e382cf87fb..50b31716a8 100644
--- a/Userland/Libraries/LibVideo/VP9/Decoder.h
+++ b/Userland/Libraries/LibVideo/VP9/Decoder.h
@@ -10,6 +10,7 @@
#include <AK/ByteBuffer.h>
#include <AK/Error.h>
#include <AK/Span.h>
+#include <LibVideo/Color/CodingIndependentCodePoints.h>
#include <LibVideo/DecoderError.h>
#include "Parser.h"
@@ -32,6 +33,8 @@ public:
Gfx::Size<size_t> get_y_plane_size();
bool get_uv_subsampling_y();
bool get_uv_subsampling_x();
+ CodingIndependentCodePoints get_cicp_color_space();
+ u8 get_bit_depth();
private:
typedef i32 Intermediate;
diff --git a/Userland/Libraries/LibVideo/VP9/Enums.h b/Userland/Libraries/LibVideo/VP9/Enums.h
index ed42f1eb2e..3389fcca0d 100644
--- a/Userland/Libraries/LibVideo/VP9/Enums.h
+++ b/Userland/Libraries/LibVideo/VP9/Enums.h
@@ -27,11 +27,6 @@ enum ColorSpace : u8 {
RGB = 7
};
-enum ColorRange {
- StudioSwing,
- FullSwing
-};
-
enum InterpolationFilter : u8 {
EightTap = 0,
EightTapSmooth = 1,
diff --git a/Userland/Libraries/LibVideo/VP9/Parser.cpp b/Userland/Libraries/LibVideo/VP9/Parser.cpp
index e457996b19..c46a21d576 100644
--- a/Userland/Libraries/LibVideo/VP9/Parser.cpp
+++ b/Userland/Libraries/LibVideo/VP9/Parser.cpp
@@ -136,8 +136,8 @@ DecoderErrorOr<FrameType> Parser::read_frame_type()
DecoderErrorOr<ColorRange> Parser::read_color_range()
{
if (TRY_READ(m_bit_stream->read_bit()))
- return FullSwing;
- return StudioSwing;
+ return ColorRange::Full;
+ return ColorRange::Studio;
}
/* (6.2) */
@@ -273,7 +273,7 @@ DecoderErrorOr<void> Parser::color_config()
m_subsampling_y = true;
}
} else {
- m_color_range = FullSwing;
+ m_color_range = ColorRange::Full;
if (m_profile == 1 || m_profile == 3) {
m_subsampling_x = false;
m_subsampling_y = false;
diff --git a/Userland/Libraries/LibVideo/VP9/Parser.h b/Userland/Libraries/LibVideo/VP9/Parser.h
index ab49308cf8..d1a4e33560 100644
--- a/Userland/Libraries/LibVideo/VP9/Parser.h
+++ b/Userland/Libraries/LibVideo/VP9/Parser.h
@@ -12,6 +12,7 @@
#include <AK/Span.h>
#include <AK/Vector.h>
#include <LibGfx/Forward.h>
+#include <LibVideo/Color/CodingIndependentCodePoints.h>
#include <LibVideo/DecoderError.h>
#include "BitStream.h"