6e407a3ea7
Build and Deploy / build-and-push (push) Successful in 3m54s
- #66: Clamp safe-area insets to 25% of window height with warn!() on excess - #68: Move fire_flush outside per-event loop in analytics (batch flush once) - #56: Persist progress before marking reward_granted to prevent XP loss on crash - #60: Add DateRolloverTimer + check_date_rollover system for midnight seed refresh - #62: Add validate_header() in replay upload with mode/draw_mode allowlists - #61: Restore two-query leaderboard opt-in check (SELECT then UPDATE); original queries already in .sqlx cache; EXISTS variant would require sqlx prepare Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
68 lines
2.4 KiB
Rust
68 lines
2.4 KiB
Rust
//! Application-icon generator — rasterises the project's icon SVG
|
||
//! into `assets/icon/icon_<size>.png` at every size in
|
||
//! `card_face_svg::ICON_SIZES` (16, 24, 32, 48, 64, 128, 256, 512,
|
||
//! 1024). Sufficient to cover Linux hicolor, Windows `.ico`, and
|
||
//! macOS `.icns` packaging targets — and the runtime `Window::icon`
|
||
//! wiring picks the 256 px slot.
|
||
//!
|
||
//! Run with:
|
||
//!
|
||
//! ```sh
|
||
//! cargo run --example icon_generator --release
|
||
//! ```
|
||
//!
|
||
//! Same shape as `card_face_generator`: SVG builder lives in
|
||
//! `solitaire_engine::assets::icon_svg` so the `icon_svg_pin`
|
||
//! integration test can call it. Rasterisation runs through
|
||
//! `assets::rasterize_svg` (the `usvg` + `resvg` + `tiny_skia`
|
||
//! pipeline already used by every other generated asset).
|
||
|
||
use bevy::math::UVec2;
|
||
use solitaire_engine::assets::icon_svg::{ICON_SIZES, icon_svg};
|
||
use solitaire_engine::assets::rasterize_svg;
|
||
use std::path::PathBuf;
|
||
use tiny_skia::{IntSize, Pixmap};
|
||
|
||
fn main() {
|
||
let icon_dir = workspace_assets_dir().join("icon");
|
||
std::fs::create_dir_all(&icon_dir).expect("create icon dir");
|
||
|
||
let svg = icon_svg();
|
||
|
||
for &size in ICON_SIZES {
|
||
let target = UVec2::new(size, size);
|
||
let pixmap = rasterize_to_pixmap(&svg, target);
|
||
let path = icon_dir.join(format!("icon_{size}.png"));
|
||
pixmap
|
||
.save_png(&path)
|
||
.unwrap_or_else(|e| panic!("write {}: {e}", path.display()));
|
||
}
|
||
|
||
println!(
|
||
"Wrote {} PNGs ({}–{} px) to {}",
|
||
ICON_SIZES.len(),
|
||
ICON_SIZES.iter().min().copied().unwrap_or(0),
|
||
ICON_SIZES.iter().max().copied().unwrap_or(0),
|
||
icon_dir.display(),
|
||
);
|
||
}
|
||
|
||
fn rasterize_to_pixmap(svg: &str, target: UVec2) -> Pixmap {
|
||
let image = rasterize_svg(svg.as_bytes(), target).expect("rasterise icon SVG");
|
||
let bytes = image.data.expect("rasterised image carries pixel data");
|
||
debug_assert_eq!(
|
||
bytes.len(),
|
||
(target.x * target.y * 4) as usize,
|
||
"rasterised buffer must match width × height × 4 RGBA bytes",
|
||
);
|
||
let size = IntSize::from_wh(target.x, target.y).expect("non-zero target size");
|
||
Pixmap::from_vec(bytes, size).expect("RGBA buffer forms a valid Pixmap")
|
||
}
|
||
|
||
fn workspace_assets_dir() -> PathBuf {
|
||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||
.parent()
|
||
.expect("solitaire_engine crate has a workspace-root parent")
|
||
.join("assets")
|
||
}
|