diff options
| author | Tavmjong Bah <tavmjong@free.fr> | 2014-03-06 10:33:08 +0000 |
|---|---|---|
| committer | tavmjong-free <tavmjong@free.fr> | 2014-03-06 10:33:08 +0000 |
| commit | 678436c3b81d9a1bdd347707e14fff54e5c056b1 (patch) | |
| tree | 7797c9c133d19e1e49b137d3f88b20220e889302 /src/sp-pattern.cpp | |
| parent | WMF: fix for files with swapped W, H in the SETWINDOWEXT record (diff) | |
| download | inkscape-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.cpp | 84 |
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); |
