summaryrefslogtreecommitdiffstats
path: root/src/sp-pattern.cpp
diff options
context:
space:
mode:
authorTavmjong Bah <tavmjong@free.fr>2014-03-06 10:33:08 +0000
committertavmjong-free <tavmjong@free.fr>2014-03-06 10:33:08 +0000
commit678436c3b81d9a1bdd347707e14fff54e5c056b1 (patch)
tree7797c9c133d19e1e49b137d3f88b20220e889302 /src/sp-pattern.cpp
parentWMF: fix for files with swapped W, H in the SETWINDOWEXT record (diff)
downloadinkscape-678436c3b81d9a1bdd347707e14fff54e5c056b1.tar.gz
inkscape-678436c3b81d9a1bdd347707e14fff54e5c056b1.zip
Rewrite of pattern geometry code. Fix for 1288489.
(bzr r13120)
Diffstat (limited to 'src/sp-pattern.cpp')
-rw-r--r--src/sp-pattern.cpp84
1 files changed, 49 insertions, 35 deletions
diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp
index 8e67141fb..62cb0b2a9 100644
--- a/src/sp-pattern.cpp
+++ b/src/sp-pattern.cpp
@@ -542,6 +542,7 @@ static bool pattern_hasItemChildren (SPPattern const *pat)
}
cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbox, double opacity) {
+
bool needs_opacity = (1.0 - opacity) >= 1e-3;
bool visible = opacity >= 1e-3;
@@ -580,62 +581,74 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b
}
}
- // viewBox to pattern server
- Geom::Affine vb2ps = Geom::identity();
+ // ****** Geometry ******
+ //
+ // * "width" and "height" determine tile size.
+ // * "viewBox" (if defined) or "patternContentUnits" determines placement of content inside
+ // tile.
+ // * "x", "y", and "patternTransform" transform tile to user space after tile is generated.
+
+ // These functions recursively search up the tree to find the values.
+ double tile_x = pattern_x(this);
+ double tile_y = pattern_y(this);
+ double tile_width = pattern_width(this);
+ double tile_height = pattern_height(this);
+ if (pattern_patternUnits(this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
+ tile_x *= bbox->width();
+ tile_y *= bbox->height();
+ tile_width *= bbox->width();
+ tile_height *= bbox->height();
+ }
+
+ // Pattern size in pattern space
+ Geom::Rect pattern_tile = Geom::Rect::from_xywh(0, 0, tile_width, tile_height);
+
+ // Content to tile (pattern space)
+ Geom::Affine content2ps;
if (this->viewBox_set) {
+ // viewBox to pattern server
Geom::Rect vb = *pattern_viewBox(this);
- gdouble tmp_x = pattern_width (this) / vb.width();
- gdouble tmp_y = pattern_height (this) / vb.height();
+ gdouble tmp_x = tile_width / vb.width();
+ gdouble tmp_y = tile_height / vb.height();
// FIXME: preserveAspectRatio must be taken into account here too!
- vb2ps = Geom::Affine(tmp_x, 0.0, 0.0, tmp_y, pattern_x(this) - vb.left() * tmp_x, pattern_y(this) - vb.top() * tmp_y);
- }
-
- // We must determine the size and scaling of the pattern at the time it is displayed and render
- // the pattern onto a surface with that size and at that resolution.
+ Geom::Affine vb2ps = Geom::Affine(tmp_x, 0.0, 0.0, tmp_y,
+ /*tile_x*/ - vb.left() * tmp_x,
+ /*tile_y*/ - vb.top() * tmp_y);
+ content2ps = vb2ps;
+ } else {
- // Pattern server to user
- Geom::Affine ps2user = pattern_patternTransform(this);
- if (!this->viewBox_set && pattern_patternContentUnits (this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
- /* BBox to user coordinate system */
- Geom::Affine bbox2user (bbox->width(), 0.0, 0.0, bbox->height(), bbox->left(), bbox->top());
- ps2user *= bbox2user;
+ // Content to bbox
+ if (pattern_patternContentUnits (this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
+ content2ps = Geom::Affine(bbox->width(), 0.0, 0.0, bbox->height(), 0,0);
+ }
}
- ps2user = Geom::Translate (pattern_x (this), pattern_y (this)) * ps2user;
- // Pattern size in pattern space
- Geom::Rect pattern_tile = Geom::Rect::from_xywh(pattern_x(this), pattern_y(this),
- pattern_width(this), pattern_height(this));
- if (pattern_patternUnits(this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
- // interpret x, y, width, height in relation to bbox
- Geom::Affine bbox2user(bbox->width(), 0.0, 0.0, bbox->height(), bbox->left(), bbox->top());
- pattern_tile = pattern_tile * bbox2user;
- }
+ // Tile (pattern space) to user.
+ Geom::Affine ps2user = Geom::Translate(tile_x,tile_y) * pattern_patternTransform(this);
+
// Transform of object with pattern (includes screen scaling)
cairo_matrix_t cm;
cairo_get_matrix(base_ct, &cm);
Geom::Affine full(cm.xx, cm.yx, cm.xy, cm.yy, 0, 0);
- // The DrawingSurface class is suppose to handle the mapping from "logical space"
+ // 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.
- // oversample the pattern slightly
+ // Oversample the pattern
// TODO: find optimum value
// TODO: this is lame. instead of using descrim(), we should extract
// the scaling component from the complete matrix and use it
// to find the optimum tile size for rendering
// c is number of pixels in buffer x and y.
// Scale factor of 1.1 is too small... see bug #1251039
- Geom::Point c(pattern_tile.dimensions()*vb2ps.descrim()*full.descrim()*2.0);
-
- c[Geom::X] = ceil(c[Geom::X]);
- c[Geom::Y] = ceil(c[Geom::Y]);
+ Geom::Point c(pattern_tile.dimensions()*ps2user.descrim()*full.descrim()*2.0);
- // Create drawing surface with size of pattern tile (in tile space) but with number of pixels
+ // 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, c.ceil());
Inkscape::DrawingContext dc(pattern_surface);
@@ -643,14 +656,14 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b
pattern_tile *= pattern_surface.drawingTransform();
Geom::IntRect one_tile = pattern_tile.roundOutwards();
- // render pattern.
+ // Render pattern.
if (needs_opacity) {
dc.pushGroup(); // this group is for pattern + opacity
}
// TODO: make sure there are no leaks.
Inkscape::UpdateContext ctx; // UpdateContext is structure with only ctm!
- ctx.ctm = ps2user * pattern_surface.drawingTransform(); // vb2ps?
+ ctx.ctm = content2ps * pattern_surface.drawingTransform();
dc.transform( pattern_surface.drawingTransform().inverse() );
drawing.update(Geom::IntRect::infinite(), ctx);
@@ -669,7 +682,8 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b
// << " width: " << cairo_image_surface_get_width( raw )
// << " height: " << cairo_image_surface_get_height( raw )
// << std::endl;
- // cairo_surface_write_to_png( pattern_surface.raw(), "sp-pattern.png" );
+ // std::string filename = "sp-pattern-" + (std::string)getId() + ".png";
+ // cairo_surface_write_to_png( pattern_surface.raw(), filename.c_str() );
if (needs_opacity) {
dc.popGroupToSource(); // pop raw pattern
@@ -678,7 +692,7 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b
cairo_pattern_t *cp = cairo_pattern_create_for_surface(pattern_surface.raw());
// Apply transformation to user space. Also compensate for oversampling.
- ink_cairo_pattern_set_matrix(cp, pattern_surface.drawingTransform() );
+ ink_cairo_pattern_set_matrix(cp, ps2user.inverse() * pattern_surface.drawingTransform() );
cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT);