1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 - This software is distributed in the hope that it will be
4 - useful, but with NO WARRANTY OF ANY KIND.
5 - No author or distributor accepts responsibility to anyone for the
6 - consequences of using this software, or for whether it serves any
7 - particular purpose or works at all, unless he or she says so in
8 - writing. Everyone is granted permission to copy, modify and
9 - redistribute this source code, for commercial or non-commercial
10 - purposes, with the following restrictions: (1) the origin of this
11 - source code must not be misrepresented; (2) modified versions must
12 - be plainly marked as such; and (3) this notice may not be removed
13 - or altered from any source or modified source distribution.
14 *====================================================================*/
15
16 /*
17 * pixafunc2.c
18 *
19 * Pixa Display (render into a pix)
20 * PIX *pixaDisplay()
21 * PIX *pixaDisplayRandomCmap()
22 * PIX *pixaDisplayOnLattice()
23 * PIX *pixaDisplayUnsplit()
24 * PIX *pixaDisplayTiled()
25 * PIX *pixaDisplayTiledInRows()
26 * PIX *pixaDisplayTiledAndScaled()
27 *
28 * Pixaa Display (render into a pix)
29 * PIX *pixaaDisplay()
30 * PIX *pixaaDisplayByPixa()
31 * PIXA *pixaaDisplayTiledAndScaled()
32 *
33 * We give seven methods for displaying a pixa in a pix.
34 * Some work for 1 bpp input; others for any input depth.
35 * Some give an output depth that depends on the input depth;
36 * others give a different output depth or allow you to choose it.
37 * Some use a boxes to determine where each pix goes; others tile
38 * onto a regular lattice; yet others tile onto an irregular lattice.
39 *
40 * Here is a brief description of what these functions do.
41 *
42 * pixaDisplay()
43 * This uses the boxes to lay out each pix. It is typically
44 * used to reconstruct a pix that has been broken into components.
45 * pixaDisplayRandomCmap()
46 * This also uses the boxes to lay out each pix. However, it creates
47 * a colormapped dest, where each 1 bpp pix is given a randomly
48 * generated color (up to 256 are used).
49 * pixaDisplayOnLattice()
50 * This puts each pix, sequentially, onto a regular lattice,
51 * omitting any pix that are too big for the lattice size.
52 * This is useful, for example, to store bitmapped fonts,
53 * where all the characters are stored in a single image.
54 * pixaDisplayUnsplit()
55 * This lays out a mosaic of tiles (the pix in the pixa) that
56 * are all of equal size. (Don't use this for unequal sized pix!)
57 * For example, it can be used to invert the action of
58 * pixaSplitPix().
59 * pixaDisplayTiled()
60 * Like pixaDisplayOnLattice(), this places each pix on a regular
61 * lattice, but here the lattice size is determined by the
62 * largest component, and no components are omitted. This is
63 * dangerous if there are thousands of small components and
64 * one or more very large one, because the size of the resulting
65 * pix can be huge!
66 * pixaDisplayTiledInRows()
67 * This puts each pix down in a series of rows, where the upper
68 * edges of each pix in a row are alined and there is a uniform
69 * spacing between the pix. The height of each row is determined
70 * by the tallest pix that was put in the row. This function
71 * is a reasonably efficient way to pack the subimages.
72 * pixaDisplayTiledAndScaled()
73 * This scales each pix to a given width and output depth,
74 * and then tiles them in rows with a given number placed in
75 * each row. This is very useful for presenting a sequence
76 * of images that can be at different resolutions, but which
77 * are derived from the same initial image.
78 */
79
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <math.h> /* for sqrt() */
84 #include "allheaders.h"
85
86
87 /*---------------------------------------------------------------------*
88 * Pixa Display *
89 *---------------------------------------------------------------------*/
90 /*!
91 * pixaDisplay()
92 *
93 * Input: pixa
94 * w, h (if set to 0, determines the size from the
95 * b.b. of the components in pixa)
96 * Return: pix, or null on error
97 *
98 * Notes:
99 * (1) This uses the boxes to place each pix in the rendered composite.
100 * (2) Set w = h = 0 to use the b.b. of the components to determine
101 * the size of the returned pix.
102 * (3) The background is written "white". On 1 bpp, each successive
103 * pix is "painted" (adding foreground), whereas for grayscale
104 * or color each successive pix is blitted with just the src.
105 * (4) If the pixa is empty, returns an empty 1 bpp pix.
106 */
107 PIX *
pixaDisplay(PIXA * pixa,l_int32 w,l_int32 h)108 pixaDisplay(PIXA *pixa,
109 l_int32 w,
110 l_int32 h)
111 {
112 l_int32 i, n, d, xb, yb, wb, hb;
113 BOXA *boxa;
114 PIX *pixt, *pixd;
115
116 PROCNAME("pixaDisplay");
117
118 if (!pixa)
119 return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
120
121 n = pixaGetCount(pixa);
122 if (n == 0 && w == 0 && h == 0)
123 return (PIX *)ERROR_PTR("no components; no size", procName, NULL);
124 if (n == 0) {
125 L_WARNING("no components; returning empty 1 bpp pix", procName);
126 return pixCreate(w, h, 1);
127 }
128
129 /* If w and h not input, determine the minimum size required
130 * to contain the origin and all c.c. */
131 if (w == 0 || h == 0) {
132 boxa = pixaGetBoxa(pixa, L_CLONE);
133 boxaGetExtent(boxa, &w, &h, NULL);
134 boxaDestroy(&boxa);
135 }
136
137 /* Use the first pix in pixa to determine the depth. */
138 pixt = pixaGetPix(pixa, 0, L_CLONE);
139 d = pixGetDepth(pixt);
140 pixDestroy(&pixt);
141
142 if ((pixd = pixCreate(w, h, d)) == NULL)
143 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
144 if (d > 1)
145 pixSetAll(pixd);
146 for (i = 0; i < n; i++) {
147 if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) {
148 L_WARNING("no box found!", procName);
149 continue;
150 }
151 pixt = pixaGetPix(pixa, i, L_CLONE);
152 if (d == 1)
153 pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
154 else
155 pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt, 0, 0);
156 pixDestroy(&pixt);
157 }
158
159 return pixd;
160 }
161
162
163 /*!
164 * pixaDisplayRandomCmap()
165 *
166 * Input: pixa (of 1 bpp components, with boxa)
167 * w, h (if set to 0, determines the size from the
168 * b.b. of the components in pixa)
169 * Return: pix (8 bpp, cmapped, with random colors on the components),
170 * or null on error
171 *
172 * Notes:
173 * (1) This uses the boxes to place each pix in the rendered composite.
174 * (2) By default, the background color is: black, cmap index 0.
175 * This can be changed by pixcmapResetColor()
176 */
177 PIX *
pixaDisplayRandomCmap(PIXA * pixa,l_int32 w,l_int32 h)178 pixaDisplayRandomCmap(PIXA *pixa,
179 l_int32 w,
180 l_int32 h)
181 {
182 l_int32 i, n, d, index, xb, yb, wb, hb;
183 BOXA *boxa;
184 PIX *pixs, *pixt, *pixd;
185 PIXCMAP *cmap;
186
187 PROCNAME("pixaDisplayRandomCmap");
188
189 if (!pixa)
190 return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
191
192 n = pixaGetCount(pixa);
193 if (n == 0)
194 return (PIX *)ERROR_PTR("no components", procName, NULL);
195
196 /* Use the first pix in pixa to verify depth is 1 bpp */
197 pixs = pixaGetPix(pixa, 0, L_CLONE);
198 d = pixGetDepth(pixs);
199 pixDestroy(&pixs);
200 if (d != 1)
201 return (PIX *)ERROR_PTR("components not 1 bpp", procName, NULL);
202
203 /* If w and h not input, determine the minimum size required
204 * to contain the origin and all c.c. */
205 if (w == 0 || h == 0) {
206 boxa = pixaGetBoxa(pixa, L_CLONE);
207 boxaGetExtent(boxa, &w, &h, NULL);
208 boxaDestroy(&boxa);
209 }
210
211 /* Set up an 8 bpp dest pix, with a colormap with 254 random colors */
212 if ((pixd = pixCreate(w, h, 8)) == NULL)
213 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
214 cmap = pixcmapCreateRandom(8, 1, 1);
215 pixSetColormap(pixd, cmap);
216
217 /* Color each component and blit it in */
218 for (i = 0; i < n; i++) {
219 index = 1 + (i % 254);
220 pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb);
221 pixs = pixaGetPix(pixa, i, L_CLONE);
222 pixt = pixConvert1To8(NULL, pixs, 0, index);
223 pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
224 pixDestroy(&pixs);
225 pixDestroy(&pixt);
226 }
227
228 return pixd;
229 }
230
231
232 /*!
233 * pixaDisplayOnLattice()
234 *
235 * Input: pixa
236 * xspace
237 * yspace
238 * Return: pix of composite images, or null on error
239 *
240 * Notes:
241 * (1) This places each pix on sequentially on a regular lattice
242 * in the rendered composite. If a pix is too large to fit in the
243 * allocated lattice space, it is not rendered.
244 * (2) If any pix has a colormap, all pix are rendered in rgb.
245 * (3) This is useful when putting bitmaps of components,
246 * such as characters, into a single image.
247 */
248 PIX *
pixaDisplayOnLattice(PIXA * pixa,l_int32 xspace,l_int32 yspace)249 pixaDisplayOnLattice(PIXA *pixa,
250 l_int32 xspace,
251 l_int32 yspace)
252 {
253 l_int32 n, nw, nh, w, h, d, wt, ht;
254 l_int32 index, i, j, hascmap;
255 PIX *pix, *pixt, *pixd;
256 PIXA *pixat;
257
258 PROCNAME("pixaDisplayOnLattice");
259
260 if (!pixa)
261 return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
262
263 /* If any pix have colormaps, generate rgb */
264 if ((n = pixaGetCount(pixa)) == 0)
265 return (PIX *)ERROR_PTR("no components", procName, NULL);
266 pixaAnyColormaps(pixa, &hascmap);
267 if (hascmap) {
268 pixat = pixaCreate(n);
269 for (i = 0; i < n; i++) {
270 pixt = pixaGetPix(pixa, i, L_CLONE);
271 pix = pixConvertTo32(pixt);
272 pixaAddPix(pixat, pix, L_INSERT);
273 pixDestroy(&pixt);
274 }
275 }
276 else
277 pixat = pixaCopy(pixa, L_CLONE);
278
279 nw = (l_int32)sqrt((l_float64)n);
280 nh = (n + nw - 1) / nw;
281 w = xspace * nw;
282 h = yspace * nh;
283
284 /* Use the first pix in pixa to determine the depth. */
285 pixaGetPixDimensions(pixat, 0, NULL, NULL, &d);
286
287 if ((pixd = pixCreate(w, h, d)) == NULL) {
288 pixaDestroy(&pixat);
289 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
290 }
291
292 index = 0;
293 for (i = 0; i < nh; i++) {
294 for (j = 0; j < nw && index < n; j++, index++) {
295 pixt = pixaGetPix(pixat, index, L_CLONE);
296 pixGetDimensions(pixt, &wt, &ht, NULL);
297 if (wt > xspace || ht > yspace) {
298 fprintf(stderr, "pix(%d) omitted; size %dx%d\n", index, wt, ht);
299 pixDestroy(&pixt);
300 continue;
301 }
302 pixRasterop(pixd, j * xspace, i * yspace, wt, ht,
303 PIX_PAINT, pixt, 0, 0);
304 pixDestroy(&pixt);
305 }
306 }
307
308 pixaDestroy(&pixat);
309 return pixd;
310 }
311
312
313 /*!
314 * pixaDisplayUnsplit()
315 *
316 * Input: pixa
317 * nx (number of mosaic cells horizontally)
318 * ny (number of mosaic cells vertically)
319 * borderwidth (of added border on all sides)
320 * bordercolor (in our RGBA format: 0xrrggbbaa)
321 * Return: pix of tiled images, or null on error
322 *
323 * Notes:
324 * (1) This is a logical inverse of pixaSplitPix(). It
325 * constructs a pix from a mosaic of tiles, all of equal size.
326 * (2) For added generality, a border of arbitrary color can
327 * be added to each of the tiles.
328 * (3) In use, pixa will typically have either been generated
329 * from pixaSplitPix() or will derived from a pixa that
330 * was so generated.
331 * (4) All pix in the pixa must be of equal depth, and, if
332 * colormapped, have the same colormap.
333 */
334 PIX *
pixaDisplayUnsplit(PIXA * pixa,l_int32 nx,l_int32 ny,l_int32 borderwidth,l_uint32 bordercolor)335 pixaDisplayUnsplit(PIXA *pixa,
336 l_int32 nx,
337 l_int32 ny,
338 l_int32 borderwidth,
339 l_uint32 bordercolor)
340 {
341 l_int32 w, h, d, wt, ht;
342 l_int32 i, j, k, x, y, n;
343 PIX *pixt, *pixd;
344
345 PROCNAME("pixaDisplayUnsplit");
346
347 if (!pixa)
348 return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
349 if (nx <= 0 || ny <= 0)
350 return (PIX *)ERROR_PTR("nx and ny must be > 0", procName, NULL);
351 if ((n = pixaGetCount(pixa)) == 0)
352 return (PIX *)ERROR_PTR("no components", procName, NULL);
353 if (n != nx * ny)
354 return (PIX *)ERROR_PTR("n != nx * ny", procName, NULL);
355 borderwidth = L_MAX(0, borderwidth);
356
357 pixaGetPixDimensions(pixa, 0, &wt, &ht, &d);
358 w = nx * (wt + 2 * borderwidth);
359 h = ny * (ht + 2 * borderwidth);
360
361 if ((pixd = pixCreate(w, h, d)) == NULL)
362 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
363 pixt = pixaGetPix(pixa, 0, L_CLONE);
364 pixCopyColormap(pixd, pixt);
365 pixDestroy(&pixt);
366 if (borderwidth > 0)
367 pixSetAllArbitrary(pixd, bordercolor);
368
369 y = borderwidth;
370 for (i = 0, k = 0; i < ny; i++) {
371 x = borderwidth;
372 for (j = 0; j < nx; j++, k++) {
373 pixt = pixaGetPix(pixa, k, L_CLONE);
374 pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pixt, 0, 0);
375 pixDestroy(&pixt);
376 x += wt + 2 * borderwidth;
377 }
378 y += ht + 2 * borderwidth;
379 }
380
381 return pixd;
382 }
383
384
385 /*!
386 * pixaDisplayTiled()
387 *
388 * Input: pixa
389 * maxwidth (of output image)
390 * background (0 for white, 1 for black)
391 * spacing
392 * Return: pix of tiled images, or null on error
393 *
394 * Notes:
395 * (1) This saves a pixa to a single image file of width not to
396 * exceed maxwidth, with background color either white or black,
397 * and with each subimage spaced on a regular lattice.
398 * (2) The lattice size is determined from the largest width and height,
399 * separately, of all pix in the pixa.
400 * (3) All pix in the pixa must be of equal depth.
401 * (4) If any pix has a colormap, all pix are rendered in rgb.
402 * (5) Careful: because no components are omitted, this is
403 * dangerous if there are thousands of small components and
404 * one or more very large one, because the size of the
405 * resulting pix can be huge!
406 */
407 PIX *
pixaDisplayTiled(PIXA * pixa,l_int32 maxwidth,l_int32 background,l_int32 spacing)408 pixaDisplayTiled(PIXA *pixa,
409 l_int32 maxwidth,
410 l_int32 background,
411 l_int32 spacing)
412 {
413 l_int32 w, h, wmax, hmax, wd, hd, d, hascmap;
414 l_int32 i, j, n, ni, ncols, nrows;
415 l_int32 ystart, xstart, wt, ht;
416 PIX *pix, *pixt, *pixd;
417 PIXA *pixat;
418
419 PROCNAME("pixaDisplayTiled");
420
421 if (!pixa)
422 return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
423
424 /* If any pix have colormaps, generate rgb */
425 if ((n = pixaGetCount(pixa)) == 0)
426 return (PIX *)ERROR_PTR("no components", procName, NULL);
427 pixaAnyColormaps(pixa, &hascmap);
428 if (hascmap) {
429 pixat = pixaCreate(n);
430 for (i = 0; i < n; i++) {
431 pixt = pixaGetPix(pixa, i, L_CLONE);
432 pix = pixConvertTo32(pixt);
433 pixaAddPix(pixat, pix, L_INSERT);
434 pixDestroy(&pixt);
435 }
436 }
437 else
438 pixat = pixaCopy(pixa, L_CLONE);
439
440 /* Find the largest width and height of the subimages */
441 wmax = hmax = 0;
442 for (i = 0; i < n; i++) {
443 pix = pixaGetPix(pixat, i, L_CLONE);
444 pixGetDimensions(pix, &w, &h, NULL);
445 if (i == 0)
446 d = pixGetDepth(pix);
447 else if (d != pixGetDepth(pix)) {
448 pixDestroy(&pix);
449 pixaDestroy(&pixat);
450 return (PIX *)ERROR_PTR("depths not equal", procName, NULL);
451 }
452 if (w > wmax)
453 wmax = w;
454 if (h > hmax)
455 hmax = h;
456 pixDestroy(&pix);
457 }
458
459 /* Get the number of rows and columns and the output image size */
460 spacing = L_MAX(spacing, 0);
461 ncols = (l_int32)((l_float32)(maxwidth - spacing) /
462 (l_float32)(wmax + spacing));
463 nrows = (n + ncols - 1) / ncols;
464 wd = wmax * ncols + spacing * (ncols + 1);
465 hd = hmax * nrows + spacing * (nrows + 1);
466 if ((pixd = pixCreate(wd, hd, d)) == NULL) {
467 pixaDestroy(&pixat);
468 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
469 }
470
471 #if 0
472 fprintf(stderr, " nrows = %d, ncols = %d, wmax = %d, hmax = %d\n",
473 nrows, ncols, wmax, hmax);
474 fprintf(stderr, " space = %d, wd = %d, hd = %d, n = %d\n",
475 space, wd, hd, n);
476 #endif
477
478 /* Reset the background color if necessary */
479 if ((background == 1 && d == 1) || (background == 0 && d != 1))
480 pixSetAll(pixd);
481
482 /* Blit the images to the dest */
483 for (i = 0, ni = 0; i < nrows; i++) {
484 ystart = spacing + i * (hmax + spacing);
485 for (j = 0; j < ncols && ni < n; j++, ni++) {
486 xstart = spacing + j * (wmax + spacing);
487 pix = pixaGetPix(pixat, ni, L_CLONE);
488 wt = pixGetWidth(pix);
489 ht = pixGetHeight(pix);
490 pixRasterop(pixd, xstart, ystart, wt, ht, PIX_SRC, pix, 0, 0);
491 pixDestroy(&pix);
492 }
493 }
494
495 pixaDestroy(&pixat);
496 return pixd;
497 }
498
499
500 /*!
501 * pixaDisplayTiledInRows()
502 *
503 * Input: pixa
504 * outdepth (output depth: 1, 8 or 32 bpp)
505 * maxwidth (of output image)
506 * scalefactor (applied to every pix; use 1.0 for no scaling)
507 * background (0 for white, 1 for black; this is the color
508 * of the spacing between the images)
509 * spacing (between images, and on outside)
510 * border (width of black border added to each image;
511 * use 0 for no border)
512 * Return: pixd (of tiled images), or null on error
513 *
514 * Notes:
515 * (1) This saves a pixa to a single image file of width not to
516 * exceed maxwidth, with background color either white or black,
517 * and with each row tiled such that the top of each pix is
518 * aligned and separated by 'spacing' from the next one.
519 * A black border can be added to each pix.
520 * (2) All pix are converted to outdepth; existing colormaps are removed.
521 * (3) This does a reasonably spacewise-efficient job of laying
522 * out the individual pix images into a tiled composite.
523 */
524 PIX *
pixaDisplayTiledInRows(PIXA * pixa,l_int32 outdepth,l_int32 maxwidth,l_float32 scalefactor,l_int32 background,l_int32 spacing,l_int32 border)525 pixaDisplayTiledInRows(PIXA *pixa,
526 l_int32 outdepth,
527 l_int32 maxwidth,
528 l_float32 scalefactor,
529 l_int32 background,
530 l_int32 spacing,
531 l_int32 border)
532 {
533 l_int32 h; /* cumulative height over all the rows */
534 l_int32 w; /* cumulative height in the current row */
535 l_int32 bordval, wtry, wt, ht;
536 l_int32 irow; /* index of current pix in current row */
537 l_int32 wmaxrow; /* width of the largest row */
538 l_int32 maxh; /* max height in row */
539 l_int32 i, j, index, n, x, y, nrows, ninrow;
540 NUMA *nainrow; /* number of pix in the row */
541 NUMA *namaxh; /* height of max pix in the row */
542 PIX *pix, *pixn, *pixt, *pixd;
543 PIXA *pixan;
544
545 PROCNAME("pixaDisplayTiledInRows");
546
547 if (!pixa)
548 return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
549 if (outdepth != 1 && outdepth != 8 && outdepth != 32)
550 return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
551 if (border < 0)
552 border = 0;
553 if (scalefactor <= 0.0) scalefactor = 1.0;
554
555 if ((n = pixaGetCount(pixa)) == 0)
556 return (PIX *)ERROR_PTR("no components", procName, NULL);
557
558 /* Normalize depths, scale, remove colormaps; optionally add border */
559 pixan = pixaCreate(n);
560 bordval = (outdepth == 1) ? 1 : 0;
561 for (i = 0; i < n; i++) {
562 if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL)
563 continue;
564
565 if (outdepth == 1)
566 pixn = pixConvertTo1(pix, 128);
567 else if (outdepth == 8)
568 pixn = pixConvertTo8(pix, FALSE);
569 else /* outdepth == 32 */
570 pixn = pixConvertTo32(pix);
571 pixDestroy(&pix);
572
573 if (scalefactor != 1.0)
574 pixt = pixScale(pixn, scalefactor, scalefactor);
575 else
576 pixt = pixClone(pixn);
577 if (border)
578 pixd = pixAddBorder(pixt, border, bordval);
579 else
580 pixd = pixClone(pixt);
581 pixDestroy(&pixn);
582 pixDestroy(&pixt);
583
584 pixaAddPix(pixan, pixd, L_INSERT);
585 }
586 if (pixaGetCount(pixan) != n) {
587 n = pixaGetCount(pixan);
588 L_WARNING_INT("only got %d components", procName, n);
589 if (n == 0) {
590 pixaDestroy(&pixan);
591 return (PIX *)ERROR_PTR("no components", procName, NULL);
592 }
593 }
594
595 /* Compute parameters for layout */
596 nainrow = numaCreate(0);
597 namaxh = numaCreate(0);
598 wmaxrow = 0;
599 w = h = spacing;
600 maxh = 0; /* max height in row */
601 for (i = 0, irow = 0; i < n; i++, irow++) {
602 pixaGetPixDimensions(pixan, i, &wt, &ht, NULL);
603 wtry = w + wt + spacing;
604 if (wtry > maxwidth) { /* end the current row and start next one */
605 numaAddNumber(nainrow, irow);
606 numaAddNumber(namaxh, maxh);
607 wmaxrow = L_MAX(wmaxrow, w);
608 h += maxh + spacing;
609 irow = 0;
610 w = wt + 2 * spacing;
611 maxh = ht;
612 } else {
613 w = wtry;
614 maxh = L_MAX(maxh, ht);
615 }
616 }
617
618 /* Enter the parameters for the last row */
619 numaAddNumber(nainrow, irow);
620 numaAddNumber(namaxh, maxh);
621 wmaxrow = L_MAX(wmaxrow, w);
622 h += maxh + spacing;
623
624 if ((pixd = pixCreate(wmaxrow, h, outdepth)) == NULL) {
625 numaDestroy(&nainrow);
626 numaDestroy(&namaxh);
627 pixaDestroy(&pixan);
628 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
629 }
630
631 /* Reset the background color if necessary */
632 if ((background == 1 && outdepth == 1) ||
633 (background == 0 && outdepth != 1))
634 pixSetAll(pixd);
635
636 /* Blit the images to the dest */
637 nrows = numaGetCount(nainrow);
638 y = spacing;
639 for (i = 0, index = 0; i < nrows; i++) { /* over rows */
640 numaGetIValue(nainrow, i, &ninrow);
641 numaGetIValue(namaxh, i, &maxh);
642 x = spacing;
643 for (j = 0; j < ninrow; j++, index++) { /* over pix in row */
644 pix = pixaGetPix(pixan, index, L_CLONE);
645 pixGetDimensions(pix, &wt, &ht, NULL);
646 pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0);
647 pixDestroy(&pix);
648 x += wt + spacing;
649 }
650 y += maxh + spacing;
651 }
652
653 numaDestroy(&nainrow);
654 numaDestroy(&namaxh);
655 pixaDestroy(&pixan);
656 return pixd;
657 }
658
659
660 /*!
661 * pixaDisplayTiledAndScaled()
662 *
663 * Input: pixa
664 * outdepth (output depth: 1, 8 or 32 bpp)
665 * tilewidth (each pix is scaled to this width)
666 * ncols (number of tiles in each row)
667 * background (0 for white, 1 for black; this is the color
668 * of the spacing between the images)
669 * spacing (between images, and on outside)
670 * border (width of additional black border on each image;
671 * use 0 for no border)
672 * Return: pix of tiled images, or null on error
673 *
674 * Notes:
675 * (1) This can be used to tile a number of renderings of
676 * an image that are at different scales and depths.
677 * (2) Each image, after scaling and optionally adding the
678 * black border, has width 'tilewidth'. Thus, the border does
679 * not affect the spacing between the image tiles. The
680 * maximum allowed border width is tilewidth / 5.
681 */
682 PIX *
pixaDisplayTiledAndScaled(PIXA * pixa,l_int32 outdepth,l_int32 tilewidth,l_int32 ncols,l_int32 background,l_int32 spacing,l_int32 border)683 pixaDisplayTiledAndScaled(PIXA *pixa,
684 l_int32 outdepth,
685 l_int32 tilewidth,
686 l_int32 ncols,
687 l_int32 background,
688 l_int32 spacing,
689 l_int32 border)
690 {
691 l_int32 x, y, w, h, wd, hd, d;
692 l_int32 i, n, nrows, maxht, ninrow, irow, bordval;
693 l_int32 *rowht;
694 l_float32 scalefact;
695 PIX *pix, *pixn, *pixt, *pixb, *pixd;
696 PIXA *pixan;
697
698 PROCNAME("pixaDisplayTiledAndScaled");
699
700 if (!pixa)
701 return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
702 if (outdepth != 1 && outdepth != 8 && outdepth != 32)
703 return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
704 if (border < 0 || border > tilewidth / 5)
705 border = 0;
706
707 if ((n = pixaGetCount(pixa)) == 0)
708 return (PIX *)ERROR_PTR("no components", procName, NULL);
709
710 /* Normalize scale and depth for each pix; optionally add border */
711 pixan = pixaCreate(n);
712 bordval = (outdepth == 1) ? 1 : 0;
713 for (i = 0; i < n; i++) {
714 if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL)
715 continue;
716
717 pixGetDimensions(pix, &w, &h, &d);
718 scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w;
719 if (d == 1 && outdepth > 1 && scalefact < 1.0)
720 pixt = pixScaleToGray(pix, scalefact);
721 else
722 pixt = pixScale(pix, scalefact, scalefact);
723
724 if (outdepth == 1)
725 pixn = pixConvertTo1(pixt, 128);
726 else if (outdepth == 8)
727 pixn = pixConvertTo8(pixt, FALSE);
728 else /* outdepth == 32 */
729 pixn = pixConvertTo32(pixt);
730 pixDestroy(&pixt);
731
732 if (border)
733 pixb = pixAddBorder(pixn, border, bordval);
734 else
735 pixb = pixClone(pixn);
736
737 pixaAddPix(pixan, pixb, L_INSERT);
738 pixDestroy(&pix);
739 pixDestroy(&pixn);
740 }
741 if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */
742 pixaDestroy(&pixan);
743 return (PIX *)ERROR_PTR("no components", procName, NULL);
744 }
745
746 /* Determine the size of each row and of pixd */
747 wd = tilewidth * ncols + spacing * (ncols + 1);
748 nrows = (n + ncols - 1) / ncols;
749 if ((rowht = (l_int32 *)CALLOC(nrows, sizeof(l_int32))) == NULL)
750 return (PIX *)ERROR_PTR("rowht array not made", procName, NULL);
751 maxht = 0;
752 ninrow = 0;
753 irow = 0;
754 for (i = 0; i < n; i++) {
755 pix = pixaGetPix(pixan, i, L_CLONE);
756 ninrow++;
757 pixGetDimensions(pix, &w, &h, NULL);
758 maxht = L_MAX(h, maxht);
759 if (ninrow == ncols) {
760 rowht[irow] = maxht;
761 maxht = ninrow = 0; /* reset */
762 irow++;
763 }
764 pixDestroy(&pix);
765 }
766 if (ninrow > 0) { /* last fencepost */
767 rowht[irow] = maxht;
768 irow++; /* total number of rows */
769 }
770 nrows = irow;
771 hd = spacing * (nrows + 1);
772 for (i = 0; i < nrows; i++)
773 hd += rowht[i];
774
775 pixd = pixCreate(wd, hd, outdepth);
776 if ((background == 1 && outdepth == 1) ||
777 (background == 0 && outdepth != 1))
778 pixSetAll(pixd);
779
780 /* Now blit images to pixd */
781 x = y = spacing;
782 irow = 0;
783 for (i = 0; i < n; i++) {
784 pix = pixaGetPix(pixan, i, L_CLONE);
785 pixGetDimensions(pix, &w, &h, NULL);
786 if (i && ((i % ncols) == 0)) { /* start new row */
787 x = spacing;
788 y += spacing + rowht[irow];
789 irow++;
790 }
791 pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0);
792 x += tilewidth + spacing;
793 pixDestroy(&pix);
794 }
795
796 pixaDestroy(&pixan);
797 FREE(rowht);
798 return pixd;
799 }
800
801
802 /*---------------------------------------------------------------------*
803 * Pixaa Display *
804 *---------------------------------------------------------------------*/
805 /*!
806 * pixaaDisplay()
807 *
808 * Input: pixaa
809 * w, h (if set to 0, determines the size from the
810 * b.b. of the components in pixaa)
811 * Return: pix, or null on error
812 *
813 * Notes:
814 * (1) Each pix of the pixaa is displayed at the location given by
815 * its box, translated by the box of the containing pixa
816 * if it exists.
817 */
818 PIX *
pixaaDisplay(PIXAA * pixaa,l_int32 w,l_int32 h)819 pixaaDisplay(PIXAA *pixaa,
820 l_int32 w,
821 l_int32 h)
822 {
823 l_int32 i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb;
824 BOXA *boxa1; /* top-level boxa */
825 BOXA *boxa;
826 PIX *pixt, *pixd;
827 PIXA *pixa;
828
829 PROCNAME("pixaaDisplay");
830
831 if (!pixaa)
832 return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL);
833
834 n = pixaaGetCount(pixaa);
835 if (n == 0)
836 return (PIX *)ERROR_PTR("no components", procName, NULL);
837
838 /* If w and h not input, determine the minimum size required
839 * to contain the origin and all c.c. */
840 boxa1 = pixaaGetBoxa(pixaa, L_CLONE);
841 nbox = boxaGetCount(boxa1);
842 if (w == 0 || h == 0) {
843 if (nbox == n)
844 boxaGetExtent(boxa1, &w, &h, NULL);
845 else { /* have to use the lower-level boxa for each pixa */
846 wmax = hmax = 0;
847 for (i = 0; i < n; i++) {
848 pixa = pixaaGetPixa(pixaa, i, L_CLONE);
849 boxa = pixaGetBoxa(pixa, L_CLONE);
850 boxaGetExtent(boxa, &w, &h, NULL);
851 wmax = L_MAX(wmax, w);
852 hmax = L_MAX(hmax, h);
853 pixaDestroy(&pixa);
854 boxaDestroy(&boxa);
855 }
856 w = wmax;
857 h = hmax;
858 }
859 }
860
861 /* Get depth from first pix */
862 pixa = pixaaGetPixa(pixaa, 0, L_CLONE);
863 pixt = pixaGetPix(pixa, 0, L_CLONE);
864 d = pixGetDepth(pixt);
865 pixaDestroy(&pixa);
866 pixDestroy(&pixt);
867
868 if ((pixd = pixCreate(w, h, d)) == NULL)
869 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
870
871 x = y = 0;
872 for (i = 0; i < n; i++) {
873 pixa = pixaaGetPixa(pixaa, i, L_CLONE);
874 if (nbox == n)
875 boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL);
876 na = pixaGetCount(pixa);
877 for (j = 0; j < na; j++) {
878 pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb);
879 pixt = pixaGetPix(pixa, j, L_CLONE);
880 pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pixt, 0, 0);
881 pixDestroy(&pixt);
882 }
883 pixaDestroy(&pixa);
884 }
885 boxaDestroy(&boxa1);
886
887 return pixd;
888 }
889
890
891 /*!
892 * pixaaDisplayByPixa()
893 *
894 * Input: pixaa
895 * xspace between pix in pixa
896 * yspace between pixa
897 * max width of output pix
898 * Return: pix, or null on error
899 *
900 * Notes:
901 * (1) Displays each pixa on a line (or set of lines),
902 * in order from top to bottom. Within each pixa,
903 * the pix are displayed in order from left to right.
904 * (2) The size of each pix in each pixa is assumed to be
905 * approximately equal to the size of the first pix in
906 * the pixa. If this assumption is not correct, this
907 * function will not work properly.
908 * (3) This ignores the boxa of the pixaa.
909 */
910 PIX *
pixaaDisplayByPixa(PIXAA * pixaa,l_int32 xspace,l_int32 yspace,l_int32 maxw)911 pixaaDisplayByPixa(PIXAA *pixaa,
912 l_int32 xspace,
913 l_int32 yspace,
914 l_int32 maxw)
915 {
916 l_int32 i, j, npixa, npix;
917 l_int32 width, height, depth, nlines, lwidth;
918 l_int32 x, y, w, h, w0, h0;
919 PIX *pixt, *pixd;
920 PIXA *pixa;
921
922 PROCNAME("pixaaDisplayByPixa");
923
924 if (!pixaa)
925 return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL);
926
927 if ((npixa = pixaaGetCount(pixaa)) == 0)
928 return (PIX *)ERROR_PTR("no components", procName, NULL);
929
930 /* Get size of output pix. The width is the minimum of the
931 * maxw and the largest pixa line width. The height is whatever
932 * it needs to be to accommodate all pixa. */
933 height = 2 * yspace;
934 width = 0;
935 for (i = 0; i < npixa; i++) {
936 pixa = pixaaGetPixa(pixaa, i, L_CLONE);
937 npix = pixaGetCount(pixa);
938 pixt = pixaGetPix(pixa, 0, L_CLONE);
939 if (i == 0)
940 depth = pixGetDepth(pixt);
941 w = pixGetWidth(pixt);
942 lwidth = npix * (w + xspace);
943 nlines = (lwidth + maxw - 1) / maxw;
944 if (nlines > 1)
945 width = maxw;
946 else
947 width = L_MAX(lwidth, width);
948 height += nlines * (pixGetHeight(pixt) + yspace);
949 pixDestroy(&pixt);
950 pixaDestroy(&pixa);
951 }
952
953 if ((pixd = pixCreate(width, height, depth)) == NULL)
954 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
955
956 /* Now layout the pix by pixa */
957 y = yspace;
958 for (i = 0; i < npixa; i++) {
959 x = 0;
960 pixa = pixaaGetPixa(pixaa, i, L_CLONE);
961 npix = pixaGetCount(pixa);
962 for (j = 0; j < npix; j++) {
963 pixt = pixaGetPix(pixa, j, L_CLONE);
964 if (j == 0) {
965 w0 = pixGetWidth(pixt);
966 h0 = pixGetHeight(pixt);
967 }
968 w = pixGetWidth(pixt);
969 if (width == maxw && x + w >= maxw) {
970 x = 0;
971 y += h0 + yspace;
972 }
973 h = pixGetHeight(pixt);
974 pixRasterop(pixd, x, y, w, h, PIX_PAINT, pixt, 0, 0);
975 pixDestroy(&pixt);
976 x += w0 + xspace;
977 }
978 y += h0 + yspace;
979 pixaDestroy(&pixa);
980 }
981
982 return pixd;
983 }
984
985
986 /*!
987 * pixaaDisplayTiledAndScaled()
988 *
989 * Input: pixaa
990 * outdepth (output depth: 1, 8 or 32 bpp)
991 * tilewidth (each pix is scaled to this width)
992 * ncols (number of tiles in each row)
993 * background (0 for white, 1 for black; this is the color
994 * of the spacing between the images)
995 * spacing (between images, and on outside)
996 * border (width of additional black border on each image;
997 * use 0 for no border)
998 * Return: pixa (of tiled images, one image for each pixa in
999 * the pixaa), or null on error
1000 *
1001 * Notes:
1002 * (1) For each pixa, this generates from all the pix a
1003 * tiled/scaled output pix, and puts it in the output pixa.
1004 * (2) See comments in pixaDisplayTiledAndScaled().
1005 */
1006 PIXA *
pixaaDisplayTiledAndScaled(PIXAA * pixaa,l_int32 outdepth,l_int32 tilewidth,l_int32 ncols,l_int32 background,l_int32 spacing,l_int32 border)1007 pixaaDisplayTiledAndScaled(PIXAA *pixaa,
1008 l_int32 outdepth,
1009 l_int32 tilewidth,
1010 l_int32 ncols,
1011 l_int32 background,
1012 l_int32 spacing,
1013 l_int32 border)
1014 {
1015 l_int32 i, n;
1016 PIX *pix;
1017 PIXA *pixa, *pixad;
1018
1019 PROCNAME("pixaaDisplayTiledAndScaled");
1020
1021 if (!pixaa)
1022 return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL);
1023 if (outdepth != 1 && outdepth != 8 && outdepth != 32)
1024 return (PIXA *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
1025 if (border < 0 || border > tilewidth / 5)
1026 border = 0;
1027
1028 if ((n = pixaaGetCount(pixaa)) == 0)
1029 return (PIXA *)ERROR_PTR("no components", procName, NULL);
1030
1031 pixad = pixaCreate(n);
1032 for (i = 0; i < n; i++) {
1033 pixa = pixaaGetPixa(pixaa, i, L_CLONE);
1034 pix = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols,
1035 background, spacing, border);
1036 pixaAddPix(pixad, pix, L_INSERT);
1037 pixaDestroy(&pixa);
1038 }
1039
1040 return pixad;
1041 }
1042