| // 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 2008 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/datamatrix/BC_DataMatrixDetector.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "xfa/fxbarcode/BC_ResultPoint.h" |
| #include "xfa/fxbarcode/common/BC_CommonBitMatrix.h" |
| #include "xfa/fxbarcode/common/BC_WhiteRectangleDetector.h" |
| #include "xfa/fxbarcode/qrcode/BC_QRDetectorResult.h" |
| #include "xfa/fxbarcode/qrcode/BC_QRFinderPatternFinder.h" |
| #include "xfa/fxbarcode/qrcode/BC_QRGridSampler.h" |
| #include "xfa/fxbarcode/utils.h" |
| |
| const int32_t CBC_DataMatrixDetector::INTEGERS[5] = {0, 1, 2, 3, 4}; |
| CBC_DataMatrixDetector::CBC_DataMatrixDetector(CBC_CommonBitMatrix* image) |
| : m_image(image), m_rectangleDetector(nullptr) {} |
| void CBC_DataMatrixDetector::Init(int32_t& e) { |
| m_rectangleDetector = new CBC_WhiteRectangleDetector(m_image); |
| m_rectangleDetector->Init(e); |
| BC_EXCEPTION_CHECK_ReturnVoid(e); |
| } |
| CBC_DataMatrixDetector::~CBC_DataMatrixDetector() { |
| delete m_rectangleDetector; |
| } |
| |
| CBC_QRDetectorResult* CBC_DataMatrixDetector::Detect(int32_t& e) { |
| CFX_ArrayTemplate<CBC_ResultPoint*>* cornerPoints = |
| m_rectangleDetector->Detect(e); |
| BC_EXCEPTION_CHECK_ReturnValue(e, nullptr); |
| CBC_ResultPoint* pointA = (*cornerPoints)[0]; |
| CBC_ResultPoint* pointB = (*cornerPoints)[1]; |
| CBC_ResultPoint* pointC = (*cornerPoints)[2]; |
| CBC_ResultPoint* pointD = (*cornerPoints)[3]; |
| delete cornerPoints; |
| |
| CFX_ArrayTemplate<CBC_ResultPointsAndTransitions*> transitions; |
| transitions.Add(TransitionsBetween(pointA, pointB)); |
| transitions.Add(TransitionsBetween(pointA, pointC)); |
| transitions.Add(TransitionsBetween(pointB, pointD)); |
| transitions.Add(TransitionsBetween(pointC, pointD)); |
| std::sort(transitions.GetData(), |
| transitions.GetData() + transitions.GetSize(), |
| [](const CBC_ResultPointsAndTransitions* a, |
| const CBC_ResultPointsAndTransitions* b) { |
| return a->GetTransitions() < b->GetTransitions(); |
| }); |
| delete transitions[2]; |
| delete transitions[3]; |
| |
| CBC_ResultPointsAndTransitions* lSideOne = transitions[0]; |
| CBC_ResultPointsAndTransitions* lSideTwo = transitions[1]; |
| CFX_MapPtrTemplate<CBC_ResultPoint*, int32_t> pointCount; |
| Increment(pointCount, lSideOne->GetFrom()); |
| Increment(pointCount, lSideOne->GetTo()); |
| Increment(pointCount, lSideTwo->GetFrom()); |
| Increment(pointCount, lSideTwo->GetTo()); |
| delete transitions[1]; |
| delete transitions[0]; |
| transitions.RemoveAll(); |
| CBC_ResultPoint* maybeTopLeft = nullptr; |
| CBC_ResultPoint* bottomLeft = nullptr; |
| CBC_ResultPoint* maybeBottomRight = nullptr; |
| FX_POSITION itBegin = pointCount.GetStartPosition(); |
| while (itBegin) { |
| CBC_ResultPoint* key = 0; |
| int32_t value = 0; |
| pointCount.GetNextAssoc(itBegin, key, value); |
| if (value == 2) { |
| bottomLeft = key; |
| } else { |
| if (maybeBottomRight) { |
| maybeTopLeft = key; |
| } else { |
| maybeBottomRight = key; |
| } |
| } |
| } |
| if (!maybeTopLeft || !bottomLeft || !maybeBottomRight) { |
| delete pointA; |
| delete pointB; |
| delete pointC; |
| delete pointD; |
| e = BCExceptionNotFound; |
| return nullptr; |
| } |
| CFX_ArrayTemplate<CBC_ResultPoint*> corners; |
| corners.SetSize(3); |
| corners[0] = maybeTopLeft; |
| corners[1] = bottomLeft; |
| corners[2] = maybeBottomRight; |
| OrderBestPatterns(&corners); |
| CBC_ResultPoint* bottomRight = corners[0]; |
| bottomLeft = corners[1]; |
| CBC_ResultPoint* topLeft = corners[2]; |
| CBC_ResultPoint* topRight = nullptr; |
| int32_t value; |
| if (!pointCount.Lookup(pointA, value)) { |
| topRight = pointA; |
| } else if (!pointCount.Lookup(pointB, value)) { |
| topRight = pointB; |
| } else if (!pointCount.Lookup(pointC, value)) { |
| topRight = pointC; |
| } else { |
| topRight = pointD; |
| } |
| int32_t dimensionTop = std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(topLeft, topRight)) |
| ->GetTransitions(); |
| int32_t dimensionRight = std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(bottomRight, topRight)) |
| ->GetTransitions(); |
| if ((dimensionTop & 0x01) == 1) { |
| dimensionTop++; |
| } |
| dimensionTop += 2; |
| if ((dimensionRight & 0x01) == 1) { |
| dimensionRight++; |
| } |
| dimensionRight += 2; |
| std::unique_ptr<CBC_CommonBitMatrix> bits; |
| std::unique_ptr<CBC_ResultPoint> correctedTopRight; |
| if (4 * dimensionTop >= 7 * dimensionRight || |
| 4 * dimensionRight >= 7 * dimensionTop) { |
| correctedTopRight.reset( |
| CorrectTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight, |
| dimensionTop, dimensionRight)); |
| if (!correctedTopRight.get()) { |
| correctedTopRight.reset(topRight); |
| } else { |
| delete topRight; |
| topRight = nullptr; |
| } |
| dimensionTop = std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(topLeft, correctedTopRight.get())) |
| ->GetTransitions(); |
| dimensionRight = |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(bottomRight, correctedTopRight.get())) |
| ->GetTransitions(); |
| if ((dimensionTop & 0x01) == 1) { |
| dimensionTop++; |
| } |
| if ((dimensionRight & 0x01) == 1) { |
| dimensionRight++; |
| } |
| bits.reset(SampleGrid(m_image, topLeft, bottomLeft, bottomRight, |
| correctedTopRight.get(), dimensionTop, dimensionRight, |
| e)); |
| BC_EXCEPTION_CHECK_ReturnValue(e, nullptr); |
| } else { |
| int32_t dimension = std::min(dimensionRight, dimensionTop); |
| correctedTopRight.reset( |
| CorrectTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension)); |
| if (!correctedTopRight.get()) { |
| correctedTopRight.reset(topRight); |
| } else { |
| delete topRight; |
| topRight = nullptr; |
| } |
| int32_t dimensionCorrected = |
| std::max(std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(topLeft, correctedTopRight.get())) |
| ->GetTransitions(), |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(bottomRight, correctedTopRight.get())) |
| ->GetTransitions()); |
| dimensionCorrected++; |
| if ((dimensionCorrected & 0x01) == 1) { |
| dimensionCorrected++; |
| } |
| bits.reset(SampleGrid(m_image, topLeft, bottomLeft, bottomRight, |
| correctedTopRight.get(), dimensionCorrected, |
| dimensionCorrected, e)); |
| BC_EXCEPTION_CHECK_ReturnValue(e, nullptr); |
| } |
| CFX_ArrayTemplate<CBC_ResultPoint*>* result = |
| new CFX_ArrayTemplate<CBC_ResultPoint*>(); |
| result->SetSize(4); |
| result->Add(topLeft); |
| result->Add(bottomLeft); |
| result->Add(bottomRight); |
| result->Add(correctedTopRight.release()); |
| return new CBC_QRDetectorResult(bits.release(), result); |
| } |
| CBC_ResultPoint* CBC_DataMatrixDetector::CorrectTopRightRectangular( |
| CBC_ResultPoint* bottomLeft, |
| CBC_ResultPoint* bottomRight, |
| CBC_ResultPoint* topLeft, |
| CBC_ResultPoint* topRight, |
| int32_t dimensionTop, |
| int32_t dimensionRight) { |
| FX_FLOAT corr = Distance(bottomLeft, bottomRight) / (FX_FLOAT)dimensionTop; |
| int32_t norm = Distance(topLeft, topRight); |
| FX_FLOAT cos = (topRight->GetX() - topLeft->GetX()) / norm; |
| FX_FLOAT sin = (topRight->GetY() - topLeft->GetY()) / norm; |
| std::unique_ptr<CBC_ResultPoint> c1(new CBC_ResultPoint( |
| topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); |
| corr = Distance(bottomLeft, topLeft) / (FX_FLOAT)dimensionRight; |
| norm = Distance(bottomRight, topRight); |
| cos = (topRight->GetX() - bottomRight->GetX()) / norm; |
| sin = (topRight->GetY() - bottomRight->GetY()) / norm; |
| std::unique_ptr<CBC_ResultPoint> c2(new CBC_ResultPoint( |
| topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); |
| if (!IsValid(c1.get())) { |
| if (IsValid(c2.get())) { |
| return c2.release(); |
| } |
| return nullptr; |
| } else if (!IsValid(c2.get())) { |
| return c1.release(); |
| } |
| int32_t l1 = FXSYS_abs(dimensionTop - |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(topLeft, c1.get())) |
| ->GetTransitions()) + |
| FXSYS_abs(dimensionRight - |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(bottomRight, c1.get())) |
| ->GetTransitions()); |
| int32_t l2 = FXSYS_abs(dimensionTop - |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(topLeft, c2.get())) |
| ->GetTransitions()) + |
| FXSYS_abs(dimensionRight - |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(bottomRight, c2.get())) |
| ->GetTransitions()); |
| if (l1 <= l2) { |
| return c1.release(); |
| } |
| return c2.release(); |
| } |
| CBC_ResultPoint* CBC_DataMatrixDetector::CorrectTopRight( |
| CBC_ResultPoint* bottomLeft, |
| CBC_ResultPoint* bottomRight, |
| CBC_ResultPoint* topLeft, |
| CBC_ResultPoint* topRight, |
| int32_t dimension) { |
| FX_FLOAT corr = Distance(bottomLeft, bottomRight) / (FX_FLOAT)dimension; |
| int32_t norm = Distance(topLeft, topRight); |
| FX_FLOAT cos = (topRight->GetX() - topLeft->GetX()) / norm; |
| FX_FLOAT sin = (topRight->GetY() - topLeft->GetY()) / norm; |
| std::unique_ptr<CBC_ResultPoint> c1(new CBC_ResultPoint( |
| topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); |
| corr = Distance(bottomLeft, bottomRight) / (FX_FLOAT)dimension; |
| norm = Distance(bottomRight, topRight); |
| cos = (topRight->GetX() - bottomRight->GetX()) / norm; |
| sin = (topRight->GetY() - bottomRight->GetY()) / norm; |
| std::unique_ptr<CBC_ResultPoint> c2(new CBC_ResultPoint( |
| topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); |
| if (!IsValid(c1.get())) { |
| if (IsValid(c2.get())) { |
| return c2.release(); |
| } |
| return nullptr; |
| } else if (!IsValid(c2.get())) { |
| return c1.release(); |
| } |
| int32_t l1 = FXSYS_abs(std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(topLeft, c1.get())) |
| ->GetTransitions() - |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(bottomRight, c1.get())) |
| ->GetTransitions()); |
| int32_t l2 = FXSYS_abs(std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(topLeft, c2.get())) |
| ->GetTransitions() - |
| std::unique_ptr<CBC_ResultPointsAndTransitions>( |
| TransitionsBetween(bottomRight, c2.get())) |
| ->GetTransitions()); |
| return l1 <= l2 ? c1.release() : c2.release(); |
| } |
| FX_BOOL CBC_DataMatrixDetector::IsValid(CBC_ResultPoint* p) { |
| return p->GetX() >= 0 && p->GetX() < m_image->GetWidth() && p->GetY() > 0 && |
| p->GetY() < m_image->GetHeight(); |
| } |
| int32_t CBC_DataMatrixDetector::Round(FX_FLOAT d) { |
| return (int32_t)(d + 0.5f); |
| } |
| int32_t CBC_DataMatrixDetector::Distance(CBC_ResultPoint* a, |
| CBC_ResultPoint* b) { |
| return Round( |
| (FX_FLOAT)sqrt((a->GetX() - b->GetX()) * (a->GetX() - b->GetX()) + |
| (a->GetY() - b->GetY()) * (a->GetY() - b->GetY()))); |
| } |
| void CBC_DataMatrixDetector::Increment( |
| CFX_MapPtrTemplate<CBC_ResultPoint*, int32_t>& table, |
| CBC_ResultPoint* key) { |
| int32_t value; |
| if (table.Lookup(key, value)) { |
| table.SetAt(key, INTEGERS[value + 1]); |
| } else { |
| table.SetAt(key, INTEGERS[1]); |
| } |
| } |
| CBC_CommonBitMatrix* CBC_DataMatrixDetector::SampleGrid( |
| CBC_CommonBitMatrix* image, |
| CBC_ResultPoint* topLeft, |
| CBC_ResultPoint* bottomLeft, |
| CBC_ResultPoint* bottomRight, |
| CBC_ResultPoint* topRight, |
| int32_t dimensionX, |
| int32_t dimensionY, |
| int32_t& e) { |
| CBC_QRGridSampler& sampler = CBC_QRGridSampler::GetInstance(); |
| CBC_CommonBitMatrix* cbm = sampler.SampleGrid( |
| image, dimensionX, dimensionY, 0.5f, 0.5f, dimensionX - 0.5f, 0.5f, |
| dimensionX - 0.5f, dimensionY - 0.5f, 0.5f, dimensionY - 0.5f, |
| topLeft->GetX(), topLeft->GetY(), topRight->GetX(), topRight->GetY(), |
| bottomRight->GetX(), bottomRight->GetY(), bottomLeft->GetX(), |
| bottomLeft->GetY(), e); |
| BC_EXCEPTION_CHECK_ReturnValue(e, nullptr); |
| return cbm; |
| } |
| CBC_ResultPointsAndTransitions* CBC_DataMatrixDetector::TransitionsBetween( |
| CBC_ResultPoint* from, |
| CBC_ResultPoint* to) { |
| int32_t fromX = (int32_t)from->GetX(); |
| int32_t fromY = (int32_t)from->GetY(); |
| int32_t toX = (int32_t)to->GetX(); |
| int32_t toY = (int32_t)to->GetY(); |
| 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 transitions = 0; |
| FX_BOOL inBlack = m_image->Get(steep ? fromY : fromX, steep ? fromX : fromY); |
| for (int32_t x = fromX, y = fromY; x != toX; x += xstep) { |
| FX_BOOL isBlack = m_image->Get(steep ? y : x, steep ? x : y); |
| if (isBlack != inBlack) { |
| transitions++; |
| inBlack = isBlack; |
| } |
| error += dy; |
| if (error > 0) { |
| if (y == toY) { |
| break; |
| } |
| y += ystep; |
| error -= dx; |
| } |
| } |
| return new CBC_ResultPointsAndTransitions(from, to, transitions); |
| } |
| void CBC_DataMatrixDetector::OrderBestPatterns( |
| CFX_ArrayTemplate<CBC_ResultPoint*>* patterns) { |
| FX_FLOAT abDistance = (FX_FLOAT)Distance((*patterns)[0], (*patterns)[1]); |
| FX_FLOAT bcDistance = (FX_FLOAT)Distance((*patterns)[1], (*patterns)[2]); |
| FX_FLOAT acDistance = (FX_FLOAT)Distance((*patterns)[0], (*patterns)[2]); |
| CBC_ResultPoint *topLeft, *topRight, *bottomLeft; |
| if (bcDistance >= abDistance && bcDistance >= acDistance) { |
| topLeft = (*patterns)[0]; |
| topRight = (*patterns)[1]; |
| bottomLeft = (*patterns)[2]; |
| } else if (acDistance >= bcDistance && acDistance >= abDistance) { |
| topLeft = (*patterns)[1]; |
| topRight = (*patterns)[0]; |
| bottomLeft = (*patterns)[2]; |
| } else { |
| topLeft = (*patterns)[2]; |
| topRight = (*patterns)[0]; |
| bottomLeft = (*patterns)[1]; |
| } |
| if ((bottomLeft->GetY() - topLeft->GetY()) * |
| (topRight->GetX() - topLeft->GetX()) < |
| (bottomLeft->GetX() - topLeft->GetX()) * |
| (topRight->GetY() - topLeft->GetY())) { |
| CBC_ResultPoint* temp = topRight; |
| topRight = bottomLeft; |
| bottomLeft = temp; |
| } |
| (*patterns)[0] = bottomLeft; |
| (*patterns)[1] = topLeft; |
| (*patterns)[2] = topRight; |
| } |
| |
| CBC_ResultPointsAndTransitions::CBC_ResultPointsAndTransitions( |
| CBC_ResultPoint* from, |
| CBC_ResultPoint* to, |
| int32_t transitions) |
| : m_from(from), m_to(to), m_transitions(transitions) {} |
| |
| CBC_ResultPointsAndTransitions::~CBC_ResultPointsAndTransitions() {} |