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}