Skip to main content

rxing/oned/rss/expanded/
rss_expanded_reader.rs

1/*
2 * Copyright (C) 2010 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
17/*
18 * These authors would like to acknowledge the Spanish Ministry of Industry,
19 * Tourism and Trade, for the support in the project TSI020301-2008-2
20 * "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled
21 * Mobile Dynamic Environments", led by Treelogic
22 * ( http://www.treelogic.com/ ):
23 *
24 *   http://www.piramidepse.com/
25 */
26
27use crate::{
28    BarcodeFormat, Binarizer, DecodeHints, Exceptions, RXingResult, RXingResultMetadataType,
29    RXingResultMetadataValue, Reader,
30    common::{BitArray, Result},
31    oned::{
32        OneDReader, record_pattern, record_pattern_in_reverse,
33        rss::{
34            AbstractRSSReaderTrait, DataCharacter, DataCharacterTrait, FinderPattern, rss_utils,
35        },
36    },
37};
38
39use super::{ExpandedPair, ExpandedRow, bit_array_builder, decoders::abstract_expanded_decoder};
40
41const FINDER_PAT_A: u32 = 0;
42const FINDER_PAT_B: u32 = 1;
43const FINDER_PAT_C: u32 = 2;
44const FINDER_PAT_D: u32 = 3;
45const FINDER_PAT_E: u32 = 4;
46const FINDER_PAT_F: u32 = 5;
47
48const FINDER_PATTERN_SEQUENCES: [&[u32]; 10] = [
49    &[FINDER_PAT_A, FINDER_PAT_A],
50    &[FINDER_PAT_A, FINDER_PAT_B, FINDER_PAT_B],
51    &[FINDER_PAT_A, FINDER_PAT_C, FINDER_PAT_B, FINDER_PAT_D],
52    &[
53        FINDER_PAT_A,
54        FINDER_PAT_E,
55        FINDER_PAT_B,
56        FINDER_PAT_D,
57        FINDER_PAT_C,
58    ],
59    &[
60        FINDER_PAT_A,
61        FINDER_PAT_E,
62        FINDER_PAT_B,
63        FINDER_PAT_D,
64        FINDER_PAT_D,
65        FINDER_PAT_F,
66    ],
67    &[
68        FINDER_PAT_A,
69        FINDER_PAT_E,
70        FINDER_PAT_B,
71        FINDER_PAT_D,
72        FINDER_PAT_E,
73        FINDER_PAT_F,
74        FINDER_PAT_F,
75    ],
76    &[
77        FINDER_PAT_A,
78        FINDER_PAT_A,
79        FINDER_PAT_B,
80        FINDER_PAT_B,
81        FINDER_PAT_C,
82        FINDER_PAT_C,
83        FINDER_PAT_D,
84        FINDER_PAT_D,
85    ],
86    &[
87        FINDER_PAT_A,
88        FINDER_PAT_A,
89        FINDER_PAT_B,
90        FINDER_PAT_B,
91        FINDER_PAT_C,
92        FINDER_PAT_C,
93        FINDER_PAT_D,
94        FINDER_PAT_E,
95        FINDER_PAT_E,
96    ],
97    &[
98        FINDER_PAT_A,
99        FINDER_PAT_A,
100        FINDER_PAT_B,
101        FINDER_PAT_B,
102        FINDER_PAT_C,
103        FINDER_PAT_C,
104        FINDER_PAT_D,
105        FINDER_PAT_E,
106        FINDER_PAT_F,
107        FINDER_PAT_F,
108    ],
109    &[
110        FINDER_PAT_A,
111        FINDER_PAT_A,
112        FINDER_PAT_B,
113        FINDER_PAT_B,
114        FINDER_PAT_C,
115        FINDER_PAT_D,
116        FINDER_PAT_D,
117        FINDER_PAT_E,
118        FINDER_PAT_E,
119        FINDER_PAT_F,
120        FINDER_PAT_F,
121    ],
122];
123
124/**
125 * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)
126 * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)
127 */
128#[derive(Default)]
129pub struct RSSExpandedReader {
130    decodeFinderCounters: [u32; 4],
131    dataCharacterCounters: [u32; 8],
132    oddRoundingErrors: [f32; 4],
133    evenRoundingErrors: [f32; 4],
134    oddCounts: [u32; 4],
135    evenCounts: [u32; 4],
136
137    pub(super) pairs: Vec<ExpandedPair>,
138    pub(super) rows: Vec<ExpandedRow>,
139    startEnd: [u32; 2],
140    startFromEven: bool,
141}
142impl AbstractRSSReaderTrait for RSSExpandedReader {}
143impl OneDReader for RSSExpandedReader {
144    fn decode_row(
145        &mut self,
146        rowNumber: u32,
147        row: &crate::common::BitArray,
148        _hints: &DecodeHints,
149    ) -> Result<crate::RXingResult> {
150        // Rows can start with even pattern in case in prev rows there where odd number of patters.
151        // So lets try twice
152        self.pairs.clear();
153        self.startFromEven = false;
154        if let Ok(decoded_two_pairs) = self.decodeRow2pairs(rowNumber, row) {
155            if let Ok(possible_result) = Self::constructRXingResult(&decoded_two_pairs) {
156                return Ok(possible_result);
157            }
158        }
159
160        self.pairs.clear();
161        self.startFromEven = true;
162        Self::constructRXingResult(&self.decodeRow2pairs(rowNumber, row)?)
163    }
164}
165impl Reader for RSSExpandedReader {
166    fn decode<B: Binarizer>(&mut self, image: &mut crate::BinaryBitmap<B>) -> Result<RXingResult> {
167        self.decode_with_hints(image, &DecodeHints::default())
168    }
169
170    // Note that we don't try rotation without the try harder flag, even if rotation was supported.
171    fn decode_with_hints<B: Binarizer>(
172        &mut self,
173        image: &mut crate::BinaryBitmap<B>,
174        hints: &DecodeHints,
175    ) -> Result<crate::RXingResult> {
176        if let Ok(res) = self._do_decode(image, hints) {
177            Ok(res)
178        } else {
179            let tryHarder = hints.TryHarder.unwrap_or(false);
180            if tryHarder && image.is_rotate_supported() {
181                let mut rotatedImage = image.rotate_counter_clockwise();
182                let mut result = self._do_decode(&mut rotatedImage, hints)?;
183                // Record that we found it rotated 90 degrees CCW / 270 degrees CW
184                let metadata = result.getRXingResultMetadata();
185                let mut orientation = 270;
186                if metadata.contains_key(&RXingResultMetadataType::ORIENTATION) {
187                    // But if we found it reversed in doDecode(), add in that result here:
188                    orientation = (orientation
189                        + if let Some(crate::RXingResultMetadataValue::Orientation(or)) =
190                            metadata.get(&RXingResultMetadataType::ORIENTATION)
191                        {
192                            *or
193                        } else {
194                            0
195                        })
196                        % 360;
197                }
198                result.putMetadata(
199                    RXingResultMetadataType::ORIENTATION,
200                    RXingResultMetadataValue::Orientation(orientation),
201                );
202                // Update result points
203                let height = rotatedImage.get_height();
204
205                let total_points = result.getPoints().len();
206                let points = result.getPointsMut();
207                for point in points.iter_mut().take(total_points) {
208                    std::mem::swap(&mut point.x, &mut point.y);
209                    point.x = height as f32 - point.x - 1.0;
210                }
211
212                Ok(result)
213            } else {
214                Err(Exceptions::NOT_FOUND)
215            }
216        }
217    }
218
219    fn reset(&mut self) {
220        self.pairs.clear();
221        self.rows.clear();
222    }
223}
224
225impl RSSExpandedReader {
226    pub fn new() -> Self {
227        Self::default()
228    }
229    const SYMBOL_WIDEST: [u32; 5] = [7, 5, 4, 3, 1];
230    const EVEN_TOTAL_SUBSET: [u32; 5] = [4, 20, 52, 104, 204];
231    const GSUM: [u32; 5] = [0, 348, 1388, 2948, 3988];
232
233    const FINDER_PATTERNS: [[u32; 4]; 6] = [
234        [1, 8, 4, 1], // A
235        [3, 6, 4, 1], // B
236        [3, 4, 6, 1], // C
237        [3, 2, 8, 1], // D
238        [2, 6, 5, 1], // E
239        [2, 2, 9, 1], // F
240    ];
241
242    const WEIGHTS: [[u32; 8]; 23] = [
243        [1, 3, 9, 27, 81, 32, 96, 77],
244        [20, 60, 180, 118, 143, 7, 21, 63],
245        [189, 145, 13, 39, 117, 140, 209, 205],
246        [193, 157, 49, 147, 19, 57, 171, 91],
247        [62, 186, 136, 197, 169, 85, 44, 132],
248        [185, 133, 188, 142, 4, 12, 36, 108],
249        [113, 128, 173, 97, 80, 29, 87, 50],
250        [150, 28, 84, 41, 123, 158, 52, 156],
251        [46, 138, 203, 187, 139, 206, 196, 166],
252        [76, 17, 51, 153, 37, 111, 122, 155],
253        [43, 129, 176, 106, 107, 110, 119, 146],
254        [16, 48, 144, 10, 30, 90, 59, 177],
255        [109, 116, 137, 200, 178, 112, 125, 164],
256        [70, 210, 208, 202, 184, 130, 179, 115],
257        [134, 191, 151, 31, 93, 68, 204, 190],
258        [148, 22, 66, 198, 172, 94, 71, 2],
259        [6, 18, 54, 162, 64, 192, 154, 40],
260        [120, 149, 25, 75, 14, 42, 126, 167],
261        [79, 26, 78, 23, 69, 207, 199, 175],
262        [103, 98, 83, 38, 114, 131, 182, 124],
263        [161, 61, 183, 127, 170, 88, 53, 159],
264        [55, 165, 73, 8, 24, 72, 5, 15],
265        [45, 135, 194, 160, 58, 174, 100, 89],
266    ];
267
268    #[allow(dead_code)]
269    const MAX_PAIRS: usize = 11;
270
271    // Not private for testing
272    pub(super) fn decodeRow2pairs(
273        &mut self,
274        rowNumber: u32,
275        row: &BitArray,
276    ) -> Result<Vec<ExpandedPair>> {
277        let mut done = false;
278        while !done {
279            let previousPairs = self.pairs.clone();
280            let to_add_res = self.retrieveNextPair(row, &previousPairs, rowNumber);
281            if let Ok(to_add) = to_add_res {
282                self.pairs.push(to_add);
283            } else if self.pairs.is_empty() {
284                return Err(to_add_res.err().unwrap_or(Exceptions::ILLEGAL_STATE));
285            } else {
286                // exit this loop when retrieveNextPair() fails and throws
287                done = true;
288            }
289        }
290
291        // TODO: verify sequence of finder patterns as in checkPairSequence()
292        if self.checkChecksum() {
293            return Ok(self.pairs.clone());
294        }
295
296        let tryStackedDecode = !self.rows.is_empty();
297        self.storeRow(rowNumber); // TODO: deal with reversed rows
298        if tryStackedDecode {
299            // When the image is 180-rotated, then rows are sorted in wrong direction.
300            // Try twice with both the directions.
301            let ps = self.checkRows(false).or(self.checkRows(true));
302            if let Some(ps) = ps {
303                return Ok(ps);
304            }
305
306            // let ps = self.checkRows(false);
307            // if let Some(ps) = ps {
308            //     return Ok(ps);
309            // }
310            // let ps = self.checkRows(true);
311            // if let Some(ps) = ps {
312            //     return Ok(ps);
313            // }
314        }
315
316        Err(Exceptions::NOT_FOUND)
317    }
318
319    fn checkRows(&mut self, reverse: bool) -> Option<Vec<ExpandedPair>> {
320        // Limit number of rows we are checking
321        // We use recursive algorithm with pure complexity and don't want it to take forever
322        // Stacked barcode can have up to 11 rows, so 25 seems reasonable enough
323        if self.rows.len() > 25 {
324            self.rows.clear(); // We will never have a chance to get result, so clear it
325            return None;
326        }
327
328        self.pairs.clear();
329        if reverse {
330            self.rows.reverse();
331        }
332
333        let mut c_rows = Vec::new();
334        let ps = self.checkRowsDetails(&mut c_rows, 0).ok();
335        // let ps = if let Ok(res) = self.checkRowsDetails(&mut c_rows, 0) {
336        //     Some(res)
337        // } else {
338        //     None
339        // };
340        // } catch (NotFoundException e) {
341        // OK
342        // }
343
344        if reverse {
345            self.rows.reverse();
346        }
347
348        ps
349    }
350
351    // Try to construct a valid rows sequence
352    // Recursion is used to implement backtracking
353    fn checkRowsDetails(
354        &mut self,
355        collectedRows: &mut Vec<ExpandedRow>,
356        currentRow: usize,
357    ) -> Result<Vec<ExpandedPair>> {
358        for i in currentRow..self.rows.len() {
359            // for (int i = currentRow; i < rows.size(); i++) {
360            let row = self.rows.get(i).ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?;
361
362            self.pairs.extend_from_slice(row.getPairs());
363
364            let addSize = row.getPairs().len();
365
366            if Self::isValidSequence(&self.pairs) {
367                if self.checkChecksum() {
368                    return Ok(self.pairs.clone());
369                }
370
371                collectedRows.push(row.clone());
372
373                // Recursion: try to add more rows
374                if let Ok(cr) = self.checkRowsDetails(collectedRows, i + 1) {
375                    return Ok(cr);
376                } else {
377                    // collectedRows.remove(collectedRows.len() - 1);
378                    collectedRows.truncate(collectedRows.len() - 1);
379                    self.pairs.truncate(self.pairs.len() - addSize);
380                }
381            } else {
382                self.pairs.truncate(self.pairs.len() - addSize);
383            }
384        }
385
386        Err(Exceptions::NOT_FOUND)
387    }
388
389    /// Whether the pairs form a valid find pattern sequence,
390    /// either complete or a prefix
391    ///
392    /// Could potentially panic in a very out of bounds situation
393    fn isValidSequence(pairs: &[ExpandedPair]) -> bool {
394        for sequence in FINDER_PATTERN_SEQUENCES.iter() {
395            // for i in 0..FINDER_PATTERN_SEQUENCES.len() {
396            // for sequence in &FINDER_PATTERN_SEQUENCES.iter() {
397            // let sequence = FINDER_PATTERN_SEQUENCES.get(i).unwrap();
398            // for (int[] sequence : FINDER_PATTERN_SEQUENCES) {
399            if pairs.len() <= sequence.len() {
400                let stop = sequence
401                    .iter()
402                    .enumerate()
403                    .take(pairs.len())
404                    .all(|(j, seq)| {
405                        pairs
406                            .get(j)
407                            .unwrap()
408                            .getFinderPattern()
409                            .as_ref()
410                            .unwrap()
411                            .getValue()
412                            == *seq
413                    });
414
415                // let mut stop = true;
416                // for (j, seq) in sequence.iter().enumerate().take(pairs.len()) {
417                //     // for (int j = 0; j < pairs.size(); j++) {
418                //     if pairs
419                //         .get(j)
420                //         .unwrap()
421                //         .getFinderPattern()
422                //         .as_ref()
423                //         .unwrap()
424                //         .getValue()
425                //         != *seq
426                //     {
427                //         stop = false;
428                //         break;
429                //         // return true;
430                //     }
431                // }
432                if stop {
433                    return true;
434                }
435            }
436        }
437
438        false
439    }
440
441    fn storeRow(&mut self, rowNumber: u32) {
442        // Discard if duplicate above or below; otherwise insert in order by row number.
443        let mut insertPos = 0;
444        let mut prevIsSame = false;
445        let mut nextIsSame = false;
446        while insertPos < self.rows.len() {
447            if let Some(erow) = self.rows.get(insertPos) {
448                if erow.getRowNumber() > rowNumber {
449                    nextIsSame = erow.isEquivalent(&self.pairs);
450                    break;
451                }
452                prevIsSame = erow.isEquivalent(&self.pairs);
453                insertPos += 1;
454            }
455        }
456        if nextIsSame || prevIsSame {
457            return;
458        }
459
460        // When the row was partially decoded (e.g. 2 pairs found instead of 3),
461        // it will prevent us from detecting the barcode.
462        // Try to merge partial rows
463
464        // Check whether the row is part of an already detected row
465        if Self::isPartialRow(&self.pairs, &self.rows) {
466            return;
467        }
468
469        self.rows
470            .insert(insertPos, ExpandedRow::new(self.pairs.clone(), rowNumber));
471
472        Self::removePartialRows(&self.pairs, &mut self.rows);
473    }
474
475    // Remove all the rows that contains only specified pairs
476    fn removePartialRows(pairs: &[ExpandedPair], rows: &mut Vec<ExpandedRow>) {
477        rows.retain(|row| {
478            // let mut allFound = true;
479            if row.getPairs().len() != pairs.len() {
480                !row.getPairs().iter().all(|p| pairs.contains(p))
481                // for p in row.getPairs() {
482                //     // for (ExpandedPair p : r.getPairs()) {
483                //     if !pairs.contains(p) {
484                //         allFound = false;
485                //         break;
486                //     }
487                // }
488            } else {
489                true
490            }
491        });
492    }
493
494    // Returns true when one of the rows already contains all the pairs
495    fn isPartialRow(pairs: &[ExpandedPair], rows: &[ExpandedRow]) -> bool {
496        rows.iter()
497            .any(|r| !pairs.iter().any(|p| !r.getPairs().contains(p)))
498        // for r in rows {
499        //     let allFound = !pairs.iter().any(|p| !r.getPairs().contains(p));
500        //     // let mut allFound = true;
501        //     // for p in pairs {
502        //     //     // let mut found = false;
503        //     //     let found = r.getPairs().contains(p);
504        //     //     // for pp in r.getPairs() {
505        //     //     //     if p == pp {
506        //     //     //         found = true;
507        //     //     //         break;
508        //     //     //     }
509        //     //     // }
510        //     //     if !found {
511        //     //         allFound = false;
512        //     //         break;
513        //     //     }
514        //     // }
515        //     if allFound {
516        //         // the row 'r' contain all the pairs from 'pairs'
517        //         return true;
518        //     }
519        // }
520        // false
521    }
522
523    // Only used for unit testing
524    #[cfg(test)]
525    #[allow(dead_code)]
526    pub(crate) fn getRowsMut(&mut self) -> &mut [ExpandedRow] {
527        &mut self.rows
528    }
529    #[cfg(test)]
530    pub(crate) fn getRows(&self) -> &[ExpandedRow] {
531        &self.rows
532    }
533
534    // Not private for unit testing
535    pub(crate) fn constructRXingResult(pairs: &[ExpandedPair]) -> Result<RXingResult> {
536        let binary = bit_array_builder::buildBitArray(pairs).ok_or(Exceptions::ILLEGAL_STATE)?;
537
538        let mut decoder = abstract_expanded_decoder::createDecoder(&binary)?;
539        let resultingString = decoder.parseInformation()?;
540
541        let firstPoints = pairs
542            .first()
543            .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
544            .getFinderPattern()
545            .as_ref()
546            .ok_or(Exceptions::ILLEGAL_STATE)?
547            .getPoints();
548        let lastPoints = pairs
549            .last()
550            .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
551            .getFinderPattern()
552            .as_ref()
553            .ok_or(Exceptions::ILLEGAL_STATE)?
554            .getPoints();
555
556        let mut result = RXingResult::new(
557            &resultingString,
558            Vec::new(),
559            vec![firstPoints[0], firstPoints[1], lastPoints[0], lastPoints[1]],
560            BarcodeFormat::RSS_EXPANDED,
561        );
562
563        result.putMetadata(
564            RXingResultMetadataType::SYMBOLOGY_IDENTIFIER,
565            RXingResultMetadataValue::SymbologyIdentifier("]e0".to_owned()),
566        );
567
568        Ok(result)
569    }
570
571    fn checkChecksum(&self) -> bool {
572        let Some(firstPair) = self.pairs.first() else {
573            return false;
574        };
575        let checkCharacter = firstPair.getLeftChar();
576        let Some(firstCharacter) = firstPair.getRightChar() else {
577            return false;
578        };
579
580        let mut checksum = firstCharacter.getChecksumPortion();
581        let mut s = 2;
582
583        for currentPair in self.pairs.iter().skip(1) {
584            let Some(currentPairLeftChar) = currentPair.getLeftChar() else {
585                return false;
586            };
587            checksum += currentPairLeftChar.getChecksumPortion();
588            s += 1;
589            if let Some(currentRightChar) = currentPair.getRightChar() {
590                checksum += currentRightChar.getChecksumPortion();
591                s += 1;
592            }
593        }
594
595        checksum %= 211;
596
597        let checkCharacterValue = (211 * (s as i64 - 4) + checksum as i64) as u32;
598
599        if let Some(checkCharacter) = checkCharacter {
600            checkCharacterValue == checkCharacter.getValue()
601        } else {
602            false
603        }
604    }
605
606    fn getNextSecondBar(row: &BitArray, initialPos: usize) -> usize {
607        let mut currentPos;
608        if row.get(initialPos) {
609            currentPos = row.getNextUnset(initialPos);
610            currentPos = row.getNextSet(currentPos);
611        } else {
612            currentPos = row.getNextSet(initialPos);
613            currentPos = row.getNextUnset(currentPos);
614        }
615        currentPos
616    }
617
618    // not private for testing
619    pub(super) fn retrieveNextPair(
620        &mut self,
621        row: &BitArray,
622        previousPairs: &[ExpandedPair],
623        rowNumber: u32,
624    ) -> Result<ExpandedPair> {
625        let mut isOddPattern = previousPairs.len() % 2 == 0;
626        if self.startFromEven {
627            isOddPattern = !isOddPattern;
628        }
629
630        let mut pattern;
631
632        let mut keepFinding = true;
633        let mut forcedOffset = -1_i32;
634        loop {
635            self.findNextPair(row, previousPairs, forcedOffset)?;
636            pattern = self.parseFoundFinderPattern(row, rowNumber, isOddPattern);
637            if pattern.is_none() {
638                forcedOffset = Self::getNextSecondBar(row, self.startEnd[0] as usize) as i32;
639            } else {
640                keepFinding = false;
641            }
642            if !keepFinding {
643                break;
644            }
645        }
646
647        // When stacked symbol is split over multiple rows, there's no way to guess if this pair can be last or not.
648        // boolean mayBeLast = checkPairSequence(previousPairs, pattern);
649
650        let leftChar = self.decodeDataCharacter(
651            row,
652            pattern.as_ref().ok_or(Exceptions::NOT_FOUND)?,
653            isOddPattern,
654            true,
655        )?;
656
657        if !previousPairs.is_empty()
658            && previousPairs
659                .last()
660                .ok_or(Exceptions::NOT_FOUND)?
661                .mustBeLast()
662        {
663            return Err(Exceptions::NOT_FOUND);
664        }
665
666        let rightChar = self
667            .decodeDataCharacter(
668                row,
669                pattern.as_ref().ok_or(Exceptions::NOT_FOUND)?,
670                isOddPattern,
671                false,
672            )
673            .ok();
674
675        Ok(ExpandedPair::new(Some(leftChar), rightChar, pattern))
676    }
677
678    fn findNextPair(
679        &mut self,
680        row: &BitArray,
681        previousPairs: &[ExpandedPair],
682        forcedOffset: i32,
683    ) -> Result<()> {
684        let counters = &mut self.decodeFinderCounters;
685        counters.fill(0);
686        // counters[0] = 0;
687        // counters[1] = 0;
688        // counters[2] = 0;
689        // counters[3] = 0;
690
691        let width = row.get_size();
692
693        let mut rowOffset;
694        if forcedOffset >= 0 {
695            rowOffset = forcedOffset;
696        } else if previousPairs.is_empty() {
697            rowOffset = 0;
698        } else {
699            let lastPair = previousPairs
700                .last()
701                .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?;
702            rowOffset = lastPair
703                .getFinderPattern()
704                .as_ref()
705                .ok_or(Exceptions::ILLEGAL_STATE)?
706                .getStartEnd()[1] as i32;
707        }
708        let mut searchingEvenPair = previousPairs.len() % 2 != 0;
709        if self.startFromEven {
710            searchingEvenPair = !searchingEvenPair;
711        }
712
713        let mut isWhite = false;
714        while rowOffset < width as i32 {
715            isWhite = !row.get(rowOffset as usize);
716            if !isWhite {
717                break;
718            }
719            rowOffset += 1;
720        }
721
722        let mut counterPosition = 0;
723        let mut patternStart = rowOffset;
724        for x in rowOffset..width as i32 {
725            // for (int x = rowOffset; x < width; x++) {
726            if row.get(x as usize) != isWhite {
727                counters[counterPosition] += 1;
728            } else {
729                if counterPosition == 3 {
730                    if searchingEvenPair {
731                        Self::reverseCounters(counters);
732                    }
733
734                    if Self::isFinderPattern(counters) {
735                        self.startEnd[0] = patternStart as u32;
736                        self.startEnd[1] = x as u32;
737                        return Ok(());
738                    }
739
740                    if searchingEvenPair {
741                        Self::reverseCounters(counters);
742                    }
743
744                    patternStart += (counters[0] + counters[1]) as i32;
745                    counters[0] = counters[2];
746                    counters[1] = counters[3];
747                    counters[2] = 0;
748                    counters[3] = 0;
749                    counterPosition -= 1;
750                } else {
751                    counterPosition += 1;
752                }
753                counters[counterPosition] = 1;
754                isWhite = !isWhite;
755            }
756        }
757        Err(Exceptions::NOT_FOUND)
758    }
759
760    fn reverseCounters<const N: usize>(counters: &mut [u32; N]) {
761        counters.reverse();
762    }
763
764    fn parseFoundFinderPattern(
765        &self,
766        row: &BitArray,
767        rowNumber: u32,
768        oddPattern: bool,
769    ) -> Option<FinderPattern> {
770        // Actually we found elements 2-5.
771        let firstCounter;
772        let start;
773        let end;
774
775        if oddPattern {
776            // If pattern number is odd, we need to locate element 1 *before* the current block.
777
778            let mut firstElementStart = self.startEnd[0] as i32 - 1;
779            // Locate element 1
780            while firstElementStart >= 0 && !row.get(firstElementStart as usize) {
781                firstElementStart -= 1;
782            }
783
784            firstElementStart += 1;
785            firstCounter = self.startEnd[0] as i32 - firstElementStart;
786            start = firstElementStart;
787            end = self.startEnd[1];
788        } else {
789            // If pattern number is even, the pattern is reversed, so we need to locate element 1 *after* the current block.
790
791            start = self.startEnd[0] as i32;
792
793            end = row.getNextUnset(self.startEnd[1] as usize + 1) as u32;
794            firstCounter = end as i32 - self.startEnd[1] as i32;
795        }
796
797        // Make 'counters' hold 1-4
798        let mut counters = self.decodeFinderCounters;
799        let counters_len = counters.len();
800        counters.copy_within(..counters_len - 1, 1);
801        // System.arraycopy(counters, 0, counters, 1, counters.length - 1);
802
803        counters[0] = firstCounter as u32;
804        let value = Self::parseFinderValue(&counters, &Self::FINDER_PATTERNS).ok()?;
805
806        Some(FinderPattern::new(
807            value,
808            [start as usize, end as usize],
809            start as usize,
810            end as usize,
811            rowNumber,
812        ))
813    }
814
815    pub(super) fn decodeDataCharacter(
816        &mut self,
817        row: &BitArray,
818        pattern: &FinderPattern,
819        isOddPattern: bool,
820        leftChar: bool,
821    ) -> Result<DataCharacter> {
822        let counters = &mut self.dataCharacterCounters;
823        counters.fill(0);
824
825        if leftChar {
826            record_pattern_in_reverse(row, pattern.getStartEnd()[0], counters)?;
827        } else {
828            record_pattern(row, pattern.getStartEnd()[1], counters)?;
829            // reverse it
830            counters.reverse();
831        } //counters[] has the pixels of the module
832
833        //left and right data characters have all the same length
834        let numModules = 17;
835
836        let elementWidth: f32 = (counters.iter().sum::<u32>() as f32) / numModules as f32;
837
838        // Logic check: element width for pattern and the character should match
839        let expectedElementWidth: f32 =
840            (pattern.getStartEnd()[1] - pattern.getStartEnd()[0]) as f32 / 15.0;
841        if (elementWidth - expectedElementWidth).abs() / expectedElementWidth > 0.3 {
842            return Err(Exceptions::NOT_FOUND);
843        }
844
845        for (i, counter) in counters.iter().enumerate() {
846            // for (int i = 0; i < counters.length; i++) {
847            let value: f32 = 1.0 * (*counter as f32) / elementWidth;
848            let mut count = (value + 0.5) as i32; // Round
849            if count < 1 {
850                if value < 0.3 {
851                    return Err(Exceptions::NOT_FOUND);
852                }
853                count = 1;
854            } else if count > 8 {
855                if value > 8.7 {
856                    return Err(Exceptions::NOT_FOUND);
857                }
858                count = 8;
859            }
860            let offset = i / 2;
861            if (i & 0x01) == 0 {
862                self.oddCounts[offset] = count as u32;
863                self.oddRoundingErrors[offset] = value - count as f32;
864            } else {
865                self.evenCounts[offset] = count as u32;
866                self.evenRoundingErrors[offset] = value - count as f32;
867            }
868        }
869
870        self.adjustOddEvenCounts(numModules as u32)?;
871
872        let weightRowNumber = (4 * pattern.getValue() as isize
873            + (if isOddPattern { 0 } else { 2 })
874            + isize::from(!leftChar)
875            - 1) as usize;
876
877        let mut oddSum = 0;
878        let mut oddChecksumPortion = 0;
879        for i in (0..self.oddCounts.len()).rev() {
880            if Self::isNotA1left(pattern, isOddPattern, leftChar) {
881                let weight = Self::WEIGHTS[weightRowNumber][2 * i];
882                oddChecksumPortion += self.oddCounts[i] * weight;
883            }
884            oddSum += self.oddCounts[i];
885        }
886        let mut evenChecksumPortion = 0;
887        for i in (0..self.evenCounts.len()).rev() {
888            if Self::isNotA1left(pattern, isOddPattern, leftChar) {
889                let weight = Self::WEIGHTS[weightRowNumber][2 * i + 1];
890                evenChecksumPortion += self.evenCounts[i] * weight;
891            }
892        }
893        let checksumPortion = oddChecksumPortion + evenChecksumPortion;
894
895        if (oddSum & 0x01) != 0 || !(4..=13).contains(&oddSum) {
896            return Err(Exceptions::NOT_FOUND);
897        }
898
899        let group = ((13 - oddSum) / 2) as usize;
900        let oddWidest = Self::SYMBOL_WIDEST[group];
901        let evenWidest = 9 - oddWidest;
902        let vOdd = rss_utils::getRSSvalue(&self.oddCounts, oddWidest, true);
903        let vEven = rss_utils::getRSSvalue(&self.evenCounts, evenWidest, false);
904        let tEven = Self::EVEN_TOTAL_SUBSET[group];
905        let gSum = Self::GSUM[group];
906        let value = vOdd * tEven + vEven + gSum;
907
908        Ok(DataCharacter::new(value, checksumPortion))
909    }
910
911    #[inline(always)]
912    fn isNotA1left(pattern: &FinderPattern, isOddPattern: bool, leftChar: bool) -> bool {
913        // A1: pattern.getValue is 0 (A), and it's an oddPattern, and it is a left char
914        !(pattern.getValue() == 0 && isOddPattern && leftChar)
915    }
916
917    fn adjustOddEvenCounts(&mut self, numModules: u32) -> Result<()> {
918        let oddSum = self.oddCounts.iter().sum::<u32>();
919        let evenSum = self.evenCounts.iter().sum::<u32>();
920
921        let mut incrementOdd = false;
922        let mut decrementOdd = false;
923
924        if oddSum > 13 {
925            decrementOdd = true;
926        } else if oddSum < 4 {
927            incrementOdd = true;
928        }
929        let mut incrementEven = false;
930        let mut decrementEven = false;
931        if evenSum > 13 {
932            decrementEven = true;
933        } else if evenSum < 4 {
934            incrementEven = true;
935        }
936
937        let mismatch = oddSum as isize + evenSum as isize - numModules as isize;
938        let oddParityBad = (oddSum & 0x01) == 1;
939        let evenParityBad = (evenSum & 0x01) == 0;
940        match mismatch {
941            1 => {
942                if oddParityBad {
943                    if evenParityBad {
944                        return Err(Exceptions::NOT_FOUND);
945                    }
946                    decrementOdd = true;
947                } else {
948                    if !evenParityBad {
949                        return Err(Exceptions::NOT_FOUND);
950                    }
951                    decrementEven = true;
952                }
953            }
954            -1 => {
955                if oddParityBad {
956                    if evenParityBad {
957                        return Err(Exceptions::NOT_FOUND);
958                    }
959                    incrementOdd = true;
960                } else {
961                    if !evenParityBad {
962                        return Err(Exceptions::NOT_FOUND);
963                    }
964                    incrementEven = true;
965                }
966            }
967            0 => {
968                if oddParityBad {
969                    if !evenParityBad {
970                        return Err(Exceptions::NOT_FOUND);
971                    }
972                    // Both bad
973                    if oddSum < evenSum {
974                        incrementOdd = true;
975                        decrementEven = true;
976                    } else {
977                        decrementOdd = true;
978                        incrementEven = true;
979                    }
980                } else if evenParityBad {
981                    return Err(Exceptions::NOT_FOUND);
982                }
983            }
984
985            _ => return Err(Exceptions::NOT_FOUND),
986        }
987
988        if incrementOdd {
989            if decrementOdd {
990                return Err(Exceptions::NOT_FOUND);
991            }
992            Self::increment(&mut self.oddCounts, &self.oddRoundingErrors);
993        }
994        if decrementOdd {
995            Self::decrement(&mut self.oddCounts, &self.oddRoundingErrors);
996        }
997        if incrementEven {
998            if decrementEven {
999                return Err(Exceptions::NOT_FOUND);
1000            }
1001            Self::increment(&mut self.evenCounts, &self.oddRoundingErrors);
1002        }
1003        if decrementEven {
1004            Self::decrement(&mut self.evenCounts, &self.evenRoundingErrors);
1005        }
1006
1007        Ok(())
1008    }
1009}