Commit 949858c7 authored by Ivan Molodetskikh's avatar Ivan Molodetskikh

Implement BackgroundImage and BackgroundAlpha

parent 4ca90fa9
......@@ -560,36 +560,36 @@ surface_get_alpha (cairo_surface_t *source,
return surface;
}
static cairo_surface_t *
rsvg_compile_bg (RsvgDrawingCtx * ctx)
{
cairo_surface_t *surface;
cairo_t *cr;
double x, y;
GList *i;
surface = _rsvg_image_surface_new (rsvg_drawing_ctx_get_width (ctx),
rsvg_drawing_ctx_get_height (ctx));
if (surface == NULL)
return NULL;
cr = cairo_create (surface);
rsvg_drawing_ctx_get_raw_offset (ctx, &x, &y);
for (i = g_list_last (ctx->cr_stack); i != NULL; i = g_list_previous (i)) {
cairo_t *draw = i->data;
gboolean nest = rsvg_drawing_ctx_is_cairo_context_nested (ctx, draw);
cairo_set_source_surface (cr, cairo_get_target (draw),
nest ? 0 : -x,
nest ? 0 : -y);
cairo_paint (cr);
}
cairo_destroy (cr);
return surface;
}
// static cairo_surface_t *
// rsvg_compile_bg (RsvgDrawingCtx * ctx)
// {
// cairo_surface_t *surface;
// cairo_t *cr;
// double x, y;
// GList *i;
//
// surface = _rsvg_image_surface_new (rsvg_drawing_ctx_get_width (ctx),
// rsvg_drawing_ctx_get_height (ctx));
// if (surface == NULL)
// return NULL;
//
// cr = cairo_create (surface);
//
// rsvg_drawing_ctx_get_raw_offset (ctx, &x, &y);
//
// for (i = g_list_last (ctx->cr_stack); i != NULL; i = g_list_previous (i)) {
// cairo_t *draw = i->data;
// gboolean nest = rsvg_drawing_ctx_is_cairo_context_nested (ctx, draw);
// cairo_set_source_surface (cr, cairo_get_target (draw),
// nest ? 0 : -x,
// nest ? 0 : -y);
// cairo_paint (cr);
// }
//
// cairo_destroy (cr);
//
// return surface;
// }
/**
* rsvg_filter_get_bg:
......
......@@ -456,6 +456,12 @@ rsvg_drawing_ctx_get_dpi (RsvgDrawingCtx *ctx, double *out_dpi_x, double *out_dp
*out_dpi_y = ctx->dpi_y;
}
GList *
rsvg_drawing_ctx_get_cr_stack (RsvgDrawingCtx *ctx)
{
return ctx->cr_stack;
}
gboolean
rsvg_drawing_ctx_is_testing (RsvgDrawingCtx *ctx)
{
......
......@@ -142,6 +142,9 @@ void rsvg_drawing_ctx_draw_node_on_surface (RsvgDrawingCtx *ctx,
G_GNUC_INTERNAL
gboolean rsvg_drawing_ctx_is_testing (RsvgDrawingCtx *ctx);
G_GNUC_INTERNAL
GList *rsvg_drawing_ctx_get_cr_stack (RsvgDrawingCtx *ctx);
G_END_DECLS
#endif /*RSVG_DRAWING_CTX_H */
......@@ -68,8 +68,21 @@ extern "C" {
out_y: *mut f64,
);
fn rsvg_drawing_ctx_get_raw_offset(
draw_ctx: *const RsvgDrawingCtx,
out_x: *mut f64,
out_y: *mut f64,
);
fn rsvg_drawing_ctx_get_bbox(draw_ctx: *const RsvgDrawingCtx) -> *mut RsvgBbox;
fn rsvg_drawing_ctx_get_cr_stack(draw_ctx: *mut RsvgDrawingCtx) -> *mut glib_sys::GList;
fn rsvg_drawing_ctx_is_cairo_context_nested(
draw_ctx: *const RsvgDrawingCtx,
cr: *mut cairo_sys::cairo_t,
) -> glib_sys::gboolean;
fn rsvg_drawing_ctx_is_testing(draw_ctx: *const RsvgDrawingCtx) -> glib_sys::gboolean;
}
......@@ -182,6 +195,17 @@ pub fn get_offset(draw_ctx: *const RsvgDrawingCtx) -> (f64, f64) {
(w, h)
}
pub fn get_raw_offset(draw_ctx: *const RsvgDrawingCtx) -> (f64, f64) {
let mut w: f64 = 0.0;
let mut h: f64 = 0.0;
unsafe {
rsvg_drawing_ctx_get_raw_offset(draw_ctx, &mut w, &mut h);
}
(w, h)
}
// remove this binding once pangocairo-rs has ContextExt::set_resolution()
fn set_resolution(context: &pango::Context, dpi: f64) {
unsafe {
......@@ -244,6 +268,27 @@ pub fn get_bbox<'a>(draw_ctx: *const RsvgDrawingCtx) -> &'a BoundingBox {
get_bbox_mut(draw_ctx)
}
pub fn get_cr_stack(draw_ctx: *mut RsvgDrawingCtx) -> Vec<cairo::Context> {
let mut res = Vec::new();
unsafe {
let list = rsvg_drawing_ctx_get_cr_stack(draw_ctx);
let mut list = glib_sys::g_list_first(mut_override(list));
while !list.is_null() {
res.push(from_glib_none((*list).data as *mut cairo_sys::cairo_t));
list = (*list).next;
}
}
res
}
pub fn is_cairo_context_nested(draw_ctx: *const RsvgDrawingCtx, cr: &cairo::Context) -> bool {
let cr = cr.to_glib_none();
from_glib(unsafe { rsvg_drawing_ctx_is_cairo_context_nested(draw_ctx, cr.0) })
}
extern "C" {
fn rsvg_drawing_ctx_get_width(draw_ctx: *const RsvgDrawingCtx) -> f64;
fn rsvg_drawing_ctx_get_height(draw_ctx: *const RsvgDrawingCtx) -> f64;
......
use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::ffi::CStr;
use std::ptr;
use cairo::prelude::SurfaceExt;
use cairo::{self, MatrixTrait};
......@@ -71,6 +73,8 @@ pub struct FilterContext {
last_result: Option<FilterOutput>,
/// Surfaces of the previous filter primitives by name.
previous_results: HashMap<String, FilterOutput>,
/// The background surface. Computed lazily.
background_surface: UnsafeCell<Option<Result<cairo::ImageSurface, cairo::Status>>>,
affine: cairo::Matrix,
paffine: cairo::Matrix,
......@@ -157,6 +161,7 @@ impl FilterContext {
source_surface,
last_result: None,
previous_results: HashMap::new(),
background_surface: UnsafeCell::new(None),
affine,
paffine,
drawing_ctx: draw_ctx,
......@@ -196,10 +201,60 @@ impl FilterContext {
extract_alpha(self.source_graphic(), bounds)
}
/// Computes and returns the background image snapshot.
fn compute_background_image(&self) -> Result<cairo::ImageSurface, cairo::Status> {
let surface = cairo::ImageSurface::create(
cairo::Format::ARgb32,
self.source_surface.get_width(),
self.source_surface.get_height(),
)?;
let (x, y) = drawing_ctx::get_raw_offset(self.drawing_ctx);
let stack = drawing_ctx::get_cr_stack(self.drawing_ctx);
let cr = cairo::Context::new(&surface);
for draw in stack.into_iter().rev() {
let nested = drawing_ctx::is_cairo_context_nested(self.drawing_ctx, &draw);
cr.set_source_surface(
&draw.get_target(),
if nested { 0f64 } else { -x },
if nested { 0f64 } else { -y },
);
cr.paint();
}
Ok(surface)
}
/// Returns the surface corresponding to the background image snapshot.
pub fn background_image(&self) -> Result<&cairo::ImageSurface, cairo::Status> {
{
// At this point either no, or only immutable references to background_surface exist, so
// it's ok to make an immutable reference.
let bg = unsafe { &*self.background_surface.get() };
// If background_surface was already computed, return the immutable reference. It will
// get bound to the &self lifetime by the function return type.
if let Some(result) = bg.as_ref() {
return result.as_ref().map_err(|&s| s);
}
}
// If we got here, then background_surface hasn't been computed yet. This means there are
// no references to it and we can create a mutable reference.
let bg = unsafe { &mut *self.background_surface.get() };
*bg = Some(self.compute_background_image());
// Return the only existing reference as immutable.
bg.as_ref().unwrap().as_ref().map_err(|&s| s)
}
/// Returns the surface containing the background image snapshot alpha.
#[inline]
pub fn background_image(&self) -> &cairo::ImageSurface {
unimplemented!()
pub fn background_alpha(&self, bounds: IRect) -> Result<cairo::ImageSurface, cairo::Status> {
self.background_image()
.and_then(|surface| extract_alpha(surface, bounds))
}
/// Returns the output of the filter primitive by its result name.
......@@ -344,8 +399,16 @@ impl FilterContext {
.ok()
.map(|surface| FilterOutput { surface, bounds })
}
Input::BackgroundImage => unimplemented!(),
Input::BackgroundAlpha => unimplemented!(),
Input::BackgroundImage => self.background_image().ok().map(|surface| FilterOutput {
surface: surface.clone(),
bounds: self.compute_bounds(None, None, None, None),
}),
Input::BackgroundAlpha => {
let bounds = self.compute_bounds(None, None, None, None);
self.background_alpha(bounds)
.ok()
.map(|surface| FilterOutput { surface, bounds })
}
// TODO
Input::FillPaint => None,
......@@ -421,7 +484,10 @@ pub unsafe extern "C" fn rsvg_filter_context_get_bg_surface(
) -> *mut cairo_surface_t {
assert!(!ctx.is_null());
(*ctx).background_image().to_glib_none().0
(*ctx)
.background_image()
.map(|surface| surface.to_glib_none().0)
.unwrap_or_else(|_| ptr::null_mut())
}
#[no_mangle]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment