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

LibPDF: Use Variant<Color, PaintStyle> instead of Color for ColorSpaces

This is in anticipation of Pattern color space support which does not
yield a simple color.
This commit is contained in:
Kyle Pereira 2023-12-05 12:51:42 +00:00 committed by Andreas Kling
parent e4b8d68039
commit 082a4197b6
6 changed files with 116 additions and 55 deletions

View file

@ -101,7 +101,7 @@ NonnullRefPtr<DeviceGrayColorSpace> DeviceGrayColorSpace::the()
return instance;
}
PDFErrorOr<Color> DeviceGrayColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceGrayColorSpace::style(ReadonlySpan<Value> arguments) const
{
VERIFY(arguments.size() == 1);
auto gray = static_cast<u8>(arguments[0].to_float() * 255.0f);
@ -119,7 +119,7 @@ NonnullRefPtr<DeviceRGBColorSpace> DeviceRGBColorSpace::the()
return instance;
}
PDFErrorOr<Color> DeviceRGBColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceRGBColorSpace::style(ReadonlySpan<Value> arguments) const
{
VERIFY(arguments.size() == 3);
auto r = static_cast<u8>(arguments[0].to_float() * 255.0f);
@ -139,7 +139,7 @@ NonnullRefPtr<DeviceCMYKColorSpace> DeviceCMYKColorSpace::the()
return instance;
}
PDFErrorOr<Color> DeviceCMYKColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceCMYKColorSpace::style(ReadonlySpan<Value> arguments) const
{
VERIFY(arguments.size() == 4);
auto c = arguments[0].to_float();
@ -197,7 +197,7 @@ DeviceNColorSpace::DeviceNColorSpace(NonnullRefPtr<ColorSpace> alternate_space,
{
}
PDFErrorOr<Color> DeviceNColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> DeviceNColorSpace::style(ReadonlySpan<Value> arguments) const
{
// FIXME: Does this need handling for the special colorant name "None"?
// FIXME: When drawing to a printer, do something else.
@ -211,7 +211,7 @@ PDFErrorOr<Color> DeviceNColorSpace::color(ReadonlySpan<Value> arguments) const
for (size_t i = 0; i < tint_output.size(); ++i)
m_tint_output_values[i] = tint_output[i];
return m_alternate_space->color(m_tint_output_values);
return m_alternate_space->style(m_tint_output_values);
}
int DeviceNColorSpace::number_of_components() const
@ -347,7 +347,7 @@ PDFErrorOr<NonnullRefPtr<CalGrayColorSpace>> CalGrayColorSpace::create(Document*
return color_space;
}
PDFErrorOr<Color> CalGrayColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> CalGrayColorSpace::style(ReadonlySpan<Value> arguments) const
{
VERIFY(arguments.size() == 1);
auto a = clamp(arguments[0].to_float(), 0.0f, 1.0f);
@ -433,7 +433,7 @@ PDFErrorOr<NonnullRefPtr<CalRGBColorSpace>> CalRGBColorSpace::create(Document* d
return color_space;
}
PDFErrorOr<Color> CalRGBColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> CalRGBColorSpace::style(ReadonlySpan<Value> arguments) const
{
VERIFY(arguments.size() == 3);
auto a = clamp(arguments[0].to_float(), 0.0f, 1.0f);
@ -493,7 +493,7 @@ ICCBasedColorSpace::ICCBasedColorSpace(NonnullRefPtr<Gfx::ICC::Profile> profile)
{
}
PDFErrorOr<Color> ICCBasedColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> ICCBasedColorSpace::style(ReadonlySpan<Value> arguments) const
{
if (!s_srgb_profile)
s_srgb_profile = TRY(Gfx::ICC::sRGB());
@ -593,7 +593,7 @@ PDFErrorOr<NonnullRefPtr<LabColorSpace>> LabColorSpace::create(Document* documen
return color_space;
}
PDFErrorOr<Color> LabColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> LabColorSpace::style(ReadonlySpan<Value> arguments) const
{
VERIFY(arguments.size() == 3);
auto L_star = clamp(arguments[0].to_float(), 0.0f, 100.0f);
@ -692,7 +692,7 @@ IndexedColorSpace::IndexedColorSpace(NonnullRefPtr<ColorSpace> base)
{
}
PDFErrorOr<Color> IndexedColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> IndexedColorSpace::style(ReadonlySpan<Value> arguments) const
{
VERIFY(arguments.size() == 1);
@ -705,7 +705,7 @@ PDFErrorOr<Color> IndexedColorSpace::color(ReadonlySpan<Value> arguments) const
for (size_t i = 0; i < n; ++i)
TRY(components.try_append(Value(m_lookup[index * n + i] / 255.0f)));
return m_base->color(components);
return m_base->style(components);
}
Vector<float> IndexedColorSpace::default_decode() const
@ -748,7 +748,7 @@ SeparationColorSpace::SeparationColorSpace(NonnullRefPtr<ColorSpace> alternate_s
{
}
PDFErrorOr<Color> SeparationColorSpace::color(ReadonlySpan<Value> arguments) const
PDFErrorOr<ColorOrStyle> SeparationColorSpace::style(ReadonlySpan<Value> 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."
@ -765,12 +765,11 @@ PDFErrorOr<Color> SeparationColorSpace::color(ReadonlySpan<Value> arguments) con
for (size_t i = 0; i < tint_output.size(); ++i)
m_tint_output_values[i] = tint_output[i];
return m_alternate_space->color(m_tint_output_values);
return m_alternate_space->style(m_tint_output_values);
}
Vector<float> SeparationColorSpace::default_decode() const
{
return { 0.0f, 1.0f };
}
}

View file

@ -10,6 +10,7 @@
#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>
@ -28,6 +29,9 @@
namespace PDF {
typedef Variant<Gfx::Color, NonnullRefPtr<Gfx::PaintStyle>> ColorOrStyle;
class Renderer;
class ColorSpaceFamily {
public:
ColorSpaceFamily(DeprecatedFlyString name, bool may_be_specified_directly)
@ -62,7 +66,7 @@ public:
virtual ~ColorSpace() = default;
virtual PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const = 0;
virtual PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const = 0;
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;
@ -74,7 +78,7 @@ public:
~DeviceGrayColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -89,7 +93,7 @@ public:
~DeviceRGBColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -104,7 +108,7 @@ public:
~DeviceCMYKColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -119,7 +123,7 @@ public:
~DeviceNColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
int number_of_components() const override;
Vector<float> default_decode() const override;
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceN; }
@ -140,7 +144,7 @@ public:
~CalGrayColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -159,7 +163,7 @@ public:
~CalRGBColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -179,7 +183,7 @@ public:
~ICCBasedColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> arguments) const override;
int number_of_components() const override;
Vector<float> default_decode() const override;
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::ICCBased; }
@ -197,7 +201,7 @@ public:
~LabColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -216,7 +220,7 @@ public:
~IndexedColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -235,7 +239,7 @@ public:
~SeparationColorSpace() override = default;
PDFErrorOr<Color> color(ReadonlySpan<Value> arguments) const override;
PDFErrorOr<ColorOrStyle> style(ReadonlySpan<Value> 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; }
@ -248,5 +252,4 @@ private:
NonnullRefPtr<Function> m_tint_transform;
Vector<Value> mutable m_tint_output_values;
};
}

View file

@ -48,13 +48,20 @@ void TrueTypeFont::set_font_size(float font_size)
m_font = m_font->with_size((font_size * POINTS_PER_INCH) / DEFAULT_DPI);
}
PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float, u8 char_code, Renderer const& renderer)
PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Renderer const& renderer)
{
auto color = renderer.state().paint_color;
auto style = renderer.state().paint_style;
// Account for the reversed font baseline
auto position = point.translated(0, -m_font->baseline());
painter.draw_glyph(position, char_code, *m_font, color);
if (style.has<Color>()) {
painter.draw_glyph(position, char_code, *m_font, style.get<Color>());
} else {
// FIXME: Bounding box and sample point look to be pretty wrong
style.get<NonnullRefPtr<Gfx::PaintStyle>>()->paint(Gfx::IntRect(position.x(), position.y(), width, 0), [&](auto sample) {
painter.draw_glyph(position, char_code, *m_font, sample(Gfx::IntPoint(position.x(), position.y())));
});
}
return {};
}

