mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-11-03 23:00:58 +00:00 
			
		
		
		
	Skia's SkColorSpace::Make() doesn't seem to like ICC v2 profiles containing table-based L* transfer curves. Now we use skcms_ApproximateCurve() to convert these tables to parametric curves that Skia supports in case Skia's SkColorSpace::Make() fails.
		
			
				
	
	
		
			159 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2024, Lucas Chollet <lucas.chollet@serenityos.org>
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: BSD-2-Clause
 | 
						|
 */
 | 
						|
 | 
						|
#include <AK/ByteBuffer.h>
 | 
						|
#include <LibGfx/ColorSpace.h>
 | 
						|
#include <LibIPC/Decoder.h>
 | 
						|
#include <LibIPC/Encoder.h>
 | 
						|
 | 
						|
#include <core/SkColorSpace.h>
 | 
						|
#include <core/SkData.h>
 | 
						|
 | 
						|
namespace Gfx {
 | 
						|
 | 
						|
namespace Details {
 | 
						|
 | 
						|
struct ColorSpaceImpl {
 | 
						|
    sk_sp<SkColorSpace> color_space;
 | 
						|
};
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
ColorSpace::ColorSpace()
 | 
						|
    : m_color_space(make<Details::ColorSpaceImpl>())
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ColorSpace::ColorSpace(ColorSpace const& other)
 | 
						|
    : m_color_space(make<Details::ColorSpaceImpl>(other.m_color_space->color_space))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ColorSpace& ColorSpace::operator=(ColorSpace const& other)
 | 
						|
{
 | 
						|
    m_color_space = make<Details::ColorSpaceImpl>(other.m_color_space->color_space);
 | 
						|
    return *this;
 | 
						|
}
 | 
						|
 | 
						|
ColorSpace::ColorSpace(ColorSpace&& other) = default;
 | 
						|
ColorSpace& ColorSpace::operator=(ColorSpace&&) = default;
 | 
						|
ColorSpace::~ColorSpace() = default;
 | 
						|
 | 
						|
ColorSpace::ColorSpace(NonnullOwnPtr<Details::ColorSpaceImpl>&& color_space)
 | 
						|
    : m_color_space(move(color_space))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ErrorOr<ColorSpace> ColorSpace::from_cicp(Media::CodingIndependentCodePoints cicp)
 | 
						|
{
 | 
						|
    if (cicp.matrix_coefficients() != Media::MatrixCoefficients::Identity)
 | 
						|
        return Error::from_string_literal("Unsupported matrix coefficients for CICP");
 | 
						|
 | 
						|
    if (cicp.video_full_range_flag() != Media::VideoFullRangeFlag::Full)
 | 
						|
        return Error::from_string_literal("Unsupported matrix coefficients for CICP");
 | 
						|
 | 
						|
    skcms_Matrix3x3 gamut = SkNamedGamut::kSRGB;
 | 
						|
    switch (cicp.color_primaries()) {
 | 
						|
    case Media::ColorPrimaries::BT709:
 | 
						|
        gamut = SkNamedGamut::kSRGB;
 | 
						|
        break;
 | 
						|
    case Media::ColorPrimaries::BT2020:
 | 
						|
        gamut = SkNamedGamut::kRec2020;
 | 
						|
        break;
 | 
						|
    case Media::ColorPrimaries::XYZ:
 | 
						|
        gamut = SkNamedGamut::kXYZ;
 | 
						|
        break;
 | 
						|
    case Media::ColorPrimaries::SMPTE432:
 | 
						|
        gamut = SkNamedGamut::kDisplayP3;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        return Error::from_string_literal("FIXME: Unsupported color primaries");
 | 
						|
    }
 | 
						|
 | 
						|
    skcms_TransferFunction transfer_function = SkNamedTransferFn::kSRGB;
 | 
						|
    switch (cicp.transfer_characteristics()) {
 | 
						|
    case Media::TransferCharacteristics::Linear:
 | 
						|
        transfer_function = SkNamedTransferFn::kLinear;
 | 
						|
        break;
 | 
						|
    case Media::TransferCharacteristics::SRGB:
 | 
						|
        transfer_function = SkNamedTransferFn::kSRGB;
 | 
						|
        break;
 | 
						|
    case Media::TransferCharacteristics::SMPTE2084:
 | 
						|
        transfer_function = SkNamedTransferFn::kPQ;
 | 
						|
        break;
 | 
						|
    case Media::TransferCharacteristics::HLG:
 | 
						|
        transfer_function = SkNamedTransferFn::kHLG;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        return Error::from_string_literal("FIXME: Unsupported transfer function");
 | 
						|
    }
 | 
						|
 | 
						|
    return ColorSpace { make<Details::ColorSpaceImpl>(SkColorSpace::MakeRGB(transfer_function, gamut)) };
 | 
						|
}
 | 
						|
 | 
						|
ErrorOr<ColorSpace> ColorSpace::load_from_icc_bytes(ReadonlyBytes icc_bytes)
 | 
						|
{
 | 
						|
    if (icc_bytes.size() != 0) {
 | 
						|
        skcms_ICCProfile icc_profile {};
 | 
						|
        if (!skcms_Parse(icc_bytes.data(), icc_bytes.size(), &icc_profile))
 | 
						|
            return Error::from_string_literal("Failed to parse the ICC profile");
 | 
						|
 | 
						|
        auto color_space_result = SkColorSpace::Make(icc_profile);
 | 
						|
 | 
						|
        if (!color_space_result) {
 | 
						|
            if (icc_profile.has_trc && icc_profile.has_toXYZD50) {
 | 
						|
                skcms_TransferFunction transfer_function;
 | 
						|
                float max_error;
 | 
						|
 | 
						|
                if (skcms_ApproximateCurve(&icc_profile.trc[0], &transfer_function, &max_error)) {
 | 
						|
                    color_space_result = SkColorSpace::MakeRGB(transfer_function, icc_profile.toXYZD50);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return ColorSpace { make<Details::ColorSpaceImpl>(color_space_result) };
 | 
						|
    }
 | 
						|
    return ColorSpace {};
 | 
						|
}
 | 
						|
 | 
						|
template<>
 | 
						|
sk_sp<SkColorSpace>& ColorSpace::color_space()
 | 
						|
{
 | 
						|
    return m_color_space->color_space;
 | 
						|
}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
namespace IPC {
 | 
						|
 | 
						|
template<>
 | 
						|
ErrorOr<void> encode(Encoder& encoder, Gfx::ColorSpace const& color_space)
 | 
						|
{
 | 
						|
    if (!color_space.m_color_space->color_space) {
 | 
						|
        TRY(encoder.encode<u64>(0));
 | 
						|
        return {};
 | 
						|
    }
 | 
						|
    auto serialized = color_space.m_color_space->color_space->serialize();
 | 
						|
    TRY(encoder.encode<u64>(serialized->size()));
 | 
						|
    TRY(encoder.append(serialized->bytes(), serialized->size()));
 | 
						|
    return {};
 | 
						|
}
 | 
						|
 | 
						|
template<>
 | 
						|
ErrorOr<Gfx::ColorSpace> decode(Decoder& decoder)
 | 
						|
{
 | 
						|
    auto size = TRY(decoder.decode<u64>());
 | 
						|
    if (size == 0)
 | 
						|
        return Gfx::ColorSpace {};
 | 
						|
 | 
						|
    auto buffer = TRY(ByteBuffer::create_uninitialized(size));
 | 
						|
    TRY(decoder.decode_into(buffer.bytes()));
 | 
						|
 | 
						|
    auto color_space = SkColorSpace::Deserialize(buffer.data(), buffer.size());
 | 
						|
    return Gfx::ColorSpace { make<::Gfx::Details::ColorSpaceImpl>(move(color_space)) };
 | 
						|
}
 | 
						|
 | 
						|
}
 |