blob: 064f6cc30b70651815b94319970401e968040e08 [file] [log] [blame]
// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
// Original code is licensed as follows:
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <memory>
#include "xfa/fxbarcode/BC_BinaryBitmap.h"
#include "xfa/fxbarcode/BC_ResultPoint.h"
#include "xfa/fxbarcode/common/BC_CommonBitArray.h"
#include "xfa/fxbarcode/common/BC_CommonBitMatrix.h"
#include "xfa/fxbarcode/pdf417/BC_PDF417Detector.h"
#include "xfa/fxbarcode/pdf417/BC_PDF417DetectorResult.h"
#include "xfa/fxbarcode/utils.h"
#define INTEGER_MAX 2147483647
int32_t CBC_Detector::INDEXES_START_PATTERN[] = {0, 4, 1, 5};
int32_t CBC_Detector::INDEXES_STOP_PATTERN[] = {6, 2, 7, 3};
int32_t CBC_Detector::INTEGER_MATH_SHIFT = 8;
int32_t CBC_Detector::PATTERN_MATCH_RESULT_SCALE_FACTOR = 1
<< INTEGER_MATH_SHIFT;
int32_t CBC_Detector::MAX_AVG_VARIANCE =
(int32_t)(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);
int32_t CBC_Detector::MAX_INDIVIDUAL_VARIANCE =
(int32_t)(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f);
int32_t CBC_Detector::START_PATTERN[] = {8, 1, 1, 1, 1, 1, 1, 3};
int32_t CBC_Detector::STOP_PATTERN[] = {7, 1, 1, 3, 1, 1, 1, 2, 1};
int32_t CBC_Detector::MAX_PIXEL_DRIFT = 3;
int32_t CBC_Detector::MAX_PATTERN_DRIFT = 5;
int32_t CBC_Detector::SKIPPED_ROW_COUNT_MAX = 25;
int32_t CBC_Detector::ROW_STEP = 5;
int32_t CBC_Detector::BARCODE_MIN_HEIGHT = 10;
CBC_Detector::CBC_Detector() {}
CBC_Detector::~CBC_Detector() {}
CBC_PDF417DetectorResult* CBC_Detector::detect(CBC_BinaryBitmap* image,
int32_t hints,
FX_BOOL multiple,
int32_t& e) {
CBC_CommonBitMatrix* bitMatrix = image->GetBlackMatrix(e);
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
CBC_ResultPointArrayArray* barcodeCoordinates = detect(multiple, bitMatrix);
if (barcodeCoordinates->GetSize() == 0) {
rotate180(bitMatrix);
barcodeCoordinates = detect(multiple, bitMatrix);
}
if (barcodeCoordinates->GetSize() == 0) {
e = BCExceptionUnSupportedBarcode;
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
}
return new CBC_PDF417DetectorResult(bitMatrix, barcodeCoordinates);
}
void CBC_Detector::rotate180(CBC_CommonBitMatrix* bitMatrix) {
int32_t width = bitMatrix->GetWidth();
int32_t height = bitMatrix->GetHeight();
CBC_CommonBitArray* firstRowBitArray = new CBC_CommonBitArray(width);
CBC_CommonBitArray* secondRowBitArray = new CBC_CommonBitArray(width);
CBC_CommonBitArray* tmpBitArray = new CBC_CommonBitArray(width);
for (int32_t y = 0; y<(height + 1)>> 1; y++) {
CBC_CommonBitArray* temp =
bitMatrix->GetRow(height - 1 - y, secondRowBitArray);
CBC_CommonBitArray* tempfirstRow = firstRowBitArray;
firstRowBitArray = bitMatrix->GetRow(y, tempfirstRow);
delete tempfirstRow;
CBC_CommonBitArray* row = mirror(temp, tmpBitArray);
delete temp;
bitMatrix->SetRow(y, row);
delete row;
CBC_CommonBitArray* rowfirstRow = mirror(firstRowBitArray, tmpBitArray);
bitMatrix->SetRow(height - 1 - y, rowfirstRow);
delete rowfirstRow;
}
delete tmpBitArray;
delete firstRowBitArray;
delete secondRowBitArray;
}
CBC_CommonBitArray* CBC_Detector::mirror(CBC_CommonBitArray* input,
CBC_CommonBitArray* result) {
CBC_CommonBitArray* array = new CBC_CommonBitArray(result->GetSize());
array->Clear();
int32_t size = input->GetSize();
for (int32_t i = 0; i < size; i++) {
if (input->Get(i)) {
array->Set(size - 1 - i);
}
}
return array;
}
CBC_ResultPointArrayArray* CBC_Detector::detect(
FX_BOOL multiple,
CBC_CommonBitMatrix* bitMatrix) {
CBC_ResultPointArrayArray* barcodeCoordinates = new CBC_ResultPointArrayArray;
int32_t row = 0;
int32_t column = 0;
FX_BOOL foundBarcodeInRow = FALSE;
while (row < bitMatrix->GetHeight()) {
CBC_ResultPointArray* vertices = findVertices(bitMatrix, row, column);
if (!vertices->GetAt(0) && !vertices->GetAt(3)) {
if (!foundBarcodeInRow) {
delete vertices;
break;
}
foundBarcodeInRow = FALSE;
column = 0;
for (int32_t i = 0; i < barcodeCoordinates->GetSize(); i++) {
CBC_ResultPointArray* barcodeCoordinate = barcodeCoordinates->GetAt(i);
if (barcodeCoordinate->GetAt(1)) {
row = row > barcodeCoordinate->GetAt(1)->GetY();
}
if (barcodeCoordinate->GetAt(3)) {
row = row > barcodeCoordinate->GetAt(3)->GetY();
}
}
row += ROW_STEP;
delete vertices;
continue;
}
foundBarcodeInRow = TRUE;
barcodeCoordinates->Add(vertices);
if (!multiple) {
break;
}
if (vertices->GetAt(2)) {
column = (int32_t)vertices->GetAt(2)->GetX();
row = (int32_t)vertices->GetAt(2)->GetY();
} else {
column = (int32_t)vertices->GetAt(4)->GetX();
row = (int32_t)vertices->GetAt(4)->GetY();
}
}
return barcodeCoordinates;
}
CBC_ResultPointArray* CBC_Detector::findVertices(CBC_CommonBitMatrix* matrix,
int32_t startRow,
int32_t startColumn) {
int32_t height = matrix->GetHeight();
int32_t width = matrix->GetWidth();
CBC_ResultPointArray* result = new CBC_ResultPointArray;
result->SetSize(8);
std::unique_ptr<CBC_ResultPointArray> startptr(
findRowsWithPattern(matrix, height, width, startRow, startColumn,
START_PATTERN, FX_ArraySize(START_PATTERN)));
copyToResult(result, startptr.get(), INDEXES_START_PATTERN,
FX_ArraySize(INDEXES_START_PATTERN));
if (result->GetAt(4)) {
startColumn = (int32_t)result->GetAt(4)->GetX();
startRow = (int32_t)result->GetAt(4)->GetY();
}
std::unique_ptr<CBC_ResultPointArray> stopptr(
findRowsWithPattern(matrix, height, width, startRow, startColumn,
STOP_PATTERN, FX_ArraySize(STOP_PATTERN)));
copyToResult(result, stopptr.get(), INDEXES_STOP_PATTERN,
FX_ArraySize(INDEXES_STOP_PATTERN));
return result;
}
void CBC_Detector::copyToResult(CBC_ResultPointArray* result,
CBC_ResultPointArray* tmpResult,
int32_t* destinationIndexes,
int32_t destinationLength) {
for (int32_t i = 0; i < destinationLength; i++) {
result->SetAt(destinationIndexes[i], tmpResult->GetAt(i));
}
}
CBC_ResultPointArray* CBC_Detector::findRowsWithPattern(
CBC_CommonBitMatrix* matrix,
int32_t height,
int32_t width,
int32_t startRow,
int32_t startColumn,
int32_t* pattern,
int32_t patternLength) {
CBC_ResultPointArray* result = new CBC_ResultPointArray;
result->SetSize(4);
FX_BOOL found = FALSE;
CFX_Int32Array counters;
counters.SetSize(patternLength);
for (; startRow < height; startRow += ROW_STEP) {
CFX_Int32Array* loc =
findGuardPattern(matrix, startColumn, startRow, width, FALSE, pattern,
patternLength, counters);
if (loc) {
while (startRow > 0) {
CFX_Int32Array* previousRowLoc =
findGuardPattern(matrix, startColumn, --startRow, width, FALSE,
pattern, patternLength, counters);
if (previousRowLoc) {
delete loc;
loc = previousRowLoc;
} else {
startRow++;
break;
}
}
result->SetAt(
0, new CBC_ResultPoint((FX_FLOAT)loc->GetAt(0), (FX_FLOAT)startRow));
result->SetAt(
1, new CBC_ResultPoint((FX_FLOAT)loc->GetAt(1), (FX_FLOAT)startRow));
found = TRUE;
delete loc;
break;
}
}
int32_t stopRow = startRow + 1;
if (found) {
int32_t skippedRowCount = 0;
CFX_Int32Array previousRowLoc;
previousRowLoc.Add((int32_t)result->GetAt(0)->GetX());
previousRowLoc.Add((int32_t)result->GetAt(1)->GetX());
for (; stopRow < height; stopRow++) {
CFX_Int32Array* loc =
findGuardPattern(matrix, previousRowLoc[0], stopRow, width, FALSE,
pattern, patternLength, counters);
if (loc && abs(previousRowLoc[0] - loc->GetAt(0)) < MAX_PATTERN_DRIFT &&
abs(previousRowLoc[1] - loc->GetAt(1)) < MAX_PATTERN_DRIFT) {
previousRowLoc.Copy(*loc);
skippedRowCount = 0;
} else {
if (skippedRowCount > SKIPPED_ROW_COUNT_MAX) {
delete loc;
break;
} else {
skippedRowCount++;
}
}
delete loc;
}
stopRow -= skippedRowCount + 1;
result->SetAt(2, new CBC_ResultPoint((FX_FLOAT)previousRowLoc.GetAt(0),
(FX_FLOAT)stopRow));
result->SetAt(3, new CBC_ResultPoint((FX_FLOAT)previousRowLoc.GetAt(1),
(FX_FLOAT)stopRow));
}
if (stopRow - startRow < BARCODE_MIN_HEIGHT) {
for (int32_t i = 0; i < result->GetSize(); i++) {
result->SetAt(i, nullptr);
}
}
return result;
}
CFX_Int32Array* CBC_Detector::findGuardPattern(CBC_CommonBitMatrix* matrix,
int32_t column,
int32_t row,
int32_t width,
FX_BOOL whiteFirst,
int32_t* pattern,
int32_t patternLength,
CFX_Int32Array& counters) {
for (int32_t i = 0; i < counters.GetSize(); i++) {
counters.SetAt(i, 0);
}
FX_BOOL isWhite = whiteFirst;
int32_t patternStart = column;
int32_t pixelDrift = 0;
CFX_Int32Array* intarray = new CFX_Int32Array;
while (matrix->Get(patternStart, row) && patternStart > 0 &&
pixelDrift++ < MAX_PIXEL_DRIFT) {
patternStart--;
}
int32_t x = patternStart;
int32_t counterPosition = 0;
for (; x < width; x++) {
FX_BOOL pixel = matrix->Get(x, row);
if (pixel ^ isWhite) {
counters[counterPosition]++;
} else {
if (counterPosition == patternLength - 1) {
if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) <
MAX_AVG_VARIANCE) {
intarray->Add(patternStart);
intarray->Add(x);
return intarray;
}
patternStart += counters[0] + counters[1];
for (int32_t l = 2, k = 0; l < patternLength; l++, k++) {
counters.SetAt(k, counters.GetAt(l));
}
counters.SetAt(patternLength - 2, 0);
counters.SetAt(patternLength - 1, 0);
counterPosition--;
} else {
counterPosition++;
}
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
if (counterPosition == patternLength - 1) {
if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) <
MAX_AVG_VARIANCE) {
intarray->Add(patternStart);
intarray->Add(x - 1);
return intarray;
}
}
delete intarray;
return nullptr;
}
int32_t CBC_Detector::patternMatchVariance(CFX_Int32Array& counters,
int32_t* pattern,
int32_t maxIndividualVariance) {
int32_t numCounters = counters.GetSize();
int32_t total = 0;
int32_t patternLength = 0;
for (int32_t i = 0; i < numCounters; i++) {
total += counters[i];
patternLength += pattern[i];
}
if (total < patternLength) {
return INTEGER_MAX;
}
int32_t unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
maxIndividualVariance =
(maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
int32_t totalVariance = 0;
for (int32_t x = 0; x < numCounters; x++) {
int32_t counter = counters[x] << INTEGER_MATH_SHIFT;
int32_t scaledPattern = pattern[x] * unitBarWidth;
int32_t variance = counter > scaledPattern ? counter - scaledPattern
: scaledPattern - counter;
if (variance > maxIndividualVariance) {
return INTEGER_MAX;
}
totalVariance += variance;
}
return totalVariance / total;
}