blob: 3ed1ea14b89d2abaa486e4ed0caf4830ec2a1b77 [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 2007 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 "xfa/fxbarcode/qrcode/BC_QRDetector.h"
#include <algorithm>
#include <memory>
#include "xfa/fxbarcode/BC_ResultPoint.h"
#include "xfa/fxbarcode/common/BC_CommonBitMatrix.h"
#include "xfa/fxbarcode/qrcode/BC_FinderPatternInfo.h"
#include "xfa/fxbarcode/qrcode/BC_QRAlignmentPattern.h"
#include "xfa/fxbarcode/qrcode/BC_QRAlignmentPatternFinder.h"
#include "xfa/fxbarcode/qrcode/BC_QRCoderVersion.h"
#include "xfa/fxbarcode/qrcode/BC_QRDetectorResult.h"
#include "xfa/fxbarcode/qrcode/BC_QRFinderPattern.h"
#include "xfa/fxbarcode/qrcode/BC_QRFinderPatternFinder.h"
#include "xfa/fxbarcode/qrcode/BC_QRGridSampler.h"
CBC_QRDetector::CBC_QRDetector(CBC_CommonBitMatrix* image) : m_image(image) {}
CBC_QRDetector::~CBC_QRDetector() {}
CBC_QRDetectorResult* CBC_QRDetector::Detect(int32_t hints, int32_t& e) {
CBC_QRFinderPatternFinder finder(m_image);
std::unique_ptr<CBC_QRFinderPatternInfo> info(finder.Find(hints, e));
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
CBC_QRDetectorResult* qdr = ProcessFinderPatternInfo(info.get(), e);
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
return qdr;
}
CBC_QRDetectorResult* CBC_QRDetector::ProcessFinderPatternInfo(
CBC_QRFinderPatternInfo* info,
int32_t& e) {
std::unique_ptr<CBC_QRFinderPattern> topLeft(info->GetTopLeft());
std::unique_ptr<CBC_QRFinderPattern> topRight(info->GetTopRight());
std::unique_ptr<CBC_QRFinderPattern> bottomLeft(info->GetBottomLeft());
FX_FLOAT moduleSize =
CalculateModuleSize(topLeft.get(), topRight.get(), bottomLeft.get());
if (moduleSize < 1.0f) {
e = BCExceptionRead;
return nullptr;
}
int32_t dimension = ComputeDimension(topLeft.get(), topRight.get(),
bottomLeft.get(), moduleSize, e);
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
CBC_QRCoderVersion* provisionalVersion =
CBC_QRCoderVersion::GetProvisionalVersionForDimension(dimension, e);
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
int32_t modulesBetweenFPCenters =
provisionalVersion->GetDimensionForVersion() - 7;
CBC_QRAlignmentPattern* alignmentPattern = nullptr;
if (provisionalVersion->GetAlignmentPatternCenters()->GetSize() > 0) {
FX_FLOAT bottomRightX =
topRight->GetX() - topLeft->GetX() + bottomLeft->GetX();
FX_FLOAT bottomRightY =
topRight->GetY() - topLeft->GetY() + bottomLeft->GetY();
FX_FLOAT correctionToTopLeft =
1.0f - 3.0f / (FX_FLOAT)modulesBetweenFPCenters;
FX_FLOAT xtemp = (topLeft->GetX() +
correctionToTopLeft * (bottomRightX - topLeft->GetX()));
int32_t estAlignmentX = (int32_t)xtemp;
FX_FLOAT ytemp = (topLeft->GetY() +
correctionToTopLeft * (bottomRightY - topLeft->GetY()));
int32_t estAlignmentY = (int32_t)ytemp;
for (int32_t i = 4; i <= 16; i <<= 1) {
CBC_QRAlignmentPattern* temp = FindAlignmentInRegion(
moduleSize, estAlignmentX, estAlignmentY, (FX_FLOAT)i, e);
if (temp) {
alignmentPattern = temp;
break;
}
}
}
CBC_CommonBitMatrix* bits =
SampleGrid(m_image, topLeft.get(), topRight.get(), bottomLeft.get(),
(CBC_ResultPoint*)(alignmentPattern), dimension, e);
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
CFX_ArrayTemplate<CBC_ResultPoint*>* points =
new CFX_ArrayTemplate<CBC_ResultPoint*>();
points->Add(bottomLeft.release());
points->Add(topLeft.release());
points->Add(topRight.release());
if (alignmentPattern)
points->Add(alignmentPattern);
return new CBC_QRDetectorResult(bits, points);
}
CBC_CommonBitMatrix* CBC_QRDetector::SampleGrid(
CBC_CommonBitMatrix* image,
CBC_ResultPoint* topLeft,
CBC_ResultPoint* topRight,
CBC_ResultPoint* bottomLeft,
CBC_ResultPoint* alignmentPattern,
int32_t dimension,
int32_t& e) {
FX_FLOAT dimMinusThree = (FX_FLOAT)dimension - 3.5f;
FX_FLOAT bottomRightX;
FX_FLOAT bottomRightY;
FX_FLOAT sourceBottomRightX;
FX_FLOAT sourceBottomRightY;
if (alignmentPattern) {
bottomRightX = alignmentPattern->GetX();
bottomRightY = alignmentPattern->GetY();
sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;
} else {
bottomRightX = (topRight->GetX() - topLeft->GetX()) + bottomLeft->GetX();
bottomRightY = (topRight->GetY() - topLeft->GetY()) + bottomLeft->GetY();
sourceBottomRightX = sourceBottomRightY = dimMinusThree;
}
CBC_QRGridSampler& sampler = CBC_QRGridSampler::GetInstance();
CBC_CommonBitMatrix* cbm = sampler.SampleGrid(
image, dimension, dimension, 3.5f, 3.5f, dimMinusThree, 3.5f,
sourceBottomRightX, sourceBottomRightY, 3.5f, dimMinusThree,
topLeft->GetX(), topLeft->GetY(), topRight->GetX(), topRight->GetY(),
bottomRightX, bottomRightY, bottomLeft->GetX(), bottomLeft->GetY(), e);
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
return cbm;
}
int32_t CBC_QRDetector::ComputeDimension(CBC_ResultPoint* topLeft,
CBC_ResultPoint* topRight,
CBC_ResultPoint* bottomLeft,
FX_FLOAT moduleSize,
int32_t& e) {
int32_t tltrCentersDimension = Round(
CBC_QRFinderPatternFinder::Distance(topLeft, topRight) / moduleSize);
int32_t tlblCentersDimension = Round(
CBC_QRFinderPatternFinder::Distance(topLeft, bottomLeft) / moduleSize);
int32_t dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
switch (dimension & 0x03) {
case 0:
dimension++;
break;
case 2:
dimension--;
break;
case 3: {
e = BCExceptionRead;
BC_EXCEPTION_CHECK_ReturnValue(e, 0);
}
}
return dimension;
}
FX_FLOAT CBC_QRDetector::CalculateModuleSize(CBC_ResultPoint* topLeft,
CBC_ResultPoint* topRight,
CBC_ResultPoint* bottomLeft) {
return (CalculateModuleSizeOneWay(topLeft, topRight) +
CalculateModuleSizeOneWay(topLeft, bottomLeft)) /
2.0f;
}
FX_FLOAT CBC_QRDetector::CalculateModuleSizeOneWay(
CBC_ResultPoint* pattern,
CBC_ResultPoint* otherPattern) {
FX_FLOAT moduleSizeEst1 = SizeOfBlackWhiteBlackRunBothWays(
(int32_t)pattern->GetX(), (int32_t)pattern->GetY(),
(int32_t)otherPattern->GetX(), (int32_t)otherPattern->GetY());
FX_FLOAT moduleSizeEst2 = SizeOfBlackWhiteBlackRunBothWays(
(int32_t)otherPattern->GetX(), (int32_t)otherPattern->GetY(),
(int32_t)pattern->GetX(), (int32_t)pattern->GetY());
if (FXSYS_isnan(moduleSizeEst1)) {
return moduleSizeEst2;
}
if (FXSYS_isnan(moduleSizeEst2)) {
return moduleSizeEst1;
}
return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
}
int32_t CBC_QRDetector::Round(FX_FLOAT d) {
return (int32_t)(d + 0.5f);
}
FX_FLOAT CBC_QRDetector::SizeOfBlackWhiteBlackRunBothWays(int32_t fromX,
int32_t fromY,
int32_t toX,
int32_t toY) {
FX_FLOAT result = SizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
int32_t otherToX = fromX - (toX - fromX);
if (otherToX < 0) {
otherToX = -1;
} else if (otherToX >= m_image->GetWidth()) {
otherToX = m_image->GetWidth();
}
int32_t otherToY = fromY - (toY - fromY);
if (otherToY < 0) {
otherToY = -1;
} else if (otherToY >= m_image->GetHeight()) {
otherToY = m_image->GetHeight();
}
result += SizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
return result - 1.0f;
}
FX_FLOAT CBC_QRDetector::SizeOfBlackWhiteBlackRun(int32_t fromX,
int32_t fromY,
int32_t toX,
int32_t toY) {
FX_BOOL steep = FXSYS_abs(toY - fromY) > FXSYS_abs(toX - fromX);
if (steep) {
int32_t temp = fromX;
fromX = fromY;
fromY = temp;
temp = toX;
toX = toY;
toY = temp;
}
int32_t dx = FXSYS_abs(toX - fromX);
int32_t dy = FXSYS_abs(toY - fromY);
int32_t error = -dx >> 1;
int32_t ystep = fromY < toY ? 1 : -1;
int32_t xstep = fromX < toX ? 1 : -1;
int32_t state = 0;
for (int32_t x = fromX, y = fromY; x != toX; x += xstep) {
int32_t realX = steep ? y : x;
int32_t realY = steep ? x : y;
if (state == 1) {
if (m_image->Get(realX, realY)) {
state++;
}
} else {
if (!m_image->Get(realX, realY)) {
state++;
}
}
if (state == 3) {
int32_t diffX = x - fromX;
int32_t diffY = y - fromY;
return (FX_FLOAT)sqrt((double)(diffX * diffX + diffY * diffY));
}
error += dy;
if (error > 0) {
y += ystep;
error -= dx;
}
}
int32_t diffX = toX - fromX;
int32_t diffY = toY - fromY;
return (FX_FLOAT)sqrt((double)(diffX * diffX + diffY * diffY));
}
CBC_QRAlignmentPattern* CBC_QRDetector::FindAlignmentInRegion(
FX_FLOAT overallEstModuleSize,
int32_t estAlignmentX,
int32_t estAlignmentY,
FX_FLOAT allowanceFactor,
int32_t& e) {
int32_t allowance = (int32_t)(allowanceFactor * overallEstModuleSize);
int32_t alignmentAreaLeftX = std::max(0, estAlignmentX - allowance);
int32_t alignmentAreaRightX =
std::min(m_image->GetWidth() - 1, estAlignmentX + allowance);
if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
e = BCExceptionRead;
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
}
int32_t alignmentAreaTopY = std::max(0, estAlignmentY - allowance);
int32_t alignmentAreaBottomY =
std::min(m_image->GetHeight() - 1, estAlignmentY + allowance);
CBC_QRAlignmentPatternFinder alignmentFinder(
m_image, alignmentAreaLeftX, alignmentAreaTopY,
alignmentAreaRightX - alignmentAreaLeftX,
alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize);
CBC_QRAlignmentPattern* qap = alignmentFinder.Find(e);
BC_EXCEPTION_CHECK_ReturnValue(e, nullptr);
return qap;
}