1use crate::color::ColorSpace;
7
8#[inline]
12pub fn white_level_from_bit_depth(bit_depth: u8) -> u16 {
13 if bit_depth >= 16 {
14 u16::MAX
15 } else if bit_depth == 0 {
16 0
17 } else {
18 (1u16 << bit_depth) - 1
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Size {
26 pub width: u32,
28 pub height: u32,
30}
31
32impl Size {
33 pub fn new(width: u32, height: u32) -> Self {
35 Self { width, height }
36 }
37
38 pub fn is_valid(&self) -> bool {
40 self.width > 0 && self.height > 0
41 }
42
43 pub fn pixel_count(&self) -> u64 {
45 self.width as u64 * self.height as u64
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct Point {
53 pub x: u32,
55 pub y: u32,
57}
58
59impl Point {
60 pub fn new(x: u32, y: u32) -> Self {
62 Self { x, y }
63 }
64
65 pub const ORIGIN: Point = Point { x: 0, y: 0 };
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct Rect {
73 pub origin: Point,
75 pub size: Size,
77}
78
79impl Rect {
80 pub fn new(origin: Point, size: Size) -> Self {
82 Self { origin, size }
83 }
84
85 pub fn from_coords(x: u32, y: u32, width: u32, height: u32) -> Self {
87 Self {
88 origin: Point::new(x, y),
89 size: Size::new(width, height),
90 }
91 }
92
93 pub fn right(&self) -> u32 {
95 self.origin.x.saturating_add(self.size.width)
96 }
97
98 pub fn bottom(&self) -> u32 {
100 self.origin.y.saturating_add(self.size.height)
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109pub enum CfaPattern {
110 Rggb,
112 Grbg,
114 Bggr,
116 Gbrg,
118}
119
120impl CfaPattern {
121 pub fn from_array(pattern: [u8; 4]) -> Option<Self> {
125 match pattern {
126 [0, 1, 1, 2] => Some(CfaPattern::Rggb),
127 [1, 0, 2, 1] => Some(CfaPattern::Grbg),
128 [2, 1, 1, 0] => Some(CfaPattern::Bggr),
129 [1, 2, 0, 1] => Some(CfaPattern::Gbrg),
130 _ => None,
131 }
132 }
133
134 pub fn to_array(self) -> [u8; 4] {
136 match self {
137 CfaPattern::Rggb => [0, 1, 1, 2],
138 CfaPattern::Grbg => [1, 0, 2, 1],
139 CfaPattern::Bggr => [2, 1, 1, 0],
140 CfaPattern::Gbrg => [1, 2, 0, 1],
141 }
142 }
143
144 pub fn name(&self) -> &'static str {
146 match self {
147 CfaPattern::Rggb => "RGGB",
148 CfaPattern::Grbg => "GRBG",
149 CfaPattern::Bggr => "BGGR",
150 CfaPattern::Gbrg => "GBRG",
151 }
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161pub struct XTransPattern {
162 pub cells: [[u8; 6]; 6],
164}
165
166impl XTransPattern {
167 pub fn standard() -> Self {
169 Self {
170 cells: [
171 [1, 2, 1, 1, 0, 1],
172 [0, 1, 0, 2, 1, 2],
173 [1, 2, 1, 1, 0, 1],
174 [1, 0, 1, 2, 1, 2],
175 [1, 2, 1, 1, 0, 1],
176 [0, 1, 0, 2, 1, 2],
177 ],
178 }
179 }
180
181 #[inline]
185 pub fn color_at(&self, x: usize, y: usize) -> u8 {
186 self.cells[y % 6][x % 6]
187 }
188}
189
190#[derive(Debug, Clone)]
195pub struct RawImage {
196 size: Size,
197 active_area: Rect,
198 bit_depth: u8,
199 cfa_pattern: CfaPattern,
200 xtrans_pattern: Option<XTransPattern>,
201 black_levels: [u16; 4],
202 white_level: u16,
203 pub data: Vec<u16>,
206 baseline_exposure: Option<f32>,
207 default_crop: Option<Rect>,
208}
209
210impl RawImage {
211 pub fn new(size: Size, active_area: Rect, bit_depth: u8, cfa_pattern: CfaPattern) -> Self {
213 let pixel_count = size.pixel_count() as usize;
214 Self {
215 size,
216 active_area,
217 bit_depth,
218 cfa_pattern,
219 xtrans_pattern: None,
220 black_levels: [0; 4],
221 white_level: white_level_from_bit_depth(bit_depth),
222 data: vec![0u16; pixel_count],
223 baseline_exposure: None,
224 default_crop: None,
225 }
226 }
227
228 pub fn builder(
230 size: Size,
231 active_area: Rect,
232 bit_depth: u8,
233 cfa_pattern: CfaPattern,
234 ) -> RawImageBuilder {
235 RawImageBuilder {
236 size,
237 active_area,
238 bit_depth,
239 cfa_pattern,
240 xtrans_pattern: None,
241 black_levels: [0; 4],
242 white_level: white_level_from_bit_depth(bit_depth),
243 data: None,
244 baseline_exposure: None,
245 default_crop: None,
246 }
247 }
248
249 pub fn size(&self) -> Size {
253 self.size
254 }
255
256 pub fn width(&self) -> u32 {
258 self.size.width
259 }
260
261 pub fn height(&self) -> u32 {
263 self.size.height
264 }
265
266 pub fn active_area(&self) -> Rect {
268 self.active_area
269 }
270
271 pub fn bit_depth(&self) -> u8 {
273 self.bit_depth
274 }
275
276 pub fn cfa_pattern(&self) -> CfaPattern {
278 self.cfa_pattern
279 }
280
281 pub fn xtrans_pattern(&self) -> Option<&XTransPattern> {
283 self.xtrans_pattern.as_ref()
284 }
285
286 pub fn black_levels(&self) -> &[u16; 4] {
288 &self.black_levels
289 }
290
291 pub fn white_level(&self) -> u16 {
293 self.white_level
294 }
295
296 pub fn baseline_exposure(&self) -> Option<f32> {
298 self.baseline_exposure
299 }
300
301 pub fn default_crop(&self) -> Option<Rect> {
303 self.default_crop
304 }
305
306 pub fn set_black_levels(&mut self, levels: [u16; 4]) {
310 self.black_levels = levels;
311 }
312
313 pub fn set_white_level(&mut self, level: u16) {
315 self.white_level = level;
316 }
317
318 pub fn set_baseline_exposure(&mut self, ev: Option<f32>) {
320 self.baseline_exposure = ev;
321 }
322
323 pub fn set_default_crop(&mut self, crop: Option<Rect>) {
325 self.default_crop = crop;
326 }
327
328 pub fn set_xtrans_pattern(&mut self, pattern: Option<XTransPattern>) {
330 self.xtrans_pattern = pattern;
331 }
332
333 pub fn set_bit_depth(&mut self, bit_depth: u8) {
335 self.bit_depth = bit_depth;
336 }
337
338 pub fn get_pixel(&self, x: u32, y: u32) -> Option<u16> {
342 if x < self.size.width && y < self.size.height {
343 let idx = (y as usize) * (self.size.width as usize) + (x as usize);
344 Some(self.data[idx])
345 } else {
346 None
347 }
348 }
349
350 pub fn set_pixel(&mut self, x: u32, y: u32, value: u16) {
352 if x < self.size.width && y < self.size.height {
353 let idx = (y as usize) * (self.size.width as usize) + (x as usize);
354 self.data[idx] = value;
355 }
356 }
357}
358
359pub struct RawImageBuilder {
361 size: Size,
362 active_area: Rect,
363 bit_depth: u8,
364 cfa_pattern: CfaPattern,
365 xtrans_pattern: Option<XTransPattern>,
366 black_levels: [u16; 4],
367 white_level: u16,
368 data: Option<Vec<u16>>,
369 baseline_exposure: Option<f32>,
370 default_crop: Option<Rect>,
371}
372
373impl RawImageBuilder {
374 pub fn black_levels(mut self, levels: [u16; 4]) -> Self {
376 self.black_levels = levels;
377 self
378 }
379
380 pub fn white_level(mut self, level: u16) -> Self {
382 self.white_level = level;
383 self
384 }
385
386 pub fn xtrans_pattern(mut self, pattern: XTransPattern) -> Self {
388 self.xtrans_pattern = Some(pattern);
389 self
390 }
391
392 pub fn baseline_exposure(mut self, ev: f32) -> Self {
394 self.baseline_exposure = Some(ev);
395 self
396 }
397
398 pub fn default_crop(mut self, crop: Rect) -> Self {
400 self.default_crop = Some(crop);
401 self
402 }
403
404 pub fn data(mut self, data: Vec<u16>) -> Self {
406 self.data = Some(data);
407 self
408 }
409
410 pub fn build(self) -> RawImage {
412 let data = self
413 .data
414 .unwrap_or_else(|| vec![0u16; self.size.pixel_count() as usize]);
415 RawImage {
416 size: self.size,
417 active_area: self.active_area,
418 bit_depth: self.bit_depth,
419 cfa_pattern: self.cfa_pattern,
420 xtrans_pattern: self.xtrans_pattern,
421 black_levels: self.black_levels,
422 white_level: self.white_level,
423 data,
424 baseline_exposure: self.baseline_exposure,
425 default_crop: self.default_crop,
426 }
427 }
428}
429
430#[derive(Debug, Clone)]
432pub struct RgbImage {
433 size: Size,
434 pub data: Vec<u16>,
436 baseline_exposure: Option<f32>,
437 default_crop: Option<Rect>,
438 color_space: ColorSpace,
439}
440
441impl RgbImage {
442 pub fn new(width: u32, height: u32, data: Vec<u16>) -> Self {
447 Self {
448 size: Size::new(width, height),
449 data,
450 baseline_exposure: None,
451 default_crop: None,
452 color_space: ColorSpace::Unknown,
453 }
454 }
455
456 pub fn with_color_space(
458 width: u32,
459 height: u32,
460 data: Vec<u16>,
461 color_space: ColorSpace,
462 ) -> Self {
463 Self {
464 size: Size::new(width, height),
465 data,
466 baseline_exposure: None,
467 default_crop: None,
468 color_space,
469 }
470 }
471
472 pub fn size(&self) -> Size {
476 self.size
477 }
478
479 pub fn width(&self) -> u32 {
481 self.size.width
482 }
483
484 pub fn height(&self) -> u32 {
486 self.size.height
487 }
488
489 pub fn baseline_exposure(&self) -> Option<f32> {
491 self.baseline_exposure
492 }
493
494 pub fn default_crop(&self) -> Option<Rect> {
496 self.default_crop
497 }
498
499 pub fn color_space(&self) -> ColorSpace {
501 self.color_space
502 }
503
504 pub fn set_baseline_exposure(&mut self, ev: Option<f32>) {
508 self.baseline_exposure = ev;
509 }
510
511 pub fn set_color_space(&mut self, color_space: ColorSpace) {
513 self.color_space = color_space;
514 }
515
516 pub fn set_default_crop(&mut self, crop: Option<Rect>) {
518 self.default_crop = crop;
519 }
520
521 pub fn set_size(&mut self, size: Size) {
523 self.size = size;
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530
531 #[test]
532 fn test_white_level_from_bit_depth() {
533 assert_eq!(white_level_from_bit_depth(0), 0);
534 assert_eq!(white_level_from_bit_depth(1), 1);
535 assert_eq!(white_level_from_bit_depth(8), 255);
536 assert_eq!(white_level_from_bit_depth(12), 4095);
537 assert_eq!(white_level_from_bit_depth(14), 16383);
538 assert_eq!(white_level_from_bit_depth(15), 32767);
539 assert_eq!(white_level_from_bit_depth(16), u16::MAX);
541 assert_eq!(white_level_from_bit_depth(32), u16::MAX);
542 assert_eq!(white_level_from_bit_depth(255), u16::MAX);
543 }
544
545 #[test]
546 fn test_size() {
547 let size = Size::new(100, 200);
548 assert_eq!(size.pixel_count(), 20000);
549 assert!(size.is_valid());
550
551 let empty = Size::new(0, 100);
552 assert!(!empty.is_valid());
553 }
554
555 #[test]
556 fn test_cfa_pattern() {
557 assert_eq!(CfaPattern::from_array([0, 1, 1, 2]), Some(CfaPattern::Rggb));
558 assert_eq!(CfaPattern::Rggb.to_array(), [0, 1, 1, 2]);
559 assert_eq!(CfaPattern::Rggb.name(), "RGGB");
560 }
561
562 #[test]
563 fn test_raw_image() {
564 let size = Size::new(10, 10);
565 let active = Rect::from_coords(0, 0, 10, 10);
566 let mut img = RawImage::new(size, active, 14, CfaPattern::Rggb);
567
568 img.set_pixel(5, 5, 1000);
569 assert_eq!(img.get_pixel(5, 5), Some(1000));
570 assert_eq!(img.get_pixel(100, 100), None);
571 }
572
573 #[test]
574 fn test_raw_image_pixel_access() {
575 let size = Size::new(4, 4);
576 let active = Rect::from_coords(0, 0, 4, 4);
577 let mut img = RawImage::new(size, active, 14, CfaPattern::Rggb);
578
579 img.set_pixel(0, 0, 100);
581 img.set_pixel(3, 0, 200);
582 img.set_pixel(0, 3, 300);
583 img.set_pixel(3, 3, 400);
584 img.set_pixel(2, 1, 500);
585
586 assert_eq!(img.get_pixel(0, 0), Some(100));
587 assert_eq!(img.get_pixel(3, 0), Some(200));
588 assert_eq!(img.get_pixel(0, 3), Some(300));
589 assert_eq!(img.get_pixel(3, 3), Some(400));
590 assert_eq!(img.get_pixel(2, 1), Some(500));
591
592 assert_eq!(img.get_pixel(4, 0), None);
594 assert_eq!(img.get_pixel(0, 4), None);
595 assert_eq!(img.get_pixel(u32::MAX, u32::MAX), None);
596 }
597
598 #[test]
599 fn test_rgb_image_indexing() {
600 let data = vec![
602 100u16, 200, 300, 400, 500, 600, ];
605 let img = RgbImage::new(2, 1, data.clone());
606
607 assert_eq!(img.data[0], 100, "pixel 0 R");
608 assert_eq!(img.data[1], 200, "pixel 0 G");
609 assert_eq!(img.data[2], 300, "pixel 0 B");
610 assert_eq!(img.data[3], 400, "pixel 1 R");
611 assert_eq!(img.data[4], 500, "pixel 1 G");
612 assert_eq!(img.data[5], 600, "pixel 1 B");
613
614 assert_eq!(img.width(), 2);
615 assert_eq!(img.height(), 1);
616 assert_eq!(img.data.len(), 6);
617 }
618
619 #[test]
620 fn test_size_pixel_count() {
621 let s = Size::new(1920, 1080);
622 assert_eq!(s.pixel_count(), 1920 * 1080);
623
624 let s = Size::new(0, 100);
626 assert_eq!(s.pixel_count(), 0);
627
628 let s = Size::new(10000, 10000);
630 assert_eq!(s.pixel_count(), 100_000_000u64);
631 }
632
633 #[test]
634 fn test_rect_dimensions() {
635 let r = Rect::from_coords(10, 20, 100, 200);
636 assert_eq!(r.origin.x, 10);
637 assert_eq!(r.origin.y, 20);
638 assert_eq!(r.size.width, 100);
639 assert_eq!(r.size.height, 200);
640 assert_eq!(r.right(), 110);
641 assert_eq!(r.bottom(), 220);
642 }
643
644 #[test]
645 fn test_raw_image_builder() {
646 let size = Size::new(10, 10);
647 let active = Rect::from_coords(0, 0, 10, 10);
648 let img = RawImage::builder(size, active, 14, CfaPattern::Rggb)
649 .black_levels([100, 100, 100, 100])
650 .white_level(16383)
651 .build();
652
653 assert_eq!(img.size(), size);
654 assert_eq!(img.active_area(), active);
655 assert_eq!(img.bit_depth(), 14);
656 assert_eq!(img.cfa_pattern(), CfaPattern::Rggb);
657 assert_eq!(*img.black_levels(), [100, 100, 100, 100]);
658 assert_eq!(img.white_level(), 16383);
659 assert_eq!(img.data.len(), 100);
660 }
661
662 #[test]
663 fn test_raw_image_builder_with_data() {
664 let size = Size::new(2, 2);
665 let active = Rect::from_coords(0, 0, 2, 2);
666 let img = RawImage::builder(size, active, 14, CfaPattern::Rggb)
667 .data(vec![1000, 2000, 3000, 4000])
668 .build();
669
670 assert_eq!(img.data, vec![1000, 2000, 3000, 4000]);
671 }
672
673 #[test]
674 fn test_rgb_image_accessors() {
675 let img = RgbImage::new(100, 200, vec![0u16; 100 * 200 * 3]);
676 assert_eq!(img.width(), 100);
677 assert_eq!(img.height(), 200);
678 assert_eq!(img.size(), Size::new(100, 200));
679 assert_eq!(img.baseline_exposure(), None);
680 assert_eq!(img.default_crop(), None);
681 }
682
683 #[test]
684 fn test_raw_image_setters() {
685 let size = Size::new(4, 4);
686 let active = Rect::from_coords(0, 0, 4, 4);
687 let mut img = RawImage::new(size, active, 14, CfaPattern::Rggb);
688
689 img.set_black_levels([100, 100, 100, 100]);
690 assert_eq!(*img.black_levels(), [100, 100, 100, 100]);
691
692 img.set_white_level(4095);
693 assert_eq!(img.white_level(), 4095);
694
695 img.set_baseline_exposure(Some(-0.8));
696 assert_eq!(img.baseline_exposure(), Some(-0.8));
697
698 let crop = Rect::from_coords(1, 1, 2, 2);
699 img.set_default_crop(Some(crop));
700 assert_eq!(img.default_crop(), Some(crop));
701
702 img.set_xtrans_pattern(Some(XTransPattern::standard()));
703 assert!(img.xtrans_pattern().is_some());
704 }
705}