mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 21:44:59 +00:00

All ColorSpace subclasses converted to float anyways, and this allows us to save lots of float->Value->float conversions during image color space processing. A bit faster: ``` N Min Max Median Avg Stddev x 50 0.99054313 1.0412271 0.99933481 1.0052408 0.012931916 + 50 0.97073889 1.0075941 0.97849107 0.98184034 0.0090329046 Difference at 95.0% confidence -0.0234004 +/- 0.00442595 -2.32785% +/- 0.440287% (Student's t, pooled s = 0.0111541) ```
267 lines
9.1 KiB
C++
267 lines
9.1 KiB
C++
/*
|
|
* Copyright (c) 2021-2022, Matthew Olsson <mattco@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/DeprecatedFlyString.h>
|
|
#include <AK/Forward.h>
|
|
#include <LibGfx/Color.h>
|
|
#include <LibGfx/ICC/Profile.h>
|
|
#include <LibGfx/PaintStyle.h>
|
|
#include <LibPDF/Function.h>
|
|
#include <LibPDF/Value.h>
|
|
|
|
#define ENUMERATE_COLOR_SPACE_FAMILIES(V) \
|
|
V(DeviceGray, true) \
|
|
V(DeviceRGB, true) \
|
|
V(DeviceCMYK, true) \
|
|
V(CalGray, false) \
|
|
V(CalRGB, false) \
|
|
V(Lab, false) \
|
|
V(ICCBased, false) \
|
|
V(Indexed, false) \
|
|
V(Pattern, true) \
|
|
V(Separation, false) \
|
|
V(DeviceN, false)
|
|
|
|
namespace PDF {
|
|
|
|
typedef Variant<Gfx::Color, NonnullRefPtr<Gfx::PaintStyle>> ColorOrStyle;
|
|
class Renderer;
|
|
|
|
class ColorSpaceFamily {
|
|
public:
|
|
ColorSpaceFamily(DeprecatedFlyString name, bool may_be_specified_directly)
|
|
: m_name(move(name))
|
|
, m_may_be_specified_directly(may_be_specified_directly)
|
|
{
|
|
}
|
|
|
|
DeprecatedFlyString name() const { return m_name; }
|
|
bool may_be_specified_directly() const { return m_may_be_specified_directly; }
|
|
static PDFErrorOr<ColorSpaceFamily> get(DeprecatedFlyString const&);
|
|
|
|
#define ENUMERATE(name, may_be_specified_directly) static ColorSpaceFamily name;
|
|
ENUMERATE_COLOR_SPACE_FAMILIES(ENUMERATE)
|
|
#undef ENUMERATE
|
|
|
|
bool operator==(ColorSpaceFamily const& other) const
|
|
{
|
|
return m_name == other.m_name;
|
|
}
|
|
|
|
private:
|
|
DeprecatedFlyString m_name;
|
|
bool m_may_be_specified_directly;
|
|
};
|
|
|
|
class ColorSpace : public RefCounted<ColorSpace> {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(Document*, NonnullRefPtr<Object>, Renderer&);
|
|
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(DeprecatedFlyString const&, Renderer&);
|
|
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(Document*, NonnullRefPtr<ArrayObject>, Renderer&);
|
|
|
|
virtual ~ColorSpace() = default;
|
|
|
|
virtual PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const = 0;
|
|
virtual PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const
|
|
{
|
|
Vector<float, 4> float_arguments;
|
|
for (auto& argument : arguments)
|
|
float_arguments.append(argument.to_float());
|
|
return style(float_arguments);
|
|
}
|
|
|
|
virtual int number_of_components() const = 0;
|
|
virtual Vector<float> default_decode() const = 0; // "TABLE 4.40 Default Decode arrays"
|
|
virtual ColorSpaceFamily const& family() const = 0;
|
|
};
|
|
|
|
class DeviceGrayColorSpace final : public ColorSpace {
|
|
public:
|
|
static NonnullRefPtr<DeviceGrayColorSpace> the();
|
|
|
|
~DeviceGrayColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 1; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceGray; }
|
|
|
|
private:
|
|
DeviceGrayColorSpace() = default;
|
|
};
|
|
|
|
class DeviceRGBColorSpace final : public ColorSpace {
|
|
public:
|
|
static NonnullRefPtr<DeviceRGBColorSpace> the();
|
|
|
|
~DeviceRGBColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 3; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceRGB; }
|
|
|
|
private:
|
|
DeviceRGBColorSpace() = default;
|
|
};
|
|
|
|
class DeviceCMYKColorSpace final : public ColorSpace {
|
|
public:
|
|
static NonnullRefPtr<DeviceCMYKColorSpace> the();
|
|
|
|
~DeviceCMYKColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 4; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceCMYK; }
|
|
|
|
private:
|
|
DeviceCMYKColorSpace() = default;
|
|
};
|
|
|
|
class DeviceNColorSpace final : public ColorSpace {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<DeviceNColorSpace>> create(Document*, Vector<Value>&& parameters, Renderer&);
|
|
|
|
~DeviceNColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override;
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceN; }
|
|
|
|
private:
|
|
DeviceNColorSpace(NonnullRefPtr<ColorSpace>, NonnullRefPtr<Function>);
|
|
|
|
Vector<ByteString> m_names;
|
|
NonnullRefPtr<ColorSpace> m_alternate_space;
|
|
NonnullRefPtr<Function> m_tint_transform;
|
|
Vector<Value> mutable m_tint_output_values;
|
|
};
|
|
|
|
class CalGrayColorSpace final : public ColorSpace {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<CalGrayColorSpace>> create(Document*, Vector<Value>&& parameters);
|
|
|
|
~CalGrayColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 1; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::CalGray; }
|
|
|
|
private:
|
|
CalGrayColorSpace() = default;
|
|
|
|
Array<float, 3> m_whitepoint { 0, 0, 0 };
|
|
Array<float, 3> m_blackpoint { 0, 0, 0 };
|
|
float m_gamma { 1 };
|
|
};
|
|
|
|
class CalRGBColorSpace final : public ColorSpace {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<CalRGBColorSpace>> create(Document*, Vector<Value>&& parameters);
|
|
|
|
~CalRGBColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 3; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::CalRGB; }
|
|
|
|
private:
|
|
CalRGBColorSpace() = default;
|
|
|
|
Array<float, 3> m_whitepoint { 0, 0, 0 };
|
|
Array<float, 3> m_blackpoint { 0, 0, 0 };
|
|
Array<float, 3> m_gamma { 1, 1, 1 };
|
|
Array<float, 9> m_matrix { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
|
|
};
|
|
|
|
class ICCBasedColorSpace final : public ColorSpace {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(Document*, Vector<Value>&& parameters, Renderer&);
|
|
|
|
~ICCBasedColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override;
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::ICCBased; }
|
|
|
|
static NonnullRefPtr<Gfx::ICC::Profile> sRGB();
|
|
|
|
private:
|
|
ICCBasedColorSpace(NonnullRefPtr<Gfx::ICC::Profile>);
|
|
|
|
static RefPtr<Gfx::ICC::Profile> s_srgb_profile;
|
|
NonnullRefPtr<Gfx::ICC::Profile> m_profile;
|
|
mutable Vector<float, 4> m_components;
|
|
mutable Vector<u8, 4> m_bytes;
|
|
Optional<Gfx::ICC::MatrixMatrixConversion> m_map;
|
|
};
|
|
|
|
class LabColorSpace final : public ColorSpace {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<LabColorSpace>> create(Document*, Vector<Value>&& parameters);
|
|
|
|
~LabColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 3; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::Lab; }
|
|
|
|
private:
|
|
LabColorSpace() = default;
|
|
|
|
Array<float, 3> m_whitepoint { 0, 0, 0 };
|
|
Array<float, 3> m_blackpoint { 0, 0, 0 };
|
|
Array<float, 4> m_range { -100, 100, -100, 100 };
|
|
};
|
|
|
|
class IndexedColorSpace final : public ColorSpace {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(Document*, Vector<Value>&& parameters, Renderer&);
|
|
|
|
~IndexedColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 1; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::Indexed; }
|
|
|
|
private:
|
|
IndexedColorSpace(NonnullRefPtr<ColorSpace>);
|
|
|
|
NonnullRefPtr<ColorSpace> m_base;
|
|
int m_hival { 0 };
|
|
Vector<u8> m_lookup;
|
|
};
|
|
|
|
class SeparationColorSpace final : public ColorSpace {
|
|
public:
|
|
static PDFErrorOr<NonnullRefPtr<SeparationColorSpace>> create(Document*, Vector<Value>&& parameters, Renderer&);
|
|
|
|
~SeparationColorSpace() override = default;
|
|
|
|
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<float> arguments) const override;
|
|
int number_of_components() const override { return 1; }
|
|
Vector<float> default_decode() const override;
|
|
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::Separation; }
|
|
|
|
private:
|
|
SeparationColorSpace(NonnullRefPtr<ColorSpace>, NonnullRefPtr<Function>);
|
|
|
|
ByteString m_name;
|
|
NonnullRefPtr<ColorSpace> m_alternate_space;
|
|
NonnullRefPtr<Function> m_tint_transform;
|
|
Vector<Value> mutable m_tint_output_values;
|
|
};
|
|
}
|