1use core::cmp::Ordering::{Greater, Less};
2use std::ops::{Add, Sub};
3
4use image::{
5 buffer::ConvertBuffer, DynamicImage, GenericImage, GenericImageView, ImageBuffer, Luma, Pixel,
6 Rgb, Rgba,
7};
8use imageproc::definitions::Clamp;
9
10use crate::{
11 file_util::PixelEffect,
12 types::{ResultImage, ViewImage},
13};
14use rvimage_domain::{rverr, to_rv, ShapeI};
15
16pub fn read_image(path: &str) -> ResultImage {
17 image::ImageReader::open(path)
18 .map_err(to_rv)?
19 .with_guessed_format()
20 .map_err(to_rv)?
21 .decode()
22 .map_err(|e| rverr!("could not decode image {:?}. {:?}", path, e))
23}
24
25pub fn clipped_add<T>(x1: T, x2: T, clip_value: T) -> T
26where
27 T: Add<Output = T> + Sub<Output = T> + PartialOrd + Copy,
28{
29 if x1 >= clip_value || x2 >= clip_value || clip_value - x1 < x2 {
30 clip_value
31 } else {
32 x1 + x2
33 }
34}
35
36pub fn apply_to_matched_image<FnRgb8, FnRgba8, FnLuma8, FnRgb32F, T>(
37 im_d: &DynamicImage,
38 fn_rgb8: FnRgb8,
39 fn_rgba8: FnRgba8,
40 fn_luma8: FnLuma8,
41 fn_rgb32f: FnRgb32F,
42) -> T
43where
44 FnRgb8: Fn(&ImageBuffer<Rgb<u8>, Vec<u8>>) -> T,
45 FnRgba8: Fn(&ImageBuffer<Rgba<u8>, Vec<u8>>) -> T,
46 FnLuma8: Fn(&ImageBuffer<Luma<u8>, Vec<u8>>) -> T,
47 FnRgb32F: Fn(&ImageBuffer<Rgb<f32>, Vec<f32>>) -> T,
48{
49 match im_d {
50 DynamicImage::ImageRgb8(im) => fn_rgb8(im),
51 DynamicImage::ImageRgba8(im) => fn_rgba8(im),
52 DynamicImage::ImageLuma8(im) => fn_luma8(im),
53 DynamicImage::ImageRgb32F(im) => fn_rgb32f(im),
54 _ => panic!("Unsupported image type. {:?}", im_d.color()),
55 }
56}
57
58pub fn orig_to_0_255(
59 im_orig: &DynamicImage,
60 im_mask: &Option<ImageBuffer<Luma<u8>, Vec<u8>>>,
61) -> ViewImage {
62 let fn_rgb32f = |im: &ImageBuffer<Rgb<f32>, Vec<f32>>| {
63 let mut im = im.clone();
64 let max_val = im
65 .as_raw()
66 .iter()
67 .copied()
68 .max_by(|a, b| {
69 if a.is_nan() {
70 Greater
71 } else if b.is_nan() {
72 Less
73 } else {
74 a.partial_cmp(b).unwrap()
75 }
76 })
77 .expect("an image should have a maximum value");
78 if max_val <= 1.0 {
79 for y in 0..im.height() {
80 for x in 0..im.width() {
81 let p = im.get_pixel_mut(x, y);
82 p.0 = [p.0[0] * 255.0, p.0[1] * 255.0, p.0[2] * 255.0];
83 }
84 }
85 } else if max_val > 255.0 {
86 for y in 0..im.height() {
87 for x in 0..im.width() {
88 let is_pixel_relevant = if let Some(im_mask) = im_mask {
89 im_mask.get_pixel(x, y)[0] > 0
90 } else {
91 true
92 };
93 let p = im.get_pixel_mut(x, y);
94 p.0 = if is_pixel_relevant {
95 [
96 p.0[0] / max_val * 255.0,
97 p.0[1] / max_val * 255.0,
98 p.0[2] / max_val * 255.0,
99 ]
100 } else {
101 [0.0, 0.0, 0.0]
102 };
103 }
104 }
105 }
106 im.convert()
107 };
108 apply_to_matched_image(
109 im_orig,
110 |im| im.clone(),
111 |im| im.convert(),
112 |im| im.convert(),
113 fn_rgb32f,
114 )
115}
116pub fn effect_per_pixel<F: PixelEffect>(shape: ShapeI, mut f: F) {
117 for y in 0..shape.h {
118 for x in 0..shape.w {
119 f(x, y);
120 }
121 }
122}
123
124pub fn to_i64(x: (u32, u32)) -> (i64, i64) {
125 ((x.0 as i64), (x.1 as i64))
126}
127pub fn to_u32(x: (usize, usize)) -> (u32, u32) {
128 ((x.0 as u32), (x.1 as u32))
129}
130
131pub fn to_01(x: u8) -> f32 {
132 x as f32 / 255.0
133}
134
135pub fn apply_alpha(pixel_rgb: &[u8; 3], color: &[u8; 3], alpha: u8) -> Rgb<u8> {
136 let alpha_amount = to_01(alpha);
137 let apply_alpha_scalar = |x_anno, x_res| {
138 ((to_01(x_anno) * alpha_amount + (1.0 - alpha_amount) * to_01(x_res)) * 255.0) as u8
139 };
140 let [r_pixel, g_pixel, b_pixel] = pixel_rgb;
141 let [r_clr, g_clr, b_clr] = color;
142 Rgb([
143 apply_alpha_scalar(*r_pixel, *r_clr),
144 apply_alpha_scalar(*g_pixel, *g_clr),
145 apply_alpha_scalar(*b_pixel, *b_clr),
146 ])
147}
148
149pub fn draw_on_image<I: GenericImage, F: Fn(&I::Pixel) -> I::Pixel>(
150 mut im: I,
151 mut boundary_points: impl Iterator<Item = (u32, u32)>,
152 inner_points: impl Iterator<Item = (u32, u32)>,
153 color: &I::Pixel,
154 fn_inner_color: F,
155) -> I
156where
157 <<I as GenericImageView>::Pixel as image::Pixel>::Subpixel: Clamp<f32>,
158 f32: From<<<I as GenericImageView>::Pixel as Pixel>::Subpixel>,
159{
160 if let Some(first) = boundary_points.next() {
161 for inner_point in inner_points {
162 let (x, y) = inner_point;
163 let rgb = im.get_pixel(x, y);
164 im.put_pixel(x, y, fn_inner_color(&rgb));
165 }
166 let first = (first.0 as i32, first.1 as i32);
167 let mut prev = (first.0, first.1);
168 let blend = imageproc::pixelops::interpolate::<I::Pixel>;
169 for bp in boundary_points {
170 let start = prev;
171 let end = (bp.0 as i32, bp.1 as i32);
172 imageproc::drawing::draw_antialiased_line_segment_mut(
173 &mut im, start, end, *color, blend,
174 );
175 prev = end;
176 }
177 }
178 im
179}