comparison mupdf-source/thirdparty/leptonica/src/boxfunc4.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27 /*!
28 * \file boxfunc4.c
29 * <pre>
30 *
31 * Boxa and Boxaa range selection
32 * BOXA *boxaSelectRange()
33 * BOXAA *boxaaSelectRange()
34 *
35 * Boxa size selection
36 * BOXA *boxaSelectBySize()
37 * NUMA *boxaMakeSizeIndicator()
38 * BOXA *boxaSelectByArea()
39 * NUMA *boxaMakeAreaIndicator()
40 * BOXA *boxaSelectByWHRatio()
41 * NUMA *boxaMakeWHRatioIndicator()
42 * BOXA *boxaSelectWithIndicator()
43 *
44 * Boxa permutation
45 * BOXA *boxaPermutePseudorandom()
46 * BOXA *boxaPermuteRandom()
47 * l_int32 boxaSwapBoxes()
48 *
49 * Boxa and box conversions
50 * PTA *boxaConvertToPta()
51 * BOXA *ptaConvertToBoxa()
52 * PTA *boxConvertToPta()
53 * BOX *ptaConvertToBox()
54 *
55 * Miscellaneous boxa functions
56 * l_int32 boxaGetExtent()
57 * l_int32 boxaGetCoverage()
58 * l_int32 boxaaSizeRange()
59 * l_int32 boxaSizeRange()
60 * l_int32 boxaLocationRange()
61 * NUMA *boxaGetSizes()
62 * l_int32 boxaGetArea()
63 * PIX *boxaDisplayTiled()
64 * </pre>
65 */
66
67 #ifdef HAVE_CONFIG_H
68 #include <config_auto.h>
69 #endif /* HAVE_CONFIG_H */
70
71 #include <math.h>
72 #include "allheaders.h"
73 #include "pix_internal.h"
74
75 /*---------------------------------------------------------------------*
76 * Boxa and boxaa range selection *
77 *---------------------------------------------------------------------*/
78 /*!
79 * \brief boxaSelectRange()
80 *
81 * \param[in] boxas
82 * \param[in] first use 0 to select from the beginning
83 * \param[in] last use -1 to select to the end
84 * \param[in] copyflag L_COPY, L_CLONE
85 * \return boxad, or NULL on error
86 *
87 * <pre>
88 * Notes:
89 * (1) The copyflag specifies what we do with each box from boxas.
90 * Specifically, L_CLONE inserts a clone into boxad of each
91 * selected box from boxas.
92 * </pre>
93 */
94 BOXA *
95 boxaSelectRange(BOXA *boxas,
96 l_int32 first,
97 l_int32 last,
98 l_int32 copyflag)
99 {
100 l_int32 n, nbox, i;
101 BOX *box;
102 BOXA *boxad;
103
104 if (!boxas)
105 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
106 if (copyflag != L_COPY && copyflag != L_CLONE)
107 return (BOXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
108 if ((n = boxaGetCount(boxas)) == 0) {
109 L_WARNING("boxas is empty\n", __func__);
110 return boxaCopy(boxas, copyflag);
111 }
112 first = L_MAX(0, first);
113 if (last < 0) last = n - 1;
114 if (first >= n)
115 return (BOXA *)ERROR_PTR("invalid first", __func__, NULL);
116 if (last >= n) {
117 L_WARNING("last = %d is beyond max index = %d; adjusting\n",
118 __func__, last, n - 1);
119 last = n - 1;
120 }
121 if (first > last)
122 return (BOXA *)ERROR_PTR("first > last", __func__, NULL);
123
124 nbox = last - first + 1;
125 boxad = boxaCreate(nbox);
126 for (i = first; i <= last; i++) {
127 box = boxaGetBox(boxas, i, copyflag);
128 boxaAddBox(boxad, box, L_INSERT);
129 }
130 return boxad;
131 }
132
133
134 /*!
135 * \brief boxaaSelectRange()
136 *
137 * \param[in] baas
138 * \param[in] first use 0 to select from the beginning
139 * \param[in] last use -1 to select to the end
140 * \param[in] copyflag L_COPY, L_CLONE
141 * \return baad, or NULL on error
142 *
143 * <pre>
144 * Notes:
145 * (1) The copyflag specifies what we do with each boxa from baas.
146 * Specifically, L_CLONE inserts a clone into baad of each
147 * selected boxa from baas.
148 * </pre>
149 */
150 BOXAA *
151 boxaaSelectRange(BOXAA *baas,
152 l_int32 first,
153 l_int32 last,
154 l_int32 copyflag)
155 {
156 l_int32 n, nboxa, i;
157 BOXA *boxa;
158 BOXAA *baad;
159
160 if (!baas)
161 return (BOXAA *)ERROR_PTR("baas not defined", __func__, NULL);
162 if (copyflag != L_COPY && copyflag != L_CLONE)
163 return (BOXAA *)ERROR_PTR("invalid copyflag", __func__, NULL);
164 if ((n = boxaaGetCount(baas)) == 0)
165 return (BOXAA *)ERROR_PTR("empty baas", __func__, NULL);
166 first = L_MAX(0, first);
167 if (last < 0) last = n - 1;
168 if (first >= n)
169 return (BOXAA *)ERROR_PTR("invalid first", __func__, NULL);
170 if (last >= n) {
171 L_WARNING("last = %d is beyond max index = %d; adjusting\n",
172 __func__, last, n - 1);
173 last = n - 1;
174 }
175 if (first > last)
176 return (BOXAA *)ERROR_PTR("first > last", __func__, NULL);
177
178 nboxa = last - first + 1;
179 baad = boxaaCreate(nboxa);
180 for (i = first; i <= last; i++) {
181 boxa = boxaaGetBoxa(baas, i, copyflag);
182 boxaaAddBoxa(baad, boxa, L_INSERT);
183 }
184 return baad;
185 }
186
187
188 /*---------------------------------------------------------------------*
189 * Boxa size selection *
190 *---------------------------------------------------------------------*/
191 /*!
192 * \brief boxaSelectBySize()
193 *
194 * \param[in] boxas
195 * \param[in] width, height threshold dimensions
196 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT,
197 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
198 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
199 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
200 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
201 * \return boxad filtered set, or NULL on error
202 *
203 * <pre>
204 * Notes:
205 * (1) The args specify constraints on the size of the
206 * components that are kept.
207 * (2) Uses box copies in the new boxa.
208 * (3) If the selection type is L_SELECT_WIDTH, the input
209 * height is ignored, and v.v.
210 * (4) To keep small components, use relation = L_SELECT_IF_LT or
211 * L_SELECT_IF_LTE.
212 * To keep large components, use relation = L_SELECT_IF_GT or
213 * L_SELECT_IF_GTE.
214 * </pre>
215 */
216 BOXA *
217 boxaSelectBySize(BOXA *boxas,
218 l_int32 width,
219 l_int32 height,
220 l_int32 type,
221 l_int32 relation,
222 l_int32 *pchanged)
223 {
224 BOXA *boxad;
225 NUMA *na;
226
227 if (pchanged) *pchanged = FALSE;
228 if (!boxas)
229 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
230 if (boxaGetCount(boxas) == 0) {
231 L_WARNING("boxas is empty\n", __func__);
232 return boxaCopy(boxas, L_COPY);
233 }
234 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
235 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
236 return (BOXA *)ERROR_PTR("invalid type", __func__, NULL);
237 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
238 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
239 return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL);
240
241 /* Compute the indicator array for saving components */
242 if ((na =
243 boxaMakeSizeIndicator(boxas, width, height, type, relation)) == NULL)
244 return (BOXA *)ERROR_PTR("na not made", __func__, NULL);
245
246 /* Filter to get output */
247 boxad = boxaSelectWithIndicator(boxas, na, pchanged);
248
249 numaDestroy(&na);
250 return boxad;
251 }
252
253
254 /*!
255 * \brief boxaMakeSizeIndicator()
256 *
257 * \param[in] boxa
258 * \param[in] width, height threshold dimensions
259 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT,
260 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
261 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
262 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
263 * \return na indicator array, or NULL on error
264 *
265 * <pre>
266 * Notes:
267 * (1) The args specify constraints on the size of the
268 * components that are kept.
269 * (2) If the selection type is L_SELECT_WIDTH, the input
270 * height is ignored, and v.v.
271 * (3) To keep small components, use relation = L_SELECT_IF_LT or
272 * L_SELECT_IF_LTE.
273 * To keep large components, use relation = L_SELECT_IF_GT or
274 * L_SELECT_IF_GTE.
275 * </pre>
276 */
277 NUMA *
278 boxaMakeSizeIndicator(BOXA *boxa,
279 l_int32 width,
280 l_int32 height,
281 l_int32 type,
282 l_int32 relation)
283 {
284 l_int32 i, n, w, h, ival;
285 NUMA *na;
286
287 if (!boxa)
288 return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL);
289 if ((n = boxaGetCount(boxa)) == 0)
290 return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL);
291 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
292 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
293 return (NUMA *)ERROR_PTR("invalid type", __func__, NULL);
294 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
295 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
296 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
297
298 na = numaCreate(n);
299 for (i = 0; i < n; i++) {
300 ival = 0;
301 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
302 switch (type)
303 {
304 case L_SELECT_WIDTH:
305 if ((relation == L_SELECT_IF_LT && w < width) ||
306 (relation == L_SELECT_IF_GT && w > width) ||
307 (relation == L_SELECT_IF_LTE && w <= width) ||
308 (relation == L_SELECT_IF_GTE && w >= width))
309 ival = 1;
310 break;
311 case L_SELECT_HEIGHT:
312 if ((relation == L_SELECT_IF_LT && h < height) ||
313 (relation == L_SELECT_IF_GT && h > height) ||
314 (relation == L_SELECT_IF_LTE && h <= height) ||
315 (relation == L_SELECT_IF_GTE && h >= height))
316 ival = 1;
317 break;
318 case L_SELECT_IF_EITHER:
319 if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) ||
320 ((relation == L_SELECT_IF_GT) && (w > width || h > height)) ||
321 ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) ||
322 ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height)))
323 ival = 1;
324 break;
325 case L_SELECT_IF_BOTH:
326 if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) ||
327 ((relation == L_SELECT_IF_GT) && (w > width && h > height)) ||
328 ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) ||
329 ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height)))
330 ival = 1;
331 break;
332 default:
333 L_WARNING("can't get here!\n", __func__);
334 break;
335 }
336 numaAddNumber(na, ival);
337 }
338
339 return na;
340 }
341
342
343 /*!
344 * \brief boxaSelectByArea()
345 *
346 * \param[in] boxas
347 * \param[in] area threshold value of width * height
348 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
349 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
350 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
351 * \return boxad filtered set, or NULL on error
352 *
353 * <pre>
354 * Notes:
355 * (1) Uses box copies in the new boxa.
356 * (2) To keep small components, use relation = L_SELECT_IF_LT or
357 * L_SELECT_IF_LTE.
358 * To keep large components, use relation = L_SELECT_IF_GT or
359 * L_SELECT_IF_GTE.
360 * </pre>
361 */
362 BOXA *
363 boxaSelectByArea(BOXA *boxas,
364 l_int32 area,
365 l_int32 relation,
366 l_int32 *pchanged)
367 {
368 BOXA *boxad;
369 NUMA *na;
370
371 if (pchanged) *pchanged = FALSE;
372 if (!boxas)
373 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
374 if (boxaGetCount(boxas) == 0) {
375 L_WARNING("boxas is empty\n", __func__);
376 return boxaCopy(boxas, L_COPY);
377 }
378 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
379 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
380 return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL);
381
382 /* Compute the indicator array for saving components */
383 na = boxaMakeAreaIndicator(boxas, area, relation);
384
385 /* Filter to get output */
386 boxad = boxaSelectWithIndicator(boxas, na, pchanged);
387
388 numaDestroy(&na);
389 return boxad;
390 }
391
392
393 /*!
394 * \brief boxaMakeAreaIndicator()
395 *
396 * \param[in] boxa
397 * \param[in] area threshold value of width * height
398 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
399 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
400 * \return na indicator array, or NULL on error
401 *
402 * <pre>
403 * Notes:
404 * (1) To keep small components, use relation = L_SELECT_IF_LT or
405 * L_SELECT_IF_LTE.
406 * To keep large components, use relation = L_SELECT_IF_GT or
407 * L_SELECT_IF_GTE.
408 * </pre>
409 */
410 NUMA *
411 boxaMakeAreaIndicator(BOXA *boxa,
412 l_int32 area,
413 l_int32 relation)
414 {
415 l_int32 i, n, w, h, ival;
416 NUMA *na;
417
418 if (!boxa)
419 return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL);
420 if ((n = boxaGetCount(boxa)) == 0)
421 return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL);
422 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
423 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
424 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
425
426 na = numaCreate(n);
427 for (i = 0; i < n; i++) {
428 ival = 0;
429 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
430
431 if ((relation == L_SELECT_IF_LT && w * h < area) ||
432 (relation == L_SELECT_IF_GT && w * h > area) ||
433 (relation == L_SELECT_IF_LTE && w * h <= area) ||
434 (relation == L_SELECT_IF_GTE && w * h >= area))
435 ival = 1;
436 numaAddNumber(na, ival);
437 }
438
439 return na;
440 }
441
442
443 /*!
444 * \brief boxaSelectByWHRatio()
445 *
446 * \param[in] boxas
447 * \param[in] ratio width/height threshold value
448 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
449 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
450 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
451 * \return boxad filtered set, or NULL on error
452 *
453 * <pre>
454 * Notes:
455 * (1) Uses box copies in the new boxa.
456 * (2) To keep narrow components, use relation = L_SELECT_IF_LT or
457 * L_SELECT_IF_LTE.
458 * To keep wide components, use relation = L_SELECT_IF_GT or
459 * L_SELECT_IF_GTE.
460 * </pre>
461 */
462 BOXA *
463 boxaSelectByWHRatio(BOXA *boxas,
464 l_float32 ratio,
465 l_int32 relation,
466 l_int32 *pchanged)
467 {
468 BOXA *boxad;
469 NUMA *na;
470
471 if (pchanged) *pchanged = FALSE;
472 if (!boxas)
473 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
474 if (boxaGetCount(boxas) == 0) {
475 L_WARNING("boxas is empty\n", __func__);
476 return boxaCopy(boxas, L_COPY);
477 }
478 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
479 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
480 return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL);
481
482 /* Compute the indicator array for saving components */
483 na = boxaMakeWHRatioIndicator(boxas, ratio, relation);
484
485 /* Filter to get output */
486 boxad = boxaSelectWithIndicator(boxas, na, pchanged);
487
488 numaDestroy(&na);
489 return boxad;
490 }
491
492
493 /*!
494 * \brief boxaMakeWHRatioIndicator()
495 *
496 * \param[in] boxa
497 * \param[in] ratio width/height threshold value
498 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
499 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
500 * \return na indicator array, or NULL on error
501 *
502 * <pre>
503 * Notes:
504 * (1) To keep narrow components, use relation = L_SELECT_IF_LT or
505 * L_SELECT_IF_LTE.
506 * To keep wide components, use relation = L_SELECT_IF_GT or
507 * L_SELECT_IF_GTE.
508 * </pre>
509 */
510 NUMA *
511 boxaMakeWHRatioIndicator(BOXA *boxa,
512 l_float32 ratio,
513 l_int32 relation)
514 {
515 l_int32 i, n, w, h, ival;
516 l_float32 whratio;
517 NUMA *na;
518
519 if (!boxa)
520 return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL);
521 if ((n = boxaGetCount(boxa)) == 0)
522 return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL);
523 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
524 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
525 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
526
527 na = numaCreate(n);
528 for (i = 0; i < n; i++) {
529 ival = 0;
530 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
531 whratio = (l_float32)w / (l_float32)h;
532
533 if ((relation == L_SELECT_IF_LT && whratio < ratio) ||
534 (relation == L_SELECT_IF_GT && whratio > ratio) ||
535 (relation == L_SELECT_IF_LTE && whratio <= ratio) ||
536 (relation == L_SELECT_IF_GTE && whratio >= ratio))
537 ival = 1;
538 numaAddNumber(na, ival);
539 }
540
541 return na;
542 }
543
544
545 /*!
546 * \brief boxaSelectWithIndicator()
547 *
548 * \param[in] boxas
549 * \param[in] na indicator numa
550 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
551 * \return boxad, or NULL on error
552 *
553 * <pre>
554 * Notes:
555 * (1) Returns a copy of the boxa if no components are removed.
556 * (2) Uses box copies in the new boxa.
557 * (3) The indicator numa has values 0 (ignore) and 1 (accept).
558 * (4) If all indicator values are 0, the returned boxa is empty.
559 * </pre>
560 */
561 BOXA *
562 boxaSelectWithIndicator(BOXA *boxas,
563 NUMA *na,
564 l_int32 *pchanged)
565 {
566 l_int32 i, n, ival, nsave;
567 BOX *box;
568 BOXA *boxad;
569
570 if (pchanged) *pchanged = FALSE;
571 if (!boxas)
572 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
573 if (!na)
574 return (BOXA *)ERROR_PTR("na not defined", __func__, NULL);
575
576 nsave = 0;
577 n = numaGetCount(na);
578 for (i = 0; i < n; i++) {
579 numaGetIValue(na, i, &ival);
580 if (ival == 1) nsave++;
581 }
582
583 if (nsave == n) {
584 if (pchanged) *pchanged = FALSE;
585 return boxaCopy(boxas, L_COPY);
586 }
587 if (pchanged) *pchanged = TRUE;
588 boxad = boxaCreate(nsave);
589 for (i = 0; i < n; i++) {
590 numaGetIValue(na, i, &ival);
591 if (ival == 0) continue;
592 box = boxaGetBox(boxas, i, L_COPY);
593 boxaAddBox(boxad, box, L_INSERT);
594 }
595
596 return boxad;
597 }
598
599
600 /*---------------------------------------------------------------------*
601 * Boxa Permutation *
602 *---------------------------------------------------------------------*/
603 /*!
604 * \brief boxaPermutePseudorandom()
605 *
606 * \param[in] boxas input boxa
607 * \return boxad with boxes permuted, or NULL on error
608 *
609 * <pre>
610 * Notes:
611 * (1) This does a pseudorandom in-place permutation of the boxes.
612 * (2) The result is guaranteed not to have any boxes in their
613 * original position, but it is not very random. If you
614 * need randomness, use boxaPermuteRandom().
615 * </pre>
616 */
617 BOXA *
618 boxaPermutePseudorandom(BOXA *boxas)
619 {
620 l_int32 n;
621 NUMA *na;
622 BOXA *boxad;
623
624 if (!boxas)
625 return (BOXA *)ERROR_PTR("boxa not defined", __func__, NULL);
626
627 n = boxaGetCount(boxas);
628 na = numaPseudorandomSequence(n, 0);
629 boxad = boxaSortByIndex(boxas, na);
630 numaDestroy(&na);
631 return boxad;
632 }
633
634
635 /*!
636 * \brief boxaPermuteRandom()
637 *
638 * \param[in] boxad [optional] can be null or equal to boxas
639 * \param[in] boxas input boxa
640 * \return boxad with boxes permuted, or NULL on error
641 *
642 * <pre>
643 * Notes:
644 * (1) If boxad is null, make a copy of boxas and permute the copy.
645 * Otherwise, boxad must be equal to boxas, and the operation
646 * is done in-place.
647 * (2) If boxas is empty, return an empty boxad.
648 * (3) This does a random in-place permutation of the boxes,
649 * by swapping each box in turn with a random box. The
650 * result is almost guaranteed not to have any boxes in their
651 * original position.
652 * (4) MSVC rand() has MAX_RAND = 2^15 - 1, so it will not do
653 * a proper permutation is the number of boxes exceeds this.
654 * </pre>
655 */
656 BOXA *
657 boxaPermuteRandom(BOXA *boxad,
658 BOXA *boxas)
659 {
660 l_int32 i, n, index;
661
662 if (!boxas)
663 return (BOXA *)ERROR_PTR("boxa not defined", __func__, NULL);
664 if (boxad && (boxad != boxas))
665 return (BOXA *)ERROR_PTR("boxad defined but in-place", __func__, NULL);
666
667 if (!boxad)
668 boxad = boxaCopy(boxas, L_COPY);
669 if ((n = boxaGetCount(boxad)) == 0)
670 return boxad;
671 index = (l_uint32)rand() % n;
672 index = L_MAX(1, index);
673 boxaSwapBoxes(boxad, 0, index);
674 for (i = 1; i < n; i++) {
675 index = (l_uint32)rand() % n;
676 if (index == i) index--;
677 boxaSwapBoxes(boxad, i, index);
678 }
679
680 return boxad;
681 }
682
683
684 /*!
685 * \brief boxaSwapBoxes()
686 *
687 * \param[in] boxa
688 * \param[in] i, j two indices of boxes, that are to be swapped
689 * \return 0 if OK, 1 on error
690 */
691 l_ok
692 boxaSwapBoxes(BOXA *boxa,
693 l_int32 i,
694 l_int32 j)
695 {
696 l_int32 n;
697 BOX *box;
698
699 if (!boxa)
700 return ERROR_INT("boxa not defined", __func__, 1);
701 n = boxaGetCount(boxa);
702 if (i < 0 || i >= n)
703 return ERROR_INT("i invalid", __func__, 1);
704 if (j < 0 || j >= n)
705 return ERROR_INT("j invalid", __func__, 1);
706 if (i == j)
707 return ERROR_INT("i == j", __func__, 1);
708
709 box = boxa->box[i];
710 boxa->box[i] = boxa->box[j];
711 boxa->box[j] = box;
712 return 0;
713 }
714
715
716 /*---------------------------------------------------------------------*
717 * Boxa and Box Conversions *
718 *---------------------------------------------------------------------*/
719 /*!
720 * \brief boxaConvertToPta()
721 *
722 * \param[in] boxa
723 * \param[in] ncorners 2 or 4 for the representation of each box
724 * \return pta with %ncorners points for each box in the boxa,
725 * or NULL on error
726 *
727 * <pre>
728 * Notes:
729 * (1) If ncorners == 2, we select the UL and LR corners.
730 * Otherwise we save all 4 corners in this order: UL, UR, LL, LR.
731 * (2) Other boxa --> pta functions are:
732 * * boxaExtractAsPta(): allows extraction of any dimension
733 * and/or side location, with each in a separate pta.
734 * * boxaExtractCorners(): extracts any of the four corners as a pta.
735 * </pre>
736 */
737 PTA *
738 boxaConvertToPta(BOXA *boxa,
739 l_int32 ncorners)
740 {
741 l_int32 i, n;
742 BOX *box;
743 PTA *pta, *pta1;
744
745 if (!boxa)
746 return (PTA *)ERROR_PTR("boxa not defined", __func__, NULL);
747 if (ncorners != 2 && ncorners != 4)
748 return (PTA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL);
749
750 n = boxaGetCount(boxa);
751 if ((pta = ptaCreate(n)) == NULL)
752 return (PTA *)ERROR_PTR("pta not made", __func__, NULL);
753 for (i = 0; i < n; i++) {
754 box = boxaGetBox(boxa, i, L_COPY);
755 pta1 = boxConvertToPta(box, ncorners);
756 ptaJoin(pta, pta1, 0, -1);
757 boxDestroy(&box);
758 ptaDestroy(&pta1);
759 }
760
761 return pta;
762 }
763
764
765 /*!
766 * \brief ptaConvertToBoxa()
767 *
768 * \param[in] pta
769 * \param[in] ncorners 2 or 4 for the representation of each box
770 * \return boxa with one box for each 2 or 4 points in the pta,
771 * or NULL on error
772 *
773 * <pre>
774 * Notes:
775 * (1) For 2 corners, the order of the 2 points is UL, LR.
776 * For 4 corners, the order of points is UL, UR, LL, LR.
777 * (2) Each derived box is the minimum size containing all corners.
778 * </pre>
779 */
780 BOXA *
781 ptaConvertToBoxa(PTA *pta,
782 l_int32 ncorners)
783 {
784 l_int32 i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax;
785 BOX *box;
786 BOXA *boxa;
787
788 if (!pta)
789 return (BOXA *)ERROR_PTR("pta not defined", __func__, NULL);
790 if (ncorners != 2 && ncorners != 4)
791 return (BOXA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL);
792 n = ptaGetCount(pta);
793 if (n % ncorners != 0)
794 return (BOXA *)ERROR_PTR("size % ncorners != 0", __func__, NULL);
795 nbox = n / ncorners;
796 if ((boxa = boxaCreate(nbox)) == NULL)
797 return (BOXA *)ERROR_PTR("boxa not made", __func__, NULL);
798 for (i = 0; i < n; i += ncorners) {
799 ptaGetIPt(pta, i, &x1, &y1);
800 ptaGetIPt(pta, i + 1, &x2, &y2);
801 if (ncorners == 2) {
802 box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
803 boxaAddBox(boxa, box, L_INSERT);
804 continue;
805 }
806 ptaGetIPt(pta, i + 2, &x3, &y3);
807 ptaGetIPt(pta, i + 3, &x4, &y4);
808 x = L_MIN(x1, x3);
809 y = L_MIN(y1, y2);
810 xmax = L_MAX(x2, x4);
811 ymax = L_MAX(y3, y4);
812 box = boxCreate(x, y, xmax - x + 1, ymax - y + 1);
813 boxaAddBox(boxa, box, L_INSERT);
814 }
815
816 return boxa;
817 }
818
819
820 /*!
821 * \brief boxConvertToPta()
822 *
823 * \param[in] box
824 * \param[in] ncorners 2 or 4 for the representation of the box
825 * \return pta with %ncorners points, or NULL on error
826 *
827 * <pre>
828 * Notes:
829 * (1) If ncorners == 2, we select the UL and LR corners.
830 * Otherwise we save all 4 corners in this order: UL, UR, LL, LR.
831 * </pre>
832 */
833 PTA *
834 boxConvertToPta(BOX *box,
835 l_int32 ncorners)
836 {
837 l_int32 x, y, w, h;
838 PTA *pta;
839
840 if (!box)
841 return (PTA *)ERROR_PTR("box not defined", __func__, NULL);
842 if (ncorners != 2 && ncorners != 4)
843 return (PTA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL);
844
845 if ((pta = ptaCreate(ncorners)) == NULL)
846 return (PTA *)ERROR_PTR("pta not made", __func__, NULL);
847 boxGetGeometry(box, &x, &y, &w, &h);
848 ptaAddPt(pta, x, y);
849 if (ncorners == 2) {
850 ptaAddPt(pta, x + w - 1, y + h - 1);
851 } else {
852 ptaAddPt(pta, x + w - 1, y);
853 ptaAddPt(pta, x, y + h - 1);
854 ptaAddPt(pta, x + w - 1, y + h - 1);
855 }
856
857 return pta;
858 }
859
860
861 /*!
862 * \brief ptaConvertToBox()
863 *
864 * \param[in] pta
865 * \return box minimum containing all points in the pta, or NULL on error
866 *
867 * <pre>
868 * Notes:
869 * (1) For 2 corners, the order of the 2 points is UL, LR.
870 * For 4 corners, the order of points is UL, UR, LL, LR.
871 * </pre>
872 */
873 BOX *
874 ptaConvertToBox(PTA *pta)
875 {
876 l_int32 n, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax;
877
878 if (!pta)
879 return (BOX *)ERROR_PTR("pta not defined", __func__, NULL);
880 n = ptaGetCount(pta);
881 if (n != 2 && n != 4)
882 return (BOX *)ERROR_PTR("n must be 2 or 4", __func__, NULL);
883 ptaGetIPt(pta, 0, &x1, &y1);
884 ptaGetIPt(pta, 1, &x2, &y2);
885 if (n == 2)
886 return boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
887
888 /* 4 corners */
889 ptaGetIPt(pta, 2, &x3, &y3);
890 ptaGetIPt(pta, 3, &x4, &y4);
891 x = L_MIN(x1, x3);
892 y = L_MIN(y1, y2);
893 xmax = L_MAX(x2, x4);
894 ymax = L_MAX(y3, y4);
895 return boxCreate(x, y, xmax - x + 1, ymax - y + 1);
896 }
897
898
899 /*---------------------------------------------------------------------*
900 * Miscellaneous Boxa functions *
901 *---------------------------------------------------------------------*/
902 /*!
903 * \brief boxaGetExtent()
904 *
905 * \param[in] boxa
906 * \param[out] pw [optional] width
907 * \param[out] ph [optional] height
908 * \param[out] pbox [optional] minimum box containing all boxes in boxa
909 * \return 0 if OK, 1 on error
910 *
911 * <pre>
912 * Notes:
913 * (1) This computes the minimum rectangular bounding region
914 * that contains all valid boxes in a boxa.
915 * (2) The returned w and h are the minimum size image
916 * that would contain all boxes untranslated.
917 * (3) If there are no valid boxes, returned w and h are 0 and
918 * all parameters in the returned box are 0. This
919 * is not an error, because an empty boxa is valid and
920 * boxaGetExtent() is required for serialization.
921 * </pre>
922 */
923 l_ok
924 boxaGetExtent(BOXA *boxa,
925 l_int32 *pw,
926 l_int32 *ph,
927 BOX **pbox)
928 {
929 l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin, found;
930
931 if (!pw && !ph && !pbox)
932 return ERROR_INT("no ptrs defined", __func__, 1);
933 if (pw) *pw = 0;
934 if (ph) *ph = 0;
935 if (pbox) *pbox = NULL;
936 if (!boxa)
937 return ERROR_INT("boxa not defined", __func__, 1);
938
939 n = boxaGetCount(boxa);
940 xmax = ymax = 0;
941 xmin = ymin = 100000000;
942 found = FALSE;
943 for (i = 0; i < n; i++) {
944 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
945 if (w <= 0 || h <= 0)
946 continue;
947 found = TRUE;
948 xmin = L_MIN(xmin, x);
949 ymin = L_MIN(ymin, y);
950 xmax = L_MAX(xmax, x + w);
951 ymax = L_MAX(ymax, y + h);
952 }
953 if (found == FALSE) /* no valid boxes in boxa */
954 xmin = ymin = 0;
955 if (pw) *pw = xmax;
956 if (ph) *ph = ymax;
957 if (pbox)
958 *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin);
959
960 return 0;
961 }
962
963
964 /*!
965 * \brief boxaGetCoverage()
966 *
967 * \param[in] boxa
968 * \param[in] wc, hc dimensions of overall clipping rectangle with UL
969 * corner at (0, 0 that is covered by the boxes.
970 * \param[in] exactflag 1 for guaranteeing an exact result; 0 for getting
971 * an exact result only if the boxes do not overlap
972 * \param[out] pfract sum of box area as fraction of w * h
973 * \return 0 if OK, 1 on error
974 *
975 * <pre>
976 * Notes:
977 * (1) The boxes in boxa are clipped to the input rectangle.
978 * (2) * When %exactflag == 1, we generate a 1 bpp pix of size
979 * wc x hc, paint all the boxes black, and count the fg pixels.
980 * This can take 1 msec on a large page with many boxes.
981 * * When %exactflag == 0, we clip each box to the wc x hc region
982 * and sum the resulting areas. This is faster.
983 * * The results are the same when none of the boxes overlap
984 * within the wc x hc region.
985 * </pre>
986 */
987 l_ok
988 boxaGetCoverage(BOXA *boxa,
989 l_int32 wc,
990 l_int32 hc,
991 l_int32 exactflag,
992 l_float32 *pfract)
993 {
994 l_int32 i, n, x, y, w, h, sum;
995 BOX *box, *boxc;
996 PIX *pixt;
997
998 if (!pfract)
999 return ERROR_INT("&fract not defined", __func__, 1);
1000 *pfract = 0.0;
1001 if (!boxa)
1002 return ERROR_INT("boxa not defined", __func__, 1);
1003
1004 n = boxaGetCount(boxa);
1005 if (n == 0)
1006 return ERROR_INT("no boxes in boxa", __func__, 1);
1007
1008 if (exactflag == 0) { /* quick and dirty */
1009 sum = 0;
1010 for (i = 0; i < n; i++) {
1011 box = boxaGetBox(boxa, i, L_CLONE);
1012 if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) {
1013 boxGetGeometry(boxc, NULL, NULL, &w, &h);
1014 sum += w * h;
1015 boxDestroy(&boxc);
1016 }
1017 boxDestroy(&box);
1018 }
1019 } else { /* slower and exact */
1020 pixt = pixCreate(wc, hc, 1);
1021 for (i = 0; i < n; i++) {
1022 box = boxaGetBox(boxa, i, L_CLONE);
1023 boxGetGeometry(box, &x, &y, &w, &h);
1024 pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0);
1025 boxDestroy(&box);
1026 }
1027 pixCountPixels(pixt, &sum, NULL);
1028 pixDestroy(&pixt);
1029 }
1030
1031 *pfract = (l_float32)sum / (l_float32)(wc * hc);
1032 return 0;
1033 }
1034
1035
1036 /*!
1037 * \brief boxaaSizeRange()
1038 *
1039 * \param[in] baa
1040 * \param[out] pminw [optional] min width of all boxes
1041 * \param[out] pmaxw [optional] max width of all boxes
1042 * \param[out] pminh [optional] min height of all boxes
1043 * \param[out] pmaxh [optional] max height of all boxes
1044 * \return 0 if OK, 1 on error
1045 */
1046 l_ok
1047 boxaaSizeRange(BOXAA *baa,
1048 l_int32 *pminw,
1049 l_int32 *pminh,
1050 l_int32 *pmaxw,
1051 l_int32 *pmaxh)
1052 {
1053 l_int32 minw, minh, maxw, maxh, minbw, minbh, maxbw, maxbh, i, n;
1054 BOXA *boxa;
1055
1056 if (!pminw && !pmaxw && !pminh && !pmaxh)
1057 return ERROR_INT("no data can be returned", __func__, 1);
1058 if (pminw) *pminw = 0;
1059 if (pminh) *pminh = 0;
1060 if (pmaxw) *pmaxw = 0;
1061 if (pmaxh) *pmaxh = 0;
1062 if (!baa)
1063 return ERROR_INT("baa not defined", __func__, 1);
1064
1065 minw = minh = 100000000;
1066 maxw = maxh = 0;
1067 n = boxaaGetCount(baa);
1068 for (i = 0; i < n; i++) {
1069 boxa = boxaaGetBoxa(baa, i, L_CLONE);
1070 boxaSizeRange(boxa, &minbw, &minbh, &maxbw, &maxbh);
1071 if (minbw < minw)
1072 minw = minbw;
1073 if (minbh < minh)
1074 minh = minbh;
1075 if (maxbw > maxw)
1076 maxw = maxbw;
1077 if (maxbh > maxh)
1078 maxh = maxbh;
1079 boxaDestroy(&boxa);
1080 }
1081
1082 if (pminw) *pminw = minw;
1083 if (pminh) *pminh = minh;
1084 if (pmaxw) *pmaxw = maxw;
1085 if (pmaxh) *pmaxh = maxh;
1086 return 0;
1087 }
1088
1089
1090 /*!
1091 * \brief boxaSizeRange()
1092 *
1093 * \param[in] boxa
1094 * \param[out] pminw [optional] min width of all boxes
1095 * \param[out] pmaxw [optional] max width of all boxes
1096 * \param[out] pminh [optional] min height of all boxes
1097 * \param[out] pmaxh [optional] max height of all boxes
1098 * \return 0 if OK, 1 on error
1099 */
1100 l_ok
1101 boxaSizeRange(BOXA *boxa,
1102 l_int32 *pminw,
1103 l_int32 *pminh,
1104 l_int32 *pmaxw,
1105 l_int32 *pmaxh)
1106 {
1107 l_int32 minw, minh, maxw, maxh, i, n, w, h;
1108
1109 if (!pminw && !pmaxw && !pminh && !pmaxh)
1110 return ERROR_INT("no data can be returned", __func__, 1);
1111 if (pminw) *pminw = 0;
1112 if (pminh) *pminh = 0;
1113 if (pmaxw) *pmaxw = 0;
1114 if (pmaxh) *pmaxh = 0;
1115 if (!boxa)
1116 return ERROR_INT("boxa not defined", __func__, 1);
1117
1118 minw = minh = 100000000;
1119 maxw = maxh = 0;
1120 n = boxaGetCount(boxa);
1121 for (i = 0; i < n; i++) {
1122 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
1123 if (w < minw)
1124 minw = w;
1125 if (h < minh)
1126 minh = h;
1127 if (w > maxw)
1128 maxw = w;
1129 if (h > maxh)
1130 maxh = h;
1131 }
1132
1133 if (pminw) *pminw = minw;
1134 if (pminh) *pminh = minh;
1135 if (pmaxw) *pmaxw = maxw;
1136 if (pmaxh) *pmaxh = maxh;
1137 return 0;
1138 }
1139
1140
1141 /*!
1142 * \brief boxaLocationRange()
1143 *
1144 * \param[in] boxa
1145 * \param[out] pminx [optional] min (UL corner) x value of all boxes
1146 * \param[out] pminy [optional] min (UL corner) y value of all boxes
1147 * \param[out] pmaxx [optional] max (UL corner) x value of all boxes
1148 * \param[out] pmaxy [optional] max (UL corner) y value of all boxes
1149 * \return 0 if OK, 1 on error
1150 */
1151 l_ok
1152 boxaLocationRange(BOXA *boxa,
1153 l_int32 *pminx,
1154 l_int32 *pminy,
1155 l_int32 *pmaxx,
1156 l_int32 *pmaxy)
1157 {
1158 l_int32 minx, miny, maxx, maxy, i, n, x, y;
1159
1160 if (!pminx && !pminy && !pmaxx && !pmaxy)
1161 return ERROR_INT("no data can be returned", __func__, 1);
1162 if (pminx) *pminx = 0;
1163 if (pminy) *pminy = 0;
1164 if (pmaxx) *pmaxx = 0;
1165 if (pmaxy) *pmaxy = 0;
1166 if (!boxa)
1167 return ERROR_INT("boxa not defined", __func__, 1);
1168
1169 minx = miny = 100000000;
1170 maxx = maxy = 0;
1171 n = boxaGetCount(boxa);
1172 for (i = 0; i < n; i++) {
1173 boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL);
1174 if (x < minx)
1175 minx = x;
1176 if (y < miny)
1177 miny = y;
1178 if (x > maxx)
1179 maxx = x;
1180 if (y > maxy)
1181 maxy = y;
1182 }
1183
1184 if (pminx) *pminx = minx;
1185 if (pminy) *pminy = miny;
1186 if (pmaxx) *pmaxx = maxx;
1187 if (pmaxy) *pmaxy = maxy;
1188
1189 return 0;
1190 }
1191
1192
1193 /*!
1194 * \brief boxaGetSizes()
1195 *
1196 * \param[in] boxa
1197 * \param[out] pnaw [optional] widths of valid boxes
1198 * \param[out] pnah [optional] heights of valid boxes
1199 * \return 0 if OK, 1 on error
1200 */
1201 l_ok
1202 boxaGetSizes(BOXA *boxa,
1203 NUMA **pnaw,
1204 NUMA **pnah)
1205 {
1206 l_int32 i, n, w, h;
1207 BOX *box;
1208
1209 if (pnaw) *pnaw = NULL;
1210 if (pnah) *pnah = NULL;
1211 if (!pnaw && !pnah)
1212 return ERROR_INT("no output requested", __func__, 1);
1213 if (!boxa)
1214 return ERROR_INT("boxa not defined", __func__, 1);
1215
1216 n = boxaGetValidCount(boxa);
1217 if (pnaw) *pnaw = numaCreate(n);
1218 if (pnah) *pnah = numaCreate(n);
1219 for (i = 0; i < n; i++) {
1220 box = boxaGetValidBox(boxa, i, L_COPY);
1221 if (box) {
1222 boxGetGeometry(box, NULL, NULL, &w, &h);
1223 if (pnaw) numaAddNumber(*pnaw, w);
1224 if (pnah) numaAddNumber(*pnah, h);
1225 boxDestroy(&box);
1226 }
1227 }
1228
1229 return 0;
1230 }
1231
1232
1233 /*!
1234 * \brief boxaGetArea()
1235 *
1236 * \param[in] boxa
1237 * \param[out] parea total area of all boxes
1238 * \return 0 if OK, 1 on error
1239 *
1240 * <pre>
1241 * Notes:
1242 * (1) Measures the total area of the boxes, without regard to overlaps.
1243 * </pre>
1244 */
1245 l_ok
1246 boxaGetArea(BOXA *boxa,
1247 l_int32 *parea)
1248 {
1249 l_int32 i, n, w, h;
1250
1251 if (!parea)
1252 return ERROR_INT("&area not defined", __func__, 1);
1253 *parea = 0;
1254 if (!boxa)
1255 return ERROR_INT("boxa not defined", __func__, 1);
1256
1257 n = boxaGetCount(boxa);
1258 for (i = 0; i < n; i++) {
1259 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
1260 *parea += w * h;
1261 }
1262 return 0;
1263 }
1264
1265
1266 /*!
1267 * \brief boxaDisplayTiled()
1268 *
1269 * \param[in] boxas
1270 * \param[in] pixa [optional] background for each box
1271 * \param[in] first index of first box
1272 * \param[in] last index of last box; use -1 to go to end
1273 * \param[in] maxwidth of output image
1274 * \param[in] linewidth width of box outlines, before scaling
1275 * \param[in] scalefactor applied to every box; use 1.0 for no scaling
1276 * \param[in] background 0 for white, 1 for black; this is the color
1277 * of the spacing between the images
1278 * \param[in] spacing between images, and on outside
1279 * \param[in] border width of black border added to each image;
1280 * use 0 for no border
1281 * \return pixd of tiled images of boxes, or NULL on error
1282 *
1283 * <pre>
1284 * Notes:
1285 * (1) Displays each box separately in a tiled 32 bpp image.
1286 * (2) If pixa is defined, it must have the same count as the boxa,
1287 * and it will be a background over with each box is rendered.
1288 * If pixa is not defined, the boxes will be rendered over
1289 * blank images of identical size.
1290 * (3) See pixaDisplayTiledInRows() for other parameters.
1291 * </pre>
1292 */
1293 PIX *
1294 boxaDisplayTiled(BOXA *boxas,
1295 PIXA *pixa,
1296 l_int32 first,
1297 l_int32 last,
1298 l_int32 maxwidth,
1299 l_int32 linewidth,
1300 l_float32 scalefactor,
1301 l_int32 background,
1302 l_int32 spacing,
1303 l_int32 border)
1304 {
1305 char buf[32];
1306 l_int32 i, n, npix, w, h, fontsize;
1307 L_BMF *bmf;
1308 BOX *box;
1309 BOXA *boxa;
1310 PIX *pix1, *pix2, *pixd;
1311 PIXA *pixat;
1312
1313 if (!boxas)
1314 return (PIX *)ERROR_PTR("boxas not defined", __func__, NULL);
1315
1316 boxa = boxaSaveValid(boxas, L_COPY);
1317 n = boxaGetCount(boxa);
1318 if (pixa) {
1319 npix = pixaGetCount(pixa);
1320 if (n != npix) {
1321 boxaDestroy(&boxa);
1322 return (PIX *)ERROR_PTR("boxa and pixa counts differ",
1323 __func__, NULL);
1324 }
1325 }
1326 first = L_MAX(0, first);
1327 if (last < 0) last = n - 1;
1328 if (first >= n) {
1329 boxaDestroy(&boxa);
1330 return (PIX *)ERROR_PTR("invalid first", __func__, NULL);
1331 }
1332 if (last >= n) {
1333 L_WARNING("last = %d is beyond max index = %d; adjusting\n",
1334 __func__, last, n - 1);
1335 last = n - 1;
1336 }
1337 if (first > last) {
1338 boxaDestroy(&boxa);
1339 return (PIX *)ERROR_PTR("first > last", __func__, NULL);
1340 }
1341
1342 /* Because the bitmap font will be reduced when tiled, choose the
1343 * font size inversely with the scale factor. */
1344 if (scalefactor > 0.8)
1345 fontsize = 6;
1346 else if (scalefactor > 0.6)
1347 fontsize = 10;
1348 else if (scalefactor > 0.4)
1349 fontsize = 14;
1350 else if (scalefactor > 0.3)
1351 fontsize = 18;
1352 else fontsize = 20;
1353 bmf = bmfCreate(NULL, fontsize);
1354
1355 pixat = pixaCreate(n);
1356 boxaGetExtent(boxa, &w, &h, NULL);
1357 for (i = first; i <= last; i++) {
1358 box = boxaGetBox(boxa, i, L_CLONE);
1359 if (!pixa) {
1360 pix1 = pixCreate(w, h, 32);
1361 pixSetAll(pix1);
1362 } else {
1363 pix1 = pixaGetPix(pixa, i, L_COPY);
1364 }
1365 pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00);
1366 snprintf(buf, sizeof(buf), "%d", i);
1367 pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000,
1368 L_ADD_BELOW, NULL);
1369 pixDestroy(&pix1);
1370 pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0);
1371 pixaAddPix(pixat, pix2, L_INSERT);
1372 boxDestroy(&box);
1373 }
1374 bmfDestroy(&bmf);
1375 boxaDestroy(&boxa);
1376
1377 pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background,
1378 spacing, border);
1379 pixaDestroy(&pixat);
1380 return pixd;
1381 }