View file

@ -67,12 +67,19 @@ void Type1Font::set_font_size(float font_size)
PDFErrorOr<void> Type1Font::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Renderer const& renderer)
{
auto color = renderer.state().paint_color;
auto style = renderer.state().paint_style;
if (!m_font_program) {
// Account for the reversed font baseline
auto position = point.translated(0, -m_font->baseline());
painter.draw_glyph(position, char_code, *m_font, color);
// FIXME: Bounding box and sample point look to be pretty wrong
if (style.has<Color>()) {
painter.draw_glyph(position, char_code, *m_font, style.get<Color>());
} else {
style.get<NonnullRefPtr<Gfx::PaintStyle>>()->paint(Gfx::IntRect(position.x(), position.y(), width, 0), [&](auto sample) {
painter.draw_glyph(position, char_code, *m_font, sample(Gfx::IntPoint(position.x(), position.y())));
});
}
return {};
}
@ -97,9 +104,18 @@ PDFErrorOr<void> Type1Font::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint po
m_glyph_cache.set(index, bitmap);
}
painter.blit_filtered(glyph_position.blit_position, *bitmap, bitmap->rect(), [color](Color pixel) -> Color {
return pixel.multiply(color);
});
if (style.has<Color>()) {
painter.blit_filtered(glyph_position.blit_position, *bitmap, bitmap->rect(), [style](Color pixel) -> Color {
return pixel.multiply(style.get<Color>());
});
} else {
style.get<NonnullRefPtr<Gfx::PaintStyle>>()->paint(bitmap->physical_rect(), [&](auto sample) {
painter.blit_filtered(glyph_position.blit_position, *bitmap, bitmap->rect(), [&](Color pixel) -> Color {
// FIXME: Presumably we need to sample at every point in the glyph, not just the top left?
return pixel.multiply(sample(glyph_position.blit_position));
});
});
}
return {};
}
}

