summaryrefslogtreecommitdiffstats
path: root/src/display/drawing-pattern.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/display/drawing-pattern.cpp')
-rw-r--r--src/display/drawing-pattern.cpp198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/display/drawing-pattern.cpp b/src/display/drawing-pattern.cpp
new file mode 100644
index 0000000..90bad03
--- /dev/null
+++ b/src/display/drawing-pattern.cpp
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Canvas belonging to SVG pattern.
+ *//*
+ * Authors:
+ * Tomasz Boczkowski <penginsbacon@gmail.com>
+ *
+ * Copyright (C) 2014 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "display/cairo-utils.h"
+#include "display/drawing-context.h"
+#include "display/drawing-pattern.h"
+#include "display/drawing-surface.h"
+
+namespace Inkscape {
+
+DrawingPattern::DrawingPattern(Drawing &drawing, bool debug)
+ : DrawingGroup(drawing)
+ , _pattern_to_user(nullptr)
+ , _overflow_steps(1)
+ , _debug(debug)
+{
+}
+
+DrawingPattern::~DrawingPattern()
+{
+ delete _pattern_to_user; // delete NULL; is safe
+}
+
+void
+DrawingPattern::setPatternToUserTransform(Geom::Affine const &new_trans) {
+ Geom::Affine current;
+ if (_pattern_to_user) {
+ current = *_pattern_to_user;
+ }
+
+ if (!Geom::are_near(current, new_trans, 1e-18)) {
+ // mark the area where the object was for redraw.
+ _markForRendering();
+ if (new_trans.isIdentity()) {
+ delete _pattern_to_user; // delete NULL; is safe
+ _pattern_to_user = nullptr;
+ } else {
+ _pattern_to_user = new Geom::Affine(new_trans);
+ }
+ _markForUpdate(STATE_ALL, true);
+ }
+}
+
+void
+DrawingPattern::setTileRect(Geom::Rect const &tile_rect) {
+ _tile_rect = tile_rect;
+}
+
+void
+DrawingPattern::setOverflow(Geom::Affine initial_transform, int steps, Geom::Affine step_transform) {
+ _overflow_initial_transform = initial_transform;
+ _overflow_steps = steps;
+ _overflow_step_transform = step_transform;
+}
+
+cairo_pattern_t *
+DrawingPattern::renderPattern(float opacity) {
+ bool needs_opacity = (1.0 - opacity) >= 1e-3;
+ bool visible = opacity >= 1e-3;
+
+ if (!visible) {
+ return nullptr;
+ }
+
+ if (!_tile_rect || (_tile_rect->area() == 0)) {
+ return nullptr;
+ }
+ Geom::Rect pattern_tile = *_tile_rect;
+
+ //TODO: If pattern_to_user set it to identity transform
+
+ // The DrawingSurface class handles the mapping from "logical space"
+ // (coordinates in the rendering) to "physical space" (surface pixels).
+ // An oversampling is done as the pattern may not pixel align with the final surface.
+ // The cairo surface is created when the DrawingContext is declared.
+ // Create drawing surface with size of pattern tile (in pattern space) but with number of pixels
+ // based on required resolution (c).
+ Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution);
+ Inkscape::DrawingContext dc(pattern_surface);
+ dc.transform( pattern_surface.drawingTransform().inverse() );
+
+ pattern_tile *= pattern_surface.drawingTransform();
+ Geom::IntRect one_tile = pattern_tile.roundOutwards();
+
+ // Render pattern.
+ if (needs_opacity) {
+ dc.pushGroup(); // this group is for pattern + opacity
+ }
+
+ if (_debug) {
+ dc.setSource(0.8, 0.0, 0.8);
+ dc.paint();
+ }
+
+ //FIXME: What flags to choose?
+ if (_overflow_steps == 1) {
+ render(dc, one_tile, RENDER_DEFAULT);
+ } else {
+ //Overflow transforms need to be transformed to the new coordinate system
+ //introduced by dc.transform( pattern_surface.drawingTransform().inverse() );
+ Geom::Affine dt = pattern_surface.drawingTransform();
+ Geom::Affine idt = pattern_surface.drawingTransform().inverse();
+ Geom::Affine initial_transform = idt * _overflow_initial_transform * dt;
+ Geom::Affine step_transform = idt * _overflow_step_transform * dt;
+ dc.transform(initial_transform);
+ for (int i = 0; i < _overflow_steps; i++) {
+ // render() fails to handle transforms applied here when using cache.
+ render(dc, one_tile, RENDER_BYPASS_CACHE);
+ dc.transform(step_transform);
+ // cairo_surface_t* raw = pattern_surface.raw();
+ // std::string filename = "drawing-pattern" + std::to_string(i) + ".png";
+ // cairo_surface_write_to_png( pattern_surface.raw(), filename.c_str() );
+ }
+ }
+
+ // Uncomment to debug
+ // cairo_surface_t* raw = pattern_surface.raw();
+ // std::cout << " cairo_surface (sp-pattern): "
+ // << " width: " << cairo_image_surface_get_width( raw )
+ // << " height: " << cairo_image_surface_get_height( raw )
+ // << std::endl;
+ // std::string filename = "drawing-pattern.png";
+ // cairo_surface_write_to_png( pattern_surface.raw(), filename.c_str() );
+
+ if (needs_opacity) {
+ dc.popGroupToSource(); // pop raw pattern
+ dc.paint(opacity); // apply opacity
+ }
+
+ cairo_pattern_t *cp = cairo_pattern_create_for_surface(pattern_surface.raw());
+ // Apply transformation to user space. Also compensate for oversampling.
+ if (_pattern_to_user) {
+ ink_cairo_pattern_set_matrix(cp, _pattern_to_user->inverse() * pattern_surface.drawingTransform());
+ } else {
+ ink_cairo_pattern_set_matrix(cp, pattern_surface.drawingTransform());
+ }
+
+ if (_debug) {
+ cairo_pattern_set_extend(cp, CAIRO_EXTEND_NONE);
+ } else {
+ cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT);
+ }
+
+ return cp;
+}
+
+// TODO investigate if area should be used.
+unsigned DrawingPattern::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
+{
+ UpdateContext pattern_ctx;
+
+ if (!_tile_rect || (_tile_rect->area() == 0)) {
+ return STATE_NONE;
+ }
+
+ Geom::Rect pattern_tile = *_tile_rect;
+ Geom::Coord det_ctm = ctx.ctm.descrim();
+ Geom::Coord det_ps2user = _pattern_to_user ? _pattern_to_user->descrim() : 1.0;
+ Geom::Coord det_child_transform = _child_transform ? _child_transform->descrim() : 1.0;
+ const double oversampling = 2.0;
+ double scale = det_ctm*det_ps2user*det_child_transform * oversampling;
+ //FIXME: When scale is too big (zooming in a hatch), cairo doesn't render the pattern
+ //More precisely it fails when setting pattern matrix in DrawingPattern::renderPattern
+ //Fully correct solution should make use of visible area bbox and change hatch tile rect
+ //accordingly
+ if (scale > 25) {
+ scale = 25;
+ }
+ Geom::Point c(pattern_tile.dimensions()*scale*oversampling);
+ _pattern_resolution = c.ceil();
+
+ Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution);
+
+ pattern_ctx.ctm = pattern_surface.drawingTransform();
+ return DrawingGroup::_updateItem(Geom::IntRect::infinite(), pattern_ctx, flags, reset);
+}
+
+} // end namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :