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:
parent
5ff2a824cc
commit
5f85aff036
3 changed files with 58 additions and 56 deletions
|
@ -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 }));
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue