rxing/
buffered_image_luminance_source.rs1use std::borrow::Cow;
18
19use image::{DynamicImage, GenericImageView, ImageBuffer, Luma, Pixel};
20use imageproc::geometric_transformations::rotate_about_center;
21
22use crate::common::Result;
23use crate::LuminanceSource;
24
25const MINUS_45_IN_RADIANS: f32 = std::f32::consts::FRAC_PI_4;
27
28pub struct BufferedImageLuminanceSource {
36    image: DynamicImage,
38    width: usize,
39    height: usize,
40}
41
42impl BufferedImageLuminanceSource {
43    pub fn new(image: DynamicImage) -> Self {
44        let width = image.width() as usize;
45        let height = image.height() as usize;
46        Self {
48            image: build_local_grey_image(image),
49            width,
50            height,
51        }
52    }
53}
54
55impl LuminanceSource for BufferedImageLuminanceSource {
56    const SUPPORTS_CROP: bool = true;
57    const SUPPORTS_ROTATION: bool = true;
58
59    fn get_row(&'_ self, y: usize) -> Option<Cow<'_, [u8]>> {
60        let buf = self.image.as_luma8()?;
61
62        let width = self.get_width();
63        let stride = buf.width() as usize; let start = y
65            .checked_mul(stride) .and_then(|off| off.checked_add(0))
67            .unwrap_or(0);
68
69        if start + width > buf.as_raw().len() {
71            return None;
72        }
73
74        Some(Cow::Borrowed(&buf.as_raw()[start..start + width]))
76    }
77
78    fn get_column(&self, x: usize) -> Vec<u8> {
79        let pixels: Vec<u8> = || -> Option<Vec<u8>> {
80            Some(self.image.as_luma8()?.rows().fold(
81                Vec::with_capacity(self.get_height()),
82                |mut acc, e| {
83                    let pix = e.into_iter().nth(x).unwrap_or(&Luma([0_u8]));
84                    acc.push(pix.0[0]);
85                    acc
86                },
87            ))
88        }()
89        .unwrap_or_default();
90
91        pixels
92    }
93
94    fn get_matrix(&self) -> Vec<u8> {
95        self.image.as_bytes().to_vec()
98        }
125
126    fn get_width(&self) -> usize {
127        self.width
128    }
129
130    fn get_height(&self) -> usize {
131        self.height
132    }
133
134    fn crop(&self, left: usize, top: usize, width: usize, height: usize) -> Result<Self> {
135        Ok(Self {
136            image: self
137                .image
138                .crop_imm(left as u32, top as u32, width as u32, height as u32),
139            width,
140            height,
141        })
142    }
143
144    fn invert(&mut self) {
145        self.image.invert()
146    }
147
148    fn rotate_counter_clockwise(&self) -> Result<Self> {
149        let img = self.image.rotate270();
150        Ok(Self {
151            width: img.width() as usize,
152            height: img.height() as usize,
153            image: img,
154        })
155    }
156
157    fn rotate_counter_clockwise_45(&self) -> Result<Self> {
158        let img = rotate_about_center(
159            &self.image.to_luma8(),
160            MINUS_45_IN_RADIANS,
161            imageproc::geometric_transformations::Interpolation::Nearest,
162            Luma([u8::MAX / 2; 1]),
163        );
164
165        let new_img = DynamicImage::from(img);
166
167        Ok(Self {
168            width: new_img.width() as usize,
169            height: new_img.height() as usize,
170            image: new_img,
171        })
172    }
173
174    fn get_luma8_point(&self, x: usize, y: usize) -> u8 {
175        self.image.get_pixel(x as u32, y as u32).to_luma().0[0]
176    }
177}
178
179fn build_local_grey_image(source: DynamicImage) -> DynamicImage {
180    let raster = match source {
181        DynamicImage::ImageLuma8(img) => img,
182        DynamicImage::ImageLumaA8(img) => {
183            let mut raster: ImageBuffer<_, Vec<_>> = ImageBuffer::new(img.width(), img.height());
184
185            for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
186                let pixel = img.get_pixel(x, y);
187                let [luma, alpha] = pixel.0;
188                if alpha == 0 {
189                    *new_pixel = Luma([0xFF])
191                } else {
192                    *new_pixel = Luma([luma.saturating_mul(alpha)])
193                }
194            }
195
196            raster
197        }
198        DynamicImage::ImageLuma16(img) => {
201            let mut raster: ImageBuffer<_, Vec<_>> = ImageBuffer::new(img.width(), img.height());
202
203            for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
204                let pixel = img.get_pixel(x, y);
205                let [luma] = pixel.0;
206
207                *new_pixel = Luma([(luma / u8::MAX as u16) as u8])
208            }
209
210            raster
211        }
212        DynamicImage::ImageLumaA16(img) => {
213            let mut raster: ImageBuffer<_, Vec<_>> = ImageBuffer::new(img.width(), img.height());
214
215            for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
216                let pixel = img.get_pixel(x, y);
217                let [luma, alpha] = pixel.0;
218                if alpha == 0 {
219                    *new_pixel = Luma([0xFF])
221                } else {
222                    *new_pixel = Luma([((luma.saturating_mul(alpha)) / u8::MAX as u16) as u8])
223                }
224            }
225
226            raster
227        }
228        _ => {
233            let img = source.to_rgba8();
234
235            let mut raster: ImageBuffer<_, Vec<_>> =
236                ImageBuffer::new(source.width(), source.height());
237
238            for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
239                let pixel = img.get_pixel(x, y);
240                let [red, green, blue, alpha] = pixel.0;
241                if alpha == 0 {
242                    *new_pixel = Luma([0xFF])
244                } else {
245                    *new_pixel = Luma([((306 * (red as u64)
249                        + 601 * (green as u64)
250                        + 117 * (blue as u64)
251                        + 0x200)
252                        >> 10) as u8])
253                }
254            }
255            raster
256        }
257    };
258
259    DynamicImage::from(raster)
260}