[librsvg/rustify-rsvg-convert] rsvg-convert: implement the different sizing strategies
- From: Paolo Borelli <pborelli src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/rustify-rsvg-convert] rsvg-convert: implement the different sizing strategies
- Date: Fri, 8 Jan 2021 21:30:31 +0000 (UTC)
commit 59d3ef5f7ff3b85ca10ef2f2cde992c0c8d7e3a2
Author: Paolo Borelli <pborelli gnome org>
Date: Fri Jan 8 20:27:03 2021 +0100
rsvg-convert: implement the different sizing strategies
src/bin/rsvg-convert/cli.rs | 18 +++-------
src/bin/rsvg-convert/main.rs | 42 ++++++++++++----------
src/bin/rsvg-convert/size.rs | 86 +++++++++++++++++++++++++++++++++++++++++---
3 files changed, 110 insertions(+), 36 deletions(-)
---
diff --git a/src/bin/rsvg-convert/cli.rs b/src/bin/rsvg-convert/cli.rs
index 7b2ecbbd..d6a01d42 100644
--- a/src/bin/rsvg-convert/cli.rs
+++ b/src/bin/rsvg-convert/cli.rs
@@ -21,8 +21,7 @@ arg_enum! {
#[derive(Debug)]
pub struct Args {
pub dpi: Dpi,
- zoom_x: Option<f64>,
- zoom_y: Option<f64>,
+ pub zoom: Scale,
pub width: Option<u32>,
pub height: Option<u32>,
pub format: Format,
@@ -209,8 +208,10 @@ impl Args {
x: value_t!(matches, "res_x", f64)?,
y: value_t!(matches, "res_y", f64)?,
},
- zoom_x: zoom.or(zoom_x),
- zoom_y: zoom.or(zoom_y),
+ zoom: Scale {
+ x: zoom.or(zoom_x).unwrap_or(1.0),
+ y: zoom.or(zoom_y).unwrap_or(1.0),
+ },
width: value_t!(matches, "size_x", u32).or_none()?,
height: value_t!(matches, "size_y", u32).or_none()?,
format,
@@ -255,15 +256,6 @@ impl Args {
pub fn input(&self) -> Input<'_> {
Input::new(&self.input)
}
-
- pub fn zoom(&self) -> Scale {
- match (self.zoom_x, self.zoom_y) {
- (None, None) => Scale { x: 1.0, y: 1.0 },
- (Some(x), None) => Scale { x, y: x },
- (None, Some(y)) => Scale { x: y, y },
- (Some(x), Some(y)) => Scale { x, y },
- }
- }
}
fn is_valid_resolution(v: String) -> Result<(), String> {
diff --git a/src/bin/rsvg-convert/main.rs b/src/bin/rsvg-convert/main.rs
index 837050ef..8eac3e16 100644
--- a/src/bin/rsvg-convert/main.rs
+++ b/src/bin/rsvg-convert/main.rs
@@ -9,11 +9,11 @@ mod surface;
use cssparser::Color;
use librsvg::rsvg_convert_only::LegacySize;
-use librsvg::{CairoRenderer, Loader, RenderingError, SvgHandle};
+use librsvg::{CairoRenderer, Loader, RenderingError};
use crate::cli::Args;
use crate::output::Stream;
-use crate::size::Size;
+use crate::size::{ResizeStrategy, Size};
use crate::surface::Surface;
#[macro_export]
@@ -40,16 +40,6 @@ fn load_stylesheet(args: &Args) -> std::io::Result<Option<String>> {
}
}
-fn get_size(
- _handle: &SvgHandle,
- renderer: &CairoRenderer,
- args: &Args,
-) -> Result<Size, RenderingError> {
- renderer
- .legacy_document_size_in_pixels()
- .map(|(w, h)| Size::new(w, h).scale(args.zoom()))
-}
-
fn main() {
let args = Args::new().unwrap_or_else(|e| e.exit());
@@ -74,17 +64,34 @@ fn main() {
let renderer = CairoRenderer::new(&handle).with_dpi(args.dpi.x, args.dpi.y);
if target.is_none() {
- let size = get_size(&handle, &renderer, &args)
+ let (width, height) = renderer
+ .legacy_document_size_in_pixels()
.unwrap_or_else(|e| exit!("Error rendering SVG {}: {}", input, e));
- if size.w == 0.0 && size.h == 0.0 {
- exit!("The SVG {} has no dimensions", input);
- }
+ let strategy = match (args.width, args.height) {
+ // when w and h are not specified, scale to the requested zoom (if any)
+ (None, None) => ResizeStrategy::Scale(args.zoom),
+
+ // when w and h are specified, but zoom is not, scale to the requested size
+ (Some(w), Some(h)) if args.zoom.is_identity() => ResizeStrategy::Fit(w, h),
+
+ // if only one between w and h is specified and there is no zoom, scale to the
+ // requested w or h and use the same scaling factor for the other
+ (Some(w), None) if args.zoom.is_identity() => ResizeStrategy::FitWidth(w),
+ (None, Some(h)) if args.zoom.is_identity() => ResizeStrategy::FitHeight(h),
+
+ // otherwise scale the image, but cap the zoom to match the requested size
+ _ => ResizeStrategy::FitLargestScale(args.zoom, args.width, args.height),
+ };
target = {
let output = Stream::new(args.output())
.unwrap_or_else(|e| exit!("Error opening output: {}", e));
+ let size = strategy
+ .apply(Size::new(width, height), args.keep_aspect_ratio)
+ .unwrap_or_else(|_| exit!("The SVG {} has no dimensions", input));
+
match Surface::new(args.format, size, output) {
Ok(surface) => Some(surface),
Err(cairo::Status::InvalidSize) => size_limit_exceeded(),
@@ -105,8 +112,7 @@ fn main() {
);
}
- let scale = args.zoom();
- cr.scale(scale.x, scale.y);
+ cr.scale(args.zoom.x, args.zoom.y);
surface
.render(&renderer, &cr, args.export_id())
diff --git a/src/bin/rsvg-convert/size.rs b/src/bin/rsvg-convert/size.rs
index 94ff5def..f30bb7e5 100644
--- a/src/bin/rsvg-convert/size.rs
+++ b/src/bin/rsvg-convert/size.rs
@@ -4,12 +4,19 @@ pub struct Dpi {
pub y: f64,
}
+#[derive(Clone, Copy, Debug)]
pub struct Scale {
pub x: f64,
pub y: f64,
}
-#[derive(Clone, Debug)]
+impl Scale {
+ pub fn is_identity(&self) -> bool {
+ self.x == 1.0 && self.y == 1.0
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
pub struct Size {
pub w: f64,
pub h: f64,
@@ -19,11 +26,80 @@ impl Size {
pub fn new(w: f64, h: f64) -> Self {
Self { w, h }
}
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum ResizeStrategy {
+ Scale(Scale),
+ Fit(u32, u32),
+ FitWidth(u32),
+ FitHeight(u32),
+ FitLargestScale(Scale, Option<u32>, Option<u32>),
+}
+
+impl ResizeStrategy {
+ pub fn apply(self, input: Size, keep_aspect_ratio: bool) -> Result<Size, ()> {
+ if input.w == 0.0 && input.h == 0.0 {
+ return Err(());
+ }
+
+ let output = match self {
+ ResizeStrategy::Scale(s) => Size {
+ w: input.w * s.x,
+ h: input.h * s.y,
+ },
+ ResizeStrategy::Fit(w, h) => Size {
+ w: f64::from(w),
+ h: f64::from(h),
+ },
+ ResizeStrategy::FitWidth(w) => Size {
+ w: f64::from(w),
+ h: input.h * f64::from(w) / input.w,
+ },
+ ResizeStrategy::FitHeight(h) => Size {
+ w: input.w * f64::from(h) / input.h,
+ h: f64::from(h),
+ },
+ ResizeStrategy::FitLargestScale(s, w, h) => {
+ let scaled_input_w = input.w * s.x;
+ let scaled_input_h = input.h * s.y;
+
+ let f = match (w.map(f64::from), h.map(f64::from)) {
+ (Some(w), Some(h)) if w < scaled_input_w || h < scaled_input_h => {
+ let sx = f64::from(w) / scaled_input_w;
+ let sy = f64::from(h) / scaled_input_h;
+ if sx > sy {
+ sy
+ } else {
+ sx
+ }
+ }
+ (Some(w), None) if w < scaled_input_w => f64::from(w) / scaled_input_w,
+ (None, Some(h)) if h < scaled_input_h => f64::from(h) / scaled_input_h,
+ _ => 1.0,
+ };
+
+ Size {
+ w: input.w * f * s.x,
+ h: input.h * f * s.y,
+ }
+ }
+ };
+
+ if !keep_aspect_ratio {
+ return Ok(output);
+ }
- pub fn scale(&self, scale: Scale) -> Self {
- Self {
- w: self.w * scale.x,
- h: self.h * scale.y,
+ if output.w < output.h {
+ Ok(Size {
+ w: output.w,
+ h: input.h * (output.w / input.w),
+ })
+ } else {
+ Ok(Size {
+ w: input.w * (output.h / input.h),
+ h: output.h,
+ })
}
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]