[librsvg: 1/3] bbox: rework API and internals
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 1/3] bbox: rework API and internals
- Date: Mon, 9 Dec 2019 19:10:02 +0000 (UTC)
commit 5d5b54845ff37eb29fba20a26d8505f19ccb4b1b
Author: Paolo Borelli <pborelli gnome org>
Date: Mon Dec 9 11:23:15 2019 +0100
bbox: rework API and internals
Make the rect arg to with_rect() and with_ink_rect mandatory, add a
clear() method to set them to None. Drop the with_extents variant
and replace it with Rectangle::from_extents. Also move the
transform_rect to an extension trait of Matrix, since this is the
same API exposed by euler.
Finally, add unit tests for the combine_rects function.
rsvg_internals/src/bbox.rs | 82 ++++++++++++++++++++--------
rsvg_internals/src/drawing_ctx.rs | 9 ++-
rsvg_internals/src/filter.rs | 39 ++++++-------
rsvg_internals/src/filters/bounds.rs | 7 ++-
rsvg_internals/src/handle.rs | 6 +-
rsvg_internals/src/image.rs | 7 +--
rsvg_internals/src/mask.rs | 26 +++------
rsvg_internals/src/rect.rs | 103 ++++++++++++++++++++---------------
rsvg_internals/src/text.rs | 36 ++++++------
9 files changed, 176 insertions(+), 139 deletions(-)
---
diff --git a/rsvg_internals/src/bbox.rs b/rsvg_internals/src/bbox.rs
index d8bde22a..2c4d7c82 100644
--- a/rsvg_internals/src/bbox.rs
+++ b/rsvg_internals/src/bbox.rs
@@ -1,6 +1,6 @@
use cairo;
-use crate::rect::RectangleExt;
+use crate::rect::{RectangleExt, TransformRect};
#[derive(Debug, Copy, Clone)]
pub struct BoundingBox {
@@ -18,20 +18,23 @@ impl BoundingBox {
}
}
- pub fn with_rect(self, rect: Option<cairo::Rectangle>) -> BoundingBox {
- BoundingBox { rect, ..self }
- }
-
- pub fn with_ink_rect(self, ink_rect: Option<cairo::Rectangle>) -> BoundingBox {
- BoundingBox { ink_rect, ..self }
+ pub fn with_rect(self, rect: cairo::Rectangle) -> BoundingBox {
+ BoundingBox {
+ rect: Some(rect),
+ ..self
+ }
}
- pub fn with_extents(self, extents: (f64, f64, f64, f64)) -> BoundingBox {
- self.with_rect(rect_from_extents(extents))
+ pub fn with_ink_rect(self, ink_rect: cairo::Rectangle) -> BoundingBox {
+ BoundingBox {
+ ink_rect: Some(ink_rect),
+ ..self
+ }
}
- pub fn with_ink_extents(self, extents: (f64, f64, f64, f64)) -> BoundingBox {
- self.with_ink_rect(rect_from_extents(extents))
+ pub fn clear(mut self) {
+ self.rect = None;
+ self.ink_rect = None;
}
fn combine(&mut self, src: &BoundingBox, clip: bool) {
@@ -58,15 +61,6 @@ impl BoundingBox {
}
}
-fn rect_from_extents((x1, y1, x2, y2): (f64, f64, f64, f64)) -> Option<cairo::Rectangle> {
- Some(cairo::Rectangle {
- x: x1,
- y: y1,
- width: x2 - x1,
- height: y2 - y1,
- })
-}
-
fn combine_rects(
r1: Option<cairo::Rectangle>,
r2: Option<cairo::Rectangle>,
@@ -75,11 +69,51 @@ fn combine_rects(
) -> Option<cairo::Rectangle> {
match (r1, r2, clip) {
(r1, None, _) => r1,
- (None, Some(r2), _) => Some(r2.transform(&affine)),
- (Some(r1), Some(r2), true) => r2
- .transform(&affine)
+ (None, Some(r2), _) => Some(affine.transform_rect(&r2)),
+ (Some(r1), Some(r2), true) => affine
+ .transform_rect(&r2)
.intersection(&r1)
.or_else(|| Some(cairo::Rectangle::new(0.0, 0.0, 0.0, 0.0))),
- (Some(r1), Some(r2), false) => Some(r2.transform(&affine).union(&r1)),
+ (Some(r1), Some(r2), false) => Some(affine.transform_rect(&r2).union(&r1)),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn combine() {
+ let r1 = cairo::Rectangle::new(1.0, 2.0, 3.0, 4.0);
+ let r2 = cairo::Rectangle::new(1.5, 2.5, 3.0, 4.0);
+ let r3 = cairo::Rectangle::new(10.0, 11.0, 12.0, 13.0);
+ let affine = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, 0.5, 0.5);
+
+ let res = combine_rects(None, None, &affine, true);
+ assert_eq!(res, None);
+
+ let res = combine_rects(None, None, &affine, false);
+ assert_eq!(res, None);
+
+ let res = combine_rects(Some(r1), None, &affine, true);
+ assert_eq!(res, Some(r1));
+
+ let res = combine_rects(Some(r1), None, &affine, false);
+ assert_eq!(res, Some(r1));
+
+ let res = combine_rects(None, Some(r2), &affine, true);
+ assert_eq!(res, Some(cairo::Rectangle::new(2.0, 3.0, 3.0, 4.0)));
+
+ let res = combine_rects(None, Some(r2), &affine, false);
+ assert_eq!(res, Some(cairo::Rectangle::new(2.0, 3.0, 3.0, 4.0)));
+
+ let res = combine_rects(Some(r1), Some(r2), &affine, true);
+ assert_eq!(res, Some(cairo::Rectangle::new(2.0, 3.0, 2.0, 3.0)));
+
+ let res = combine_rects(Some(r1), Some(r3), &affine, true);
+ assert_eq!(res, Some(cairo::Rectangle::new(0.0, 0.0, 0.0, 0.0)));
+
+ let res = combine_rects(Some(r1), Some(r2), &affine, false);
+ assert_eq!(res, Some(cairo::Rectangle::new(1.0, 2.0, 4.0, 5.0)));
}
}
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 29e805f1..a776b468 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -965,19 +965,22 @@ fn compute_stroke_and_fill_box(cr: &cairo::Context, values: &ComputedValues) ->
// paths for the icon's shape. We need to be able to compute the bounding
// rectangle's extents, even when it has no fill nor stroke.
- let fb = BoundingBox::new(&affine).with_ink_extents(cr.fill_extents());
+ let (x0, y0, x1, y1) = cr.fill_extents();
+ let fb = BoundingBox::new(&affine).with_ink_rect(cairo::Rectangle::from_extents(x0, y0, x1, y1));
bbox.insert(&fb);
// Bounding box for stroke
if values.stroke.0 != PaintServer::None {
- let sb = BoundingBox::new(&affine).with_ink_extents(cr.stroke_extents());
+ let (x0, y0, x1, y1) = cr.stroke_extents();
+ let sb = BoundingBox::new(&affine).with_ink_rect(cairo::Rectangle::from_extents(x0, y0, x1, y1));
bbox.insert(&sb);
}
// objectBoundingBox
- let ob = BoundingBox::new(&affine).with_extents(cr.path_extents());
+ let (x0, y0, x1, y1) = cr.path_extents();
+ let ob = BoundingBox::new(&affine).with_rect(cairo::Rectangle::from_extents(x0, y0, x1, y1));
bbox.insert(&ob);
// restore tolerance
diff --git a/rsvg_internals/src/filter.rs b/rsvg_internals/src/filter.rs
index a1779740..3d5cd5f1 100644
--- a/rsvg_internals/src/filter.rs
+++ b/rsvg_internals/src/filter.rs
@@ -10,6 +10,7 @@ use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
+use crate::rect::RectangleExt;
/// The <filter> node.
pub struct Filter {
@@ -75,36 +76,32 @@ impl Filter {
// With filterunits == ObjectBoundingBox, lengths represent fractions or percentages of the
// referencing node. No units are allowed (it's checked during attribute parsing).
- let rect = if self.filterunits == CoordUnits::ObjectBoundingBox {
- cairo::Rectangle {
- x: self.x.length,
- y: self.y.length,
- width: self.width.length,
- height: self.height.length,
- }
+ let (x, y, w, h) = if self.filterunits == CoordUnits::ObjectBoundingBox {
+ (
+ self.x.length,
+ self.y.length,
+ self.width.length,
+ self.height.length,
+ )
} else {
- cairo::Rectangle {
- x: self.x.normalize(values, ¶ms),
- y: self.y.normalize(values, ¶ms),
- width: self.width.normalize(values, ¶ms),
- height: self.height.normalize(values, ¶ms),
- }
+ (
+ self.x.normalize(values, ¶ms),
+ self.y.normalize(values, ¶ms),
+ self.width.normalize(values, ¶ms),
+ self.height.normalize(values, ¶ms),
+ )
};
- let other_bbox = BoundingBox::new(&affine).with_rect(Some(rect));
+ let rect = cairo::Rectangle::new(x, y, w, h);
+ let other_bbox = BoundingBox::new(&affine).with_rect(rect);
// At this point all of the previous viewbox and matrix business gets converted to pixel
// coordinates in the final surface, because bbox is created with an identity affine.
bbox.insert(&other_bbox);
// Finally, clip to the width and height of our surface.
- let rect = cairo::Rectangle {
- x: 0f64,
- y: 0f64,
- width,
- height,
- };
- let other_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(Some(rect));
+ let rect = cairo::Rectangle::from_size(width, height);
+ let other_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(rect);
bbox.clip(&other_bbox);
bbox
diff --git a/rsvg_internals/src/filters/bounds.rs b/rsvg_internals/src/filters/bounds.rs
index 10094f9d..178a2461 100644
--- a/rsvg_internals/src/filters/bounds.rs
+++ b/rsvg_internals/src/filters/bounds.rs
@@ -64,8 +64,8 @@ impl<'a> BoundsBuilder<'a> {
self.standard_input_was_referenced = true;
}
FilterInput::PrimitiveOutput(ref output) => {
- let input_bbox = BoundingBox::new(&cairo::Matrix::identity())
- .with_rect(Some(output.bounds.into()));
+ let input_bbox =
+ BoundingBox::new(&cairo::Matrix::identity()).with_rect(output.bounds.into());
self.bbox.insert(&input_bbox);
}
}
@@ -99,7 +99,8 @@ impl<'a> BoundsBuilder<'a> {
let effects_region = self.ctx.effects_region();
// Clear out the rect.
- self.bbox = self.bbox.with_rect(None);
+ self.bbox.clear();
+
// Convert into the paffine coordinate system.
self.bbox.insert(&effects_region);
}
diff --git a/rsvg_internals/src/handle.rs b/rsvg_internals/src/handle.rs
index d2da4a8b..853d55db 100644
--- a/rsvg_internals/src/handle.rs
+++ b/rsvg_internals/src/handle.rs
@@ -320,10 +320,8 @@ impl Handle {
if let Some((root_width, root_height)) =
node.borrow().get_impl::<Svg>().get_size(&values, dpi)
{
- let ink_r = cairo::Rectangle::from_size(
- f64::from(root_width),
- f64::from(root_height),
- );
+ let ink_r =
+ cairo::Rectangle::from_size(f64::from(root_width), f64::from(root_height));
let logical_r = ink_r;
diff --git a/rsvg_internals/src/image.rs b/rsvg_internals/src/image.rs
index 873b75e5..23b4d3af 100644
--- a/rsvg_internals/src/image.rs
+++ b/rsvg_internals/src/image.rs
@@ -99,12 +99,7 @@ impl NodeTrait for Image {
// The bounding box for <image> is decided by the values of x, y, w, h and not by
// the final computed image bounds.
- let bbox = dc.empty_bbox().with_rect(Some(cairo::Rectangle {
- x,
- y,
- width: w,
- height: h,
- }));
+ let bbox = dc.empty_bbox().with_rect(cairo::Rectangle::new(x, y, w, h));
dc.with_saved_cr(&mut |dc| {
let cr = dc.get_cairo_context();
diff --git a/rsvg_internals/src/mask.rs b/rsvg_internals/src/mask.rs
index 4f67d197..ee25288d 100644
--- a/rsvg_internals/src/mask.rs
+++ b/rsvg_internals/src/mask.rs
@@ -55,6 +55,8 @@ impl Mask {
}
let bbox_rect = bbox.rect.as_ref().unwrap();
+ let (bb_x, bb_y) = (bbox_rect.x, bbox_rect.y);
+ let (bb_w, bb_h) = (bbox_rect.width, bbox_rect.height);
let cascaded = CascadedValues::new_from_node(mask_node);
let values = cascaded.get();
@@ -88,27 +90,17 @@ impl Mask {
draw_ctx.push_cairo_context(mask_cr);
- if mask_units == CoordUnits::ObjectBoundingBox {
- draw_ctx.clip(
- x * bbox_rect.width + bbox_rect.x,
- y * bbox_rect.height + bbox_rect.y,
- w * bbox_rect.width,
- h * bbox_rect.height,
- );
+ let (x, y, w, h) = if mask_units == CoordUnits::ObjectBoundingBox {
+ (x * bb_w + bb_x, y * bb_h + bb_y, w * bb_w, h * bb_h)
} else {
- draw_ctx.clip(x, y, w, h);
- }
+ (x, y, w, h)
+ };
+
+ draw_ctx.clip(x, y, w, h);
{
let _params = if content_units == CoordUnits::ObjectBoundingBox {
- let bbtransform = cairo::Matrix::new(
- bbox_rect.width,
- 0.0,
- 0.0,
- bbox_rect.height,
- bbox_rect.x,
- bbox_rect.y,
- );
+ let bbtransform = cairo::Matrix::new(bb_w, 0.0, 0.0, bb_h, bb_x, bb_y);
draw_ctx.get_cairo_context().transform(bbtransform);
diff --git a/rsvg_internals/src/rect.rs b/rsvg_internals/src/rect.rs
index 8fa400c9..5c875be2 100644
--- a/rsvg_internals/src/rect.rs
+++ b/rsvg_internals/src/rect.rs
@@ -6,10 +6,10 @@ use crate::float_eq_cairo::ApproxEqCairo;
pub trait RectangleExt {
fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle;
fn from_size(width: f64, height: f64) -> cairo::Rectangle;
+ fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle;
fn is_empty(&self) -> bool;
fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle>;
fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
- fn transform(&self, affine: &cairo::Matrix) -> cairo::Rectangle;
fn translate(&self, by: (f64, f64)) -> cairo::Rectangle;
fn outer(&self) -> cairo::Rectangle;
}
@@ -33,6 +33,15 @@ impl RectangleExt for cairo::Rectangle {
}
}
+ fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle {
+ cairo::Rectangle {
+ x: x0,
+ y: y0,
+ width: x1 - x0,
+ height: y1 - y0,
+ }
+ }
+
fn is_empty(&self) -> bool {
self.width.approx_eq_cairo(0.0) || self.height.approx_eq_cairo(0.0)
}
@@ -73,12 +82,38 @@ impl RectangleExt for cairo::Rectangle {
}
}
- fn transform(&self, affine: &cairo::Matrix) -> cairo::Rectangle {
+ fn translate(&self, by: (f64, f64)) -> cairo::Rectangle {
+ cairo::Rectangle {
+ x: self.x + by.0,
+ y: self.y + by.1,
+ width: self.width,
+ height: self.height,
+ }
+ }
+
+ fn outer(&self) -> cairo::Rectangle {
+ let (x, y) = (self.x.floor(), self.y.floor());
+
+ cairo::Rectangle {
+ x,
+ y,
+ width: (self.x + self.width).ceil() - x,
+ height: (self.y + self.height).ceil() - y,
+ }
+ }
+}
+
+pub trait TransformRect {
+ fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
+}
+
+impl TransformRect for cairo::Matrix {
+ fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
let points = vec![
- affine.transform_point(self.x, self.y),
- affine.transform_point(self.x + self.width, self.y),
- affine.transform_point(self.x, self.y + self.height),
- affine.transform_point(self.x + self.width, self.y + self.height),
+ self.transform_point(rect.x, rect.y),
+ self.transform_point(rect.x + rect.width, rect.y),
+ self.transform_point(rect.x, rect.y + rect.height),
+ self.transform_point(rect.x + rect.width, rect.y + rect.height),
];
let (mut xmin, mut ymin, mut xmax, mut ymax) = {
@@ -112,26 +147,6 @@ impl RectangleExt for cairo::Rectangle {
height: ymax - ymin,
}
}
-
- fn translate(&self, by: (f64, f64)) -> cairo::Rectangle {
- cairo::Rectangle {
- x: self.x + by.0,
- y: self.y + by.1,
- width: self.width,
- height: self.height,
- }
- }
-
- fn outer(&self) -> cairo::Rectangle {
- let (x, y) = (self.x.floor(), self.y.floor());
-
- cairo::Rectangle {
- x,
- y,
- width: (self.x + self.width).ceil() - x,
- height: (self.y + self.height).ceil() - y,
- }
- }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -332,6 +347,22 @@ mod tests {
assert_approx_eq_cairo!(4.34_f64, r.height);
}
+ #[test]
+ fn outer_rect() {
+ let r = cairo::Rectangle {
+ x: 1.42,
+ y: 1.42,
+ width: 3.14,
+ height: 3.14,
+ };
+
+ let or = r.outer();
+ assert_eq!(1.0, or.x);
+ assert_eq!(1.0, or.y);
+ assert_eq!(4.0, or.width);
+ assert_eq!(4.0, or.height);
+ }
+
#[test]
fn transform_rect() {
let r = cairo::Rectangle {
@@ -342,30 +373,14 @@ mod tests {
};
let m = cairo::Matrix::identity();
- let tr = r.transform(&m);
+ let tr = m.transform_rect(&r);
assert_eq!(tr, r);
let m = cairo::Matrix::new(2.0, 0.0, 0.0, 2.0, 1.5, 1.5);
- let tr = r.transform(&m);
+ let tr = m.transform_rect(&r);
assert_approx_eq_cairo!(2.34_f64, tr.x);
assert_approx_eq_cairo!(2.34_f64, tr.y);
assert_approx_eq_cairo!(6.28_f64, tr.width);
assert_approx_eq_cairo!(6.28_f64, tr.height);
}
-
- #[test]
- fn outer_rect() {
- let r = cairo::Rectangle {
- x: 1.42,
- y: 1.42,
- width: 3.14,
- height: 3.14,
- };
-
- let or = r.outer();
- assert_eq!(1.0, or.x);
- assert_eq!(1.0, or.y);
- assert_eq!(4.0, or.width);
- assert_eq!(4.0, or.height);
- }
}
diff --git a/rsvg_internals/src/text.rs b/rsvg_internals/src/text.rs
index 29f13c84..90cea086 100644
--- a/rsvg_internals/src/text.rs
+++ b/rsvg_internals/src/text.rs
@@ -28,6 +28,7 @@ use crate::property_defs::{
XmlLang,
XmlSpace,
};
+use crate::rect::RectangleExt;
use crate::space::{xml_space_normalize, NormalizeDefault, XmlSpaceNormalize};
/// An absolutely-positioned array of `Span`s
@@ -350,7 +351,9 @@ impl PositionedSpan {
pangocairo::functions::layout_path(&cr, &self.layout);
if !clipping {
- let ib = BoundingBox::new(&affine).with_ink_extents(cr.stroke_extents());
+ let (x0, y0, x1, y1) = cr.stroke_extents();
+ let r = cairo::Rectangle::from_extents(x0, y0, x1, y1);
+ let ib = BoundingBox::new(&affine).with_ink_rect(r);
cr.stroke();
bbox.insert(&ib);
}
@@ -378,25 +381,24 @@ impl PositionedSpan {
let ink_height = f64::from(ink.height);
let pango_scale = f64::from(pango::SCALE);
- let rect = if gravity_is_vertical(gravity) {
- cairo::Rectangle {
- x: x + (ink_x - ink_height) / pango_scale,
- y: y + ink_y / pango_scale,
- width: ink_height / pango_scale,
- height: ink_width / pango_scale,
- }
+ let (x, y, w, h) = if gravity_is_vertical(gravity) {
+ (
+ x + (ink_x - ink_height) / pango_scale,
+ y + ink_y / pango_scale,
+ ink_height / pango_scale,
+ ink_width / pango_scale,
+ )
} else {
- cairo::Rectangle {
- x: x + ink_x / pango_scale,
- y: y + ink_y / pango_scale,
- width: ink_width / pango_scale,
- height: ink_height / pango_scale,
- }
+ (
+ x + ink_x / pango_scale,
+ y + ink_y / pango_scale,
+ ink_width / pango_scale,
+ ink_height / pango_scale,
+ )
};
- let bbox = BoundingBox::new(affine)
- .with_rect(Some(rect))
- .with_ink_rect(Some(rect));
+ let r = cairo::Rectangle::new(x, y, w, h);
+ let bbox = BoundingBox::new(affine).with_rect(r).with_ink_rect(r);
Some(bbox)
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]