rxing/planar_yuv_luminance_source.rs
1// /*
2// * Copyright 2013 ZXing authors
3// *
4// * Licensed under the Apache License, Version 2.0 (the "License");
5// * you may not use this file except in compliance with the License.
6// * You may obtain a copy of the License at
7// *
8// * http://www.apache.org/licenses/LICENSE-2.0
9// *
10// * Unless required by applicable law or agreed to in writing, software
11// * distributed under the License is distributed on an "AS IS" BASIS,
12// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// * See the License for the specific language governing permissions and
14// * limitations under the License.
15// */
16// //package com.google.zxing;
17
18// /**
19// * A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes
20// * white and vice versa, and each value becomes (255-value).
21// *
22// * @author Sean Owen
23// */
24// pub struct InvertedLuminanceSource {
25// width: usize,
26// height: usize,
27// delegate: Box<dyn LuminanceSource>,
28// }
29
30// impl InvertedLuminanceSource {
31// pub fn new_with_delegate(delegate: Box<dyn LuminanceSource>) -> Self {
32// Self {
33// width: delegate.getWidth(),
34// height: delegate.getHeight(),
35// delegate,
36// }
37// }
38// }
39
40// impl LuminanceSource for InvertedLuminanceSource {
41// fn getRow(&self, y: usize, row: &Vec<u8>) -> Vec<u8> {
42// let mut new_row = self.delegate.getRow(y, row);
43// let width = self.getWidth();
44// for i in 0..width {
45// //for (int i = 0; i < width; i++) {
46// new_row[i] = 255 - (new_row[i] & 0xFF);
47// }
48// return new_row;
49// }
50
51// fn getMatrix(&self) -> Vec<u8> {
52// let matrix = self.delegate.getMatrix();
53// let length = self.getWidth() * self.getHeight();
54// let mut invertedMatrix = Vec::with_capacity(length);
55// for i in 0..length {
56// //for (int i = 0; i < length; i++) {
57// invertedMatrix[i] = 255 - (matrix[i] & 0xFF);
58// }
59// return invertedMatrix;
60// }
61
62// fn getWidth(&self) -> usize {
63// self.width
64// }
65
66// fn getHeight(&self) -> usize {
67// self.height
68// }
69
70// fn isCropSupported(&self) -> bool {
71// return self.delegate.isCropSupported();
72// }
73
74// fn crop(
75// &self,
76// left: usize,
77// top: usize,
78// width: usize,
79// height: usize,
80// ) -> Result<Box<dyn LuminanceSource>, UnsupportedOperationException> {
81// let crop = self.delegate.crop(left, top, width, height)?;
82// return Ok(Box::new(InvertedLuminanceSource::new_with_delegate(crop)));
83// }
84
85// fn isRotateSupported(&self) -> bool {
86// return self.delegate.isRotateSupported();
87// }
88
89// /**
90// * @return original delegate {@link LuminanceSource} since invert undoes itself
91// */
92// fn invert(&self) -> Box<dyn LuminanceSource> {
93// return self.delegate;
94// }
95
96// fn rotateCounterClockwise(
97// &self,
98// ) -> Result<Box<dyn LuminanceSource>, UnsupportedOperationException> {
99// let rot = self.delegate.rotateCounterClockwise()?;
100// return Ok(Box::new(InvertedLuminanceSource::new_with_delegate(rot)));
101// }
102
103// fn rotateCounterClockwise45(
104// &self,
105// ) -> Result<Box<dyn LuminanceSource>, UnsupportedOperationException> {
106// let rot_45 = self.delegate.rotateCounterClockwise45()?;
107// return Ok(Box::new(InvertedLuminanceSource::new_with_delegate(rot_45)));
108// }
109// }
110
111/*
112 * Copyright 2009 ZXing authors
113 *
114 * Licensed under the Apache License, Version 2.0 (the "License");
115 * you may not use this file except in compliance with the License.
116 * You may obtain a copy of the License at
117 *
118 * http://www.apache.org/licenses/LICENSE-2.0
119 *
120 * Unless required by applicable law or agreed to in writing, software
121 * distributed under the License is distributed on an "AS IS" BASIS,
122 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123 * See the License for the specific language governing permissions and
124 * limitations under the License.
125 */
126
127//package com.google.zxing;
128
129use std::borrow::Cow;
130
131use crate::common::Result;
132use crate::{Exceptions, LuminanceSource};
133
134const THUMBNAIL_SCALE_FACTOR: usize = 2;
135
136/**
137 * This object extends LuminanceSource around an array of YUV data returned from the camera driver,
138 * with the option to crop to a rectangle within the full data. This can be used to exclude
139 * superfluous pixels around the perimeter and speed up decoding.
140 *
141 * It works for any pixel format where the Y channel is planar and appears first, including
142 * YCbCr_420_SP and YCbCr_422_SP.
143 *
144 * @author dswitkin@google.com (Daniel Switkin)
145 */
146#[derive(Debug, Clone)]
147pub struct PlanarYUVLuminanceSource {
148 yuv_data: Box<[u8]>,
149 data_width: usize,
150 data_height: usize,
151 left: usize,
152 top: usize,
153 width: usize,
154 height: usize,
155 invert: bool,
156}
157
158impl PlanarYUVLuminanceSource {
159 #[allow(clippy::too_many_arguments)]
160 pub fn new_with_all(
161 yuv_data: Vec<u8>,
162 data_width: usize,
163 data_height: usize,
164 left: usize,
165 top: usize,
166 width: usize,
167 height: usize,
168 reverse_horizontal: bool,
169 inverted: bool,
170 ) -> Result<Self> {
171 if left + width > data_width || top + height > data_height {
172 return Err(Exceptions::illegal_argument_with(
173 "Crop rectangle does not fit within image data.",
174 ));
175 }
176
177 let mut new_s: Self = Self {
178 yuv_data: yuv_data.into_boxed_slice(),
179 data_width,
180 data_height,
181 left,
182 top,
183 width,
184 height,
185 invert: inverted,
186 };
187
188 if reverse_horizontal {
189 new_s.reverseHorizontal(width, height);
190 }
191
192 Ok(new_s)
193 }
194
195 pub fn renderThumbnail(&self) -> Vec<u8> {
196 let width = self.get_width() / THUMBNAIL_SCALE_FACTOR;
197 let height = self.get_height() / THUMBNAIL_SCALE_FACTOR;
198 let mut pixels = vec![0; width * height];
199 let yuv = &self.yuv_data;
200 let mut input_offset = self.top * self.data_width + self.left;
201
202 for y in 0..height {
203 let output_offset = y * width;
204 for x in 0..width {
205 let grey = yuv[input_offset + x * THUMBNAIL_SCALE_FACTOR];
206 pixels[output_offset + x] = (0xFF000000 | (grey as u32 * 0x00010101)) as u8;
207 }
208 input_offset += self.data_width * THUMBNAIL_SCALE_FACTOR;
209 }
210 pixels
211 }
212
213 /**
214 * @return width of image from {@link #renderThumbnail()}
215 */
216 pub fn getThumbnailWidth(&self) -> usize {
217 self.get_width() / THUMBNAIL_SCALE_FACTOR
218 }
219
220 /**
221 * @return height of image from {@link #renderThumbnail()}
222 */
223 pub fn getThumbnailHeight(&self) -> usize {
224 self.get_height() / THUMBNAIL_SCALE_FACTOR
225 }
226
227 fn reverseHorizontal(&mut self, width: usize, height: usize) {
228 let mut rowStart = self.top * self.data_width + self.left;
229 for _y in 0..height {
230 let middle = rowStart + width / 2;
231 let mut x2 = rowStart + width - 1;
232 for x1 in rowStart..middle {
233 self.yuv_data.swap(x1, x2);
234 x2 -= 1;
235 }
236 rowStart += self.data_width;
237 }
238 }
239}
240
241impl LuminanceSource for PlanarYUVLuminanceSource {
242 const SUPPORTS_CROP: bool = true;
243 const SUPPORTS_ROTATION: bool = false;
244
245 fn get_row(&self, y: usize) -> Option<Cow<'_, [u8]>> {
246 if y >= self.get_height() {
247 // //throw new IllegalArgumentException("Requested row is outside the image: " + y);
248 // panic!("Requested row is outside the image: {y}");
249 return None;
250 }
251 let width = self.get_width();
252
253 let offset = (y + self.top) * self.data_width + self.left;
254
255 if self.invert {
256 Some(Cow::Owned(self.invert_block_of_bytes(
257 self.yuv_data[offset..width + offset].to_vec(),
258 )))
259 } else {
260 Some(Cow::Borrowed(&self.yuv_data[offset..width + offset]))
261 }
262 }
263
264 fn get_column(&self, _x: usize) -> Vec<u8> {
265 unimplemented!()
266 }
267
268 fn get_matrix(&self) -> Vec<u8> {
269 let width = self.get_width();
270 let height = self.get_height();
271
272 // If the caller asks for the entire underlying image, save the copy and give them the
273 // original data. The docs specifically warn that result.length must be ignored.
274 if width == self.data_width && height == self.data_height {
275 let mut v = self.yuv_data.to_vec();
276 if self.invert {
277 v = self.invert_block_of_bytes(v);
278 }
279 return v;
280 }
281
282 let area = width * height;
283 let mut matrix = vec![0; area];
284 let mut inputOffset = self.top * self.data_width + self.left;
285
286 // If the width matches the full width of the underlying data, perform a single copy.
287 if width == self.data_width {
288 matrix[0..area].clone_from_slice(&self.yuv_data[inputOffset..area]);
289 if self.invert {
290 matrix = self.invert_block_of_bytes(matrix);
291 }
292 return matrix;
293 }
294
295 // Otherwise copy one cropped row at a time.
296 for y in 0..height {
297 let outputOffset = y * width;
298 matrix[outputOffset..outputOffset + width]
299 .clone_from_slice(&self.yuv_data[inputOffset..inputOffset + width]);
300 inputOffset += self.data_width;
301 }
302
303 if self.invert {
304 matrix = self.invert_block_of_bytes(matrix);
305 }
306
307 matrix
308 }
309
310 fn get_width(&self) -> usize {
311 self.width
312 }
313
314 fn get_height(&self) -> usize {
315 self.height
316 }
317
318 fn crop(&self, left: usize, top: usize, width: usize, height: usize) -> Result<Self> {
319 Ok(Self {
320 yuv_data: self.yuv_data.clone(),
321 data_width: self.data_width,
322 data_height: self.data_height,
323 left: self.left + left,
324 top: self.top + top,
325 width,
326 height,
327 invert: self.invert,
328 })
329 }
330
331 fn invert(&mut self) {
332 self.invert = !self.invert;
333 }
334
335 fn get_luma8_point(&self, _x: usize, _y: usize) -> u8 {
336 unimplemented!()
337 }
338}