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);
}
Exemple #3
0
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();
        }
    }
}