[librsvg] (#471): Fix blurry semi-opaque objects when rendering with a scaled transformation
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg] (#471): Fix blurry semi-opaque objects when rendering with a scaled transformation
- Date: Tue, 18 Jun 2019 17:09:53 +0000 (UTC)
commit 6dfee76bc239147e358796eb141a8c8cae129493
Author: Federico Mena Quintero <federico gnome org>
Date: Tue Jun 18 09:57:30 2019 -0500
(#471): Fix blurry semi-opaque objects when rendering with a scaled transformation
The temporary surface was being created at a size relative to the
current transformation. However, when the initial transformation was
scaled not to 1:1, the temporary surface would in effect get created
at a different pixel size than what is needed for compositing at 1:1.
So, we now create the temporary surface in what is effectively device
pixels. This way, when initial transform *is* scaled, the temporary
surface will get scaled up or down to the "natural" size with 1:1
pixels.
Fixes https://gitlab.gnome.org/GNOME/librsvg/issues/471
librsvg_crate/tests/primitives.rs | 50 +++++++++++++++++++++++++++++++++++++++
rsvg_internals/src/drawing_ctx.rs | 29 ++++++++++++++---------
2 files changed, 68 insertions(+), 11 deletions(-)
---
diff --git a/librsvg_crate/tests/primitives.rs b/librsvg_crate/tests/primitives.rs
index 4f4a270a..13f6f15a 100644
--- a/librsvg_crate/tests/primitives.rs
+++ b/librsvg_crate/tests/primitives.rs
@@ -96,6 +96,56 @@ fn simple_opacity_with_offset_viewport() {
);
}
+#[test]
+// https://gitlab.gnome.org/GNOME/librsvg/issues/471
+fn simple_opacity_with_scale() {
+ let svg = load_svg(
+ br#"<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <g opacity="0.5">
+ <rect x="10" y="10" width="30" height="30" fill="blue"/>
+ </g>
+</svg>
+"#,
+ );
+
+ let output_surf = render_to_viewport(
+ &svg,
+ SurfaceSize(500, 500),
+ |cr| {
+ cr.translate(50.0, 50.0);
+ cr.scale(8.0, 8.0);
+ },
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 50.0,
+ height: 50.0,
+ },
+ )
+ .unwrap();
+
+ let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 500, 500).unwrap();
+
+ {
+ let cr = cairo::Context::new(&reference_surf);
+
+ cr.translate(50.0, 50.0);
+ cr.scale(8.0, 8.0);
+ cr.rectangle(10.0, 10.0, 30.0, 30.0);
+ cr.set_source_rgba(0.0, 0.0, 1.0, 0.5);
+ cr.fill();
+ }
+
+ let reference_surf = SharedImageSurface::new(reference_surf, SurfaceType::SRgb).unwrap();
+
+ compare_to_surface(
+ &output_surf,
+ &reference_surf,
+ "simple_opacity_with_scale",
+ );
+}
+
#[test]
fn opacity_inside_transformed_group() {
let svg = load_svg(
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index ec7f4ca9..ce0a6874 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -225,10 +225,17 @@ impl DrawingCtx {
}
fn size_for_temporary_surface(&self) -> (i32, i32) {
+ let (viewport_width, viewport_height) = (self.rect.width, self.rect.height);
+
+ let (scaled_width, scaled_height) = self.initial_affine_with_offset().transform_distance(
+ viewport_width,
+ viewport_height,
+ );
+
// We need a size in whole pixels, so use ceil() to ensure the whole viewport fits
// into the temporary surface.
- let width = self.rect.width.ceil() as i32;
- let height = self.rect.height.ceil() as i32;
+ let width = scaled_width.ceil() as i32;
+ let height = scaled_height.ceil() as i32;
(width, height)
}
@@ -896,26 +903,26 @@ impl CompositingAffines {
cairo::Matrix::multiply(¤t, &initial_inverse)
};
+ let (scale_x, scale_y) = initial.transform_distance(1.0, 1.0);
+
let for_temporary_surface = if is_topmost_temporary_surface {
let untransformed = cairo::Matrix::multiply(¤t, &initial_inverse);
- untransformed
+ let mut scaled_to_temp_surface = untransformed;
+ scaled_to_temp_surface.scale(scale_x, scale_y);
+ scaled_to_temp_surface
} else {
current
};
let compositing = if is_topmost_temporary_surface {
- initial
+ let mut scaled = initial;
+ scaled.scale(1.0 / scale_x, 1.0 / scale_y);
+ scaled
} else {
cairo::Matrix::identity()
};
- // This is the inverse of "compositing"; we do it this way
- // instead of inverting that one to preserve accuracy.
- let for_snapshot = if is_topmost_temporary_surface {
- initial_inverse
- } else {
- cairo::Matrix::identity()
- };
+ let for_snapshot = compositing.try_invert().unwrap();
CompositingAffines {
outside_temporary_surface,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]