Commit 0d6e1b81 authored by Ivan Molodetskikh's avatar Ivan Molodetskikh

Port feMerge to Rust

parent 7c92faea
......@@ -34,7 +34,6 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES = \
librsvg/filters/image.c \
librsvg/filters/light_source.c \
librsvg/filters/light_source.h \
librsvg/filters/merge.c \
librsvg/filters/specular_lighting.c \
librsvg/filters/tile.c \
librsvg/filters/turbulence.c \
......@@ -88,6 +87,7 @@ RUST_SRC = \
rsvg_internals/src/filters/context.rs \
rsvg_internals/src/filters/error.rs \
rsvg_internals/src/filters/ffi.rs \
rsvg_internals/src/filters/merge.rs \
rsvg_internals/src/filters/mod.rs \
rsvg_internals/src/filters/node.rs \
rsvg_internals/src/filters/input.rs \
......
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 sts=4 expandtab: */
/*
rsvg-filter.c: Provides filters
Copyright (C) 2004 Caleb Moore
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Author: Caleb Moore <c.moore@student.unsw.edu.au>
*/
#include "config.h"
#include "../rsvg-private.h"
#include "../rsvg-styles.h"
#include "../rsvg-css.h"
#include "../rsvg-drawing-ctx.h"
#include "common.h"
typedef struct _RsvgFilterPrimitiveMerge RsvgFilterPrimitiveMerge;
struct _RsvgFilterPrimitiveMerge {
RsvgFilterPrimitive super;
};
static void
merge_render_child(RsvgNode *node,
cairo_surface_t *output,
RsvgIRect boundarys,
RsvgFilterContext *ctx)
{
RsvgFilterPrimitive *fp;
cairo_surface_t *in;
if (rsvg_node_get_type (node) != RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE_NODE)
return;
fp = rsvg_rust_cnode_get_impl (node);
in = rsvg_filter_get_in (fp->in, ctx);
if (in == NULL)
return;
rsvg_alpha_blt (in,
boundarys.x0,
boundarys.y0,
boundarys.x1 - boundarys.x0,
boundarys.y1 - boundarys.y0,
output,
boundarys.x0,
boundarys.y0);
cairo_surface_destroy (in);
}
static void
rsvg_filter_primitive_merge_render (RsvgNode *node, RsvgComputedValues *values, RsvgFilterPrimitive *primitive, RsvgFilterContext *ctx)
{
RsvgNodeChildrenIter *iter;
RsvgNode *child;
RsvgIRect boundarys;
cairo_surface_t *output;
boundarys = rsvg_filter_primitive_get_bounds (primitive, ctx);
output = _rsvg_image_surface_new (rsvg_filter_context_get_width (ctx), rsvg_filter_context_get_height (ctx));
if (output == NULL) {
return;
}
iter = rsvg_node_children_iter_begin (node);
while (rsvg_node_children_iter_next (iter, &child)) {
merge_render_child (child, output, boundarys, ctx);
child = rsvg_node_unref (child);
}
rsvg_node_children_iter_end (iter);
RsvgFilterPrimitiveOutput op;
op.surface = output;
op.bounds = boundarys;
rsvg_filter_store_output(primitive->result, op, ctx);
/* rsvg_filter_store_result (primitive->result, output, ctx); */
cairo_surface_destroy (output);
}
static void
rsvg_filter_primitive_merge_set_atts (RsvgNode *node, gpointer impl, RsvgHandle *handle, RsvgPropertyBag atts)
{
RsvgFilterPrimitiveMerge *filter = impl;
RsvgPropertyBagIter *iter;
const char *key;
RsvgAttribute attr;
const char *value;
filter_primitive_set_x_y_width_height_atts ((RsvgFilterPrimitive *) filter, atts);
iter = rsvg_property_bag_iter_begin (atts);
while (rsvg_property_bag_iter_next (iter, &key, &attr, &value)) {
switch (attr) {
case RSVG_ATTRIBUTE_RESULT:
g_string_assign (filter->super.result, value);
break;
default:
break;
}
}
rsvg_property_bag_iter_end (iter);
}
RsvgNode *
rsvg_new_filter_primitive_merge (const char *element_name, RsvgNode *parent)
{
RsvgFilterPrimitiveMerge *filter;
filter = g_new0 (RsvgFilterPrimitiveMerge, 1);
filter->super.result = g_string_new ("none");
filter->super.render = rsvg_filter_primitive_merge_render;
return rsvg_rust_cnode_new (RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE,
parent,
filter,
rsvg_filter_primitive_merge_set_atts,
rsvg_filter_primitive_free);
}
static void
rsvg_filter_primitive_merge_node_set_atts (RsvgNode *node, gpointer impl, RsvgHandle *handle, RsvgPropertyBag atts)
{
RsvgFilterPrimitive *primitive = impl;
RsvgPropertyBagIter *iter;
const char *key;
RsvgAttribute attr;
const char *value;
iter = rsvg_property_bag_iter_begin (atts);
while (rsvg_property_bag_iter_next (iter, &key, &attr, &value)) {
switch (attr) {
case RSVG_ATTRIBUTE_IN:
/* see bug 145149 - sodipodi generates bad SVG... */
g_string_assign (primitive->in, value);
break;
default:
break;
}
}
rsvg_property_bag_iter_end (iter);
}
static void
rsvg_filter_primitive_merge_node_render (RsvgNode *node, RsvgComputedValues *values, RsvgFilterPrimitive *primitive, RsvgFilterContext *ctx)
{
/* todo */
}
RsvgNode *
rsvg_new_filter_primitive_merge_node (const char *element_name, RsvgNode *parent)
{
RsvgFilterPrimitive *filter;
filter = g_new0 (RsvgFilterPrimitive, 1);
filter->in = g_string_new ("none");
filter->render = rsvg_filter_primitive_merge_node_render;
return rsvg_rust_cnode_new (RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE_NODE,
parent,
filter,
rsvg_filter_primitive_merge_node_set_atts,
rsvg_filter_primitive_free);
}
......@@ -81,7 +81,9 @@ pub fn filter_render(
})
.filter(|c| !c.is_in_error())
.for_each(|mut c| match c.get_type() {
NodeType::FilterPrimitiveOffset | NodeType::FilterPrimitiveComposite => {
NodeType::FilterPrimitiveOffset
| NodeType::FilterPrimitiveComposite
| NodeType::FilterPrimitiveMerge => {
let render = unsafe {
*(&c.get_c_impl() as *const *const RsvgCNodeImpl as *const RenderFunctionType)
};
......
use std::cell::RefCell;
use cairo::{self, ImageSurface};
use libc::c_char;
use attributes::Attribute;
use handle::RsvgHandle;
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::parse;
use property_bag::PropertyBag;
use srgb::{linearize_surface, unlinearize_surface};
use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::input::Input;
use super::{get_surface, Filter, FilterError, Primitive};
/// The `feMerge` filter primitive.
struct Merge {
base: Primitive,
}
/// The `<feMergeNode>` element.
struct MergeNode {
in_: RefCell<Option<Input>>,
}
impl Merge {
/// Constructs a new `Merge` with empty properties.
#[inline]
fn new() -> Merge {
Merge {
base: Primitive::new::<Self>(),
}
}
}
impl MergeNode {
/// Constructs a new `MergeNode` with empty properties.
#[inline]
fn new() -> MergeNode {
MergeNode {
in_: RefCell::new(None),
}
}
}
impl NodeTrait for Merge {
#[inline]
fn set_atts(
&self,
node: &RsvgNode,
handle: *const RsvgHandle,
pbag: &PropertyBag,
) -> NodeResult {
self.base.set_atts(node, handle, pbag)
}
#[inline]
fn get_c_impl(&self) -> *const RsvgCNodeImpl {
self.base.get_c_impl()
}
}
impl NodeTrait for MergeNode {
#[inline]
fn set_atts(
&self,
_node: &RsvgNode,
_handle: *const RsvgHandle,
pbag: &PropertyBag,
) -> NodeResult {
for (_key, attr, value) in pbag.iter() {
match attr {
Attribute::In => {
self.in_.replace(Some(parse("in", value, (), None)?));
}
_ => (),
}
}
Ok(())
}
}
impl MergeNode {
fn render(
&self,
ctx: &FilterContext,
bounds: IRect,
output_surface: Option<ImageSurface>,
) -> Result<ImageSurface, FilterError> {
let input_surface = get_surface(ctx.get_input(self.in_.borrow().as_ref()))?;
let input_surface =
linearize_surface(&input_surface, bounds).map_err(FilterError::BadInputSurfaceStatus)?;
if output_surface.is_none() {
return Ok(input_surface);
}
let output_surface = output_surface.unwrap();
let cr = cairo::Context::new(&output_surface);
cr.rectangle(
bounds.x0 as f64,
bounds.y0 as f64,
(bounds.x1 - bounds.x0) as f64,
(bounds.y1 - bounds.y0) as f64,
);
cr.clip();
cr.set_source_surface(&input_surface, 0f64, 0f64);
cr.set_operator(cairo::Operator::Over);
cr.paint();
Ok(output_surface)
}
}
impl Filter for Merge {
fn render(&self, node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
let bounds = self.base.get_bounds(ctx);
let mut output_surface = None;
for child in node
.children()
.filter(|c| c.get_type() == NodeType::FilterPrimitiveMergeNode)
{
output_surface =
Some(child.with_impl(move |c: &MergeNode| c.render(ctx, bounds, output_surface))?);
}
let output_surface = output_surface
.map(|surface| unlinearize_surface(&surface, bounds))
.unwrap_or_else(|| {
ImageSurface::create(
cairo::Format::ARgb32,
ctx.source_graphic().get_width(),
ctx.source_graphic().get_height(),
)
})
.map_err(FilterError::OutputSurfaceCreation)?;
Ok(FilterResult {
name: self.base.result.borrow().clone(),
output: FilterOutput {
surface: output_surface,
bounds,
},
})
}
}
/// Returns a new `feMerge` node.
#[no_mangle]
pub unsafe extern "C" fn rsvg_new_filter_primitive_merge(
_element_name: *const c_char,
parent: *mut RsvgNode,
) -> *mut RsvgNode {
let filter = Merge::new();
boxed_node_new(NodeType::FilterPrimitiveMerge, parent, Box::new(filter))
}
/// Returns a new `feMergeNode` node.
#[no_mangle]
pub unsafe extern "C" fn rsvg_new_filter_primitive_merge_node(
_element_name: *const c_char,
parent: *mut RsvgNode,
) -> *mut RsvgNode {
let filter = MergeNode::new();
boxed_node_new(NodeType::FilterPrimitiveMergeNode, parent, Box::new(filter))
}
......@@ -27,6 +27,7 @@ pub mod iterators;
pub mod node;
pub mod composite;
pub mod merge;
pub mod offset;
/// A filter primitive interface.
......
......@@ -65,6 +65,7 @@ pub use filters::context::{
pub use filters::node::rsvg_new_filter;
pub use filters::composite::rsvg_new_filter_primitive_composite;
pub use filters::merge::{rsvg_new_filter_primitive_merge, rsvg_new_filter_primitive_merge_node};
pub use filters::offset::rsvg_new_filter_primitive_offset;
pub use gradient::{rsvg_node_linear_gradient_new, rsvg_node_radial_gradient_new};
......
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