blob: 953b0603e74488e7ba652a92ef63ac584de69559 [file] [log] [blame] [edit]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* pixafunc2.c
*
* Pixa Display (render into a pix)
* PIX *pixaDisplay()
* PIX *pixaDisplayRandomCmap()
* PIX *pixaDisplayOnLattice()
* PIX *pixaDisplayUnsplit()
* PIX *pixaDisplayTiled()
* PIX *pixaDisplayTiledInRows()
* PIX *pixaDisplayTiledAndScaled()
*
* Pixaa Display (render into a pix)
* PIX *pixaaDisplay()
* PIX *pixaaDisplayByPixa()
* PIXA *pixaaDisplayTiledAndScaled()
*
* We give seven methods for displaying a pixa in a pix.
* Some work for 1 bpp input; others for any input depth.
* Some give an output depth that depends on the input depth;
* others give a different output depth or allow you to choose it.
* Some use a boxes to determine where each pix goes; others tile
* onto a regular lattice; yet others tile onto an irregular lattice.
*
* Here is a brief description of what these functions do.
*
* pixaDisplay()
* This uses the boxes to lay out each pix. It is typically
* used to reconstruct a pix that has been broken into components.
* pixaDisplayRandomCmap()
* This also uses the boxes to lay out each pix. However, it creates
* a colormapped dest, where each 1 bpp pix is given a randomly
* generated color (up to 256 are used).
* pixaDisplayOnLattice()
* This puts each pix, sequentially, onto a regular lattice,
* omitting any pix that are too big for the lattice size.
* This is useful, for example, to store bitmapped fonts,
* where all the characters are stored in a single image.
* pixaDisplayUnsplit()
* This lays out a mosaic of tiles (the pix in the pixa) that
* are all of equal size. (Don't use this for unequal sized pix!)
* For example, it can be used to invert the action of
* pixaSplitPix().
* pixaDisplayTiled()
* Like pixaDisplayOnLattice(), this places each pix on a regular
* lattice, but here the lattice size is determined by the
* largest component, and no components are omitted. This is
* dangerous if there are thousands of small components and
* one or more very large one, because the size of the resulting
* pix can be huge!
* pixaDisplayTiledInRows()
* This puts each pix down in a series of rows, where the upper
* edges of each pix in a row are alined and there is a uniform
* spacing between the pix. The height of each row is determined
* by the tallest pix that was put in the row. This function
* is a reasonably efficient way to pack the subimages.
* pixaDisplayTiledAndScaled()
* This scales each pix to a given width and output depth,
* and then tiles them in rows with a given number placed in
* each row. This is very useful for presenting a sequence
* of images that can be at different resolutions, but which
* are derived from the same initial image.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> /* for sqrt() */
#include "allheaders.h"
/*---------------------------------------------------------------------*
* Pixa Display *
*---------------------------------------------------------------------*/
/*!
* pixaDisplay()
*
* Input: pixa
* w, h (if set to 0, determines the size from the
* b.b. of the components in pixa)
* Return: pix, or null on error
*
* Notes:
* (1) This uses the boxes to place each pix in the rendered composite.
* (2) Set w = h = 0 to use the b.b. of the components to determine
* the size of the returned pix.
* (3) The background is written "white". On 1 bpp, each successive
* pix is "painted" (adding foreground), whereas for grayscale
* or color each successive pix is blitted with just the src.
* (4) If the pixa is empty, returns an empty 1 bpp pix.
*/
PIX *
pixaDisplay(PIXA *pixa,
l_int32 w,
l_int32 h)
{
l_int32 i, n, d, xb, yb, wb, hb;
BOXA *boxa;
PIX *pixt, *pixd;
PROCNAME("pixaDisplay");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
n = pixaGetCount(pixa);
if (n == 0 && w == 0 && h == 0)
return (PIX *)ERROR_PTR("no components; no size", procName, NULL);
if (n == 0) {
L_WARNING("no components; returning empty 1 bpp pix", procName);
return pixCreate(w, h, 1);
}
/* If w and h not input, determine the minimum size required
* to contain the origin and all c.c. */
if (w == 0 || h == 0) {
boxa = pixaGetBoxa(pixa, L_CLONE);
boxaGetExtent(boxa, &w, &h, NULL);
boxaDestroy(&boxa);
}
/* Use the first pix in pixa to determine the depth. */
pixt = pixaGetPix(pixa, 0, L_CLONE);
d = pixGetDepth(pixt);
pixDestroy(&pixt);
if ((pixd = pixCreate(w, h, d)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
if (d > 1)
pixSetAll(pixd);
for (i = 0; i < n; i++) {
if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) {
L_WARNING("no box found!", procName);
continue;
}
pixt = pixaGetPix(pixa, i, L_CLONE);
if (d == 1)
pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
else
pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt, 0, 0);
pixDestroy(&pixt);
}
return pixd;
}
/*!
* pixaDisplayRandomCmap()
*
* Input: pixa (of 1 bpp components, with boxa)
* w, h (if set to 0, determines the size from the
* b.b. of the components in pixa)
* Return: pix (8 bpp, cmapped, with random colors on the components),
* or null on error
*
* Notes:
* (1) This uses the boxes to place each pix in the rendered composite.
* (2) By default, the background color is: black, cmap index 0.
* This can be changed by pixcmapResetColor()
*/
PIX *
pixaDisplayRandomCmap(PIXA *pixa,
l_int32 w,
l_int32 h)
{
l_int32 i, n, d, index, xb, yb, wb, hb;
BOXA *boxa;
PIX *pixs, *pixt, *pixd;
PIXCMAP *cmap;
PROCNAME("pixaDisplayRandomCmap");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
n = pixaGetCount(pixa);
if (n == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
/* Use the first pix in pixa to verify depth is 1 bpp */
pixs = pixaGetPix(pixa, 0, L_CLONE);
d = pixGetDepth(pixs);
pixDestroy(&pixs);
if (d != 1)
return (PIX *)ERROR_PTR("components not 1 bpp", procName, NULL);
/* If w and h not input, determine the minimum size required
* to contain the origin and all c.c. */
if (w == 0 || h == 0) {
boxa = pixaGetBoxa(pixa, L_CLONE);
boxaGetExtent(boxa, &w, &h, NULL);
boxaDestroy(&boxa);
}
/* Set up an 8 bpp dest pix, with a colormap with 254 random colors */
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreateRandom(8, 1, 1);
pixSetColormap(pixd, cmap);
/* Color each component and blit it in */
for (i = 0; i < n; i++) {
index = 1 + (i % 254);
pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb);
pixs = pixaGetPix(pixa, i, L_CLONE);
pixt = pixConvert1To8(NULL, pixs, 0, index);
pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
pixDestroy(&pixs);
pixDestroy(&pixt);
}
return pixd;
}
/*!
* pixaDisplayOnLattice()
*
* Input: pixa
* xspace
* yspace
* Return: pix of composite images, or null on error
*
* Notes:
* (1) This places each pix on sequentially on a regular lattice
* in the rendered composite. If a pix is too large to fit in the
* allocated lattice space, it is not rendered.
* (2) If any pix has a colormap, all pix are rendered in rgb.
* (3) This is useful when putting bitmaps of components,
* such as characters, into a single image.
*/
PIX *
pixaDisplayOnLattice(PIXA *pixa,
l_int32 xspace,
l_int32 yspace)
{
l_int32 n, nw, nh, w, h, d, wt, ht;
l_int32 index, i, j, hascmap;
PIX *pix, *pixt, *pixd;
PIXA *pixat;
PROCNAME("pixaDisplayOnLattice");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
/* If any pix have colormaps, generate rgb */
if ((n = pixaGetCount(pixa)) == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
pixaAnyColormaps(pixa, &hascmap);
if (hascmap) {
pixat = pixaCreate(n);
for (i = 0; i < n; i++) {
pixt = pixaGetPix(pixa, i, L_CLONE);
pix = pixConvertTo32(pixt);
pixaAddPix(pixat, pix, L_INSERT);
pixDestroy(&pixt);
}
}
else
pixat = pixaCopy(pixa, L_CLONE);
nw = (l_int32)sqrt((l_float64)n);
nh = (n + nw - 1) / nw;
w = xspace * nw;
h = yspace * nh;
/* Use the first pix in pixa to determine the depth. */
pixaGetPixDimensions(pixat, 0, NULL, NULL, &d);
if ((pixd = pixCreate(w, h, d)) == NULL) {
pixaDestroy(&pixat);
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
index = 0;
for (i = 0; i < nh; i++) {
for (j = 0; j < nw && index < n; j++, index++) {
pixt = pixaGetPix(pixat, index, L_CLONE);
pixGetDimensions(pixt, &wt, &ht, NULL);
if (wt > xspace || ht > yspace) {
fprintf(stderr, "pix(%d) omitted; size %dx%d\n", index, wt, ht);
pixDestroy(&pixt);
continue;
}
pixRasterop(pixd, j * xspace, i * yspace, wt, ht,
PIX_PAINT, pixt, 0, 0);
pixDestroy(&pixt);
}
}
pixaDestroy(&pixat);
return pixd;
}
/*!
* pixaDisplayUnsplit()
*
* Input: pixa
* nx (number of mosaic cells horizontally)
* ny (number of mosaic cells vertically)
* borderwidth (of added border on all sides)
* bordercolor (in our RGBA format: 0xrrggbbaa)
* Return: pix of tiled images, or null on error
*
* Notes:
* (1) This is a logical inverse of pixaSplitPix(). It
* constructs a pix from a mosaic of tiles, all of equal size.
* (2) For added generality, a border of arbitrary color can
* be added to each of the tiles.
* (3) In use, pixa will typically have either been generated
* from pixaSplitPix() or will derived from a pixa that
* was so generated.
* (4) All pix in the pixa must be of equal depth, and, if
* colormapped, have the same colormap.
*/
PIX *
pixaDisplayUnsplit(PIXA *pixa,
l_int32 nx,
l_int32 ny,
l_int32 borderwidth,
l_uint32 bordercolor)
{
l_int32 w, h, d, wt, ht;
l_int32 i, j, k, x, y, n;
PIX *pixt, *pixd;
PROCNAME("pixaDisplayUnsplit");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
if (nx <= 0 || ny <= 0)
return (PIX *)ERROR_PTR("nx and ny must be > 0", procName, NULL);
if ((n = pixaGetCount(pixa)) == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
if (n != nx * ny)
return (PIX *)ERROR_PTR("n != nx * ny", procName, NULL);
borderwidth = L_MAX(0, borderwidth);
pixaGetPixDimensions(pixa, 0, &wt, &ht, &d);
w = nx * (wt + 2 * borderwidth);
h = ny * (ht + 2 * borderwidth);
if ((pixd = pixCreate(w, h, d)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixt = pixaGetPix(pixa, 0, L_CLONE);
pixCopyColormap(pixd, pixt);
pixDestroy(&pixt);
if (borderwidth > 0)
pixSetAllArbitrary(pixd, bordercolor);
y = borderwidth;
for (i = 0, k = 0; i < ny; i++) {
x = borderwidth;
for (j = 0; j < nx; j++, k++) {
pixt = pixaGetPix(pixa, k, L_CLONE);
pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pixt, 0, 0);
pixDestroy(&pixt);
x += wt + 2 * borderwidth;
}
y += ht + 2 * borderwidth;
}
return pixd;
}
/*!
* pixaDisplayTiled()
*
* Input: pixa
* maxwidth (of output image)
* background (0 for white, 1 for black)
* spacing
* Return: pix of tiled images, or null on error
*
* Notes:
* (1) This saves a pixa to a single image file of width not to
* exceed maxwidth, with background color either white or black,
* and with each subimage spaced on a regular lattice.
* (2) The lattice size is determined from the largest width and height,
* separately, of all pix in the pixa.
* (3) All pix in the pixa must be of equal depth.
* (4) If any pix has a colormap, all pix are rendered in rgb.
* (5) Careful: because no components are omitted, this is
* dangerous if there are thousands of small components and
* one or more very large one, because the size of the
* resulting pix can be huge!
*/
PIX *
pixaDisplayTiled(PIXA *pixa,
l_int32 maxwidth,
l_int32 background,
l_int32 spacing)
{
l_int32 w, h, wmax, hmax, wd, hd, d, hascmap;
l_int32 i, j, n, ni, ncols, nrows;
l_int32 ystart, xstart, wt, ht;
PIX *pix, *pixt, *pixd;
PIXA *pixat;
PROCNAME("pixaDisplayTiled");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
/* If any pix have colormaps, generate rgb */
if ((n = pixaGetCount(pixa)) == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
pixaAnyColormaps(pixa, &hascmap);
if (hascmap) {
pixat = pixaCreate(n);
for (i = 0; i < n; i++) {
pixt = pixaGetPix(pixa, i, L_CLONE);
pix = pixConvertTo32(pixt);
pixaAddPix(pixat, pix, L_INSERT);
pixDestroy(&pixt);
}
}
else
pixat = pixaCopy(pixa, L_CLONE);
/* Find the largest width and height of the subimages */
wmax = hmax = 0;
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixat, i, L_CLONE);
pixGetDimensions(pix, &w, &h, NULL);
if (i == 0)
d = pixGetDepth(pix);
else if (d != pixGetDepth(pix)) {
pixDestroy(&pix);
pixaDestroy(&pixat);
return (PIX *)ERROR_PTR("depths not equal", procName, NULL);
}
if (w > wmax)
wmax = w;
if (h > hmax)
hmax = h;
pixDestroy(&pix);
}
/* Get the number of rows and columns and the output image size */
spacing = L_MAX(spacing, 0);
ncols = (l_int32)((l_float32)(maxwidth - spacing) /
(l_float32)(wmax + spacing));
nrows = (n + ncols - 1) / ncols;
wd = wmax * ncols + spacing * (ncols + 1);
hd = hmax * nrows + spacing * (nrows + 1);
if ((pixd = pixCreate(wd, hd, d)) == NULL) {
pixaDestroy(&pixat);
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
#if 0
fprintf(stderr, " nrows = %d, ncols = %d, wmax = %d, hmax = %d\n",
nrows, ncols, wmax, hmax);
fprintf(stderr, " space = %d, wd = %d, hd = %d, n = %d\n",
space, wd, hd, n);
#endif
/* Reset the background color if necessary */
if ((background == 1 && d == 1) || (background == 0 && d != 1))
pixSetAll(pixd);
/* Blit the images to the dest */
for (i = 0, ni = 0; i < nrows; i++) {
ystart = spacing + i * (hmax + spacing);
for (j = 0; j < ncols && ni < n; j++, ni++) {
xstart = spacing + j * (wmax + spacing);
pix = pixaGetPix(pixat, ni, L_CLONE);
wt = pixGetWidth(pix);
ht = pixGetHeight(pix);
pixRasterop(pixd, xstart, ystart, wt, ht, PIX_SRC, pix, 0, 0);
pixDestroy(&pix);
}
}
pixaDestroy(&pixat);
return pixd;
}
/*!
* pixaDisplayTiledInRows()
*
* Input: pixa
* maxwidth (of output image)
* background (0 for white, 1 for black)
* spacing
* Return: pixd (of tiled images), or null on error
*
* Notes:
* (1) This saves a pixa to a single image file of width not to
* exceed maxwidth, with background color either white or black,
* and with each row tiled such that the top of each pix is
* aligned and separated by 'spacing' from the next one.
* (2) All pix in the pixa must be of equal depth.
* (3) If any pix has a colormap, all pix are rendered in rgb.
* (4) This does a reasonably spacewise-efficient job of laying
* out the individual pix images into a tiled composite.
*/
PIX *
pixaDisplayTiledInRows(PIXA *pixa,
l_int32 maxwidth,
l_int32 background,
l_int32 spacing)
{
l_int32 h; /* cumulative height over all the rows */
l_int32 w; /* cumulative height in the current row */
l_int32 wtry, wt, ht, d, hascmap;
l_int32 irow; /* index of current pix in current row */
l_int32 wmaxrow; /* width of the largest row */
l_int32 maxh; /* max height in row */
l_int32 i, j, index, n, x, y, nrows, ninrow;
NUMA *nainrow; /* number of pix in the row */
NUMA *namaxh; /* height of max pix in the row */
PIX *pix, *pixt, *pixd;
PIXA *pixat;
PROCNAME("pixaDisplayTiledInRows");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
/* If any pix have colormaps, generate rgb */
if ((n = pixaGetCount(pixa)) == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
pixaAnyColormaps(pixa, &hascmap);
if (hascmap) {
pixat = pixaCreate(n);
for (i = 0; i < n; i++) {
pixt = pixaGetPix(pixa, i, L_CLONE);
pix = pixConvertTo32(pixt);
pixaAddPix(pixat, pix, L_INSERT);
pixDestroy(&pixt);
}
}
else
pixat = pixaCopy(pixa, L_CLONE);
nainrow = numaCreate(0);
namaxh = numaCreate(0);
wmaxrow = 0;
/* Compute parameters for layout */
w = h = spacing;
maxh = 0; /* max height in row */
for (i = 0, irow = 0; i < n; i++, irow++) {
pixt = pixaGetPix(pixat, i, L_CLONE);
if (i == 0)
d = pixGetDepth(pixt);
else if (d != pixGetDepth(pixt)) {
pixDestroy(&pixt);
pixaDestroy(&pixat);
return (PIX *)ERROR_PTR("depths not equal", procName, NULL);
}
pixGetDimensions(pixt, &wt, &ht, NULL);
pixDestroy(&pixt);
wtry = w + wt + spacing;
if (wtry > maxwidth) { /* end the current row and start next one */
numaAddNumber(nainrow, irow);
numaAddNumber(namaxh, maxh);
wmaxrow = L_MAX(wmaxrow, w);
h += maxh + spacing;
irow = 0;
w = wt + 2 * spacing;
maxh = ht;
} else {
w = wtry;
maxh = L_MAX(maxh, ht);
}
}
/* Enter the parameters for the last row */
numaAddNumber(nainrow, irow);
numaAddNumber(namaxh, maxh);
wmaxrow = L_MAX(wmaxrow, w);
h += maxh + spacing;
if ((pixd = pixCreate(wmaxrow, h, d)) == NULL) {
pixaDestroy(&pixat);
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
/* Reset the background color if necessary */
if ((background == 1 && d == 1) || (background == 0 && d != 1))
pixSetAll(pixd);
/* Blit the images to the dest */
nrows = numaGetCount(nainrow);
y = spacing;
for (i = 0, index = 0; i < nrows; i++) { /* over rows */
numaGetIValue(nainrow, i, &ninrow);
numaGetIValue(namaxh, i, &maxh);
x = spacing;
for (j = 0; j < ninrow; j++, index++) { /* over pix in row */
pixt = pixaGetPix(pixat, index, L_CLONE);
pixGetDimensions(pixt, &wt, &ht, NULL);
pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pixt, 0, 0);
pixDestroy(&pixt);
x += wt + spacing;
}
y += maxh + spacing;
}
numaDestroy(&nainrow);
numaDestroy(&namaxh);
pixaDestroy(&pixat);
return pixd;
}
/*!
* pixaDisplayTiledAndScaled()
*
* Input: pixa
* outdepth (output depth: 1, 8 or 32 bpp)
* tilewidth (each pix is scaled to this width)
* ncols (number of tiles in each row)
* background (0 for white, 1 for black; this is the color
* of the spacing between the images)
* spacing (between images, and on outside)
* border (width of additional black border on each image;
* use 0 for no border)
* Return: pix of tiled images, or null on error
*
* Notes:
* (1) This can be used to tile a number of renderings of
* an image that are at different scales and depths.
* (2) Each image, after scaling and optionally adding the
* black border, has width 'tilewidth'. Thus, the border does
* not affect the spacing between the image tiles. The
* maximum allowed border width is tilewidth / 5.
*/
PIX *
pixaDisplayTiledAndScaled(PIXA *pixa,
l_int32 outdepth,
l_int32 tilewidth,
l_int32 ncols,
l_int32 background,
l_int32 spacing,
l_int32 border)
{
l_int32 x, y, w, h, wd, hd, d;
l_int32 i, n, nrows, maxht, ninrow, irow, bordval;
l_int32 *rowht;
l_float32 scalefact;
PIX *pix, *pixn, *pixt, *pixb, *pixd;
PIXA *pixan;
PROCNAME("pixaDisplayTiledAndScaled");
if (!pixa)
return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
if (outdepth != 1 && outdepth != 8 && outdepth != 32)
return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
if (border < 0 || border > tilewidth / 5)
border = 0;
if ((n = pixaGetCount(pixa)) == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
/* Normalize scale and depth for each pix; optionally add border */
pixan = pixaCreate(n);
bordval = (outdepth == 1) ? 1 : 0;
for (i = 0; i < n; i++) {
if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL)
continue;
pixGetDimensions(pix, &w, &h, &d);
scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w;
if (d == 1 && outdepth > 1 && scalefact < 1.0)
pixt = pixScaleToGray(pix, scalefact);
else
pixt = pixScale(pix, scalefact, scalefact);
if (outdepth == 1)
pixn = pixConvertTo1(pixt, 128);
else if (outdepth == 8)
pixn = pixConvertTo8(pixt, FALSE);
else /* outdepth == 32 */
pixn = pixConvertTo32(pixt);
pixDestroy(&pixt);
if (border)
pixb = pixAddBorder(pixn, border, bordval);
else
pixb = pixClone(pixn);
pixaAddPix(pixan, pixb, L_INSERT);
pixDestroy(&pix);
pixDestroy(&pixn);
}
if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */
pixaDestroy(&pixan);
return (PIX *)ERROR_PTR("no components", procName, NULL);
}
/* Determine the size of each row and of pixd */
wd = tilewidth * ncols + spacing * (ncols + 1);
nrows = (n + ncols - 1) / ncols;
if ((rowht = (l_int32 *)CALLOC(nrows, sizeof(l_int32))) == NULL)
return (PIX *)ERROR_PTR("rowht array not made", procName, NULL);
maxht = 0;
ninrow = 0;
irow = 0;
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixan, i, L_CLONE);
ninrow++;
pixGetDimensions(pix, &w, &h, NULL);
maxht = L_MAX(h, maxht);
if (ninrow == ncols) {
rowht[irow] = maxht;
maxht = ninrow = 0; /* reset */
irow++;
}
pixDestroy(&pix);
}
if (ninrow > 0) { /* last fencepost */
rowht[irow] = maxht;
irow++; /* total number of rows */
}
nrows = irow;
hd = spacing * (nrows + 1);
for (i = 0; i < nrows; i++)
hd += rowht[i];
pixd = pixCreate(wd, hd, outdepth);
if ((background == 1 && outdepth == 1) ||
(background == 0 && outdepth != 1))
pixSetAll(pixd);
/* Now blit images to pixd */
x = y = spacing;
irow = 0;
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixan, i, L_CLONE);
pixGetDimensions(pix, &w, &h, NULL);
if (i && ((i % ncols) == 0)) { /* start new row */
x = spacing;
y += spacing + rowht[irow];
irow++;
}
pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0);
x += tilewidth + spacing;
pixDestroy(&pix);
}
pixaDestroy(&pixan);
FREE(rowht);
return pixd;
}
/*---------------------------------------------------------------------*
* Pixaa Display *
*---------------------------------------------------------------------*/
/*!
* pixaaDisplay()
*
* Input: pixaa
* w, h (if set to 0, determines the size from the
* b.b. of the components in pixaa)
* Return: pix, or null on error
*
* Notes:
* (1) Each pix of the pixaa is displayed at the location given by
* its box, translated by the box of the containing pixa
* if it exists.
*/
PIX *
pixaaDisplay(PIXAA *pixaa,
l_int32 w,
l_int32 h)
{
l_int32 i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb;
BOXA *boxa1; /* top-level boxa */
BOXA *boxa;
PIX *pixt, *pixd;
PIXA *pixa;
PROCNAME("pixaaDisplay");
if (!pixaa)
return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL);
n = pixaaGetCount(pixaa);
if (n == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
/* If w and h not input, determine the minimum size required
* to contain the origin and all c.c. */
boxa1 = pixaaGetBoxa(pixaa, L_CLONE);
nbox = boxaGetCount(boxa1);
if (w == 0 || h == 0) {
if (nbox == n)
boxaGetExtent(boxa1, &w, &h, NULL);
else { /* have to use the lower-level boxa for each pixa */
wmax = hmax = 0;
for (i = 0; i < n; i++) {
pixa = pixaaGetPixa(pixaa, i, L_CLONE);
boxa = pixaGetBoxa(pixa, L_CLONE);
boxaGetExtent(boxa, &w, &h, NULL);
wmax = L_MAX(wmax, w);
hmax = L_MAX(hmax, h);
pixaDestroy(&pixa);
boxaDestroy(&boxa);
}
w = wmax;
h = hmax;
}
}
/* Get depth from first pix */
pixa = pixaaGetPixa(pixaa, 0, L_CLONE);
pixt = pixaGetPix(pixa, 0, L_CLONE);
d = pixGetDepth(pixt);
pixaDestroy(&pixa);
pixDestroy(&pixt);
if ((pixd = pixCreate(w, h, d)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
x = y = 0;
for (i = 0; i < n; i++) {
pixa = pixaaGetPixa(pixaa, i, L_CLONE);
if (nbox == n)
boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL);
na = pixaGetCount(pixa);
for (j = 0; j < na; j++) {
pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb);
pixt = pixaGetPix(pixa, j, L_CLONE);
pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pixt, 0, 0);
pixDestroy(&pixt);
}
pixaDestroy(&pixa);
}
boxaDestroy(&boxa1);
return pixd;
}
/*!
* pixaaDisplayByPixa()
*
* Input: pixaa
* xspace between pix in pixa
* yspace between pixa
* max width of output pix
* Return: pix, or null on error
*
* Notes:
* (1) Displays each pixa on a line (or set of lines),
* in order from top to bottom. Within each pixa,
* the pix are displayed in order from left to right.
* (2) The size of each pix in each pixa is assumed to be
* approximately equal to the size of the first pix in
* the pixa. If this assumption is not correct, this
* function will not work properly.
* (3) This ignores the boxa of the pixaa.
*/
PIX *
pixaaDisplayByPixa(PIXAA *pixaa,
l_int32 xspace,
l_int32 yspace,
l_int32 maxw)
{
l_int32 i, j, npixa, npix;
l_int32 width, height, depth, nlines, lwidth;
l_int32 x, y, w, h, w0, h0;
PIX *pixt, *pixd;
PIXA *pixa;
PROCNAME("pixaaDisplayByPixa");
if (!pixaa)
return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL);
if ((npixa = pixaaGetCount(pixaa)) == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
/* Get size of output pix. The width is the minimum of the
* maxw and the largest pixa line width. The height is whatever
* it needs to be to accommodate all pixa. */
height = 2 * yspace;
width = 0;
for (i = 0; i < npixa; i++) {
pixa = pixaaGetPixa(pixaa, i, L_CLONE);
npix = pixaGetCount(pixa);
pixt = pixaGetPix(pixa, 0, L_CLONE);
if (i == 0)
depth = pixGetDepth(pixt);
w = pixGetWidth(pixt);
lwidth = npix * (w + xspace);
nlines = (lwidth + maxw - 1) / maxw;
if (nlines > 1)
width = maxw;
else
width = L_MAX(lwidth, width);
height += nlines * (pixGetHeight(pixt) + yspace);
pixDestroy(&pixt);
pixaDestroy(&pixa);
}
if ((pixd = pixCreate(width, height, depth)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
/* Now layout the pix by pixa */
y = yspace;
for (i = 0; i < npixa; i++) {
x = 0;
pixa = pixaaGetPixa(pixaa, i, L_CLONE);
npix = pixaGetCount(pixa);
for (j = 0; j < npix; j++) {
pixt = pixaGetPix(pixa, j, L_CLONE);
if (j == 0) {
w0 = pixGetWidth(pixt);
h0 = pixGetHeight(pixt);
}
w = pixGetWidth(pixt);
if (width == maxw && x + w >= maxw) {
x = 0;
y += h0 + yspace;
}
h = pixGetHeight(pixt);
pixRasterop(pixd, x, y, w, h, PIX_PAINT, pixt, 0, 0);
pixDestroy(&pixt);
x += w0 + xspace;
}
y += h0 + yspace;
pixaDestroy(&pixa);
}
return pixd;
}
/*!
* pixaaDisplayTiledAndScaled()
*
* Input: pixaa
* outdepth (output depth: 1, 8 or 32 bpp)
* tilewidth (each pix is scaled to this width)
* ncols (number of tiles in each row)
* background (0 for white, 1 for black; this is the color
* of the spacing between the images)
* spacing (between images, and on outside)
* border (width of additional black border on each image;
* use 0 for no border)
* Return: pixa (of tiled images, one image for each pixa in
* the pixaa), or null on error
*
* Notes:
* (1) For each pixa, this generates from all the pix a
* tiled/scaled output pix, and puts it in the output pixa.
* (2) See comments in pixaDisplayTiledAndScaled().
*/
PIXA *
pixaaDisplayTiledAndScaled(PIXAA *pixaa,
l_int32 outdepth,
l_int32 tilewidth,
l_int32 ncols,
l_int32 background,
l_int32 spacing,
l_int32 border)
{
l_int32 i, n;
PIX *pix;
PIXA *pixa, *pixad;
PROCNAME("pixaaDisplayTiledAndScaled");
if (!pixaa)
return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL);
if (outdepth != 1 && outdepth != 8 && outdepth != 32)
return (PIXA *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
if (border < 0 || border > tilewidth / 5)
border = 0;
if ((n = pixaaGetCount(pixaa)) == 0)
return (PIXA *)ERROR_PTR("no components", procName, NULL);
pixad = pixaCreate(n);
for (i = 0; i < n; i++) {
pixa = pixaaGetPixa(pixaa, i, L_CLONE);
pix = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols,
background, spacing, border);
pixaAddPix(pixad, pix, L_INSERT);
pixaDestroy(&pixa);
}
return pixad;
}