Ejemplo n.º 1
0
void
Drawing::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags)
{
    if (_root) {
        _root->render(ct, area, flags);
    }

    if (colorMode() == COLORMODE_GRAYSCALE) {
        // apply grayscale filter on top of everything
        cairo_surface_t *input = ct.rawTarget();
        cairo_surface_t *out = ink_cairo_surface_create_identical(input);
        ink_cairo_surface_filter(input, out, _grayscale_colormatrix);
        Geom::Point origin = ct.targetLogicalBounds().min();
        ct.setSource(out, origin[Geom::X], origin[Geom::Y]);
        ct.setOperator(CAIRO_OPERATOR_SOURCE);
        ct.paint();
        ct.setOperator(CAIRO_OPERATOR_OVER);
    
        cairo_surface_destroy(out);
    }
}
Ejemplo n.º 2
0
/**
 * Rasterize items.
 * This method submits the drawing opeartions required to draw this item
 * to the supplied DrawingContext, restricting drawing the specified area.
 *
 * This method does some common tasks and calls the item-specific rendering
 * function, _renderItem(), to render e.g. paths or bitmaps.
 *
 * @param flags Rendering options. This deals mainly with cache control.
 */
unsigned
DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
{
    bool outline = _drawing.outline();
    bool render_filters = _drawing.renderFilters();

    // stop_at is handled in DrawingGroup, but this check is required to handle the case
    // where a filtered item with background-accessing filter has enable-background: new
    if (this == stop_at) return RENDER_STOP;

    // If we are invisible, return immediately
    if (!_visible) return RENDER_OK;
    if (_ctm.isSingular(1e-18)) return RENDER_OK;

    // TODO convert outline rendering to a separate virtual function
    if (outline) {
        _renderOutline(dc, area, flags);
        return RENDER_OK;
    }

    // carea is the area to paint
    Geom::OptIntRect carea = Geom::intersect(area, _drawbox);
    if (!carea) return RENDER_OK;

    if (_antialias) {
        cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_DEFAULT);
    } else {
        cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE);
    }

    // render from cache if possible
    if (_cached) {
        if (_cache) {
            _cache->prepare();
            set_cairo_blend_operator( dc, _mix_blend_mode );

            _cache->paintFromCache(dc, carea);
            if (!carea) return RENDER_OK;
        } else {
            // There is no cache. This could be because caching of this item
            // was just turned on after the last update phase, or because
            // we were previously outside of the canvas.
            Geom::OptIntRect cl = _drawing.cacheLimit();
            cl.intersectWith(_drawbox);
            if (cl) {
                _cache = new DrawingCache(*cl);
            }
        }
    } else {
        // if our caching was turned off after the last update, it was already
        // deleted in setCached()
    }

    // determine whether this shape needs intermediate rendering.
    bool needs_intermediate_rendering = false;
    bool &nir = needs_intermediate_rendering;
    bool needs_opacity = (_opacity < 0.995);

    // this item needs an intermediate rendering if:
    nir |= (_clip != NULL); // 1. it has a clipping path
    nir |= (_mask != NULL); // 2. it has a mask
    nir |= (_filter != NULL && render_filters); // 3. it has a filter
    nir |= needs_opacity; // 4. it is non-opaque
    nir |= (_cache != NULL); // 5. it is cached
    nir |= (_mix_blend_mode != SP_CSS_BLEND_NORMAL); // 6. Blend mode not normal
    nir |= (_isolation == SP_CSS_ISOLATION_ISOLATE); // 7. Explicit isolatiom

    /* How the rendering is done.
     *
     * Clipping, masking and opacity are done by rendering them to a surface
     * and then compositing the object's rendering onto it with the IN operator.
     * The object itself is rendered to a group.
     *
     * Opacity is done by rendering the clipping path with an alpha
     * value corresponding to the opacity. If there is no clipping path,
     * the entire intermediate surface is painted with alpha corresponding
     * to the opacity value.
     */

    // Short-circuit the simple case.
    // We also use this path for filter background rendering, because masking, clipping,
    // filters and opacity do not apply when rendering the ancestors of the filtered
    // element
    if ((flags & RENDER_FILTER_BACKGROUND) || !needs_intermediate_rendering) {
        return _renderItem(dc, *carea, flags & ~RENDER_FILTER_BACKGROUND, stop_at);
    }

    // iarea is the bounding box for intermediate rendering
    // Note 1: Pixels inside iarea but outside carea are invalid
    //         (incomplete filter dependence region).
    // Note 2: We only need to render carea of clip and mask, but
    //         iarea of the object.
    Geom::OptIntRect iarea = carea;
    // expand carea to contain the dependent area of filters.
    if (_filter && render_filters) {
        _filter->area_enlarge(*iarea, this);
        iarea.intersectWith(_drawbox);
    }

    DrawingSurface intermediate(*iarea);
    DrawingContext ict(intermediate);
    unsigned render_result = RENDER_OK;

    // 1. Render clipping path with alpha = opacity.
    ict.setSource(0,0,0,_opacity);
    // Since clip can be combined with opacity, the result could be incorrect
    // for overlapping clip children. To fix this we use the SOURCE operator
    // instead of the default OVER.
    ict.setOperator(CAIRO_OPERATOR_SOURCE);
    ict.paint();
    if (_clip) {
        ict.pushGroup();
        _clip->clip(ict, *carea); // fixme: carea or area?
        ict.popGroupToSource();
        ict.setOperator(CAIRO_OPERATOR_IN);
        ict.paint();
    }
    ict.setOperator(CAIRO_OPERATOR_OVER); // reset back to default

    // 2. Render the mask if present and compose it with the clipping path + opacity.
    if (_mask) {
        ict.pushGroup();
        _mask->render(ict, *carea, flags);

        cairo_surface_t *mask_s = ict.rawTarget();
        // Convert mask's luminance to alpha
        ink_cairo_surface_filter(mask_s, mask_s, MaskLuminanceToAlpha());
        ict.popGroupToSource();
        ict.setOperator(CAIRO_OPERATOR_IN);
        ict.paint();
        ict.setOperator(CAIRO_OPERATOR_OVER);
    }

    // 3. Render object itself
    ict.pushGroup();
    render_result = _renderItem(ict, *iarea, flags, stop_at);

    // 4. Apply filter.
    if (_filter && render_filters) {
        bool rendered = false;
        if (_filter->uses_background() && _background_accumulate) {
            DrawingItem *bg_root = this;
            for (; bg_root; bg_root = bg_root->_parent) {
                if (bg_root->_background_new) break;
            }
            if (bg_root) {
                DrawingSurface bg(*iarea);
                DrawingContext bgdc(bg);
                bg_root->render(bgdc, *iarea, flags | RENDER_FILTER_BACKGROUND, this);
                _filter->render(this, ict, &bgdc);
                rendered = true;
            }
        }
        if (!rendered) {
            _filter->render(this, ict, NULL);
        }
        // Note that because the object was rendered to a group,
        // the internals of the filter need to use cairo_get_group_target()
        // instead of cairo_get_target().
    }

    // 5. Render object inside the composited mask + clip
    ict.popGroupToSource();
    ict.setOperator(CAIRO_OPERATOR_IN);
    ict.paint();

    // 6. Paint the completed rendering onto the base context (or into cache)
    if (_cached && _cache) {
        DrawingContext cachect(*_cache);
        cachect.rectangle(*carea);
        cachect.setOperator(CAIRO_OPERATOR_SOURCE);
        cachect.setSource(&intermediate);
        cachect.fill();
        _cache->markClean(*carea);
    }
    dc.rectangle(*carea);
    dc.setSource(&intermediate);
    set_cairo_blend_operator( dc, _mix_blend_mode );
    dc.fill();
    dc.setSource(0,0,0,0);
    // the call above is to clear a ref on the intermediate surface held by dc

    return render_result;
}