bitmapfont_creator/
image_loader.rs1use std::path::Path;
4use image::{DynamicImage, GenericImageView, ImageBuffer, Rgba};
5
6use crate::error::{BitmapFontError, Result};
7use crate::config::FontConfig;
8
9#[derive(Debug)]
11pub struct CharImage {
12 pub char: String,
14 pub image: DynamicImage,
16 pub width: u32,
18 pub height: u32,
20 pub original_width: u32,
22 pub original_height: u32,
24 pub padding: u32,
26}
27
28pub fn load_images(config: &FontConfig, base_path: &Path) -> Result<Vec<CharImage>> {
30 let mut images = Vec::new();
31 let mut sheet_cache: std::collections::HashMap<std::path::PathBuf, DynamicImage> = std::collections::HashMap::new();
33
34 for (char_key, char_config) in &config.chars {
35 let full_path = if char_config.path.is_absolute() {
36 char_config.path.clone()
37 } else {
38 base_path.join(&char_config.path)
39 };
40
41 let sheet = if let Some(cached) = sheet_cache.get(&full_path) {
43 cached.clone()
44 } else {
45 let img = image::open(&full_path)
46 .map_err(|e| BitmapFontError::ImageLoad(
47 format!("Failed to load image '{}': {}", full_path.display(), e)
48 ))?;
49 sheet_cache.insert(full_path.clone(), img.clone());
50 img
51 };
52
53 let (original_width, original_height, char_img) = if let Some(frame) = &char_config.frame {
55 let frame_img = extract_frame(&sheet, frame)?;
57 (frame.w, frame.h, frame_img)
58 } else {
59 (sheet.width(), sheet.height(), sheet.clone())
61 };
62
63 let padding = char_config.padding;
65 let padded_img = apply_padding(&char_img, padding);
66 let width = original_width + padding * 2;
67 let height = original_height + padding * 2;
68
69 images.push(CharImage {
70 char: char_key.clone(),
71 image: padded_img,
72 width,
73 height,
74 original_width,
75 original_height,
76 padding,
77 });
78 }
79
80 Ok(images)
81}
82
83fn extract_frame(sheet: &DynamicImage, frame: &crate::config::Frame) -> Result<DynamicImage> {
85 if frame.x + frame.w > sheet.width() || frame.y + frame.h > sheet.height() {
87 return Err(BitmapFontError::ImageLoad(
88 format!(
89 "Frame ({}, {}, {}, {}) is out of bounds for image size ({}, {})",
90 frame.x, frame.y, frame.w, frame.h,
91 sheet.width(), sheet.height()
92 )
93 ));
94 }
95
96 let mut frame_img: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::new(frame.w, frame.h);
98
99 for y in 0..frame.h {
100 for x in 0..frame.w {
101 let pixel = sheet.get_pixel(frame.x + x, frame.y + y);
102 frame_img.put_pixel(x, y, pixel);
103 }
104 }
105
106 Ok(DynamicImage::ImageRgba8(frame_img))
107}
108
109fn apply_padding(img: &DynamicImage, padding: u32) -> DynamicImage {
111 if padding == 0 {
112 return img.clone();
113 }
114
115 let width = img.width() + padding * 2;
116 let height = img.height() + padding * 2;
117
118 let mut padded: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::new(width, height);
119
120 for pixel in padded.pixels_mut() {
122 *pixel = Rgba([0, 0, 0, 0]);
123 }
124
125 for y in 0..img.height() {
127 for x in 0..img.width() {
128 let pixel = img.get_pixel(x, y);
129 padded.put_pixel(x + padding, y + padding, pixel);
130 }
131 }
132
133 DynamicImage::ImageRgba8(padded)
134}