OLD | NEW |
| (Empty) |
1 // Copyright 2014 PDFium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
6 // Original code is licensed as follows: | |
7 /* | |
8 * Copyright 2008 ZXing authors | |
9 * | |
10 * Licensed under the Apache License, Version 2.0 (the "License"); | |
11 * you may not use this file except in compliance with the License. | |
12 * You may obtain a copy of the License at | |
13 * | |
14 * http://www.apache.org/licenses/LICENSE-2.0 | |
15 * | |
16 * Unless required by applicable law or agreed to in writing, software | |
17 * distributed under the License is distributed on an "AS IS" BASIS, | |
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
19 * See the License for the specific language governing permissions and | |
20 * limitations under the License. | |
21 */ | |
22 | |
23 #include "xfa/src/fxbarcode/datamatrix/BC_DataMatrixDetector.h" | |
24 | |
25 #include <algorithm> | |
26 #include <memory> | |
27 | |
28 #include "xfa/src/fxbarcode/BC_ResultPoint.h" | |
29 #include "xfa/src/fxbarcode/common/BC_CommonBitMatrix.h" | |
30 #include "xfa/src/fxbarcode/common/BC_WhiteRectangleDetector.h" | |
31 #include "xfa/src/fxbarcode/qrcode/BC_QRDetectorResult.h" | |
32 #include "xfa/src/fxbarcode/qrcode/BC_QRFinderPatternFinder.h" | |
33 #include "xfa/src/fxbarcode/qrcode/BC_QRGridSampler.h" | |
34 #include "xfa/src/fxbarcode/utils.h" | |
35 | |
36 const int32_t CBC_DataMatrixDetector::INTEGERS[5] = {0, 1, 2, 3, 4}; | |
37 CBC_DataMatrixDetector::CBC_DataMatrixDetector(CBC_CommonBitMatrix* image) | |
38 : m_image(image), m_rectangleDetector(NULL) {} | |
39 void CBC_DataMatrixDetector::Init(int32_t& e) { | |
40 m_rectangleDetector = new CBC_WhiteRectangleDetector(m_image); | |
41 m_rectangleDetector->Init(e); | |
42 BC_EXCEPTION_CHECK_ReturnVoid(e); | |
43 } | |
44 CBC_DataMatrixDetector::~CBC_DataMatrixDetector() { | |
45 delete m_rectangleDetector; | |
46 } | |
47 inline FX_BOOL ResultPointsAndTransitionsComparator(void* a, void* b) { | |
48 return ((CBC_ResultPointsAndTransitions*)b)->GetTransitions() > | |
49 ((CBC_ResultPointsAndTransitions*)a)->GetTransitions(); | |
50 } | |
51 CBC_QRDetectorResult* CBC_DataMatrixDetector::Detect(int32_t& e) { | |
52 CFX_PtrArray* cornerPoints = m_rectangleDetector->Detect(e); | |
53 BC_EXCEPTION_CHECK_ReturnValue(e, NULL); | |
54 CBC_ResultPoint* pointA = (CBC_ResultPoint*)(*cornerPoints)[0]; | |
55 CBC_ResultPoint* pointB = (CBC_ResultPoint*)(*cornerPoints)[1]; | |
56 CBC_ResultPoint* pointC = (CBC_ResultPoint*)(*cornerPoints)[2]; | |
57 CBC_ResultPoint* pointD = (CBC_ResultPoint*)(*cornerPoints)[3]; | |
58 delete cornerPoints; | |
59 cornerPoints = NULL; | |
60 CFX_PtrArray transitions; | |
61 transitions.Add(TransitionsBetween(pointA, pointB)); | |
62 transitions.Add(TransitionsBetween(pointA, pointC)); | |
63 transitions.Add(TransitionsBetween(pointB, pointD)); | |
64 transitions.Add(TransitionsBetween(pointC, pointD)); | |
65 BC_FX_PtrArray_Sort(transitions, &ResultPointsAndTransitionsComparator); | |
66 delete ((CBC_ResultPointsAndTransitions*)transitions[2]); | |
67 delete ((CBC_ResultPointsAndTransitions*)transitions[3]); | |
68 CBC_ResultPointsAndTransitions* lSideOne = | |
69 (CBC_ResultPointsAndTransitions*)transitions[0]; | |
70 CBC_ResultPointsAndTransitions* lSideTwo = | |
71 (CBC_ResultPointsAndTransitions*)transitions[1]; | |
72 CFX_MapPtrTemplate<CBC_ResultPoint*, int32_t> pointCount; | |
73 Increment(pointCount, lSideOne->GetFrom()); | |
74 Increment(pointCount, lSideOne->GetTo()); | |
75 Increment(pointCount, lSideTwo->GetFrom()); | |
76 Increment(pointCount, lSideTwo->GetTo()); | |
77 delete ((CBC_ResultPointsAndTransitions*)transitions[1]); | |
78 delete ((CBC_ResultPointsAndTransitions*)transitions[0]); | |
79 transitions.RemoveAll(); | |
80 CBC_ResultPoint* maybeTopLeft = NULL; | |
81 CBC_ResultPoint* bottomLeft = NULL; | |
82 CBC_ResultPoint* maybeBottomRight = NULL; | |
83 FX_POSITION itBegin = pointCount.GetStartPosition(); | |
84 while (itBegin) { | |
85 CBC_ResultPoint* key = 0; | |
86 int32_t value = 0; | |
87 pointCount.GetNextAssoc(itBegin, key, value); | |
88 if (value == 2) { | |
89 bottomLeft = key; | |
90 } else { | |
91 if (maybeBottomRight == NULL) { | |
92 maybeBottomRight = key; | |
93 } else { | |
94 maybeTopLeft = key; | |
95 } | |
96 } | |
97 } | |
98 if (maybeTopLeft == NULL || bottomLeft == NULL || maybeBottomRight == NULL) { | |
99 delete pointA; | |
100 delete pointB; | |
101 delete pointC; | |
102 delete pointD; | |
103 e = BCExceptionNotFound; | |
104 return NULL; | |
105 } | |
106 CFX_PtrArray corners; | |
107 corners.SetSize(3); | |
108 corners[0] = maybeTopLeft; | |
109 corners[1] = bottomLeft; | |
110 corners[2] = maybeBottomRight; | |
111 OrderBestPatterns(&corners); | |
112 CBC_ResultPoint* bottomRight = (CBC_ResultPoint*)corners[0]; | |
113 bottomLeft = (CBC_ResultPoint*)corners[1]; | |
114 CBC_ResultPoint* topLeft = (CBC_ResultPoint*)corners[2]; | |
115 CBC_ResultPoint* topRight = NULL; | |
116 int32_t value; | |
117 if (!pointCount.Lookup(pointA, value)) { | |
118 topRight = pointA; | |
119 } else if (!pointCount.Lookup(pointB, value)) { | |
120 topRight = pointB; | |
121 } else if (!pointCount.Lookup(pointC, value)) { | |
122 topRight = pointC; | |
123 } else { | |
124 topRight = pointD; | |
125 } | |
126 int32_t dimensionTop = std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
127 TransitionsBetween(topLeft, topRight)) | |
128 ->GetTransitions(); | |
129 int32_t dimensionRight = std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
130 TransitionsBetween(bottomRight, topRight)) | |
131 ->GetTransitions(); | |
132 if ((dimensionTop & 0x01) == 1) { | |
133 dimensionTop++; | |
134 } | |
135 dimensionTop += 2; | |
136 if ((dimensionRight & 0x01) == 1) { | |
137 dimensionRight++; | |
138 } | |
139 dimensionRight += 2; | |
140 std::unique_ptr<CBC_CommonBitMatrix> bits; | |
141 std::unique_ptr<CBC_ResultPoint> correctedTopRight; | |
142 if (4 * dimensionTop >= 7 * dimensionRight || | |
143 4 * dimensionRight >= 7 * dimensionTop) { | |
144 correctedTopRight.reset( | |
145 CorrectTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight, | |
146 dimensionTop, dimensionRight)); | |
147 if (correctedTopRight.get() == NULL) { | |
148 correctedTopRight.reset(topRight); | |
149 } else { | |
150 delete topRight; | |
151 topRight = NULL; | |
152 } | |
153 dimensionTop = std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
154 TransitionsBetween(topLeft, correctedTopRight.get())) | |
155 ->GetTransitions(); | |
156 dimensionRight = | |
157 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
158 TransitionsBetween(bottomRight, correctedTopRight.get())) | |
159 ->GetTransitions(); | |
160 if ((dimensionTop & 0x01) == 1) { | |
161 dimensionTop++; | |
162 } | |
163 if ((dimensionRight & 0x01) == 1) { | |
164 dimensionRight++; | |
165 } | |
166 bits.reset(SampleGrid(m_image, topLeft, bottomLeft, bottomRight, | |
167 correctedTopRight.get(), dimensionTop, dimensionRight, | |
168 e)); | |
169 BC_EXCEPTION_CHECK_ReturnValue(e, NULL); | |
170 } else { | |
171 int32_t dimension = std::min(dimensionRight, dimensionTop); | |
172 correctedTopRight.reset( | |
173 CorrectTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension)); | |
174 if (correctedTopRight.get() == NULL) { | |
175 correctedTopRight.reset(topRight); | |
176 } else { | |
177 delete topRight; | |
178 topRight = NULL; | |
179 } | |
180 int32_t dimensionCorrected = | |
181 std::max(std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
182 TransitionsBetween(topLeft, correctedTopRight.get())) | |
183 ->GetTransitions(), | |
184 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
185 TransitionsBetween(bottomRight, correctedTopRight.get())) | |
186 ->GetTransitions()); | |
187 dimensionCorrected++; | |
188 if ((dimensionCorrected & 0x01) == 1) { | |
189 dimensionCorrected++; | |
190 } | |
191 bits.reset(SampleGrid(m_image, topLeft, bottomLeft, bottomRight, | |
192 correctedTopRight.get(), dimensionCorrected, | |
193 dimensionCorrected, e)); | |
194 BC_EXCEPTION_CHECK_ReturnValue(e, NULL); | |
195 } | |
196 CFX_PtrArray* result = new CFX_PtrArray; | |
197 result->SetSize(4); | |
198 result->Add(topLeft); | |
199 result->Add(bottomLeft); | |
200 result->Add(bottomRight); | |
201 result->Add(correctedTopRight.release()); | |
202 return new CBC_QRDetectorResult(bits.release(), result); | |
203 } | |
204 CBC_ResultPoint* CBC_DataMatrixDetector::CorrectTopRightRectangular( | |
205 CBC_ResultPoint* bottomLeft, | |
206 CBC_ResultPoint* bottomRight, | |
207 CBC_ResultPoint* topLeft, | |
208 CBC_ResultPoint* topRight, | |
209 int32_t dimensionTop, | |
210 int32_t dimensionRight) { | |
211 FX_FLOAT corr = Distance(bottomLeft, bottomRight) / (FX_FLOAT)dimensionTop; | |
212 int32_t norm = Distance(topLeft, topRight); | |
213 FX_FLOAT cos = (topRight->GetX() - topLeft->GetX()) / norm; | |
214 FX_FLOAT sin = (topRight->GetY() - topLeft->GetY()) / norm; | |
215 std::unique_ptr<CBC_ResultPoint> c1(new CBC_ResultPoint( | |
216 topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); | |
217 corr = Distance(bottomLeft, topLeft) / (FX_FLOAT)dimensionRight; | |
218 norm = Distance(bottomRight, topRight); | |
219 cos = (topRight->GetX() - bottomRight->GetX()) / norm; | |
220 sin = (topRight->GetY() - bottomRight->GetY()) / norm; | |
221 std::unique_ptr<CBC_ResultPoint> c2(new CBC_ResultPoint( | |
222 topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); | |
223 if (!IsValid(c1.get())) { | |
224 if (IsValid(c2.get())) { | |
225 return c2.release(); | |
226 } | |
227 return NULL; | |
228 } else if (!IsValid(c2.get())) { | |
229 return c1.release(); | |
230 } | |
231 int32_t l1 = FXSYS_abs(dimensionTop - | |
232 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
233 TransitionsBetween(topLeft, c1.get())) | |
234 ->GetTransitions()) + | |
235 FXSYS_abs(dimensionRight - | |
236 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
237 TransitionsBetween(bottomRight, c1.get())) | |
238 ->GetTransitions()); | |
239 int32_t l2 = FXSYS_abs(dimensionTop - | |
240 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
241 TransitionsBetween(topLeft, c2.get())) | |
242 ->GetTransitions()) + | |
243 FXSYS_abs(dimensionRight - | |
244 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
245 TransitionsBetween(bottomRight, c2.get())) | |
246 ->GetTransitions()); | |
247 if (l1 <= l2) { | |
248 return c1.release(); | |
249 } | |
250 return c2.release(); | |
251 } | |
252 CBC_ResultPoint* CBC_DataMatrixDetector::CorrectTopRight( | |
253 CBC_ResultPoint* bottomLeft, | |
254 CBC_ResultPoint* bottomRight, | |
255 CBC_ResultPoint* topLeft, | |
256 CBC_ResultPoint* topRight, | |
257 int32_t dimension) { | |
258 FX_FLOAT corr = Distance(bottomLeft, bottomRight) / (FX_FLOAT)dimension; | |
259 int32_t norm = Distance(topLeft, topRight); | |
260 FX_FLOAT cos = (topRight->GetX() - topLeft->GetX()) / norm; | |
261 FX_FLOAT sin = (topRight->GetY() - topLeft->GetY()) / norm; | |
262 std::unique_ptr<CBC_ResultPoint> c1(new CBC_ResultPoint( | |
263 topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); | |
264 corr = Distance(bottomLeft, bottomRight) / (FX_FLOAT)dimension; | |
265 norm = Distance(bottomRight, topRight); | |
266 cos = (topRight->GetX() - bottomRight->GetX()) / norm; | |
267 sin = (topRight->GetY() - bottomRight->GetY()) / norm; | |
268 std::unique_ptr<CBC_ResultPoint> c2(new CBC_ResultPoint( | |
269 topRight->GetX() + corr * cos, topRight->GetY() + corr * sin)); | |
270 if (!IsValid(c1.get())) { | |
271 if (IsValid(c2.get())) { | |
272 return c2.release(); | |
273 } | |
274 return NULL; | |
275 } else if (!IsValid(c2.get())) { | |
276 return c1.release(); | |
277 } | |
278 int32_t l1 = FXSYS_abs(std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
279 TransitionsBetween(topLeft, c1.get())) | |
280 ->GetTransitions() - | |
281 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
282 TransitionsBetween(bottomRight, c1.get())) | |
283 ->GetTransitions()); | |
284 int32_t l2 = FXSYS_abs(std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
285 TransitionsBetween(topLeft, c2.get())) | |
286 ->GetTransitions() - | |
287 std::unique_ptr<CBC_ResultPointsAndTransitions>( | |
288 TransitionsBetween(bottomRight, c2.get())) | |
289 ->GetTransitions()); | |
290 return l1 <= l2 ? c1.release() : c2.release(); | |
291 } | |
292 FX_BOOL CBC_DataMatrixDetector::IsValid(CBC_ResultPoint* p) { | |
293 return p->GetX() >= 0 && p->GetX() < m_image->GetWidth() && p->GetY() > 0 && | |
294 p->GetY() < m_image->GetHeight(); | |
295 } | |
296 int32_t CBC_DataMatrixDetector::Round(FX_FLOAT d) { | |
297 return (int32_t)(d + 0.5f); | |
298 } | |
299 int32_t CBC_DataMatrixDetector::Distance(CBC_ResultPoint* a, | |
300 CBC_ResultPoint* b) { | |
301 return Round( | |
302 (FX_FLOAT)sqrt((a->GetX() - b->GetX()) * (a->GetX() - b->GetX()) + | |
303 (a->GetY() - b->GetY()) * (a->GetY() - b->GetY()))); | |
304 } | |
305 void CBC_DataMatrixDetector::Increment( | |
306 CFX_MapPtrTemplate<CBC_ResultPoint*, int32_t>& table, | |
307 CBC_ResultPoint* key) { | |
308 int32_t value; | |
309 if (table.Lookup(key, value)) { | |
310 table.SetAt(key, INTEGERS[value + 1]); | |
311 } else { | |
312 table.SetAt(key, INTEGERS[1]); | |
313 } | |
314 } | |
315 CBC_CommonBitMatrix* CBC_DataMatrixDetector::SampleGrid( | |
316 CBC_CommonBitMatrix* image, | |
317 CBC_ResultPoint* topLeft, | |
318 CBC_ResultPoint* bottomLeft, | |
319 CBC_ResultPoint* bottomRight, | |
320 CBC_ResultPoint* topRight, | |
321 int32_t dimensionX, | |
322 int32_t dimensionY, | |
323 int32_t& e) { | |
324 CBC_QRGridSampler& sampler = CBC_QRGridSampler::GetInstance(); | |
325 CBC_CommonBitMatrix* cbm = sampler.SampleGrid( | |
326 image, dimensionX, dimensionY, 0.5f, 0.5f, dimensionX - 0.5f, 0.5f, | |
327 dimensionX - 0.5f, dimensionY - 0.5f, 0.5f, dimensionY - 0.5f, | |
328 topLeft->GetX(), topLeft->GetY(), topRight->GetX(), topRight->GetY(), | |
329 bottomRight->GetX(), bottomRight->GetY(), bottomLeft->GetX(), | |
330 bottomLeft->GetY(), e); | |
331 BC_EXCEPTION_CHECK_ReturnValue(e, NULL); | |
332 return cbm; | |
333 } | |
334 CBC_ResultPointsAndTransitions* CBC_DataMatrixDetector::TransitionsBetween( | |
335 CBC_ResultPoint* from, | |
336 CBC_ResultPoint* to) { | |
337 int32_t fromX = (int32_t)from->GetX(); | |
338 int32_t fromY = (int32_t)from->GetY(); | |
339 int32_t toX = (int32_t)to->GetX(); | |
340 int32_t toY = (int32_t)to->GetY(); | |
341 FX_BOOL steep = FXSYS_abs(toY - fromY) > FXSYS_abs(toX - fromX); | |
342 if (steep) { | |
343 int32_t temp = fromX; | |
344 fromX = fromY; | |
345 fromY = temp; | |
346 temp = toX; | |
347 toX = toY; | |
348 toY = temp; | |
349 } | |
350 int32_t dx = FXSYS_abs(toX - fromX); | |
351 int32_t dy = FXSYS_abs(toY - fromY); | |
352 int32_t error = -dx >> 1; | |
353 int32_t ystep = fromY < toY ? 1 : -1; | |
354 int32_t xstep = fromX < toX ? 1 : -1; | |
355 int32_t transitions = 0; | |
356 FX_BOOL inBlack = m_image->Get(steep ? fromY : fromX, steep ? fromX : fromY); | |
357 for (int32_t x = fromX, y = fromY; x != toX; x += xstep) { | |
358 FX_BOOL isBlack = m_image->Get(steep ? y : x, steep ? x : y); | |
359 if (isBlack != inBlack) { | |
360 transitions++; | |
361 inBlack = isBlack; | |
362 } | |
363 error += dy; | |
364 if (error > 0) { | |
365 if (y == toY) { | |
366 break; | |
367 } | |
368 y += ystep; | |
369 error -= dx; | |
370 } | |
371 } | |
372 return new CBC_ResultPointsAndTransitions(from, to, transitions); | |
373 } | |
374 void CBC_DataMatrixDetector::OrderBestPatterns(CFX_PtrArray* patterns) { | |
375 FX_FLOAT abDistance = (FX_FLOAT)Distance((CBC_ResultPoint*)(*patterns)[0], | |
376 (CBC_ResultPoint*)(*patterns)[1]); | |
377 FX_FLOAT bcDistance = (FX_FLOAT)Distance((CBC_ResultPoint*)(*patterns)[1], | |
378 (CBC_ResultPoint*)(*patterns)[2]); | |
379 FX_FLOAT acDistance = (FX_FLOAT)Distance((CBC_ResultPoint*)(*patterns)[0], | |
380 (CBC_ResultPoint*)(*patterns)[2]); | |
381 CBC_ResultPoint *topLeft, *topRight, *bottomLeft; | |
382 if (bcDistance >= abDistance && bcDistance >= acDistance) { | |
383 topLeft = (CBC_ResultPoint*)(*patterns)[0]; | |
384 topRight = (CBC_ResultPoint*)(*patterns)[1]; | |
385 bottomLeft = (CBC_ResultPoint*)(*patterns)[2]; | |
386 } else if (acDistance >= bcDistance && acDistance >= abDistance) { | |
387 topLeft = (CBC_ResultPoint*)(*patterns)[1]; | |
388 topRight = (CBC_ResultPoint*)(*patterns)[0]; | |
389 bottomLeft = (CBC_ResultPoint*)(*patterns)[2]; | |
390 } else { | |
391 topLeft = (CBC_ResultPoint*)(*patterns)[2]; | |
392 topRight = (CBC_ResultPoint*)(*patterns)[0]; | |
393 bottomLeft = (CBC_ResultPoint*)(*patterns)[1]; | |
394 } | |
395 if ((bottomLeft->GetY() - topLeft->GetY()) * | |
396 (topRight->GetX() - topLeft->GetX()) < | |
397 (bottomLeft->GetX() - topLeft->GetX()) * | |
398 (topRight->GetY() - topLeft->GetY())) { | |
399 CBC_ResultPoint* temp = topRight; | |
400 topRight = bottomLeft; | |
401 bottomLeft = temp; | |
402 } | |
403 (*patterns)[0] = bottomLeft; | |
404 (*patterns)[1] = topLeft; | |
405 (*patterns)[2] = topRight; | |
406 } | |
OLD | NEW |