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}