1use crate::error::{Error, Result};
11
12#[derive(Debug, Clone)]
19pub struct Channel {
20 data: Vec<i32>,
22 width: usize,
24 height: usize,
26 pub hshift: u32,
28 pub vshift: u32,
30 pub component: i32,
34}
35
36impl Channel {
37 pub fn new(width: usize, height: usize) -> Result<Self> {
39 if width == 0 || height == 0 {
40 return Err(Error::InvalidImageDimensions(width, height));
41 }
42
43 let size = width
44 .checked_mul(height)
45 .ok_or(Error::InvalidImageDimensions(width, height))?;
46
47 let mut data = Vec::new();
48 data.try_reserve_exact(size)?;
49 data.resize(size, 0);
50
51 Ok(Self {
52 data,
53 width,
54 height,
55 hshift: 0,
56 vshift: 0,
57 component: -1,
58 })
59 }
60
61 pub fn from_vec(data: Vec<i32>, width: usize, height: usize) -> Result<Self> {
63 if width == 0 || height == 0 {
64 return Err(Error::InvalidImageDimensions(width, height));
65 }
66 if data.len() != width * height {
67 return Err(Error::InvalidImageDimensions(width, height));
68 }
69 Ok(Self {
70 data,
71 width,
72 height,
73 hshift: 0,
74 vshift: 0,
75 component: -1,
76 })
77 }
78
79 #[inline]
81 pub fn width(&self) -> usize {
82 self.width
83 }
84
85 #[inline]
87 pub fn height(&self) -> usize {
88 self.height
89 }
90
91 #[inline]
93 pub fn len(&self) -> usize {
94 self.data.len()
95 }
96
97 #[inline]
99 pub fn is_empty(&self) -> bool {
100 self.data.is_empty()
101 }
102
103 #[inline]
105 pub fn get(&self, x: usize, y: usize) -> i32 {
106 debug_assert!(x < self.width && y < self.height);
107 self.data[y * self.width + x]
108 }
109
110 #[inline]
112 pub fn set(&mut self, x: usize, y: usize, value: i32) {
113 debug_assert!(x < self.width && y < self.height);
114 self.data[y * self.width + x] = value;
115 }
116
117 #[inline]
119 pub fn row(&self, y: usize) -> &[i32] {
120 debug_assert!(y < self.height);
121 let start = y * self.width;
122 &self.data[start..start + self.width]
123 }
124
125 #[inline]
127 pub fn row_mut(&mut self, y: usize) -> &mut [i32] {
128 debug_assert!(y < self.height);
129 let start = y * self.width;
130 &mut self.data[start..start + self.width]
131 }
132
133 #[inline]
135 pub fn data(&self) -> &[i32] {
136 &self.data
137 }
138
139 #[inline]
141 pub fn data_mut(&mut self) -> &mut [i32] {
142 &mut self.data
143 }
144
145 #[inline]
147 pub fn get_clamped(&self, x: isize, y: isize) -> i32 {
148 if x < 0 || y < 0 || x >= self.width as isize || y >= self.height as isize {
149 0
150 } else {
151 self.data[y as usize * self.width + x as usize]
152 }
153 }
154
155 pub fn extract_shifted_region(
161 &self,
162 rect_x0: usize,
163 rect_y0: usize,
164 rect_xsize: usize,
165 rect_ysize: usize,
166 ) -> Option<Channel> {
167 let x0 = rect_x0 >> self.hshift;
168 let y0 = rect_y0 >> self.vshift;
169 let xsize = (rect_xsize >> self.hshift).min(self.width.saturating_sub(x0));
170 let ysize = (rect_ysize >> self.vshift).min(self.height.saturating_sub(y0));
171
172 if xsize == 0 || ysize == 0 {
173 return None;
174 }
175
176 let mut data = Vec::with_capacity(xsize * ysize);
177 for y in 0..ysize {
178 for x in 0..xsize {
179 data.push(self.get(x0 + x, y0 + y));
180 }
181 }
182
183 let mut ch = Channel::from_vec(data, xsize, ysize).ok()?;
184 ch.hshift = self.hshift;
185 ch.vshift = self.vshift;
186 ch.component = self.component;
187 Some(ch)
188 }
189
190 pub fn extract_grid_cell(&self, gx: usize, gy: usize, group_dim: usize) -> Option<Channel> {
202 let grid_w = group_dim >> self.hshift;
203 let grid_h = group_dim >> self.vshift;
204
205 if grid_w == 0 || grid_h == 0 {
206 return None;
207 }
208
209 let bx = gx * grid_w;
210 let by = gy * grid_h;
211
212 if bx >= self.width || by >= self.height {
213 return None;
214 }
215
216 let xsize = (self.width - bx).min(grid_w);
217 let ysize = (self.height - by).min(grid_h);
218
219 if xsize == 0 || ysize == 0 {
220 return None;
221 }
222
223 let mut data = Vec::with_capacity(xsize * ysize);
224 for y in 0..ysize {
225 for x in 0..xsize {
226 data.push(self.get(bx + x, by + y));
227 }
228 }
229
230 let mut ch = Channel::from_vec(data, xsize, ysize).ok()?;
231 ch.hshift = self.hshift;
232 ch.vshift = self.vshift;
233 ch.component = self.component;
234 Some(ch)
235 }
236
237 #[inline]
239 pub fn get_clamped_to_edge(&self, x: isize, y: isize) -> i32 {
240 let x = x.clamp(0, self.width as isize - 1) as usize;
241 let y = y.clamp(0, self.height as isize - 1) as usize;
242 self.data[y * self.width + x]
243 }
244}
245
246#[derive(Debug, Clone)]
251pub struct ModularImage {
252 pub channels: Vec<Channel>,
254 pub bit_depth: u32,
256 pub is_grayscale: bool,
258 pub has_alpha: bool,
260}
261
262impl ModularImage {
263 pub fn from_rgb8(data: &[u8], width: usize, height: usize) -> Result<Self> {
265 if data.len() != width * height * 3 {
266 return Err(Error::InvalidImageDimensions(width, height));
267 }
268
269 let mut channels = Vec::with_capacity(3);
270 for c in 0..3 {
271 let mut channel = Channel::new(width, height)?;
272 for y in 0..height {
273 for x in 0..width {
274 let idx = (y * width + x) * 3 + c;
275 channel.set(x, y, data[idx] as i32);
276 }
277 }
278 channels.push(channel);
279 }
280
281 Ok(Self {
282 channels,
283 bit_depth: 8,
284 is_grayscale: false,
285 has_alpha: false,
286 })
287 }
288
289 pub fn from_rgba8(data: &[u8], width: usize, height: usize) -> Result<Self> {
291 if data.len() != width * height * 4 {
292 return Err(Error::InvalidImageDimensions(width, height));
293 }
294
295 let mut channels = Vec::with_capacity(4);
296 for c in 0..4 {
297 let mut channel = Channel::new(width, height)?;
298 for y in 0..height {
299 for x in 0..width {
300 let idx = (y * width + x) * 4 + c;
301 channel.set(x, y, data[idx] as i32);
302 }
303 }
304 channels.push(channel);
305 }
306
307 Ok(Self {
308 channels,
309 bit_depth: 8,
310 is_grayscale: false,
311 has_alpha: true,
312 })
313 }
314
315 pub fn from_gray8(data: &[u8], width: usize, height: usize) -> Result<Self> {
317 if data.len() != width * height {
318 return Err(Error::InvalidImageDimensions(width, height));
319 }
320
321 let mut channel = Channel::new(width, height)?;
322 for (i, &val) in data.iter().enumerate() {
323 let x = i % width;
324 let y = i / width;
325 channel.set(x, y, val as i32);
326 }
327
328 Ok(Self {
329 channels: vec![channel],
330 bit_depth: 8,
331 is_grayscale: true,
332 has_alpha: false,
333 })
334 }
335
336 pub fn from_rgb16(data: &[u8], width: usize, height: usize) -> Result<Self> {
338 if data.len() != width * height * 6 {
339 return Err(Error::InvalidImageDimensions(width, height));
340 }
341
342 let mut channels = Vec::with_capacity(3);
343 for c in 0..3 {
344 let mut channel = Channel::new(width, height)?;
345 for y in 0..height {
346 for x in 0..width {
347 let idx = (y * width + x) * 6 + c * 2;
348 let val = u16::from_be_bytes([data[idx], data[idx + 1]]);
349 channel.set(x, y, val as i32);
350 }
351 }
352 channels.push(channel);
353 }
354
355 Ok(Self {
356 channels,
357 bit_depth: 16,
358 is_grayscale: false,
359 has_alpha: false,
360 })
361 }
362
363 pub fn from_rgb16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
368 if data.len() != width * height * 6 {
369 return Err(Error::InvalidImageDimensions(width, height));
370 }
371 let pixels: &[u16] = bytemuck::cast_slice(data);
372 let mut channels = Vec::with_capacity(3);
373 for c in 0..3 {
374 let mut channel = Channel::new(width, height)?;
375 for y in 0..height {
376 for x in 0..width {
377 let idx = (y * width + x) * 3 + c;
378 channel.set(x, y, pixels[idx] as i32);
379 }
380 }
381 channels.push(channel);
382 }
383 Ok(Self {
384 channels,
385 bit_depth: 16,
386 is_grayscale: false,
387 has_alpha: false,
388 })
389 }
390
391 pub fn from_rgba16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
395 if data.len() != width * height * 8 {
396 return Err(Error::InvalidImageDimensions(width, height));
397 }
398 let pixels: &[u16] = bytemuck::cast_slice(data);
399 let mut channels = Vec::with_capacity(4);
400 for c in 0..4 {
401 let mut channel = Channel::new(width, height)?;
402 for y in 0..height {
403 for x in 0..width {
404 let idx = (y * width + x) * 4 + c;
405 channel.set(x, y, pixels[idx] as i32);
406 }
407 }
408 channels.push(channel);
409 }
410 Ok(Self {
411 channels,
412 bit_depth: 16,
413 is_grayscale: false,
414 has_alpha: true,
415 })
416 }
417
418 pub fn from_grayalpha8(data: &[u8], width: usize, height: usize) -> Result<Self> {
420 if data.len() != width * height * 2 {
421 return Err(Error::InvalidImageDimensions(width, height));
422 }
423 let mut gray = Channel::new(width, height)?;
424 let mut alpha = Channel::new(width, height)?;
425 for y in 0..height {
426 for x in 0..width {
427 let idx = (y * width + x) * 2;
428 gray.set(x, y, data[idx] as i32);
429 alpha.set(x, y, data[idx + 1] as i32);
430 }
431 }
432 Ok(Self {
433 channels: vec![gray, alpha],
434 bit_depth: 8,
435 is_grayscale: true,
436 has_alpha: true,
437 })
438 }
439
440 pub fn from_gray16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
444 if data.len() != width * height * 2 {
445 return Err(Error::InvalidImageDimensions(width, height));
446 }
447 let pixels: &[u16] = bytemuck::cast_slice(data);
448 let mut channel = Channel::new(width, height)?;
449 for (i, &val) in pixels.iter().enumerate() {
450 let x = i % width;
451 let y = i / width;
452 channel.set(x, y, val as i32);
453 }
454 Ok(Self {
455 channels: vec![channel],
456 bit_depth: 16,
457 is_grayscale: true,
458 has_alpha: false,
459 })
460 }
461
462 pub fn from_grayalpha16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
466 if data.len() != width * height * 4 {
467 return Err(Error::InvalidImageDimensions(width, height));
468 }
469 let pixels: &[u16] = bytemuck::cast_slice(data);
470 let mut gray = Channel::new(width, height)?;
471 let mut alpha = Channel::new(width, height)?;
472 for y in 0..height {
473 for x in 0..width {
474 let idx = (y * width + x) * 2;
475 gray.set(x, y, pixels[idx] as i32);
476 alpha.set(x, y, pixels[idx + 1] as i32);
477 }
478 }
479 Ok(Self {
480 channels: vec![gray, alpha],
481 bit_depth: 16,
482 is_grayscale: true,
483 has_alpha: true,
484 })
485 }
486
487 pub fn width(&self) -> usize {
489 self.channels.first().map_or(0, |c| c.width())
490 }
491
492 pub fn height(&self) -> usize {
494 self.channels.first().map_or(0, |c| c.height())
495 }
496
497 pub fn num_channels(&self) -> usize {
499 self.channels.len()
500 }
501
502 pub fn channel(&self, idx: usize) -> &Channel {
504 &self.channels[idx]
505 }
506
507 pub fn channel_mut(&mut self, idx: usize) -> &mut Channel {
509 &mut self.channels[idx]
510 }
511
512 pub fn extract_region(
517 &self,
518 x_start: usize,
519 y_start: usize,
520 x_end: usize,
521 y_end: usize,
522 ) -> Result<Self> {
523 let region_width = x_end.saturating_sub(x_start);
524 let region_height = y_end.saturating_sub(y_start);
525
526 if region_width == 0 || region_height == 0 {
527 return Err(Error::InvalidImageDimensions(region_width, region_height));
528 }
529
530 let mut channels = Vec::with_capacity(self.channels.len());
531 for src_channel in &self.channels {
532 let mut dst_channel = Channel::new(region_width, region_height)?;
533
534 for dy in 0..region_height {
535 let sy = y_start + dy;
536 if sy >= src_channel.height() {
537 continue;
538 }
539
540 for dx in 0..region_width {
541 let sx = x_start + dx;
542 if sx >= src_channel.width() {
543 continue;
544 }
545
546 dst_channel.set(dx, dy, src_channel.get(sx, sy));
547 }
548 }
549
550 channels.push(dst_channel);
551 }
552
553 Ok(Self {
554 channels,
555 bit_depth: self.bit_depth,
556 is_grayscale: self.is_grayscale,
557 has_alpha: self.has_alpha,
558 })
559 }
560}
561
562#[cfg(test)]
563mod tests {
564 use super::*;
565
566 #[test]
567 fn test_channel_creation() {
568 let channel = Channel::new(100, 100).unwrap();
569 assert_eq!(channel.width(), 100);
570 assert_eq!(channel.height(), 100);
571 assert_eq!(channel.len(), 10000);
572 }
573
574 #[test]
575 fn test_channel_access() {
576 let mut channel = Channel::new(10, 10).unwrap();
577 channel.set(5, 5, 42);
578 assert_eq!(channel.get(5, 5), 42);
579 }
580
581 #[test]
582 fn test_channel_clamped() {
583 let mut channel = Channel::new(10, 10).unwrap();
584 channel.set(0, 0, 100);
585 assert_eq!(channel.get_clamped(-1, -1), 0);
586 assert_eq!(channel.get_clamped(0, 0), 100);
587 assert_eq!(channel.get_clamped(100, 100), 0);
588 }
589
590 #[test]
591 fn test_modular_image_rgb8() {
592 let data = vec![
593 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0, ];
598 let img = ModularImage::from_rgb8(&data, 2, 2).unwrap();
599
600 assert_eq!(img.num_channels(), 3);
601 assert_eq!(img.width(), 2);
602 assert_eq!(img.height(), 2);
603
604 assert_eq!(img.channel(0).get(0, 0), 255);
606 assert_eq!(img.channel(0).get(1, 0), 0);
607
608 assert_eq!(img.channel(1).get(0, 0), 0);
610 assert_eq!(img.channel(1).get(1, 0), 255);
611
612 assert_eq!(img.channel(2).get(0, 0), 0);
614 assert_eq!(img.channel(2).get(0, 1), 255);
615 }
616}