use markup5ever::{expanded_name, local_name, namespace_url, ns};
use crate::aspect_ratio::AspectRatio;
use crate::bbox::BoundingBox;
use crate::document::{AcquiredNodes, Document, Resource};
use crate::drawing_ctx::{DrawingCtx, SvgNesting, Viewport};
use crate::element::{set_attribute, ElementTrait};
use crate::error::*;
use crate::href::{is_href, set_href};
use crate::layout::{self, Layer, LayerKind, StackingContext};
use crate::length::*;
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::parsers::ParseValue;
use crate::rect::Rect;
use crate::rsvg_log;
use crate::session::Session;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::xml::Attributes;
#[derive(Default)]
pub struct Image {
aspect: AspectRatio,
href: Option<String>,
}
impl ElementTrait for Image {
fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
for (attr, value) in attrs.iter() {
match attr.expanded() {
expanded_name!("", "preserveAspectRatio") => {
set_attribute(&mut self.aspect, attr.parse(value), session)
}
ref a if is_href(a) || *a == expanded_name!("", "path") => {
set_href(a, &mut self.href, Some(value.to_string()))
}
_ => (),
}
}
}
fn layout(
&self,
node: &Node,
acquired_nodes: &mut AcquiredNodes<'_>,
cascaded: &CascadedValues<'_>,
viewport: &Viewport,
draw_ctx: &mut DrawingCtx,
_clipping: bool,
) -> Result<Option<Layer>, InternalRenderingError> {
if let Some(ref url) = self.href {
self.layout_from_url(url, node, acquired_nodes, cascaded, viewport, draw_ctx)
} else {
Ok(None)
}
}
fn draw(
&self,
node: &Node,
acquired_nodes: &mut AcquiredNodes<'_>,
cascaded: &CascadedValues<'_>,
viewport: &Viewport,
draw_ctx: &mut DrawingCtx,
clipping: bool,
) -> Result<BoundingBox, InternalRenderingError> {
let layer = self.layout(node, acquired_nodes, cascaded, viewport, draw_ctx, clipping)?;
if let Some(layer) = layer {
draw_ctx.draw_layer(&layer, acquired_nodes, clipping, viewport)
} else {
Ok(draw_ctx.empty_bbox())
}
}
}
impl Image {
fn layout_from_url(
&self,
url: &str,
node: &Node,
acquired_nodes: &mut AcquiredNodes<'_>,
cascaded: &CascadedValues<'_>,
viewport: &Viewport,
draw_ctx: &mut DrawingCtx,
) -> Result<Option<Layer>, InternalRenderingError> {
match acquired_nodes.lookup_resource(url) {
Ok(Resource::Image(surface)) => self.layout_from_surface(
&surface,
node,
acquired_nodes,
cascaded,
viewport,
draw_ctx,
),
Ok(Resource::Document(document)) => self.layout_from_svg(
&document,
node,
acquired_nodes,
cascaded,
viewport,
draw_ctx,
),
Err(e) => {
rsvg_log!(
draw_ctx.session(),
"could not load image \"{}\": {}",
url,
e
);
Ok(None)
}
}
}
fn layout_from_surface(
&self,
surface: &SharedImageSurface,
node: &Node,
acquired_nodes: &mut AcquiredNodes<'_>,
cascaded: &CascadedValues<'_>,
viewport: &Viewport,
draw_ctx: &mut DrawingCtx,
) -> Result<Option<Layer>, InternalRenderingError> {
let values = cascaded.get();
let params = NormalizeParams::new(values, viewport);
let x = values.x().0.to_user(¶ms);
let y = values.y().0.to_user(¶ms);
let w = match values.width().0 {
LengthOrAuto::Length(l) => l.to_user(¶ms),
LengthOrAuto::Auto => surface.width() as f64,
};
let h = match values.height().0 {
LengthOrAuto::Length(l) => l.to_user(¶ms),
LengthOrAuto::Auto => surface.height() as f64,
};
let is_visible = values.is_visible();
let rect = Rect::new(x, y, x + w, y + h);
let overflow = values.overflow();
let image = Box::new(layout::Image {
surface: surface.clone(),
is_visible,
rect,
aspect: self.aspect,
overflow,
image_rendering: values.image_rendering(),
});
let elt = node.borrow_element();
let stacking_ctx = StackingContext::new(
draw_ctx.session(),
acquired_nodes,
&elt,
values.transform(),
None,
values,
);
let layer = Layer {
kind: LayerKind::Image(image),
stacking_ctx,
};
Ok(Some(layer))
}
fn layout_from_svg(
&self,
document: &Document,
node: &Node,
acquired_nodes: &mut AcquiredNodes<'_>,
cascaded: &CascadedValues<'_>,
viewport: &Viewport,
draw_ctx: &mut DrawingCtx,
) -> Result<Option<Layer>, InternalRenderingError> {
let dimensions = document.get_intrinsic_dimensions();
let values = cascaded.get();
let params = NormalizeParams::new(values, viewport);
let x = values.x().0.to_user(¶ms);
let y = values.y().0.to_user(¶ms);
let w = match values.width().0 {
LengthOrAuto::Length(l) => l.to_user(¶ms),
LengthOrAuto::Auto => dimensions.width.to_user(¶ms),
};
let h = match values.height().0 {
LengthOrAuto::Length(l) => l.to_user(¶ms),
LengthOrAuto::Auto => dimensions.height.to_user(¶ms),
};
let is_visible = values.is_visible();
let rect = Rect::new(x, y, x + w, y + h);
let overflow = values.overflow();
let dest_rect = match dimensions.vbox {
None => Rect::from_size(w, h),
Some(vbox) => self.aspect.compute(&vbox, &Rect::new(x, y, x + w, y + h)),
};
let dest_size = dest_rect.size();
let surface_dest_rect = Rect::from_size(dest_size.0, dest_size.1);
let surface_width = checked_i32(dest_size.0.ceil())?;
let surface_height = checked_i32(dest_size.1.ceil())?;
let surface =
cairo::ImageSurface::create(cairo::Format::ARgb32, surface_width, surface_height)?;
{
let cr = cairo::Context::new(&surface)?;
let options = draw_ctx.rendering_options(SvgNesting::ReferencedFromImageElement);
document.render_document(
draw_ctx.session(),
&cr,
&cairo::Rectangle::from(surface_dest_rect),
&options,
)?;
}
let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;
let image = Box::new(layout::Image {
surface,
is_visible,
rect,
aspect: self.aspect,
overflow,
image_rendering: values.image_rendering(),
});
let elt = node.borrow_element();
let stacking_ctx = StackingContext::new(
draw_ctx.session(),
acquired_nodes,
&elt,
values.transform(),
None,
values,
);
let layer = Layer {
kind: LayerKind::Image(image),
stacking_ctx,
};
Ok(Some(layer))
}
}
pub fn checked_i32(x: f64) -> Result<i32, cairo::Error> {
cast::i32(x).map_err(|_| cairo::Error::InvalidSize)
}