Ver Fonte

LibPDF: Improve path clipping support

The existing path clipping support was broken, as it performed the
clipping operation as soon as the path clipping commands (W/W*) were
received. The correct behavior is to keep a clipping path in the
graphic state, *intersect* that with the current path upon receiving
W/W*, and apply the clipping when performing painting operations. On top
of that, the intersection happening at W/W* time does not affect the
painting operation happening on the current on-build path, but takes
effect only after the current path is cleared; therefore a current and a
next clipping path need to be kept track of.

Path clipping is not yet supported on the Painter class, nor is path
intersection. We thus continue using the same simplified bounding box
approach to calculate clipping paths.

Since now we are dealing with more rectangles-as-path code, I've made
helper functions to build a rectangle path and reuse it as needed.
Rodrigo Tobar há 2 anos atrás
pai
commit
a1e36e8f78

+ 57 - 17
Userland/Libraries/LibPDF/Renderer.cpp

@@ -26,6 +26,28 @@ PDFErrorOr<void> Renderer::render(Document& document, Page const& page, RefPtr<G
     return Renderer(document, page, bitmap).render();
 }
 
+static void rect_path(Gfx::Path& path, float x, float y, float width, float height)
+{
+    path.move_to({ x, y });
+    path.line_to({ x + width, y });
+    path.line_to({ x + width, y + height });
+    path.line_to({ x, y + height });
+    path.close();
+}
+
+static Gfx::Path rect_path(float x, float y, float width, float height)
+{
+    Gfx::Path path;
+    rect_path(path, x, y, width, height);
+    return path;
+}
+
+template<typename T>
+static Gfx::Path rect_path(Gfx::Rect<T> rect)
+{
+    return rect_path(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
 Renderer::Renderer(RefPtr<Document> document, Page const& page, RefPtr<Gfx::Bitmap> bitmap)
     : m_document(document)
     , m_bitmap(bitmap)
@@ -53,7 +75,8 @@ Renderer::Renderer(RefPtr<Document> document, Page const& page, RefPtr<Gfx::Bitm
     userspace_matrix.multiply(horizontal_reflection_matrix);
     userspace_matrix.translate(0.0f, -height);
 
-    m_graphics_state_stack.append(GraphicsState { userspace_matrix });
+    auto initial_clipping_path = rect_path(0, 0, width, height);
+    m_graphics_state_stack.append(GraphicsState { userspace_matrix, { initial_clipping_path, initial_clipping_path } });
 
     m_bitmap->fill(Gfx::Color::NamedColor::White);
 }
@@ -212,19 +235,33 @@ RENDERER_HANDLER(path_append_rect)
     //        in the PDF file seem to be correct, with the same flipped-ness as
     //        everything else in a PDF file.
     pos.set_y(m_bitmap->height() - pos.y() - size.height());
-
-    m_current_path.move_to(pos);
-    m_current_path.line_to({ pos.x() + size.width(), pos.y() });
-    m_current_path.line_to({ pos.x() + size.width(), pos.y() + size.height() });
-    m_current_path.line_to({ pos.x(), pos.y() + size.height() });
-    m_current_path.close();
+    rect_path(m_current_path, pos.x(), pos.y(), size.width(), size.height());
     return {};
 }
 
+///
+// Path painting operations
+///
+
+void Renderer::begin_path_paint()
+{
+    auto bounding_box = map(state().clipping_paths.current.bounding_box());
+    m_painter.clear_clip_rect();
+    m_painter.add_clip_rect(bounding_box.to_type<int>());
+}
+
+void Renderer::end_path_paint()
+{
+    m_current_path.clear();
+    m_painter.clear_clip_rect();
+    state().clipping_paths.current = state().clipping_paths.next;
+}
+
 RENDERER_HANDLER(path_stroke)
 {
+    begin_path_paint();
     m_anti_aliasing_painter.stroke_path(m_current_path, state().stroke_color, state().line_width);
-    m_current_path.clear();
+    end_path_paint();
     return {};
 }
 
@@ -237,8 +274,9 @@ RENDERER_HANDLER(path_close_and_stroke)
 
 RENDERER_HANDLER(path_fill_nonzero)
 {
+    begin_path_paint();
     m_anti_aliasing_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::Nonzero);
-    m_current_path.clear();
+    end_path_paint();
     return {};
 }
 
@@ -250,8 +288,9 @@ RENDERER_HANDLER(path_fill_nonzero_deprecated)
 
 RENDERER_HANDLER(path_fill_evenodd)
 {
+    begin_path_paint();
     m_anti_aliasing_painter.fill_path(m_current_path, state().paint_color, Gfx::Painter::WindingRule::EvenOdd);
-    m_current_path.clear();
+    end_path_paint();
     return {};
 }
 
@@ -285,23 +324,24 @@ RENDERER_HANDLER(path_close_fill_stroke_evenodd)
 
 RENDERER_HANDLER(path_end)
 {
+    begin_path_paint();
+    end_path_paint();
     return {};
 }
 
 RENDERER_HANDLER(path_intersect_clip_nonzero)
 {
-    // FIXME: Support arbitrary path clipping in the painter and utilize that here
-    auto bounding_box = map(m_current_path.bounding_box());
-    m_painter.add_clip_rect(bounding_box.to_type<int>());
+    // FIXME: Support arbitrary path clipping in Path and utilize that here
+    auto next_clipping_bbox = state().clipping_paths.next.bounding_box();
+    next_clipping_bbox.intersect(m_current_path.bounding_box());
+    state().clipping_paths.next = rect_path(next_clipping_bbox);
     return {};
 }
 
 RENDERER_HANDLER(path_intersect_clip_evenodd)
 {
-    // FIXME: Support arbitrary path clipping in the painter and utilize that here
-    auto bounding_box = map(m_current_path.bounding_box());
-    m_painter.add_clip_rect(bounding_box.to_type<int>());
-    return {};
+    // FIXME: Should have different behavior than path_intersect_clip_nonzero
+    return handle_path_intersect_clip_nonzero(args);
 }
 
 RENDERER_HANDLER(text_begin)

+ 8 - 0
Userland/Libraries/LibPDF/Renderer.h

@@ -64,8 +64,14 @@ struct TextState {
     bool knockout { true };
 };
 
+struct ClippingPaths {
+    Gfx::Path current;
+    Gfx::Path next;
+};
+
 struct GraphicsState {
     Gfx::AffineTransform ctm;
+    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 };
@@ -95,6 +101,8 @@ private:
     PDFErrorOr<void> handle_text_next_line_show_string(Vector<Value> const& args);
     PDFErrorOr<void> handle_text_next_line_show_string_set_spacing(Vector<Value> const& args);
 
+    void begin_path_paint();
+    void end_path_paint();
     PDFErrorOr<void> set_graphics_state_from_dict(NonnullRefPtr<DictObject>);
     void show_text(String const&);
     PDFErrorOr<NonnullRefPtr<ColorSpace>> get_color_space(Value const&);