fastpack_core/imaging/
scale.rs1use anyhow::Result;
2use image::{DynamicImage, GenericImageView, imageops::FilterType};
3
4use crate::types::{
5 config::ScaleMode,
6 rect::{Point, Size, SourceRect},
7 sprite::{NinePatch, Sprite},
8};
9
10use super::pixel_art;
11
12pub fn scale_sprite(sprite: &Sprite, factor: f32, mode: ScaleMode) -> Result<Sprite> {
22 if (factor - 1.0).abs() < f32::EPSILON {
23 return Ok(sprite.clone());
24 }
25
26 let (src_w, src_h) = sprite.image.dimensions();
27 let target_w = ((src_w as f32 * factor).round() as u32).max(1);
28 let target_h = ((src_h as f32 * factor).round() as u32).max(1);
29
30 let scaled_image = match mode {
31 ScaleMode::Smooth => sprite
32 .image
33 .resize_exact(target_w, target_h, FilterType::Lanczos3),
34 ScaleMode::Fast => sprite
35 .image
36 .resize_exact(target_w, target_h, FilterType::Nearest),
37 ScaleMode::Scale2x => {
38 let up = pixel_art::scale2x(&sprite.image.to_rgba8());
39 resize_to(DynamicImage::ImageRgba8(up), target_w, target_h)
40 }
41 ScaleMode::Scale3x => {
42 let up = pixel_art::scale3x(&sprite.image.to_rgba8());
43 resize_to(DynamicImage::ImageRgba8(up), target_w, target_h)
44 }
45 ScaleMode::Hq2x => {
46 let up = pixel_art::hq2x(&sprite.image.to_rgba8());
47 resize_to(DynamicImage::ImageRgba8(up), target_w, target_h)
48 }
49 ScaleMode::Eagle => {
50 let up = pixel_art::eagle2x(&sprite.image.to_rgba8());
51 resize_to(DynamicImage::ImageRgba8(up), target_w, target_h)
52 }
53 };
54
55 let original_size = Size {
56 w: ((sprite.original_size.w as f32 * factor).round() as u32).max(1),
57 h: ((sprite.original_size.h as f32 * factor).round() as u32).max(1),
58 };
59
60 let trim_rect = sprite.trim_rect.as_ref().map(|tr| SourceRect {
61 x: (tr.x as f32 * factor).round() as i32,
62 y: (tr.y as f32 * factor).round() as i32,
63 w: ((tr.w as f32 * factor).round() as u32).max(1),
64 h: ((tr.h as f32 * factor).round() as u32).max(1),
65 });
66
67 let nine_patch = sprite.nine_patch.map(|np| NinePatch {
68 top: (np.top as f32 * factor).round() as u32,
69 right: (np.right as f32 * factor).round() as u32,
70 bottom: (np.bottom as f32 * factor).round() as u32,
71 left: (np.left as f32 * factor).round() as u32,
72 });
73
74 let polygon = sprite.polygon.as_ref().map(|pts| {
75 pts.iter()
76 .map(|p| Point {
77 x: p.x * factor,
78 y: p.y * factor,
79 })
80 .collect()
81 });
82
83 Ok(Sprite {
84 id: sprite.id.clone(),
85 source_path: sprite.source_path.clone(),
86 image: scaled_image,
87 trim_rect,
88 original_size,
89 polygon,
90 nine_patch,
91 pivot: sprite.pivot,
92 content_hash: sprite.content_hash,
93 extrude: (sprite.extrude as f32 * factor).round() as u32,
94 alias_of: sprite.alias_of.clone(),
95 })
96}
97
98fn resize_to(img: DynamicImage, w: u32, h: u32) -> DynamicImage {
101 if img.width() == w && img.height() == h {
102 img
103 } else {
104 img.resize_exact(w, h, FilterType::Nearest)
105 }
106}