image_renderer/
surface.rs1use crate::error::{ImageError, ImageResult};
6use image::{ImageBuffer, Rgba, RgbaImage};
7use std::path::Path;
8
9#[derive(Clone)]
11pub struct Surface {
12 image: RgbaImage,
14}
15
16impl Surface {
17 pub fn new(width: u32, height: u32) -> ImageResult<Self> {
19 if width == 0 || height == 0 {
20 return Err(ImageError::InvalidDimensions { width, height });
21 }
22
23 let image = ImageBuffer::new(width, height);
24 Ok(Self { image })
25 }
26
27 pub fn new_with_color(width: u32, height: u32, color: Rgba<u8>) -> ImageResult<Self> {
29 if width == 0 || height == 0 {
30 return Err(ImageError::InvalidDimensions { width, height });
31 }
32
33 let image = ImageBuffer::from_pixel(width, height, color);
34 Ok(Self { image })
35 }
36
37 pub fn from_file<P: AsRef<Path>>(path: P) -> ImageResult<Self> {
39 let dynamic_image = image::open(path.as_ref())
40 .map_err(|e| ImageError::LoadError(format!("Failed to open image: {}", e)))?;
41
42 Ok(Self {
43 image: dynamic_image.to_rgba8(),
44 })
45 }
46
47 pub fn from_bytes(data: &[u8]) -> ImageResult<Self> {
49 let dynamic_image = image::load_from_memory(data).map_err(|e| {
50 ImageError::LoadError(format!("Failed to load image from memory: {}", e))
51 })?;
52
53 Ok(Self {
54 image: dynamic_image.to_rgba8(),
55 })
56 }
57
58 pub fn from_rgba_image(image: RgbaImage) -> Self {
60 Self { image }
61 }
62
63 pub fn save<P: AsRef<Path>>(&self, path: P) -> ImageResult<()> {
65 self.image
66 .save(path.as_ref())
67 .map_err(|e| ImageError::SaveError(format!("Failed to save image: {}", e)))
68 }
69
70 pub fn to_png_bytes(&self) -> ImageResult<Vec<u8>> {
72 let mut bytes = Vec::new();
73 let mut cursor = std::io::Cursor::new(&mut bytes);
74
75 self.image
76 .write_to(&mut cursor, image::ImageFormat::Png)
77 .map_err(|e| ImageError::SaveError(format!("Failed to encode PNG: {}", e)))?;
78
79 Ok(bytes)
80 }
81
82 pub fn to_jpeg_bytes(&self, quality: u8) -> ImageResult<Vec<u8>> {
84 let mut bytes = Vec::new();
85 let mut cursor = std::io::Cursor::new(&mut bytes);
86
87 let encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut cursor, quality);
88 self.image
89 .write_with_encoder(encoder)
90 .map_err(|e| ImageError::SaveError(format!("Failed to encode JPEG: {}", e)))?;
91
92 Ok(bytes)
93 }
94
95 pub fn width(&self) -> u32 {
97 self.image.width()
98 }
99
100 pub fn height(&self) -> u32 {
102 self.image.height()
103 }
104
105 pub fn dimensions(&self) -> (u32, u32) {
107 self.image.dimensions()
108 }
109
110 pub(crate) fn image_mut(&mut self) -> &mut RgbaImage {
112 &mut self.image
113 }
114
115 #[allow(dead_code)]
117 pub(crate) fn image(&self) -> &RgbaImage {
118 &self.image
119 }
120
121 pub fn get_pixel(&self, x: u32, y: u32) -> Option<Rgba<u8>> {
123 if x < self.width() && y < self.height() {
124 Some(*self.image.get_pixel(x, y))
125 } else {
126 None
127 }
128 }
129
130 pub fn put_pixel(&mut self, x: u32, y: u32, color: Rgba<u8>) {
132 if x < self.width() && y < self.height() {
133 self.image.put_pixel(x, y, color);
134 }
135 }
136
137 pub fn clear(&mut self, color: Rgba<u8>) {
139 for pixel in self.image.pixels_mut() {
140 *pixel = color;
141 }
142 }
143
144 pub fn crop(&self, x: u32, y: u32, width: u32, height: u32) -> ImageResult<Self> {
146 if x + width > self.width() || y + height > self.height() {
147 return Err(ImageError::ProcessingError(
148 "Crop region exceeds image bounds".to_string(),
149 ));
150 }
151
152 let cropped = image::imageops::crop_imm(&self.image, x, y, width, height).to_image();
153 Ok(Self::from_rgba_image(cropped))
154 }
155
156 pub fn resize(&self, width: u32, height: u32) -> ImageResult<Self> {
158 if width == 0 || height == 0 {
159 return Err(ImageError::InvalidDimensions { width, height });
160 }
161
162 let resized = image::imageops::resize(
163 &self.image,
164 width,
165 height,
166 image::imageops::FilterType::Lanczos3,
167 );
168 Ok(Self::from_rgba_image(resized))
169 }
170
171 pub fn flip_horizontal(&self) -> Self {
173 let flipped = image::imageops::flip_horizontal(&self.image);
174 Self::from_rgba_image(flipped)
175 }
176
177 pub fn flip_vertical(&self) -> Self {
179 let flipped = image::imageops::flip_vertical(&self.image);
180 Self::from_rgba_image(flipped)
181 }
182
183 pub fn rotate90(&self) -> Self {
185 let rotated = image::imageops::rotate90(&self.image);
186 Self::from_rgba_image(rotated)
187 }
188
189 pub fn rotate180(&self) -> Self {
191 let rotated = image::imageops::rotate180(&self.image);
192 Self::from_rgba_image(rotated)
193 }
194
195 pub fn rotate270(&self) -> Self {
197 let rotated = image::imageops::rotate270(&self.image);
198 Self::from_rgba_image(rotated)
199 }
200
201 pub fn clone_surface(&self) -> Self {
203 Self {
204 image: self.image.clone(),
205 }
206 }
207}