1use image::{DynamicImage, ImageBuffer, Rgba};
26use std::io::Cursor;
27use std::fmt;
28use std::error::Error as StdError;
29
30#[derive(Debug)]
35pub enum TextureError {
36 ImageError(image::ImageError),
37 IoError(std::io::Error),
38}
39
40impl fmt::Display for TextureError {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 TextureError::ImageError(err) => write!(f, "Image error: {}", err),
44 TextureError::IoError(err) => write!(f, "IO error: {}", err),
45 }
46 }
47}
48
49impl StdError for TextureError {
50 fn source(&self) -> Option<&(dyn StdError + 'static)> {
51 match self {
52 TextureError::ImageError(err) => Some(err),
53 TextureError::IoError(err) => Some(err),
54 }
55 }
56}
57
58impl From<image::ImageError> for TextureError {
59 fn from(err: image::ImageError) -> Self {
60 TextureError::ImageError(err)
61 }
62}
63
64impl From<std::io::Error> for TextureError {
65 fn from(err: std::io::Error) -> Self {
66 TextureError::IoError(err)
67 }
68}
69
70pub type Result<T> = std::result::Result<T, TextureError>;
72
73#[derive(Debug, Clone, Copy, PartialEq)]
75pub enum TextureFormat {
76 PNG,
77 JPEG,
78}
79
80impl TextureFormat {
81 pub fn mime_type(&self) -> &'static str {
83 match self {
84 TextureFormat::PNG => "image/png",
85 TextureFormat::JPEG => "image/jpeg",
86 }
87 }
88}
89
90pub fn generate_checkerboard(
92 width: u32,
93 height: u32,
94 cell_size: u32,
95 color1: Rgba<u8>,
96 color2: Rgba<u8>,
97) -> DynamicImage {
98 let mut img = ImageBuffer::new(width, height);
99
100 for y in 0..height {
101 for x in 0..width {
102 let cell_x = x / cell_size;
103 let cell_y = y / cell_size;
104
105 let color = if (cell_x + cell_y) % 2 == 0 {
107 color1
108 } else {
109 color2
110 };
111
112 img.put_pixel(x, y, color);
113 }
114 }
115
116 DynamicImage::ImageRgba8(img)
117}
118
119pub fn image_to_png_bytes(image: &DynamicImage) -> Result<Vec<u8>> {
121 let mut bytes: Vec<u8> = Vec::new();
122 let mut cursor = Cursor::new(&mut bytes);
123 image.write_to(&mut cursor, image::ImageOutputFormat::Png)?;
124 Ok(bytes)
125}
126
127pub fn image_to_jpeg_bytes(image: &DynamicImage, quality: u8) -> Result<Vec<u8>> {
129 let mut bytes: Vec<u8> = Vec::new();
130 let mut cursor = Cursor::new(&mut bytes);
131 image.write_to(&mut cursor, image::ImageOutputFormat::Jpeg(quality))?;
132 Ok(bytes)
133}
134
135pub fn image_to_bytes(image: &DynamicImage, format: TextureFormat) -> Result<Vec<u8>> {
137 match format {
138 TextureFormat::PNG => image_to_png_bytes(image),
139 TextureFormat::JPEG => image_to_jpeg_bytes(image, 90), }
141}
142
143pub fn create_uv_test_pattern(width: u32, height: u32) -> DynamicImage {
145 generate_checkerboard(
146 width,
147 height,
148 width / 8, Rgba([255, 255, 255, 255]), Rgba([0, 0, 0, 255]), )
152}
153
154pub fn create_colored_checkerboard(
156 width: u32,
157 height: u32,
158 cell_size: u32,
159 color1: [u8; 3],
160 color2: [u8; 3],
161) -> DynamicImage {
162 generate_checkerboard(
163 width,
164 height,
165 cell_size,
166 Rgba([color1[0], color1[1], color1[2], 255]),
167 Rgba([color2[0], color2[1], color2[2], 255]),
168 )
169}