cairo_rectangle_int_t DrawingCache::_convertRect(Geom::IntRect const &area) { cairo_rectangle_int_t ret; ret.x = area.left(); ret.y = area.top(); ret.width = area.width(); ret.height = area.height(); return ret; }
static void sp_canvas_arena_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area) { SPCanvasArena *arena = SP_CANVAS_ARENA(item); // make the cache limit larger than screen to facilitate smooth scrolling Geom::IntRect expanded = new_area; Geom::IntPoint expansion(new_area.width()/2, new_area.height()/2); expanded.expandBy(expansion); arena->drawing.setCacheLimit(expanded); }
static int sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, void *data) { struct SPEBP *ebp = (struct SPEBP *) data; if (ebp->status) { if (!ebp->status((float) row / ebp->height, ebp->data)) return 0; } num_rows = MIN(num_rows, static_cast<int>(ebp->sheight)); num_rows = MIN(num_rows, static_cast<int>(ebp->height - row)); /* Set area of interest */ // bbox is now set to the entire image to prevent discontinuities // in the image when blur is used (the borders may still be a bit // off, but that's less noticeable). Geom::IntRect bbox = Geom::IntRect::from_xywh(0, row, ebp->width, num_rows); /* Update to renderable state */ ebp->drawing->update(bbox); int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, ebp->width); unsigned char *px = g_new(guchar, num_rows * stride); cairo_surface_t *s = cairo_image_surface_create_for_data( px, CAIRO_FORMAT_ARGB32, ebp->width, num_rows, stride); Inkscape::DrawingContext ct(s, bbox.min()); ct.setSource(ebp->background); ct.setOperator(CAIRO_OPERATOR_SOURCE); ct.paint(); ct.setOperator(CAIRO_OPERATOR_OVER); /* Render */ ebp->drawing->render(ct, bbox); cairo_surface_destroy(s); *to_free = px; // PNG stores data as unpremultiplied big-endian RGBA, which means // it's identical to the GdkPixbuf format. convert_pixels_argb32_to_pixbuf(px, ebp->width, num_rows, stride); for (int r = 0; r < num_rows; r++) { rows[r] = px + r * stride; } return num_rows; }
/** * Rasterize the clipping path. * This method submits drawing operations required to draw a basic filled shape * of the item to the supplied drawing context. Rendering is limited to the * given area. The rendering of the clipped object is composited into * the result of this call using the IN operator. See the implementation * of render() for details. */ void DrawingItem::clip(Inkscape::DrawingContext &dc, Geom::IntRect const &area) { // don't bother if the object does not implement clipping (e.g. DrawingImage) if (!_canClip()) return; if (!_visible) return; if (!area.intersects(_bbox)) return; dc.setSource(0,0,0,1); dc.pushGroup(); // rasterize the clipping path _clipItem(dc, area); if (_clip) { // The item used as the clipping path itself has a clipping path. // Render this item's clipping path onto a temporary surface, then composite it // with the item using the IN operator dc.pushGroup(); _clip->clip(dc, area); dc.popGroupToSource(); dc.setOperator(CAIRO_OPERATOR_IN); dc.paint(); } dc.popGroupToSource(); dc.setOperator(CAIRO_OPERATOR_OVER); dc.paint(); dc.setSource(0,0,0,0); }
void FilterTile::area_enlarge(Geom::IntRect &area, Geom::Affine const &trans) { // We need to enlarge enough to get tile source... we don't the area of the source tile in this // function so we guess. This is VERY inefficient. Geom::Point enlarge(200, 200); enlarge *= trans; area.expandBy( enlarge[Geom::X] < 100 ? 100: enlarge[Geom::X] ); }
void sp_canvas_arena_render_surface (SPCanvasArena *ca, cairo_surface_t *surface, Geom::IntRect const &r) { g_return_if_fail (ca != NULL); g_return_if_fail (SP_IS_CANVAS_ARENA (ca)); Inkscape::DrawingContext dc(surface, r.min()); ca->drawing.update(Geom::IntRect::infinite(), ca->ctx); ca->drawing.render(dc, r); }
static void sp_canvas_arena_request_render(SPCanvasArena *ca, Geom::IntRect const &area) { SPCanvas *canvas = SP_CANVAS_ITEM(ca)->canvas; canvas->requestRedraw(area.left(), area.top(), area.right(), area.bottom()); }
/** * Creates a surface with the given physical extents. * When a drawing context is created for this surface, its pixels * will cover the area under the given rectangle. */ DrawingSurface::DrawingSurface(Geom::IntRect const &area) : _surface(NULL) , _origin(area.min()) , _scale(1, 1) , _pixels(area.dimensions()) {}
/** * 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(); } } }