Skip to content

Commit 648b562

Browse files
committed
[0.6.5][rasterize] better argument names
1 parent 3e0ba85 commit 648b562

File tree

5 files changed

+122
-101
lines changed

5 files changed

+122
-101
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rasterize"
3-
version = "0.6.4"
3+
version = "0.6.5"
44
authors = ["Pavel Aslanov <asl.pavel@gmail.com>"]
55
description = "Simple and small 2D rendering library"
66
edition = "2024"

examples/rasterize.rs

Lines changed: 34 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use rasterize::*;
55
use std::{
66
env,
77
fs::File,
8-
io::{BufReader, BufWriter, Read, Write},
9-
str::FromStr,
8+
io::{BufReader, BufWriter, Read},
109
sync::Arc,
1110
};
1211
use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan};
@@ -19,47 +18,11 @@ enum RasterizerType {
1918
SignedDifference,
2019
}
2120

22-
#[derive(Debug, Clone, Copy)]
23-
enum OutputFormat {
24-
Bmp,
25-
Rgba,
26-
#[cfg(feature = "png")]
27-
Png,
28-
}
29-
30-
impl OutputFormat {
31-
fn write(self, image: &Layer<LinColor>, out: impl Write) -> Result<(), Error> {
32-
match self {
33-
OutputFormat::Bmp => image.write_bmp(out)?,
34-
#[cfg(feature = "png")]
35-
OutputFormat::Png => image.write_png(out)?,
36-
OutputFormat::Rgba => image.write_rgba(out)?,
37-
}
38-
Ok(())
39-
}
40-
}
41-
42-
impl FromStr for OutputFormat {
43-
type Err = Error;
44-
45-
fn from_str(s: &str) -> Result<Self, Self::Err> {
46-
match s {
47-
"bmp" => Ok(OutputFormat::Bmp),
48-
"rgba" => Ok(OutputFormat::Rgba),
49-
#[cfg(feature = "png")]
50-
"png" => Ok(OutputFormat::Png),
51-
#[cfg(not(feature = "png"))]
52-
"png" => Err("png feature is disabled".into()),
53-
_ => Err(format!("Invalid output format: {s}").into()),
54-
}
55-
}
56-
}
57-
5821
#[derive(Debug)]
5922
struct Args {
6023
input_file: String,
6124
output_file: String,
62-
output_format: OutputFormat,
25+
output_format: ImageWriteFormat,
6326
outline: bool,
6427
size: Size,
6528
stroke: Option<Scalar>,
@@ -84,7 +47,7 @@ impl Args {
8447
let mut result = Args {
8548
input_file: String::new(),
8649
output_file: String::new(),
87-
output_format: OutputFormat::Bmp,
50+
output_format: ImageWriteFormat::Bmp,
8851
outline: false,
8952
size: Size {
9053
height: 0,
@@ -109,8 +72,8 @@ impl Args {
10972
"-w" => {
11073
result.size.width = args.next().ok_or("-w requires argument")?.parse()?;
11174
}
112-
"-b" => {
113-
let view_box = args.next().ok_or("-b requires argument")?;
75+
"-v" => {
76+
let view_box = args.next().ok_or("-v requires argument")?;
11477
result.view_box.replace(view_box.parse()?);
11578
}
11679
"-t" => {
@@ -121,21 +84,8 @@ impl Args {
12184
let stroke = args.next().ok_or("-s requres argument")?;
12285
result.stroke.replace(stroke.parse()?);
12386
}
124-
"-o" => {
125-
result.outline = true;
126-
}
127-
"-of" => {
128-
result.output_format = args.next().ok_or("-of requries argument")?.parse()?;
129-
}
130-
"-a" => {
131-
result.rasterizer = RasterizerType::ActiveEdge;
132-
}
13387
"-f" => {
134-
let flatness: Scalar = args.next().ok_or("-f requres argument")?.parse()?;
135-
if flatness < EPSILON {
136-
return Err("flatness is too small".into());
137-
}
138-
result.flatness = flatness;
88+
result.output_format = args.next().ok_or("-of requries argument")?.parse()?;
13989
}
14090
"-fg" => {
14191
let fg = args
@@ -151,6 +101,19 @@ impl Args {
151101
.parse()?;
152102
result.bg.replace(bg);
153103
}
104+
"-ro" => {
105+
result.outline = true;
106+
}
107+
"-ra" => {
108+
result.rasterizer = RasterizerType::ActiveEdge;
109+
}
110+
"-rf" => {
111+
let flatness: Scalar = args.next().ok_or("-rf requres argument")?.parse()?;
112+
if flatness < EPSILON {
113+
return Err("flatness is too small".into());
114+
}
115+
result.flatness = flatness;
116+
}
154117
_ => {
155118
positional += 1;
156119
match positional {
@@ -163,35 +126,38 @@ impl Args {
163126
}
164127
if positional < 2 {
165128
eprintln!(
166-
"Very simple tool that accepts SVG path as an input and produces rasterized image"
129+
"Very simple tool that accepts SVG path as an input and produces rasterized image",
167130
);
168131
eprintln!("\nUSAGE:");
169132
eprintln!(
170-
" rasterize [-h <height>] [-w <width>] [-b <bbox>] [-t <transform>] [-s <stroke>]",
133+
" rasterize [-h <height>] [-w <width>] [-v <view_box>] [-t <transform>] [-s <stroke>]",
171134
);
172135
eprintln!(
173-
" [-f <flatness>] [-o] [-of <format>] [-a] [-fg <color>] [-bg <color>]",
136+
" [-fg <color>] [-bg <color>] [-f <format>] [-rf <flatness>] [-ro] [-ra]",
174137
);
175138
eprintln!(" <input_file> <output_file>");
176139
eprintln!("\nARGS:");
177140
eprintln!(" -h <height> height in pixels of the output image");
178141
eprintln!(" -w <width> width in pixels of the output image");
179-
eprintln!(" -b <view_box> view box");
142+
eprintln!(" -v <view_box> view box");
180143
eprintln!(" -t <transform> apply transform");
181144
eprintln!(" -s <stroke_width> stroke path before rendering");
182-
eprintln!(" -o show outline with control points instead of filling");
183-
eprintln!(" -of <format> output file format (bmp, png, rgba)");
184-
eprintln!(
185-
" -a use active-edge instead of signed-difference rasterizer"
186-
);
145+
eprintln!(" -f <format> output file format (bmp, png, rgba)");
187146
eprintln!(" -fg <color> foreground color");
188147
eprintln!(" -bg <color> background color");
189148
eprintln!(
190-
" -f <flatness> flatness used by rasterizer (defualt: {})",
149+
" -rf <flatness> flatness used by rasterizer (defualt: {})",
191150
DEFAULT_FLATNESS
192151
);
152+
eprintln!(
153+
" -ro rasterize outline with control points instead of filling"
154+
);
155+
eprintln!(
156+
" -ra rasterize active-edge instead of signed-difference rasterizer"
157+
);
193158
eprintln!(" <input_file> file containing SVG path ('-' means stdin)");
194159
eprintln!(" <output_file> image rendered in the BMP format ('-' means stdout)");
160+
eprintln!("\nVERSION: {}", env!("CARGO_PKG_VERSION"));
195161
std::process::exit(1);
196162
}
197163
Ok(result)
@@ -350,9 +316,9 @@ fn main() -> Result<(), Error> {
350316
let _ = save.enter();
351317
if args.output_file != "-" {
352318
let mut image_file = BufWriter::new(File::create(args.output_file)?);
353-
args.output_format.write(&image, &mut image_file)?;
319+
image.write(args.output_format, &mut image_file)?;
354320
} else {
355-
args.output_format.write(&image, std::io::stdout())?;
321+
image.write(args.output_format, std::io::stdout())?;
356322
}
357323
}
358324

examples/scene.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use rasterize::*;
44
use std::{
55
fs::File,
6-
io::{BufReader, BufWriter},
6+
io::{BufReader, BufWriter, Read},
77
};
88
use tracing::debug_span;
99
use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan};
@@ -14,29 +14,38 @@ type Error = Box<dyn std::error::Error>;
1414
struct Args {
1515
input_file: String,
1616
output_file: String,
17-
tr: Transform,
17+
tr: Option<Transform>,
18+
view_box: Option<BBox>,
1819
bg: Option<LinColor>,
19-
width: Option<usize>,
20+
size: Size,
21+
output_format: ImageWriteFormat,
2022
}
2123

2224
impl Args {
2325
fn parse() -> Result<Self, Error> {
2426
let mut result = Self::default();
2527
let mut args = std::env::args();
2628
let mut positional = 0;
27-
let cmd = args.next().unwrap_or_else(|| "scene".to_string());
29+
let _cmd = args.next().unwrap_or_else(|| "scene".to_string());
2830
while let Some(arg) = args.next() {
2931
match arg.as_ref() {
3032
"-h" => {
31-
positional = 0;
32-
break;
33+
result.size.height = args.next().ok_or("-h requires argument")?.parse()?;
3334
}
3435
"-w" => {
35-
let width = args.next().ok_or("-w requires argument")?;
36-
result.width = Some(width.parse()?);
36+
result.size.width = args.next().ok_or("-w requires argument")?.parse()?;
37+
}
38+
"-v" => {
39+
let view_box = args.next().ok_or("-v requires argument")?;
40+
result.view_box.replace(view_box.parse()?);
3741
}
3842
"-t" => {
39-
result.tr = args.next().ok_or("-t requires argument")?.parse()?;
43+
result
44+
.tr
45+
.replace(args.next().ok_or("-t requires argument")?.parse()?);
46+
}
47+
"-f" => {
48+
result.output_format = args.next().ok_or("-of requries argument")?.parse()?;
4049
}
4150
"-bg" => {
4251
let bg = args
@@ -60,17 +69,19 @@ impl Args {
6069
eprintln!("Render scene from JSON serialized representation");
6170
eprintln!();
6271
eprintln!("USAGE:");
63-
eprintln!(
64-
" {} [-w <width>] [-bg <color>] [-t <transform>] <scene> <output>",
65-
cmd
66-
);
72+
eprintln!(" scene [-h <height>] [-w <width>] [-v <view_box>] [-t <transform>]",);
73+
eprintln!(" [-f <format>] [-bg <color>] <scene> <output>",);
6774
eprintln!();
6875
eprintln!("ARGS:");
76+
eprintln!(" -h <height> height in pixels of the output image");
6977
eprintln!(" -w <width> width in pixels of the output image");
78+
eprintln!(" -v <view_box> view box");
7079
eprintln!(" -t <transform> apply transform");
80+
eprintln!(" -f <format> output file format (bmp, png, rgba)");
7181
eprintln!(" -bg <color> background color");
7282
eprintln!(" <scene> file containing JSON scene ('-' means stdin)");
7383
eprintln!(" <output> rendered scene ('-' means stdout)");
84+
eprintln!("\nVERSION: {}", env!("CARGO_PKG_VERSION"));
7485
std::process::exit(1);
7586
}
7687

@@ -87,35 +98,39 @@ fn main() -> Result<(), Error> {
8798

8899
let args = Args::parse()?;
89100

90-
let scene: Scene = if args.input_file != "-" {
91-
let input = BufReader::new(File::open(args.input_file)?);
92-
serde_json::from_reader(input)?
93-
} else {
94-
serde_json::from_reader(BufReader::new(std::io::stdin()))?
101+
// scene
102+
let scene: Scene = {
103+
let file: &mut dyn Read = match args.input_file.as_str() {
104+
"-" => &mut std::io::stdin(),
105+
input_file => &mut File::open(input_file)?,
106+
};
107+
serde_json::from_reader(BufReader::new(file))?
108+
};
109+
let scene = match args.tr {
110+
None => scene,
111+
Some(tr) => scene.transform(tr),
95112
};
96113

97-
let tr = match args.width {
98-
Some(width) if width > 2 => {
99-
let src_bbox = scene.bbox(args.tr).ok_or("scene is empty")?;
100-
let width = width as Scalar;
101-
let height = src_bbox.height() * width / src_bbox.width();
102-
let dst_bbox = BBox::new(Point::new(1.0, 1.0), Point::new(width - 1.0, height - 1.0));
103-
Transform::fit_bbox(src_bbox, dst_bbox, Align::Mid) * args.tr
104-
}
105-
_ => args.tr,
114+
// render transform
115+
let Some(view_box) = args.view_box.or_else(|| scene.bbox(Transform::identity())) else {
116+
return Err("nothing to render".into());
106117
};
118+
let (size, tr) = Transform::fit_size(view_box, args.size, Align::Mid);
119+
let bbox = BBox::new((0.0, 0.0), (size.width as Scalar, size.height as Scalar));
107120

121+
// render
108122
let image = debug_span!("[render]")
109-
.in_scope(|| scene.render(&ActiveEdgeRasterizer::default(), tr, None, args.bg));
123+
.in_scope(|| scene.render(&ActiveEdgeRasterizer::default(), tr, Some(bbox), args.bg));
110124

125+
// save
111126
let save = debug_span!("[save]");
112127
{
113128
let _ = save.enter();
114129
if args.output_file != "-" {
115130
let mut image_file = BufWriter::new(File::create(args.output_file)?);
116-
image.write_bmp(&mut image_file)?;
131+
image.write(args.output_format, &mut image_file)?;
117132
} else {
118-
image.write_bmp(std::io::stdout())?;
133+
image.write(args.output_format, std::io::stdout())?;
119134
}
120135
}
121136

src/image.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{Color, Size};
2-
use std::{any::type_name, fmt, io::Write, sync::Arc};
2+
use std::{any::type_name, fmt, io::Write, str::FromStr, sync::Arc};
33

44
/// Shape defines size and layout of the data inside an image
55
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -118,6 +118,20 @@ pub trait Image {
118118
}
119119
}
120120

121+
fn write(self, fmt: ImageWriteFormat, out: impl Write) -> Result<(), std::io::Error>
122+
where
123+
Self::Pixel: Color,
124+
Self: Sized,
125+
{
126+
match fmt {
127+
ImageWriteFormat::Bmp => self.write_bmp(out)?,
128+
#[cfg(feature = "png")]
129+
ImageWriteFormat::Png => self.write_png(out)?,
130+
ImageWriteFormat::Rgba => self.write_rgba(out)?,
131+
}
132+
Ok(())
133+
}
134+
121135
/// Write raw (height, width, RGBA...) data
122136
fn write_rgba<W>(&self, mut out: W) -> Result<(), std::io::Error>
123137
where
@@ -217,6 +231,31 @@ pub trait Image {
217231
}
218232
}
219233

234+
#[derive(Debug, Clone, Copy, Default)]
235+
pub enum ImageWriteFormat {
236+
#[default]
237+
Bmp,
238+
Rgba,
239+
#[cfg(feature = "png")]
240+
Png,
241+
}
242+
243+
impl FromStr for ImageWriteFormat {
244+
type Err = String;
245+
246+
fn from_str(s: &str) -> Result<Self, Self::Err> {
247+
match s {
248+
"bmp" => Ok(ImageWriteFormat::Bmp),
249+
"rgba" => Ok(ImageWriteFormat::Rgba),
250+
#[cfg(feature = "png")]
251+
"png" => Ok(ImageWriteFormat::Png),
252+
#[cfg(not(feature = "png"))]
253+
"png" => Err("png feature is disabled".into()),
254+
_ => Err(format!("Invalid output format: {s}")),
255+
}
256+
}
257+
}
258+
220259
/// Immutable iterator over pixels
221260
pub struct ImageIter<'a, P> {
222261
index: usize,

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ pub use geometry::{
5252
};
5353
pub use grad::{GradLinear, GradRadial, GradSpread, GradStop, GradStops};
5454
pub use image::{
55-
Image, ImageIter, ImageMut, ImageMutIter, ImageMutRef, ImageOwned, ImageRef, Shape,
55+
Image, ImageIter, ImageMut, ImageMutIter, ImageMutRef, ImageOwned, ImageRef, ImageWriteFormat,
56+
Shape,
5657
};
5758
pub use path::{
5859
DEFAULT_FLATNESS, FillRule, LineCap, LineJoin, Path, PathBuilder, StrokeStyle, SubPath,

0 commit comments

Comments
 (0)