unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext const &ctx, unsigned /*flags*/, unsigned /*reset*/) { DrawingText *ggroup = dynamic_cast<DrawingText *>(_parent); if (!ggroup) { throw InvalidItemException(); } if (!_font || !ggroup->_style) { return STATE_ALL; } _pick_bbox = Geom::IntRect(); _bbox = Geom::IntRect(); Geom::OptRect b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm); if (b && (ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE)) { float width, scale; scale = ctx.ctm.descrim(); if (_transform) { scale /= _transform->descrim(); // FIXME temporary hack } width = MAX(0.125, ggroup->_nrstyle.stroke_width * scale); if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true b->expandBy(0.5 * width); } // save bbox without miters for picking _pick_bbox = b->roundOutwards(); float miterMax = width * ggroup->_nrstyle.miter_limit; if ( miterMax > 0.01 ) { // grunt mode. we should compute the various miters instead // (one for each point on the curve) b->expandBy(miterMax); } _bbox = b->roundOutwards(); } else if (b) { _bbox = b->roundOutwards(); _pick_bbox = *_bbox; } return STATE_ALL; }
/** * Update derived data before operations. * The purpose of this call is to recompute internal data which depends * on the attributes of the object, but is not directly settable by the user. * Precomputing this data speeds up later rendering, because some items * can be omitted. * * Currently this method handles updating the visual and geometric bounding boxes * in pixels, storing the total transformation from item space to the screen * and cache invalidation. * * @param area Area to which the update should be restricted. Only takes effect * if the bounding box is known. * @param ctx A structure to store cascading state. * @param flags Which internal data should be recomputed. This can be any combination * of StateFlags. * @param reset State fields that should be reset before processing them. This is * a means to force a recomputation of internal data even if the item * considers it up to date. Mainly for internal use, such as * propagating bounding box recomputation to children when the item's * transform changes. */ void DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) { bool render_filters = _drawing.renderFilters(); bool outline = _drawing.outline(); // Set reset flags according to propagation status reset |= _propagate_state; _propagate_state = 0; _state &= ~reset; // reset state of this item if ((~_state & flags) == 0) return; // nothing to do // TODO this might be wrong if (_state & STATE_BBOX) { // we have up-to-date bbox if (!area.intersects(outline ? _bbox : _drawbox)) return; } // compute which elements need an update unsigned to_update = _state ^ flags; // this needs to be called before we recurse into children if (to_update & STATE_BACKGROUND) { _background_accumulate = _background_new; if (_child_type == CHILD_NORMAL && _parent->_background_accumulate) _background_accumulate = true; } UpdateContext child_ctx(ctx); if (_transform) { child_ctx.ctm = *_transform * ctx.ctm; } /* Remember the transformation matrix */ Geom::Affine ctm_change = _ctm.inverse() * child_ctx.ctm; _ctm = child_ctx.ctm; // update _bbox and call this function for children _state = _updateItem(area, child_ctx, flags, reset); if (to_update & STATE_BBOX) { // compute drawbox if (_filter && render_filters) { Geom::OptRect enlarged = _filter->filter_effect_area(_item_bbox); if (enlarged) { *enlarged *= ctm(); _drawbox = enlarged->roundOutwards(); } else { _drawbox = Geom::OptIntRect(); } } else { _drawbox = _bbox; } // Clipping if (_clip) { _clip->update(area, child_ctx, flags, reset); if (outline) { _bbox.unionWith(_clip->_bbox); } else { _drawbox.intersectWith(_clip->_bbox); } } // Masking if (_mask) { _mask->update(area, child_ctx, flags, reset); if (outline) { _bbox.unionWith(_mask->_bbox); } else { // for masking, we need full drawbox of mask _drawbox.intersectWith(_mask->_drawbox); } } } if (to_update & STATE_CACHE) { // Update cache score for this item if (_has_cache_iterator) { // remove old score information _drawing._candidate_items.erase(_cache_iterator); _has_cache_iterator = false; } double score = _cacheScore(); if (score >= _drawing._cache_score_threshold) { CacheRecord cr; cr.score = score; // if _cacheRect() is empty, a negative score will be returned from _cacheScore(), // so this will not execute (cache score threshold must be positive) cr.cache_size = _cacheRect()->area() * 4; cr.item = this; _drawing._candidate_items.push_front(cr); _cache_iterator = _drawing._candidate_items.begin(); _has_cache_iterator = true; } /* Update cache if enabled. * General note: here we only tell the cache how it has to transform * during the render phase. The transformation is deferred because * after the update the item can have its caching turned off, * e.g. because its filter was removed. This way we avoid tempoerarily * using more memory than the cache budget */ if (_cache) { Geom::OptIntRect cl = _cacheRect(); if (_visible && cl) { // never create cache for invisible items // this takes care of invalidation on transform _cache->scheduleTransform(*cl, ctm_change); } else { // Destroy cache for this item - outside of canvas or invisible. // The opposite transition (invisible -> visible or object // entering the canvas) is handled during the render phase delete _cache; _cache = NULL; } } } if (to_update & STATE_RENDER) { // now that we know drawbox, dirty the corresponding rect on canvas // unless filtered, groups do not need to render by themselves, only their members if (_fill_pattern) { _fill_pattern->update(area, child_ctx, flags, reset); } if (_stroke_pattern) { _stroke_pattern->update(area, child_ctx, flags, reset); } if (!is_drawing_group(this) || (_filter && render_filters)) { _markForRendering(); } } }