1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:07:46 +00:00

LibPDF: Move ColorSpace::style() to take ReadonlySpan<float>

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)
```
This commit is contained in:
Nico Weber 2024-01-08 21:36:21 -05:00 committed by Sam Atkins
parent 5ff2a824cc
commit 5f85aff036
3 changed files with 58 additions and 56 deletions

View file

@ -102,10 +102,10 @@ NonnullRefPtr<DeviceGrayColorSpace> DeviceGrayColorSpace::the()
return instance;
}
PDFErrorOr<ColorOrStyle> DeviceGrayColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceGrayColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 1);
auto gray = static_cast<u8>(arguments[0].to_float() * 255.0f);
auto gray = static_cast<u8>(arguments[0] * 255.0f);
return Color(gray, gray, gray);
}
@ -120,12 +120,12 @@ NonnullRefPtr<DeviceRGBColorSpace> DeviceRGBColorSpace::the()
return instance;
}
PDFErrorOr<ColorOrStyle> DeviceRGBColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceRGBColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 3);
auto r = static_cast<u8>(arguments[0].to_float() * 255.0f);
auto g = static_cast<u8>(arguments[1].to_float() * 255.0f);
auto b = static_cast<u8>(arguments[2].to_float() * 255.0f);
auto r = static_cast<u8>(arguments[0] * 255.0f);
auto g = static_cast<u8>(arguments[1] * 255.0f);
auto b = static_cast<u8>(arguments[2] * 255.0f);
return Color(r, g, b);
}
@ -140,13 +140,13 @@ NonnullRefPtr<DeviceCMYKColorSpace> DeviceCMYKColorSpace::the()
return instance;
}
PDFErrorOr<ColorOrStyle> DeviceCMYKColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceCMYKColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 4);
auto c = arguments[0].to_float();
auto m = arguments[1].to_float();
auto y = arguments[2].to_float();
auto k = arguments[3].to_float();
auto c = arguments[0];
auto m = arguments[1];
auto y = arguments[2];
auto k = arguments[3];
return Color::from_cmyk(c, m, y, k);
}
@ -198,15 +198,11 @@ DeviceNColorSpace::DeviceNColorSpace(NonnullRefPtr<ColorSpace> alternate_space,
{
}
PDFErrorOr<ColorOrStyle> DeviceNColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceNColorSpace::style(ReadonlySpan<float> arguments) const
{
// FIXME: Does this need handling for the special colorant name "None"?
// FIXME: When drawing to a printer, do something else.
m_tint_input_values.resize(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i)
m_tint_input_values[i] = arguments[i].to_float();
auto tint_output = TRY(m_tint_transform->evaluate(m_tint_input_values.span()));
auto tint_output = TRY(m_tint_transform->evaluate(arguments));
m_tint_output_values.resize(tint_output.size());
for (size_t i = 0; i < tint_output.size(); ++i)
@ -351,10 +347,10 @@ PDFErrorOr<NonnullRefPtr<CalGrayColorSpace>> CalGrayColorSpace::create(Document*
return color_space;
}
PDFErrorOr<ColorOrStyle> CalGrayColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> CalGrayColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 1);
auto a = clamp(arguments[0].to_float(), 0.0f, 1.0f);
auto a = clamp(arguments[0], 0.0f, 1.0f);
auto ag = powf(a, m_gamma);
@ -437,12 +433,12 @@ PDFErrorOr<NonnullRefPtr<CalRGBColorSpace>> CalRGBColorSpace::create(Document* d
return color_space;
}
PDFErrorOr<ColorOrStyle> CalRGBColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> CalRGBColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 3);
auto a = clamp(arguments[0].to_float(), 0.0f, 1.0f);
auto b = clamp(arguments[1].to_float(), 0.0f, 1.0f);
auto c = clamp(arguments[2].to_float(), 0.0f, 1.0f);
auto a = clamp(arguments[0], 0.0f, 1.0f);
auto b = clamp(arguments[1], 0.0f, 1.0f);
auto c = clamp(arguments[2], 0.0f, 1.0f);
auto agr = powf(a, m_gamma[0]);
auto bgg = powf(b, m_gamma[1]);
@ -498,32 +494,31 @@ ICCBasedColorSpace::ICCBasedColorSpace(NonnullRefPtr<Gfx::ICC::Profile> profile)
m_map = sRGB()->matrix_matrix_conversion(profile);
}
PDFErrorOr<ColorOrStyle> ICCBasedColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> ICCBasedColorSpace::style(ReadonlySpan<float> arguments) const
{
if (m_profile->data_color_space() == Gfx::ICC::ColorSpace::CIELAB) {
m_components.resize(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
auto const& arg = arguments[i];
VERIFY(arg.has_number());
float number = arg.to_float();
float number = arguments[i];
if (m_profile->data_color_space() == Gfx::ICC::ColorSpace::CIELAB) {
// CIELAB channels go from 0..100 and -128..127 instead of from 0..1.
// FIXME: We should probably have an API on Gfx::ICC::Profile that takes floats instead of bytes and that does this internally instead.
if (i == 0)
number /= 100.0f;
else
number = (number + 128.0f) / 255.0f;
}
m_components[i] = number;
}
arguments = m_components;
}
if (m_map.has_value())
return m_map->map(FloatVector3 { m_components[0], m_components[1], m_components[2] });
return m_map->map(FloatVector3 { arguments[0], arguments[1], arguments[2] });
m_bytes.resize(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i)
m_bytes[i] = static_cast<u8>(m_components[i] * 255.0f);
m_bytes[i] = static_cast<u8>(arguments[i] * 255.0f);
auto pcs = TRY(m_profile->to_pcs(m_bytes));
Array<u8, 3> output;
@ -609,12 +604,12 @@ PDFErrorOr<NonnullRefPtr<LabColorSpace>> LabColorSpace::create(Document* documen
return color_space;
}
PDFErrorOr<ColorOrStyle> LabColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> LabColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 3);
auto L_star = clamp(arguments[0].to_float(), 0.0f, 100.0f);
auto a_star = clamp(arguments[1].to_float(), m_range[0], m_range[1]);
auto b_star = clamp(arguments[2].to_float(), m_range[2], m_range[3]);
auto L_star = clamp(arguments[0], 0.0f, 100.0f);
auto a_star = clamp(arguments[1], m_range[0], m_range[1]);
auto b_star = clamp(arguments[2], m_range[2], m_range[3]);
auto L = (L_star + 16) / 116 + a_star / 500;
auto M = (L_star + 16) / 116;
@ -708,11 +703,11 @@ IndexedColorSpace::IndexedColorSpace(NonnullRefPtr<ColorSpace> base)
{
}
PDFErrorOr<ColorOrStyle> IndexedColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> IndexedColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 1);
auto index = arguments[0].to_int();
auto index = static_cast<int>(arguments[0]);
if (index < 0 || index > m_hival)
return Error { Error::Type::MalformedPDF, "Indexed color space index out of range" };
@ -764,7 +759,7 @@ SeparationColorSpace::SeparationColorSpace(NonnullRefPtr<ColorSpace> alternate_s
{
}
PDFErrorOr<ColorOrStyle> SeparationColorSpace::style(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> SeparationColorSpace::style(ReadonlySpan<float> arguments) const
{
// "For an additive device such as a computer display, a Separation color space never applies a process colorant directly;
// it always reverts to the alternate color space as described below."
@ -773,7 +768,7 @@ PDFErrorOr<ColorOrStyle> SeparationColorSpace::style(ReadonlySpan<Value> argumen
// FIXME: Does this need handling for the special colorant names "All" and "None"?
// FIXME: When drawing to a printer, do something else.
VERIFY(arguments.size() == 1);
auto a = arguments[0].to_float();
auto a = arguments[0];
auto tint_output = TRY(m_tint_transform->evaluate(ReadonlySpan<float> { &a, 1 }));

View file

@ -66,7 +66,15 @@ public:
virtual ~ColorSpace() = default;
virtual PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const = 0;
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;
@ -78,7 +86,7 @@ public:
~DeviceGrayColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -93,7 +101,7 @@ public:
~DeviceRGBColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -108,7 +116,7 @@ public:
~DeviceCMYKColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -123,7 +131,7 @@ public:
~DeviceNColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -134,7 +142,6 @@ private:
Vector<ByteString> m_names;
NonnullRefPtr<ColorSpace> m_alternate_space;
NonnullRefPtr<Function> m_tint_transform;
Vector<float> mutable m_tint_input_values;
Vector<Value> mutable m_tint_output_values;
};
@ -144,7 +151,7 @@ public:
~CalGrayColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -163,7 +170,7 @@ public:
~CalRGBColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -183,7 +190,7 @@ public:
~ICCBasedColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -206,7 +213,7 @@ public:
~LabColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -225,7 +232,7 @@ public:
~IndexedColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }
@ -244,7 +251,7 @@ public:
~SeparationColorSpace() override = default;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
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; }

View file

@ -1129,7 +1129,7 @@ PDFErrorOr<Renderer::LoadedImage> Renderer::load_image(NonnullRefPtr<StreamObjec
int x = 0;
int y = 0;
auto const bytes_per_component = bits_per_component / 8;
Vector<Value> component_values;
Vector<float> component_values;
component_values.resize(n_components);
while (!content.is_empty() && y < height) {
auto sample = content.slice(0, bytes_per_component * n_components);
@ -1137,7 +1137,7 @@ PDFErrorOr<Renderer::LoadedImage> Renderer::load_image(NonnullRefPtr<StreamObjec
for (int i = 0; i < n_components; ++i) {
auto component = sample.slice(0, bytes_per_component);
sample = sample.slice(bytes_per_component);
component_values[i] = Value { component_value_decoders[i].interpolate(component[0]) };
component_values[i] = component_value_decoders[i].interpolate(component[0]);
}
auto color = TRY(color_space->style(component_values)).get<Color>();
bitmap->set_pixel(x, y, color);