View file

@ -311,7 +311,11 @@ void Renderer::end_path_paint()
RENDERER_HANDLER(path_stroke)
{
begin_path_paint();
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_color, state().ctm.x_scale() * state().line_width);
if (state().stroke_style.has<NonnullRefPtr<Gfx::PaintStyle>>()) {
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_style.get<NonnullRefPtr<Gfx::PaintStyle>>(), state().ctm.x_scale() * state().line_width);
} else {
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_style.get<Color>(), state().ctm.x_scale() * state().line_width);
}
end_path_paint();
return {};
}
@ -327,7 +331,11 @@ RENDERER_HANDLER(path_fill_nonzero)
{
begin_path_paint();
m_current_path.close_all_subpaths();
m_anti_aliasing_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::Nonzero);
if (state().paint_style.has<NonnullRefPtr<Gfx::PaintStyle>>()) {
m_anti_aliasing_painter.fill_path(m_current_path, state().paint_style.get<NonnullRefPtr<Gfx::PaintStyle>>(), 1.0, Gfx::Painter::WindingRule::Nonzero);
} else {
m_anti_aliasing_painter.fill_path(m_current_path, state().paint_style.get<Color>(), Gfx::Painter::WindingRule::Nonzero);
}
end_path_paint();
return {};
}
@ -341,20 +349,32 @@ RENDERER_HANDLER(path_fill_evenodd)
{
begin_path_paint();
m_current_path.close_all_subpaths();
m_anti_aliasing_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::EvenOdd);
if (state().paint_style.has<NonnullRefPtr<Gfx::PaintStyle>>()) {
m_anti_aliasing_painter.fill_path(m_current_path, state().paint_style.get<NonnullRefPtr<Gfx::PaintStyle>>(), 1.0, Gfx::Painter::WindingRule::EvenOdd);
} else {
m_anti_aliasing_painter.fill_path(m_current_path, state().paint_style.get<Color>(), Gfx::Painter::WindingRule::EvenOdd);
}
end_path_paint();
return {};
}
RENDERER_HANDLER(path_fill_stroke_nonzero)
{
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_color, state().ctm.x_scale() * state().line_width);
if (state().stroke_style.has<NonnullRefPtr<Gfx::PaintStyle>>()) {
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_style.get<NonnullRefPtr<Gfx::PaintStyle>>(), state().ctm.x_scale() * state().line_width);
} else {
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_style.get<Color>(), state().ctm.x_scale() * state().line_width);
}
return handle_path_fill_nonzero(args);
}
RENDERER_HANDLER(path_fill_stroke_evenodd)
{
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_color, state().ctm.x_scale() * state().line_width);
if (state().stroke_style.has<NonnullRefPtr<Gfx::PaintStyle>>()) {
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_style.get<NonnullRefPtr<Gfx::PaintStyle>>(), state().ctm.x_scale() * state().line_width);
} else {
m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_style.get<Color>(), state().ctm.x_scale() * state().line_width);
}
return handle_path_fill_evenodd(args);
}
@ -590,7 +610,7 @@ RENDERER_HANDLER(set_painting_space)
RENDERER_HANDLER(set_stroking_color)
{
state().stroke_color = TRY(state().stroke_color_space->color(args));
state().stroke_style = TRY(state().stroke_color_space->style(args));
return {};
}
@ -603,13 +623,13 @@ RENDERER_HANDLER(set_stroking_color_extended)
return Error::rendering_unsupported_error("Pattern color spaces not yet implemented");
}
state().stroke_color = TRY(state().stroke_color_space->color(args));
state().stroke_style = TRY(state().stroke_color_space->style(args));
return {};
}
RENDERER_HANDLER(set_painting_color)
{
state().paint_color = TRY(state().paint_color_space->color(args));
state().paint_style = TRY(state().paint_color_space->style(args));
return {};
}
@ -622,49 +642,49 @@ RENDERER_HANDLER(set_painting_color_extended)
return Error::rendering_unsupported_error("Pattern color spaces not yet implemented");
}
state().paint_color = TRY(state().paint_color_space->color(args));
state().paint_style = TRY(state().paint_color_space->style(args));
return {};
}
RENDERER_HANDLER(set_stroking_color_and_space_to_gray)
{
state().stroke_color_space = DeviceGrayColorSpace::the();
state().stroke_color = TRY(state().stroke_color_space->color(args));
state().stroke_style = TRY(state().stroke_color_space->style(args));
return {};
}
RENDERER_HANDLER(set_painting_color_and_space_to_gray)
{
state().paint_color_space = DeviceGrayColorSpace::the();
state().paint_color = TRY(state().paint_color_space->color(args));
state().paint_style = TRY(state().paint_color_space->style(args));
return {};
}
RENDERER_HANDLER(set_stroking_color_and_space_to_rgb)
{
state().stroke_color_space = DeviceRGBColorSpace::the();
state().stroke_color = TRY(state().stroke_color_space->color(args));
state().stroke_style = TRY(state().stroke_color_space->style(args));
return {};
}
RENDERER_HANDLER(set_painting_color_and_space_to_rgb)
{
state().paint_color_space = DeviceRGBColorSpace::the();
state().paint_color = TRY(state().paint_color_space->color(args));
state().paint_style = TRY(state().paint_color_space->style(args));
return {};
}
RENDERER_HANDLER(set_stroking_color_and_space_to_cmyk)
{
state().stroke_color_space = DeviceCMYKColorSpace::the();
state().stroke_color = TRY(state().stroke_color_space->color(args));
state().stroke_style = TRY(state().stroke_color_space->style(args));
return {};
}
RENDERER_HANDLER(set_painting_color_and_space_to_cmyk)
{
state().paint_color_space = DeviceCMYKColorSpace::the();
state().paint_color = TRY(state().paint_color_space->color(args));
state().paint_style = TRY(state().paint_color_space->style(args));
return {};
}
@ -956,8 +976,16 @@ PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> Renderer::load_image(NonnullRefPtr<Stream
sample = sample.slice(bytes_per_component);
component_values[i] = Value { component_value_decoders[i].interpolate(component[0]) };
}
auto color = TRY(color_space->color(component_values));
bitmap->set_pixel(x, y, color);
auto color = TRY(color_space->style(component_values));
if (color.has<Color>()) {
auto c = color.get<Color>();
bitmap->set_pixel(x, y, c);
} else {
auto paint_style = color.get<NonnullRefPtr<Gfx::PaintStyle>>();
paint_style->paint(bitmap->rect(), [&](auto sample) {
bitmap->set_pixel(x, y, sample(Gfx::IntPoint(x, y)));
});
}
++x;
if (x == width) {
x = 0;

View file

@ -74,8 +74,8 @@ struct GraphicsState {
ClippingPaths clipping_paths;
RefPtr<ColorSpace> stroke_color_space { DeviceGrayColorSpace::the() };
RefPtr<ColorSpace> paint_color_space { DeviceGrayColorSpace::the() };
Gfx::Color stroke_color { Gfx::Color::NamedColor::Black };
Gfx::Color paint_color { Gfx::Color::NamedColor::Black };
ColorOrStyle stroke_style { Color::Black };
ColorOrStyle paint_style { Color::Black };
DeprecatedString color_rendering_intent { "RelativeColorimetric"sv };
float flatness_tolerance { 0.0f };
float line_width { 1.0f };
@ -286,8 +286,16 @@ struct Formatter<PDF::GraphicsState> : Formatter<StringView> {
StringBuilder builder;
builder.append("GraphicsState {\n"sv);
builder.appendff(" ctm={}\n", state.ctm);
builder.appendff(" stroke_color={}\n", state.stroke_color);
builder.appendff(" paint_color={}\n", state.paint_color);
if (state.stroke_style.has<Color>()) {
builder.appendff(" stroke_style={}\n", state.stroke_style.get<Color>());
} else {
builder.appendff(" stroke_style={}\n", state.stroke_style.get<NonnullRefPtr<Gfx::PaintStyle>>());
}
if (state.paint_style.has<Color>()) {
builder.appendff(" paint_style={}\n", state.paint_style.get<Color>());
} else {
builder.appendff(" paint_style={}\n", state.paint_style.get<NonnullRefPtr<Gfx::PaintStyle>>());
}
builder.appendff(" color_rendering_intent={}\n", state.color_rendering_intent);
builder.appendff(" flatness_tolerance={}\n", state.flatness_tolerance);
builder.appendff(" line_width={}\n", state.line_width);