Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / xfa / src / fxbarcode / src / BC_QRDetector.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "barcode.h"\r
8 #include "include/BC_CommonBitMatrix.h"\r
9 #include "include/BC_ResultPoint.h"\r
10 #include "include/BC_QRFinderPattern.h"\r
11 #include "include/BC_QRCoderVersion.h"\r
12 #include "include/BC_FinderPatternInfo.h"\r
13 #include "include/BC_QRGridSampler.h"\r
14 #include "include/BC_QRAlignmentPatternFinder.h"\r
15 #include "include/BC_QRFinderPatternFinder.h"\r
16 #include "include/BC_QRDetectorResult.h"\r
17 #include "include/BC_QRDetector.h"\r
18 CBC_QRDetector::CBC_QRDetector(CBC_CommonBitMatrix *image): m_image(image)\r
19 {\r
20 }\r
21 CBC_QRDetector::~CBC_QRDetector()\r
22 {\r
23 }\r
24 CBC_QRDetectorResult *CBC_QRDetector::Detect(FX_INT32 hints, FX_INT32 &e)\r
25 {\r
26     CBC_QRFinderPatternFinder finder(m_image);\r
27     CBC_QRFinderPatternInfo* qpi = finder.Find(hints, e);\r
28     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
29     CBC_AutoPtr<CBC_QRFinderPatternInfo> info(qpi);\r
30     CBC_QRDetectorResult* qdr = ProcessFinderPatternInfo(info.get(), e);\r
31     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
32     return qdr;\r
33 }\r
34 CBC_QRDetectorResult* CBC_QRDetector::ProcessFinderPatternInfo(CBC_QRFinderPatternInfo *info, FX_INT32 &e)\r
35 {\r
36     CBC_AutoPtr<CBC_QRFinderPattern> topLeft(info->GetTopLeft());\r
37     CBC_AutoPtr<CBC_QRFinderPattern> topRight(info->GetTopRight());\r
38     CBC_AutoPtr<CBC_QRFinderPattern> bottomLeft(info->GetBottomLeft());\r
39     FX_FLOAT moduleSize = CalculateModuleSize(topLeft.get(), topRight.get(), bottomLeft.get());\r
40     if(moduleSize < 1.0f) {\r
41         e = BCExceptionRead;\r
42         BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
43     }\r
44     FX_INT32 dimension = ComputeDimension(topLeft.get(), topRight.get(), bottomLeft.get(), moduleSize, e);\r
45     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
46     CBC_QRCoderVersion *provisionalVersion = CBC_QRCoderVersion::GetProvisionalVersionForDimension(dimension, e);\r
47     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
48     FX_INT32 modulesBetweenFPCenters = provisionalVersion->GetDimensionForVersion() - 7;\r
49     CBC_QRAlignmentPattern * alignmentPattern = NULL;\r
50     if(provisionalVersion->GetAlignmentPatternCenters()->GetSize() > 0) {\r
51         FX_FLOAT  bottomRightX = topRight->GetX() - topLeft->GetX() + bottomLeft->GetX();\r
52         FX_FLOAT bottomRightY = topRight->GetY() - topLeft->GetY() + bottomLeft->GetY();\r
53         FX_FLOAT correctionToTopLeft = 1.0f - 3.0f / (FX_FLOAT) modulesBetweenFPCenters;\r
54         FX_FLOAT xtemp = (topLeft->GetX() + correctionToTopLeft * (bottomRightX - topLeft->GetX()));\r
55         FX_INT32 estAlignmentX = (FX_INT32)xtemp ;\r
56         FX_FLOAT ytemp =  (topLeft->GetY() + correctionToTopLeft * (bottomRightY - topLeft->GetY()));\r
57         FX_INT32 estAlignmentY = (FX_INT32)ytemp;\r
58         for(FX_INT32 i = 4; i <= 16; i <<= 1) {\r
59             CBC_QRAlignmentPattern *temp = FindAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (FX_FLOAT) i, e);\r
60             alignmentPattern = temp;\r
61             break;\r
62         }\r
63     }\r
64     CBC_CommonBitMatrix *bits = SampleGrid(m_image, topLeft.get(), topRight.get(), bottomLeft.get(), (CBC_ResultPoint*)(alignmentPattern), dimension, e);\r
65     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
66     CFX_PtrArray *points = FX_NEW CFX_PtrArray;\r
67     if(alignmentPattern == NULL) {\r
68         points->Add(bottomLeft.release());\r
69         points->Add(topLeft.release());\r
70         points->Add(topRight.release());\r
71     } else {\r
72         points->Add(bottomLeft.release());\r
73         points->Add(topLeft.release());\r
74         points->Add(topRight.release());\r
75         points->Add(alignmentPattern);\r
76     }\r
77     return FX_NEW CBC_QRDetectorResult(bits, points);\r
78 }\r
79 CBC_CommonBitMatrix *CBC_QRDetector::SampleGrid(CBC_CommonBitMatrix *image, CBC_ResultPoint *topLeft, CBC_ResultPoint *topRight,\r
80         CBC_ResultPoint *bottomLeft, CBC_ResultPoint* alignmentPattern,\r
81         FX_INT32 dimension, FX_INT32 &e)\r
82 {\r
83     FX_FLOAT dimMinusThree = (FX_FLOAT) dimension - 3.5f;\r
84     FX_FLOAT bottomRightX;\r
85     FX_FLOAT bottomRightY;\r
86     FX_FLOAT sourceBottomRightX;\r
87     FX_FLOAT sourceBottomRightY;\r
88     if (alignmentPattern != NULL) {\r
89         bottomRightX = alignmentPattern->GetX();\r
90         bottomRightY = alignmentPattern->GetY();\r
91         sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;\r
92     } else {\r
93         bottomRightX = (topRight->GetX() - topLeft->GetX()) + bottomLeft->GetX();\r
94         bottomRightY = (topRight->GetY() - topLeft->GetY()) + bottomLeft->GetY();\r
95         sourceBottomRightX = sourceBottomRightY = dimMinusThree;\r
96     }\r
97     CBC_QRGridSampler &sampler = CBC_QRGridSampler::GetInstance();\r
98     CBC_CommonBitMatrix* cbm = sampler.SampleGrid(image,\r
99                                dimension, dimension,\r
100                                3.5f,\r
101                                3.5f,\r
102                                dimMinusThree,\r
103                                3.5f,\r
104                                sourceBottomRightX,\r
105                                sourceBottomRightY,\r
106                                3.5f,\r
107                                dimMinusThree,\r
108                                topLeft->GetX(),\r
109                                topLeft->GetY(),\r
110                                topRight->GetX(),\r
111                                topRight->GetY(),\r
112                                bottomRightX,\r
113                                bottomRightY,\r
114                                bottomLeft->GetX(),\r
115                                bottomLeft->GetY(), e);\r
116     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
117     return cbm;\r
118 }\r
119 FX_INT32 CBC_QRDetector::ComputeDimension(CBC_ResultPoint *topLeft, CBC_ResultPoint *topRight,\r
120         CBC_ResultPoint *bottomLeft, FX_FLOAT moduleSize, FX_INT32 &e)\r
121 {\r
122     FX_INT32 tltrCentersDimension = Round(CBC_QRFinderPatternFinder::Distance(topLeft, topRight) / moduleSize);\r
123     FX_INT32 tlblCentersDimension = Round(CBC_QRFinderPatternFinder::Distance(topLeft, bottomLeft) / moduleSize);\r
124     FX_INT32 dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;\r
125     switch(dimension & 0x03) {\r
126         case 0:\r
127             dimension++;\r
128             break;\r
129         case 2:\r
130             dimension--;\r
131             break;\r
132         case 3: {\r
133                 e = BCExceptionRead;\r
134                 BC_EXCEPTION_CHECK_ReturnValue(e, 0);\r
135             }\r
136     }\r
137     return dimension;\r
138 }\r
139 FX_FLOAT CBC_QRDetector::CalculateModuleSize(CBC_ResultPoint *topLeft, CBC_ResultPoint *topRight, CBC_ResultPoint *bottomLeft)\r
140 {\r
141     return (CalculateModuleSizeOneWay(topLeft, topRight) + CalculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;\r
142 }\r
143 FX_FLOAT CBC_QRDetector::CalculateModuleSizeOneWay(CBC_ResultPoint *pattern, CBC_ResultPoint *otherPattern)\r
144 {\r
145     FX_FLOAT moduleSizeEst1 = SizeOfBlackWhiteBlackRunBothWays((FX_INT32) pattern->GetX(),\r
146                               (FX_INT32) pattern->GetY(),\r
147                               (FX_INT32) otherPattern->GetX(),\r
148                               (FX_INT32) otherPattern->GetY());\r
149     FX_FLOAT moduleSizeEst2 = SizeOfBlackWhiteBlackRunBothWays((FX_INT32) otherPattern->GetX(),\r
150                               (FX_INT32) otherPattern->GetY(),\r
151                               (FX_INT32) pattern->GetX(),\r
152                               (FX_INT32) pattern->GetY());\r
153     if (FXSYS_isnan(moduleSizeEst1)) {\r
154         return moduleSizeEst2;\r
155     }\r
156     if (FXSYS_isnan(moduleSizeEst2)) {\r
157         return moduleSizeEst1;\r
158     }\r
159     return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;\r
160 }\r
161 FX_INT32 CBC_QRDetector::Round(FX_FLOAT d)\r
162 {\r
163     return (FX_INT32)(d + 0.5f);\r
164 }\r
165 FX_FLOAT CBC_QRDetector::SizeOfBlackWhiteBlackRunBothWays(FX_INT32 fromX, FX_INT32 fromY, FX_INT32 toX, FX_INT32 toY)\r
166 {\r
167     FX_FLOAT result = SizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);\r
168     FX_INT32 otherToX = fromX - (toX - fromX);\r
169     if (otherToX < 0) {\r
170         otherToX = -1;\r
171     } else if (otherToX >= m_image->GetWidth()) {\r
172         otherToX = m_image->GetWidth();\r
173     }\r
174     FX_INT32 otherToY = fromY - (toY - fromY);\r
175     if (otherToY < 0) {\r
176         otherToY = -1;\r
177     } else if (otherToY >= m_image->GetHeight()) {\r
178         otherToY = m_image->GetHeight();\r
179     }\r
180     result += SizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);\r
181     return result - 1.0f;\r
182 }\r
183 FX_FLOAT CBC_QRDetector::SizeOfBlackWhiteBlackRun(FX_INT32 fromX, FX_INT32 fromY, FX_INT32 toX, FX_INT32 toY)\r
184 {\r
185     FX_BOOL steep = FXSYS_abs(toY - fromY) > FXSYS_abs(toX - fromX);\r
186     if (steep) {\r
187         FX_INT32 temp = fromX;\r
188         fromX = fromY;\r
189         fromY = temp;\r
190         temp = toX;\r
191         toX = toY;\r
192         toY = temp;\r
193     }\r
194     FX_INT32 dx = FXSYS_abs(toX - fromX);\r
195     FX_INT32 dy = FXSYS_abs(toY - fromY);\r
196     FX_INT32 error = -dx >> 1;\r
197     FX_INT32 ystep = fromY < toY ? 1 : -1;\r
198     FX_INT32 xstep = fromX < toX ? 1 : -1;\r
199     FX_INT32 state = 0;\r
200     for (FX_INT32 x = fromX, y = fromY; x != toX; x += xstep) {\r
201         FX_INT32 realX = steep ? y : x;\r
202         FX_INT32 realY = steep ? x : y;\r
203         if (state == 1) {\r
204             if (m_image->Get(realX, realY)) {\r
205                 state++;\r
206             }\r
207         } else {\r
208             if (!m_image->Get(realX, realY)) {\r
209                 state++;\r
210             }\r
211         }\r
212         if (state == 3) {\r
213             FX_INT32 diffX = x - fromX;\r
214             FX_INT32 diffY = y - fromY;\r
215             return (FX_FLOAT) sqrt((double) (diffX * diffX + diffY * diffY));\r
216         }\r
217         error += dy;\r
218         if (error > 0) {\r
219             y += ystep;\r
220             error -= dx;\r
221         }\r
222     }\r
223     FX_INT32 diffX = toX - fromX;\r
224     FX_INT32 diffY = toY - fromY;\r
225     return (FX_FLOAT) sqrt((double) (diffX * diffX + diffY * diffY));\r
226 }\r
227 CBC_QRAlignmentPattern *CBC_QRDetector::FindAlignmentInRegion(FX_FLOAT overallEstModuleSize, FX_INT32 estAlignmentX,\r
228         FX_INT32 estAlignmentY, FX_FLOAT allowanceFactor, FX_INT32 &e)\r
229 {\r
230     FX_INT32 allowance = (FX_INT32) (allowanceFactor * overallEstModuleSize);\r
231     FX_INT32 alignmentAreaLeftX = FX_MAX(0, estAlignmentX - allowance);\r
232     FX_INT32 alignmentAreaRightX = FX_MIN(m_image->GetWidth() - 1, estAlignmentX + allowance);\r
233     if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {\r
234         e = BCExceptionRead;\r
235         BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
236     }\r
237     FX_INT32 alignmentAreaTopY = FX_MAX(0, estAlignmentY - allowance);\r
238     FX_INT32 alignmentAreaBottomY = FX_MIN(m_image->GetHeight() - 1, estAlignmentY + allowance);\r
239     CBC_QRAlignmentPatternFinder alignmentFinder(m_image,\r
240             alignmentAreaLeftX,\r
241             alignmentAreaTopY,\r
242             alignmentAreaRightX - alignmentAreaLeftX,\r
243             alignmentAreaBottomY - alignmentAreaTopY,\r
244             overallEstModuleSize);\r
245     CBC_QRAlignmentPattern *qap = alignmentFinder.Find(e);\r
246     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
247     return qap;\r
248 }\r