Переглянути джерело

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)
```
Nico Weber 1 рік тому
батько
коміт
5f85aff036

+ 37 - 42
Userland/Libraries/LibPDF/ColorSpace.cpp

@@ -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
 {
-    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();
+    if (m_profile->data_color_space() == Gfx::ICC::ColorSpace::CIELAB) {
+        m_components.resize(arguments.size());
+        for (size_t i = 0; i < arguments.size(); ++i) {
+            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;
+            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 }));
 

+ 19 - 12
Userland/Libraries/LibPDF/ColorSpace.h

@@ -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; }

+ 2 - 2
Userland/Libraries/LibPDF/Renderer.cpp

@@ -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);