genpng/
genpng.rs

1// Copyright (C) 2017 Steve Sprang
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16#![allow(clippy::cast_lossless)]
17
18extern crate cairo;
19#[macro_use]
20extern crate clap;
21extern crate core;
22
23use cairo::{Context, Format, ImageSurface, Operator, Rectangle};
24use std::f64::consts::FRAC_PI_2;
25use std::fs::File;
26use std::{io, mem};
27
28use core::deck::cards;
29use core::graphics::*;
30use core::utils::clamp;
31
32const VERSION: &str = env!("CARGO_PKG_VERSION");
33const CARD_ASPECT_RATIO: f64 = 3.5 / 2.25;
34
35fn generate_card_images(path: &str, card_width: i32, border: i32,
36                        vertical: bool, scheme: ColorScheme) -> io::Result<()>
37{
38    let card_height = (card_width as f64 / CARD_ASPECT_RATIO).ceil() as i32;
39    let card_rect = Rectangle {
40        // offset by (border, border)
41        x: border as f64,
42        y: border as f64,
43        width: card_width as f64,
44        height: card_height as f64,
45    };
46
47    // add space for the border on each edge
48    let mut ctx_width = card_width + border * 2;
49    let mut ctx_height = card_height + border * 2;
50    if vertical {
51        mem::swap(&mut ctx_width, &mut ctx_height);
52    }
53
54    // create the surface and context
55    let surface = ImageSurface::create(Format::ARgb32, ctx_width, ctx_height)
56        .expect("Could not create surface.");
57    let ctx = Context::new(&surface);
58    if vertical {
59        // adjust the transform to account for the vertical orientation
60        ctx.rotate(FRAC_PI_2);
61        ctx.translate(0.0, -ctx_width as f64);
62    }
63
64    for card in cards() {
65        // completely clear the context to avoid accumulating color on
66        // any edge that antialiases over the transparent background
67        // (e.g. rounded card corners)
68        ctx.save();
69        ctx.set_operator(Operator::Clear);
70        ctx.paint();
71        ctx.restore();
72
73        if border > 0 {
74            ctx.rounded_rect(card_rect, card_corner_radius(card_rect));
75            ctx.set_source_gray(0.0);
76            // half the stroke will be covered by the card
77            ctx.set_line_width(border as f64 * 2.);
78            ctx.stroke();
79        }
80
81        ctx.draw_card(card, card_rect, None, scheme);
82
83        let filename = format!("{}/{}.png", path, card.index());
84        let mut image = File::create(&filename)?;
85
86        surface.write_to_png(&mut image)
87            .unwrap_or_else(|_| println!("Error writing {}", filename));
88    }
89
90    Ok(())
91}
92
93fn main() {
94    let matches = clap_app!(genpng =>
95        (version: VERSION)
96        (about: "Generate an image for each Marmoset card.")
97        (@arg DIRECTORY: +required "Sets the directory in which to place the images")
98        (@arg VERTICAL: -v --("render-vertically") "Orients cards vertically")
99        (@arg CLASSIC: -c --("classic-colors") "Uses classic SET colors")
100        (@arg BORDER: -b --border +takes_value "Sets the border width in pixels")
101        (@arg WIDTH: -w --width +takes_value "Sets the card width in pixels")
102    ).get_matches();
103
104    let path = matches.value_of("DIRECTORY").unwrap();
105    let width = value_t!(matches, "WIDTH", i32).unwrap_or(350);
106    let border = value_t!(matches, "BORDER", i32).unwrap_or(0);
107    let render_vertically = matches.is_present("VERTICAL");
108    let classic_colors = matches.is_present("CLASSIC");
109
110    // keep values within reasonable ranges
111    let width = clamp(width, (64, 6400));
112    let border = clamp(border, (0, 64));
113    let scheme = if classic_colors { ColorScheme::Classic } else { ColorScheme::CMYK };
114
115    generate_card_images(&path, width, border, render_vertically, scheme)
116        .unwrap_or_else(|e| println!("{}", e));
117}