diff options
| author | Tomasz Boczkowski <penginsbacon@gmail.com> | 2014-10-14 11:30:31 +0000 |
|---|---|---|
| committer | Tomasz Boczkowski <penginsbacon@gmail.com> | 2014-10-14 11:30:31 +0000 |
| commit | bf11ecdfe29082801833fa565f0a1710f6fcd281 (patch) | |
| tree | 6030ebac0996bd586d648567544e9f9198a3b654 /src | |
| parent | Merged hatch rendering code (diff) | |
| download | inkscape-bf11ecdfe29082801833fa565f0a1710f6fcd281.tar.gz inkscape-bf11ecdfe29082801833fa565f0a1710f6fcd281.zip | |
Merged hatch pdf and png export code
(bzr r13611.1.9)
Diffstat (limited to 'src')
| -rw-r--r-- | src/display/drawing-pattern.cpp | 1 | ||||
| -rw-r--r-- | src/display/drawing-pattern.h | 1 | ||||
| -rw-r--r-- | src/extension/internal/cairo-render-context.cpp | 116 | ||||
| -rw-r--r-- | src/extension/internal/cairo-render-context.h | 1 | ||||
| -rw-r--r-- | src/extension/internal/cairo-renderer.cpp | 16 | ||||
| -rw-r--r-- | src/extension/internal/cairo-renderer.h | 2 | ||||
| -rw-r--r-- | src/sp-hatch-path.cpp | 61 | ||||
| -rw-r--r-- | src/sp-hatch-path.h | 7 | ||||
| -rw-r--r-- | src/sp-hatch.cpp | 124 | ||||
| -rw-r--r-- | src/sp-hatch.h | 26 | ||||
| -rw-r--r-- | src/sp-item.cpp | 32 | ||||
| -rw-r--r-- | src/sp-paint-server.h | 2 |
12 files changed, 315 insertions, 74 deletions
diff --git a/src/display/drawing-pattern.cpp b/src/display/drawing-pattern.cpp index 77752bce3..cf6358278 100644 --- a/src/display/drawing-pattern.cpp +++ b/src/display/drawing-pattern.cpp @@ -159,7 +159,6 @@ DrawingPattern::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, } 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; diff --git a/src/display/drawing-pattern.h b/src/display/drawing-pattern.h index b614785dc..7483ba067 100644 --- a/src/display/drawing-pattern.h +++ b/src/display/drawing-pattern.h @@ -59,7 +59,6 @@ public: protected: virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset); - Geom::IntPoint _patternDimentions(); Geom::Affine *_pattern_to_user; Geom::Affine _overflow_initial_transform; diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index c09b8e9c8..7e61cdbbb 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -39,6 +39,7 @@ #include "sp-item.h" #include "sp-item-group.h" #include "style.h" +#include "sp-hatch.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" #include "sp-pattern.h" @@ -1130,6 +1131,82 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver } cairo_pattern_t* +CairoRenderContext::_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox) { + g_assert( SP_IS_HATCH(paintserver) ); + SPHatch *hatch = SP_HATCH(paintserver); + + g_assert(hatch->pitch() > 0); + + // create drawing and group + Inkscape::Drawing drawing; + unsigned dkey = SPItem::display_key_new(1); + + hatch->show(drawing, dkey, pbox); + + SPHatch::RenderInfo render_info = hatch->calculateRenderInfo(dkey); + Geom::Rect tile_rect = render_info.tile_rect; + + // Cairo requires an integer pattern surface width/height. + // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel. + // Multiply by SUBPIX_SCALE to allow for less than a pixel precision + const int subpix_scale = 10; + double surface_width = MAX(ceil(subpix_scale * tile_rect.width() - 0.5), 1); + double surface_height = MAX(ceil(subpix_scale * tile_rect.height() - 0.5), 1); + Geom::Affine drawing_scale = Geom::Scale(surface_width / tile_rect.width(), surface_height / tile_rect.height()); + Geom::Affine drawing_transform = Geom::Translate(-tile_rect.min()) * drawing_scale; + + Geom::Affine child_transform = render_info.child_transform; + child_transform *= drawing_transform; + + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + gdouble overflow_right_strip = 0.0; + int overflow_steps = 1; + Geom::Affine overflow_transform; + if (hatch->style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = hatch->bounds(); + overflow_right_strip = floor(bounds.max() / hatch->pitch()) * hatch->pitch(); + overflow_steps = ceil((overflow_right_strip - bounds.min()) / hatch->pitch()) + 1; + overflow_transform = Geom::Translate(hatch->pitch(), 0.0); + } + + CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height); + pattern_ctx->setTransform(child_transform); + pattern_ctx->transform(Geom::Translate(-overflow_right_strip, 0.0)); + pattern_ctx->pushState(); + + std::vector<SPHatchPath *> children; + hatch->hatchPaths(children); + + for (int i = 0; i < overflow_steps; i++) { + for (std::vector<SPHatchPath *>::iterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *path = *iter; + _renderer->renderHatchPath(pattern_ctx, *path, dkey); + } + pattern_ctx->transform(overflow_transform); + } + + pattern_ctx->popState(); + + // setup a cairo_pattern_t + cairo_surface_t *pattern_surface = pattern_ctx->getSurface(); + TEST(pattern_ctx->saveAsPng("hatch.png")); + cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface); + cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT); + + Geom::Affine pattern_transform; + pattern_transform = render_info.pattern_to_user_transform.inverse() * drawing_transform; + ink_cairo_pattern_set_matrix(result, pattern_transform); + + hatch->hide(dkey); + + delete pattern_ctx; + return result; +} + +cairo_pattern_t* CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha) { @@ -1182,8 +1259,9 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha); } } else if (SP_IS_PATTERN (paintserver)) { - pattern = _createPatternPainter(paintserver, pbox); + } else if (SP_IS_HATCH (paintserver)) { + pattern = _createHatchPainter(paintserver, pbox); } else { return NULL; } @@ -1249,26 +1327,29 @@ CairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect cons TRACE(("merged op=%f\n", alpha)); } - if (style->fill.isColor()) { - float rgb[3]; - sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + SPPaintServer const *paint_server = style->getFillPaintServer(); + if (paint_server && paint_server->isValid()) { - cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); - - } else if (!style->fill.set) { // unset fill is black - cairo_set_source_rgba(_cr, 0, 0, 0, alpha); - - } else { - g_assert( style->fill.isPaintserver() - || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) ); - - cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha); + g_assert(SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) + || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_FILL_SERVER(style))); + cairo_pattern_t *pattern = _createPatternForPaintServer(paint_server, pbox, alpha); if (pattern) { cairo_set_source(_cr, pattern); cairo_pattern_destroy(pattern); } + } else if (style->fill.colorSet) { + float rgb[3]; + sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + + cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); + + } else { // unset fill is black + g_assert(!style->fill.set + || (paint_server && !paint_server->isValid())); + + cairo_set_source_rgba(_cr, 0, 0, 0, alpha); } } @@ -1279,7 +1360,7 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p if (_state->merge_opacity) alpha *= _state->opacity; - if (style->stroke.isColor()) { + if (style->stroke.isColor() || (style->stroke.isPaintserver() && !style->getStrokePaintServer()->isValid())) { float rgb[3]; sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); @@ -1287,7 +1368,8 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p } else { g_assert( style->stroke.isPaintserver() || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) ); + || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_STROKE_SERVER(style))); cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha); diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h index 8d3e63775..59781a49c 100644 --- a/src/extension/internal/cairo-render-context.h +++ b/src/extension/internal/cairo-render-context.h @@ -201,6 +201,7 @@ protected: cairo_pattern_t *_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha); cairo_pattern_t *_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); + cairo_pattern_t *_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); unsigned int _showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke); diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 6fbc85c05..ec302bbfc 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -50,6 +50,7 @@ #include "sp-use.h" #include "sp-text.h" #include "sp-flowtext.h" +#include "sp-hatch-path.h" #include "sp-image.h" #include "sp-symbol.h" #include "sp-pattern.h" @@ -596,6 +597,21 @@ void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) ctx->popState(); } +void CairoRenderer::renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key) { + ctx->pushState(); + ctx->setStateForStyle(hatchPath.style); + ctx->transform(Geom::Translate(hatchPath.offset.computed, 0)); + + SPCurve *curve = hatchPath.calculateRenderCurve(key); + Geom::PathVector const & pathv =curve->get_pathvector(); + if (!pathv.empty()) { + ctx->renderPathVector(pathv, hatchPath.style, Geom::OptRect()); + } + + curve->unref(); + ctx->popState(); +} + bool CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base) { diff --git a/src/extension/internal/cairo-renderer.h b/src/extension/internal/cairo-renderer.h index cfef6bdea..abc0447d8 100644 --- a/src/extension/internal/cairo-renderer.h +++ b/src/extension/internal/cairo-renderer.h @@ -29,6 +29,7 @@ class SPClipPath; class SPMask; +class SPHatchPath; namespace Inkscape { namespace Extension { @@ -57,6 +58,7 @@ public: /** Traverses the object tree and invokes the render methods. */ void renderItem(CairoRenderContext *ctx, SPItem *item); + void renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key); }; // FIXME: this should be a static method of CairoRenderer diff --git a/src/sp-hatch-path.cpp b/src/sp-hatch-path.cpp index bdefd32d8..f7138fac2 100644 --- a/src/sp-hatch-path.cpp +++ b/src/sp-hatch-path.cpp @@ -25,6 +25,7 @@ #include "display/drawing-surface.h" #include "display/drawing.h" #include "display/drawing-shape.h" +#include "helper/geom.h" #include "attributes.h" #include "document-private.h" #include "uri.h" @@ -45,6 +46,7 @@ bool hatchRegistered = SPFactory::instance().registerObject("svg:hatchPath", cre SPHatchPath::SPHatchPath() : _curve(NULL) + , _continuous(false) { offset.unset(); } @@ -131,6 +133,8 @@ void SPHatchPath::update(SPCtx* ctx, unsigned int flags) { if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { if (this->style->stroke_width.unit == SP_CSS_UNIT_PERCENT) { + //TODO: Check specification + SPItemCtx *ictx = (SPItemCtx *) ctx; double const aw = 1.0 / ictx->i2vp.descrim(); this->style->stroke_width.computed = this->style->stroke_width.value * aw; @@ -155,9 +159,10 @@ bool SPHatchPath::isValid() const { return true; } -Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key) { +Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents) { Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing); _display.push_front(View(s, key)); + _display.front().extents = extents; _updateView(_display.front()); @@ -185,6 +190,36 @@ void SPHatchPath::setStripExtents(unsigned int key, Geom::OptInterval const &ext } } +Geom::Interval SPHatchPath::bounds() const { + Geom::OptRect bbox; + Geom::Interval result; + + Geom::Affine transform = Geom::Translate(offset.computed, 0); + if (!this->_curve) { + SPCurve test_curve; + test_curve.moveto(Geom::Point(0, 0)); + test_curve.moveto(Geom::Point(0, 1)); + bbox = bounds_exact_transformed(test_curve.get_pathvector(), transform); + } else { + bbox = bounds_exact_transformed(this->_curve->get_pathvector(), transform); + } + + gdouble stroke_width = style->stroke_width.computed; + result.setMin(bbox->left() - stroke_width / 2); + result.setMax(bbox->right() + stroke_width / 2); + return result; +} + +SPCurve *SPHatchPath::calculateRenderCurve(unsigned key) const { + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderCurve(*iter); + } + } + g_assert_not_reached(); + return NULL; +} + gdouble SPHatchPath::_repeatLength() const { if (!_curve) { return 0; @@ -198,10 +233,22 @@ gdouble SPHatchPath::_repeatLength() const { } void SPHatchPath::_updateView(View &view) { + SPCurve *calculated_curve = _calculateRenderCurve(view); + + Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); + view.arenaitem->setTransform(offset_transform); + style->fill.setNone(); + view.arenaitem->setStyle(this->style); + view.arenaitem->setPath(calculated_curve); + + calculated_curve->unref(); +} + +SPCurve *SPHatchPath::_calculateRenderCurve(View const &view) const { SPCurve *calculated_curve = new SPCurve; if (!view.extents) { - return; + return calculated_curve; } if (!_curve) { @@ -230,16 +277,10 @@ void SPHatchPath::_updateView(View &view) { segment->unref(); } } - - Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); - view.arenaitem->setTransform(offset_transform); - style->fill.setNone(); - view.arenaitem->setStyle(this->style); - view.arenaitem->setPath(calculated_curve); - - calculated_curve->unref(); + return calculated_curve; } + void SPHatchPath::_readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join) { if (!str) { return; diff --git a/src/sp-hatch-path.h b/src/sp-hatch-path.h index ff091c6f9..57b3a8237 100644 --- a/src/sp-hatch-path.h +++ b/src/sp-hatch-path.h @@ -40,10 +40,13 @@ public: bool isValid() const; - Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key); + Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents); void hide(unsigned int key); void setStripExtents(unsigned int key, Geom::OptInterval const &extents); + Geom::Interval bounds() const; + + SPCurve *calculateRenderCurve(unsigned key) const; protected: virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); @@ -61,10 +64,12 @@ private: unsigned int key; }; typedef std::list<SPHatchPath::View>::iterator ViewIterator; + typedef std::list<SPHatchPath::View>::const_iterator ConstViewIterator; std::list<View> _display; gdouble _repeatLength() const; void _updateView(View &view); + SPCurve *_calculateRenderCurve(View const &view) const; void _readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join); diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp index cde6c86cb..98cc596d7 100644 --- a/src/sp-hatch.cpp +++ b/src/sp-hatch.cpp @@ -77,6 +77,7 @@ void SPHatch::build(SPDocument* doc, Inkscape::XML::Node* repr) { this->readAttr("pitch"); this->readAttr("rotate"); this->readAttr("xlink:href"); + this->readAttr( "style" ); /* Register ourselves */ doc->addResource("hatch", this); @@ -89,7 +90,7 @@ void SPHatch::release() { } std::vector<SPHatchPath *> children; - _children(children); + hatchPaths(children); for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { for (ChildIterator child_iter = children.begin(); child_iter != children.end(); child_iter++) { @@ -117,10 +118,9 @@ void SPHatch::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) if (path_child) { for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { - Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key); + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key, extents); - Geom::OptInterval strip_extents = _calculateStripExtents(iter->bbox); - path_child->setStripExtents(iter->key, strip_extents); path_child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); if (ac) { iter->arenaitem->prependChild(ac); @@ -224,7 +224,12 @@ void SPHatch::set(unsigned int key, const gchar* value) { break; default: - SPPaintServer::set(key, value); + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPPaintServer::set(key, value); + } break; } } @@ -238,7 +243,7 @@ bool SPHatch::_hasHatchPatchChildren(SPHatch const *hatch) { return false; } -void SPHatch::_children(std::vector<SPHatchPath*>& l) { +void SPHatch::hatchPaths(std::vector<SPHatchPath*>& l) { SPHatch *src = chase_hrefs<SPHatch>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); if (src) { @@ -250,7 +255,7 @@ void SPHatch::_children(std::vector<SPHatchPath*>& l) { } } -void SPHatch::_children(std::vector<SPHatchPath const*>& l) const { +void SPHatch::hatchPaths(std::vector<SPHatchPath const*>& l) const { SPHatch const *src = chase_hrefs<SPHatch const>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); if (src) { @@ -275,7 +280,7 @@ void SPHatch::update(SPCtx* ctx, unsigned int flags) { flags &= SP_OBJECT_MODIFIED_CASCADE; std::vector<SPHatchPath *> children; - _children(children); + hatchPaths(children); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPHatchPath* child = *iter; @@ -308,7 +313,7 @@ void SPHatch::modified(unsigned int flags) { flags &= SP_OBJECT_MODIFIED_CASCADE; std::vector<SPHatchPath *> children; - _children(children); + hatchPaths(children); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPObject *child = *iter; @@ -337,29 +342,28 @@ void SPHatch::_onRefChanged(SPObject *old_ref, SPObject *ref) { if (!_hasHatchPatchChildren(this)) { SPHatch *old_shown = NULL; SPHatch *new_shown = NULL; - std::vector<SPHatchPath *> old_children; - std::vector<SPHatchPath *> new_children; + std::vector<SPHatchPath *> oldhatchPaths; + std::vector<SPHatchPath *> newhatchPaths; if (SP_IS_HATCH(old_ref)) { old_shown = SP_HATCH(old_ref)->rootHatch(); - old_shown->_children(old_children); + old_shown->hatchPaths(oldhatchPaths); } if (SP_IS_HATCH(ref)) { new_shown = SP_HATCH(ref)->rootHatch(); - new_shown->_children(new_children); + new_shown->hatchPaths(newhatchPaths); } if (old_shown != new_shown) { for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { Geom::OptInterval extents = _calculateStripExtents(iter->bbox); - for (ChildIterator child_iter = old_children.begin(); child_iter != old_children.end(); child_iter++) { + for (ChildIterator child_iter = oldhatchPaths.begin(); child_iter != oldhatchPaths.end(); child_iter++) { SPHatchPath *child = *child_iter; child->hide(iter->key); } - for (ChildIterator child_iter = new_children.begin(); child_iter != new_children.end(); child_iter++) { + for (ChildIterator child_iter = newhatchPaths.begin(); child_iter != newhatchPaths.end(); child_iter++) { SPHatchPath *child = *child_iter; - Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key); - child->setStripExtents(iter->key, extents); + Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key, extents); child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); if (cai) { iter->arenaitem->appendChild(cai); @@ -457,7 +461,7 @@ bool SPHatch::isValid() const { } std::vector<SPHatchPath const *> children; - _children(children); + hatchPaths(children); if (children.empty()) { return false; } @@ -471,17 +475,19 @@ bool SPHatch::isValid() const { return true; } -Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key) { +Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) { Inkscape::DrawingPattern *ai = new Inkscape::DrawingPattern(drawing); //TODO: set some debug flag to see DrawingPattern _display.push_front(View(ai, key)); + _display.front().bbox = bbox; std::vector<SPHatchPath *> children; - _children(children); + hatchPaths(children); + Geom::OptInterval extents = _calculateStripExtents(bbox); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPHatchPath *child = *iter; - Inkscape::DrawingItem *cai = child->show(drawing, key); + Inkscape::DrawingItem *cai = child->show(drawing, key, extents); if (cai) { ai->appendChild(cai); } @@ -495,7 +501,7 @@ Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int void SPHatch::hide(unsigned int key) { std::vector<SPHatchPath *> children; - _children(children); + hatchPaths(children); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPHatchPath *child = *iter; @@ -513,10 +519,56 @@ void SPHatch::hide(unsigned int key) { g_assert_not_reached(); } + +Geom::Interval SPHatch::bounds() const { + Geom::Interval result; + std::vector<SPHatchPath const *> children; + hatchPaths(children); + + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (result.extent() == 0) { + result = child->bounds(); + } else { + result |= child->bounds(); + } + } + return result; +} + +SPHatch::RenderInfo SPHatch::calculateRenderInfo(unsigned key) const { + RenderInfo info; + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderInfo(*iter); + } + } + g_assert_not_reached(); + return info; +} + void SPHatch::_updateView(View &view) { + RenderInfo info = _calculateRenderInfo(view); + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + + + view.arenaitem->setChildTransform(info.child_transform); + view.arenaitem->setPatternToUserTransform(info.pattern_to_user_transform); + view.arenaitem->setTileRect(info.tile_rect); + view.arenaitem->setStyle(this->style); + view.arenaitem->setOverflow(info.overflow_initial_transform, info.overflow_steps, + info.overflow_step_transform); +} + +SPHatch::RenderInfo SPHatch::_calculateRenderInfo(View const &view) const { + RenderInfo info; + Geom::OptInterval extents = _calculateStripExtents(view.bbox); if (!extents) { - return; + return info; } double tile_x = x(); @@ -545,14 +597,26 @@ void SPHatch::_updateView(View &view) { // Tile (hatch space) to user. Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); - view.arenaitem->setChildTransform(content2ps); - view.arenaitem->setPatternToUserTransform(ps2user); - view.arenaitem->setTileRect(hatch_tile); - view.arenaitem->setStyle(this->style); + info.child_transform = content2ps; + info.pattern_to_user_transform = ps2user; + info.tile_rect = hatch_tile; + + if (style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = this->bounds(); + gdouble pitch = this->pitch(); + gdouble overflow_right_strip = floor(bounds.max() / pitch) * pitch; + info.overflow_steps = ceil((overflow_right_strip - bounds.min()) / pitch) + 1; + info.overflow_step_transform = Geom::Translate(pitch, 0.0); + info.overflow_initial_transform = Geom::Translate(-overflow_right_strip, 0.0); + } else { + info.overflow_steps = 1; + } + + return info; } //calculates strip extents in content space -Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) { +Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) const { if (!bbox || (bbox->area() == 0)) { return Geom::OptInterval(); } @@ -590,9 +654,7 @@ cairo_pattern_t* SPHatch::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbo } void SPHatch::setBBox(unsigned int key, Geom::OptRect const &bbox) { - typedef std::list<View>::iterator ViewIter; - - for (ViewIter iter = _display.begin(); iter != _display.end(); iter++) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { if (iter->key == key) { iter->bbox = bbox; break; diff --git a/src/sp-hatch.h b/src/sp-hatch.h index 170f68a7a..dc6ee0add 100644 --- a/src/sp-hatch.h +++ b/src/sp-hatch.h @@ -48,6 +48,16 @@ public: UNITS_OBJECTBOUNDINGBOX }; + struct RenderInfo { + Geom::Affine child_transform; + Geom::Affine pattern_to_user_transform; + Geom::Rect tile_rect; + + int overflow_steps; + Geom::Affine overflow_step_transform; + Geom::Affine overflow_initial_transform; + }; + SPHatch(); virtual ~SPHatch(); @@ -64,12 +74,17 @@ public: Geom::Affine const &hatchTransform() const; SPHatch *rootHatch(); //TODO: const + void hatchPaths(std::vector<SPHatchPath*>& l); + void hatchPaths(std::vector<SPHatchPath const *>& l) const; + bool isValid() const; - Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key); + Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox); void hide(unsigned int key); virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); + RenderInfo calculateRenderInfo(unsigned key) const; + Geom::Interval bounds() const; void setBBox(unsigned int key, Geom::OptRect const &bbox); protected: @@ -89,17 +104,18 @@ private: Geom::OptRect bbox; unsigned int key; }; + typedef std::vector<SPHatchPath *>::iterator ChildIterator; typedef std::vector<SPHatchPath const *>::const_iterator ConstChildIterator; typedef std::list<View>::iterator ViewIterator; - void _updateView(View &view); + typedef std::list<View>::const_iterator ConstViewIterator; static bool _hasHatchPatchChildren(SPHatch const* hatch); - void _children(std::vector<SPHatchPath*>& l); - void _children(std::vector<SPHatchPath const *>& l) const; + void _updateView(View &view); + RenderInfo _calculateRenderInfo(View const &view) const; + Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox) const; - Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox); /** Gets called when the hatch is reattached to another <hatch> diff --git a/src/sp-item.cpp b/src/sp-item.cpp index da6e6ea58..f6de165a6 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -615,9 +615,8 @@ void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { v->arenaitem->setKey(SPItem::display_key_new(3)); } Inkscape::DrawingPattern *pi = new_fill_ps->show( - v->arenaitem->drawing(), v->arenaitem->key()); + v->arenaitem->drawing(), v->arenaitem->key(), bbox); v->arenaitem->setFillPattern(pi); - new_fill_ps->setBBox(v->arenaitem->key(), bbox); if (pi) { new_fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } @@ -625,8 +624,29 @@ void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { } } -void SPItem::stroke_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item) { +void SPItem::stroke_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { + SPPaintServer *old_stroke_ps = SP_PAINT_SERVER(old_ps); + if (old_stroke_ps) { + for (SPItemView *v =item->display; v != NULL; v = v->next) { + old_stroke_ps->hide(v->arenaitem->key()); + } + } + SPPaintServer *new_stroke_ps = SP_PAINT_SERVER(ps); + if (new_stroke_ps) { + Geom::OptRect bbox = item->geometricBounds(); + for (SPItemView *v = item->display; v != NULL; v = v->next) { + if (!v->arenaitem->key()) { + v->arenaitem->setKey(SPItem::display_key_new(3)); + } + Inkscape::DrawingPattern *pi = new_stroke_ps->show( + v->arenaitem->drawing(), v->arenaitem->key(), bbox); + v->arenaitem->setStrokePattern(pi); + if (pi) { + new_stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + } } void SPItem::update(SPCtx* /*ctx*/, guint flags) { @@ -1142,9 +1162,8 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned } int fill_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key); + Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key, item_bbox); ai->setFillPattern(ap); - fill_ps->setBBox(fill_key, item_bbox); if (ap) { fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } @@ -1156,9 +1175,8 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned } int stroke_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key); + Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key, item_bbox); ai->setStrokePattern(ap); - stroke_ps->setBBox(stroke_key, item_bbox); if (ap) { stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index 91d292424..6c88199b9 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -43,7 +43,7 @@ public: //on demand by pattern_new method. It is used for gradients. The other one is to add elements //representing PaintServer in NR tree. It is used by hatches and patterns. //Either pattern new or all three methods show, hide, setBBox need to be implemented - virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key) {return NULL;} + virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) {return NULL;} virtual void hide(unsigned int key) {}; virtual void setBBox(unsigned int key, Geom::OptRect const &bbox) {}; |
