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