1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -
4  -  Redistribution and use in source and binary forms, with or without
5  -  modification, are permitted provided that the following conditions
6  -  are met:
7  -  1. Redistributions of source code must retain the above copyright
8  -     notice, this list of conditions and the following disclaimer.
9  -  2. Redistributions in binary form must reproduce the above
10  -     copyright notice, this list of conditions and the following
11  -     disclaimer in the documentation and/or other materials
12  -     provided with the distribution.
13  -
14  -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18  -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
27 /*!
28  * \file  pixcomp.c
29  * <pre>
30  *
31  *      Pixcomp creation and destruction
32  *           PIXC     *pixcompCreateFromPix()
33  *           PIXC     *pixcompCreateFromString()
34  *           PIXC     *pixcompCreateFromFile()
35  *           void      pixcompDestroy()
36  *           PIXC     *pixcompCopy()
37 
38  *      Pixcomp accessors
39  *           l_int32   pixcompGetDimensions()
40  *           l_int32   pixcompGetParameters()
41  *
42  *      Pixcomp compression selection
43  *           l_int32   pixcompDetermineFormat()
44  *
45  *      Pixcomp conversion to Pix
46  *           PIX      *pixCreateFromPixcomp()
47  *
48  *      Pixacomp creation and destruction
49  *           PIXAC    *pixacompCreate()
50  *           PIXAC    *pixacompCreateWithInit()
51  *           PIXAC    *pixacompCreateFromPixa()
52  *           PIXAC    *pixacompCreateFromFiles()
53  *           PIXAC    *pixacompCreateFromSA()
54  *           void      pixacompDestroy()
55  *
56  *      Pixacomp addition/replacement
57  *           l_int32   pixacompAddPix()
58  *           l_int32   pixacompAddPixcomp()
59  *           static l_int32  pixacompExtendArray()
60  *           l_int32   pixacompReplacePix()
61  *           l_int32   pixacompReplacePixcomp()
62  *           l_int32   pixacompAddBox()
63  *
64  *      Pixacomp accessors
65  *           l_int32   pixacompGetCount()
66  *           PIXC     *pixacompGetPixcomp()
67  *           PIX      *pixacompGetPix()
68  *           l_int32   pixacompGetPixDimensions()
69  *           BOXA     *pixacompGetBoxa()
70  *           l_int32   pixacompGetBoxaCount()
71  *           BOX      *pixacompGetBox()
72  *           l_int32   pixacompGetBoxGeometry()
73  *           l_int32   pixacompGetOffset()
74  *           l_int32   pixacompSetOffset()
75  *
76  *      Pixacomp conversion to Pixa
77  *           PIXA     *pixaCreateFromPixacomp()
78  *
79  *      Combining pixacomp
80  *           l_int32   pixacompJoin()
81  *           PIXAC    *pixacompInterleave()
82  *
83  *      Pixacomp serialized I/O
84  *           PIXAC    *pixacompRead()
85  *           PIXAC    *pixacompReadStream()
86  *           PIXAC    *pixacompReadMem()
87  *           l_int32   pixacompWrite()
88  *           l_int32   pixacompWriteStream()
89  *           l_int32   pixacompWriteMem()
90  *
91  *      Conversion to pdf
92  *           l_int32   pixacompConvertToPdf()
93  *           l_int32   pixacompConvertToPdfData()
94  *           l_int32   pixacompFastConvertToPdfData()
95  *
96  *      Output for debugging
97  *           l_int32   pixacompWriteStreamInfo()
98  *           l_int32   pixcompWriteStreamInfo()
99  *           PIX      *pixacompDisplayTiledAndScaled()
100  *           l_int32   pixacompWriteFiles()
101  *           l_int32   pixcompWriteFile()
102  *
103  *   The Pixacomp is an array of Pixcomp, where each Pixcomp is a compressed
104  *   string of the image.  We don't use reference counting here.
105  *   The basic application is to allow a large array of highly
106  *   compressible images to reside in memory.  We purposely don't
107  *   reuse the Pixa for this, to avoid confusion and programming errors.
108  *
109  *   Three compression formats are used: g4, png and jpeg.
110  *   The compression type can be either specified or defaulted.
111  *   If specified and it is not possible to compress (for example,
112  *   you specify a jpeg on a 1 bpp image or one with a colormap),
113  *   the compression type defaults to png.  The jpeg compression quality
114  *   can be specified using l_setJpegQuality(); otherwise the default is 75.
115  *
116  *   The serialized version of the Pixacomp is similar to that for
117  *   a Pixa, except that each Pixcomp can be compressed by one of
118  *   tiffg4, png, or jpeg.  Unlike serialization of the Pixa,
119  *   serialization of the Pixacomp does not require any imaging
120  *   libraries because it simply reads and writes the compressed data.
121  *
122  *   There are two modes of use in accumulating images:
123  *     (1) addition to the end of the array
124  *     (2) random insertion (replacement) into the array
125  *
126  *   In use, we assume that the array is fully populated up to the
127  *   index value (n - 1), where n is the value of the pixcomp field n.
128  *   Addition can only be made to the end of the fully populated array,
129  *   at the index value n.  Insertion can be made randomly, but again
130  *   only within the array of pixcomps; i.e., within the set of
131  *   indices {0 .... n-1}.  The functions are pixacompReplacePix()
132  *   and pixacompReplacePixcomp(), and they destroy the existing pixcomp.
133  *
134  *   For addition to the end of the array, initialize the pixacomp with
135  *   pixacompCreate(), which generates an empty array of pixcomps ptrs.
136  *   For random insertion and replacement of pixcomp into a pixacomp,
137  *   initialize a fully populated array using pixacompCreateWithInit().
138  *
139  *   The offset field allows you to use an offset-based index to
140  *   access the 0-based ptr array in the pixacomp.  This would typically
141  *   be used to map the pixacomp array index to a page number, or v.v.
142  *   By default, the offset is 0.  For example, suppose you have 50 images,
143  *   corresponding to page numbers 10 - 59.  Then you could use
144  *      pixac = pixacompCreateWithInit(50, 10, ...);
145  *   This would allocate an array of 50 pixcomps, but if you asked for
146  *   the pix at index 10, using pixacompGetPix(pixac, 10), it would
147  *   apply the offset internally, returning the pix at index 0 in the array.
148  * </pre>
149  */
150 
151 #include <string.h>
152 #include "allheaders.h"
153 
154 static const l_int32  INITIAL_PTR_ARRAYSIZE = 20;   /* n'import quoi */
155 
156     /* These two globals are defined in writefile.c */
157 extern l_int32 NumImageFileFormatExtensions;
158 extern const char *ImageFileFormatExtensions[];
159 
160     /* Static functions */
161 static l_int32 pixacompExtendArray(PIXAC *pixac);
162 static l_int32 pixcompFastConvertToPdfData(PIXC *pixc, const char *title,
163                                            l_uint8 **pdata, size_t *pnbytes);
164 
165 
166 /*---------------------------------------------------------------------*
167  *                  Pixcomp creation and destruction                   *
168  *---------------------------------------------------------------------*/
169 /*!
170  * \brief   pixcompCreateFromPix()
171  *
172  * \param[in]    pix
173  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
174  * \return  pixc, or NULL on error
175  *
176  * <pre>
177  * Notes:
178  *      (1) Use %comptype == IFF_DEFAULT to have the compression
179  *          type automatically determined.
180  *      (2) To compress jpeg with a quality other than the default (75), use
181  *             l_jpegSetQuality()
182  * </pre>
183  */
184 PIXC *
pixcompCreateFromPix(PIX * pix,l_int32 comptype)185 pixcompCreateFromPix(PIX     *pix,
186                      l_int32  comptype)
187 {
188 size_t    size;
189 char     *text;
190 l_int32   ret, format;
191 l_uint8  *data;
192 PIXC     *pixc;
193 
194     PROCNAME("pixcompCreateFromPix");
195 
196     if (!pix)
197         return (PIXC *)ERROR_PTR("pix not defined", procName, NULL);
198     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
199         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
200         return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL);
201 
202     if ((pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC))) == NULL)
203         return (PIXC *)ERROR_PTR("pixc not made", procName, NULL);
204     pixGetDimensions(pix, &pixc->w, &pixc->h, &pixc->d);
205     pixGetResolution(pix, &pixc->xres, &pixc->yres);
206     if (pixGetColormap(pix))
207         pixc->cmapflag = 1;
208     if ((text = pixGetText(pix)) != NULL)
209         pixc->text = stringNew(text);
210 
211     pixcompDetermineFormat(comptype, pixc->d, pixc->cmapflag, &format);
212     pixc->comptype = format;
213     ret = pixWriteMem(&data, &size, pix, format);
214     if (ret) {
215         L_ERROR("write to memory failed\n", procName);
216         pixcompDestroy(&pixc);
217         return NULL;
218     }
219     pixc->data = data;
220     pixc->size = size;
221 
222     return pixc;
223 }
224 
225 
226 /*!
227  * \brief   pixcompCreateFromString()
228  *
229  * \param[in]    data compressed string
230  * \param[in]    size number of bytes
231  * \param[in]    copyflag L_INSERT or L_COPY
232  * \return  pixc, or NULL on error
233  *
234  * <pre>
235  * Notes:
236  *      (1) This works when the compressed string is png, jpeg or tiffg4.
237  *      (2) The copyflag determines if the data in the new Pixcomp is
238  *          a copy of the input data.
239  * </pre>
240  */
241 PIXC *
pixcompCreateFromString(l_uint8 * data,size_t size,l_int32 copyflag)242 pixcompCreateFromString(l_uint8  *data,
243                         size_t    size,
244                         l_int32   copyflag)
245 {
246 l_int32  format, w, h, d, bps, spp, iscmap;
247 PIXC    *pixc;
248 
249     PROCNAME("pixcompCreateFromString");
250 
251     if (!data)
252         return (PIXC *)ERROR_PTR("data not defined", procName, NULL);
253     if (copyflag != L_INSERT && copyflag != L_COPY)
254         return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL);
255 
256     if (pixReadHeaderMem(data, size, &format, &w, &h, &bps, &spp, &iscmap) == 1)
257         return (PIXC *)ERROR_PTR("header data not read", procName, NULL);
258     if ((pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC))) == NULL)
259         return (PIXC *)ERROR_PTR("pixc not made", procName, NULL);
260     d = (spp == 3) ? 32 : bps * spp;
261     pixc->w = w;
262     pixc->h = h;
263     pixc->d = d;
264     pixc->comptype = format;
265     pixc->cmapflag = iscmap;
266     if (copyflag == L_INSERT)
267         pixc->data = data;
268     else
269         pixc->data = l_binaryCopy(data, size);
270     pixc->size = size;
271     return pixc;
272 }
273 
274 
275 /*!
276  * \brief   pixcompCreateFromFile()
277  *
278  * \param[in]    filename
279  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
280  * \return  pixc, or NULL on error
281  *
282  * <pre>
283  * Notes:
284  *      (1) Use %comptype == IFF_DEFAULT to have the compression
285  *          type automatically determined.
286  *      (2) If the comptype is invalid for this file, the default will
287  *          be substituted.
288  * </pre>
289  */
290 PIXC *
pixcompCreateFromFile(const char * filename,l_int32 comptype)291 pixcompCreateFromFile(const char  *filename,
292                       l_int32      comptype)
293 {
294 l_int32   format;
295 size_t    nbytes;
296 l_uint8  *data;
297 PIX      *pix;
298 PIXC     *pixc;
299 
300     PROCNAME("pixcompCreateFromFile");
301 
302     if (!filename)
303         return (PIXC *)ERROR_PTR("filename not defined", procName, NULL);
304     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
305         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
306         return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL);
307 
308     findFileFormat(filename, &format);
309     if (format == IFF_UNKNOWN) {
310         L_ERROR("unreadable file: %s\n", procName, filename);
311         return NULL;
312     }
313 
314         /* Can we accept the encoded file directly?  Remember that
315          * png is the "universal" compression type, so if requested
316          * it takes precedence.  Otherwise, if the file is already
317          * compressed in g4 or jpeg, just accept the string. */
318     if ((format == IFF_TIFF_G4 && comptype != IFF_PNG) ||
319         (format == IFF_JFIF_JPEG && comptype != IFF_PNG))
320         comptype = format;
321     if (comptype != IFF_DEFAULT && comptype == format) {
322         data = l_binaryRead(filename, &nbytes);
323         if ((pixc = pixcompCreateFromString(data, nbytes, L_INSERT)) == NULL) {
324             LEPT_FREE(data);
325             return (PIXC *)ERROR_PTR("pixc not made (string)", procName, NULL);
326         }
327         return pixc;
328     }
329 
330         /* Need to recompress in the default format */
331     if ((pix = pixRead(filename)) == NULL)
332         return (PIXC *)ERROR_PTR("pix not read", procName, NULL);
333     if ((pixc = pixcompCreateFromPix(pix, comptype)) == NULL) {
334         pixDestroy(&pix);
335         return (PIXC *)ERROR_PTR("pixc not made", procName, NULL);
336     }
337     pixDestroy(&pix);
338     return pixc;
339 }
340 
341 
342 /*!
343  * \brief   pixcompDestroy()
344  *
345  * \param[in,out]   ppixc will be nulled
346  * \return  void
347  *
348  * <pre>
349  * Notes:
350  *      (1) Always nulls the input ptr.
351  * </pre>
352  */
353 void
pixcompDestroy(PIXC ** ppixc)354 pixcompDestroy(PIXC  **ppixc)
355 {
356 PIXC  *pixc;
357 
358     PROCNAME("pixcompDestroy");
359 
360     if (!ppixc) {
361         L_WARNING("ptr address is null!\n", procName);
362         return;
363     }
364 
365     if ((pixc = *ppixc) == NULL)
366         return;
367 
368     LEPT_FREE(pixc->data);
369     if (pixc->text)
370         LEPT_FREE(pixc->text);
371     LEPT_FREE(pixc);
372     *ppixc = NULL;
373     return;
374 }
375 
376 
377 /*!
378  * \brief   pixcompCopy()
379  *
380  * \param[in]    pixcs
381  * \return  pixcd, or NULL on error
382  */
383 PIXC *
pixcompCopy(PIXC * pixcs)384 pixcompCopy(PIXC  *pixcs)
385 {
386 size_t    size;
387 l_uint8  *datas, *datad;
388 PIXC     *pixcd;
389 
390     PROCNAME("pixcompCopy");
391 
392     if (!pixcs)
393         return (PIXC *)ERROR_PTR("pixcs not defined", procName, NULL);
394 
395     if ((pixcd = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC))) == NULL)
396         return (PIXC *)ERROR_PTR("pixcd not made", procName, NULL);
397     pixcd->w = pixcs->w;
398     pixcd->h = pixcs->h;
399     pixcd->d = pixcs->d;
400     pixcd->xres = pixcs->xres;
401     pixcd->yres = pixcs->yres;
402     pixcd->comptype = pixcs->comptype;
403     if (pixcs->text != NULL)
404         pixcd->text = stringNew(pixcs->text);
405     pixcd->cmapflag = pixcs->cmapflag;
406 
407         /* Copy image data */
408     size = pixcs->size;
409     datas = pixcs->data;
410     datad = (l_uint8 *)LEPT_CALLOC(size, sizeof(l_int8));
411     memcpy((char*)datad, (char*)datas, size);
412     pixcd->data = datad;
413     pixcd->size = size;
414     return pixcd;
415 }
416 
417 
418 /*---------------------------------------------------------------------*
419  *                           Pixcomp accessors                         *
420  *---------------------------------------------------------------------*/
421 /*!
422  * \brief   pixcompGetDimensions()
423  *
424  * \param[in]    pixc
425  * \param[out]   pw, ph, pd [optional]
426  * \return  0 if OK, 1 on error
427  */
428 l_int32
pixcompGetDimensions(PIXC * pixc,l_int32 * pw,l_int32 * ph,l_int32 * pd)429 pixcompGetDimensions(PIXC     *pixc,
430                      l_int32  *pw,
431                      l_int32  *ph,
432                      l_int32  *pd)
433 {
434     PROCNAME("pixcompGetDimensions");
435 
436     if (!pixc)
437         return ERROR_INT("pixc not defined", procName, 1);
438     if (pw) *pw = pixc->w;
439     if (ph) *ph = pixc->h;
440     if (pd) *pd = pixc->d;
441     return 0;
442 }
443 
444 
445 /*!
446  * \brief   pixcompGetParameters()
447  *
448  * \param[in]    pixc
449  * \param[out]   pxres, pyres, pcomptype, pcmapflag [all optional]
450  * \return  0 if OK, 1 on error
451  */
452 l_int32
pixcompGetParameters(PIXC * pixc,l_int32 * pxres,l_int32 * pyres,l_int32 * pcomptype,l_int32 * pcmapflag)453 pixcompGetParameters(PIXC     *pixc,
454                      l_int32  *pxres,
455                      l_int32  *pyres,
456                      l_int32  *pcomptype,
457                      l_int32  *pcmapflag)
458 {
459     PROCNAME("pixcompGetParameters");
460 
461     if (!pixc)
462         return ERROR_INT("pixc not defined", procName, 1);
463     if (pxres) *pxres = pixc->xres;
464     if (pyres) *pyres = pixc->yres;
465     if (pcomptype) *pcomptype = pixc->comptype;
466     if (pcmapflag) *pcmapflag = pixc->cmapflag;
467     return 0;
468 }
469 
470 
471 /*---------------------------------------------------------------------*
472  *                    Pixcomp compression selection                    *
473  *---------------------------------------------------------------------*/
474 /*!
475  * \brief   pixcompDetermineFormat()
476  *
477  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
478  * \param[in]    d pix depth
479  * \param[in]    cmapflag 1 if pix to be compressed as a colormap; 0 otherwise
480  * \param[out]   pformat return IFF_TIFF, IFF_PNG or IFF_JFIF_JPEG
481  * \return  0 if OK; 1 on error
482  *
483  * <pre>
484  * Notes:
485  *      (1) This determines the best format for a pix, given both
486  *          the request (%comptype) and the image characteristics.
487  *      (2) If %comptype == IFF_DEFAULT, this does not necessarily result
488  *          in png encoding.  Instead, it returns one of the three formats
489  *          that is both valid and most likely to give best compression.
490  *      (3) If the pix cannot be compressed by the input value of
491  *          %comptype, this selects IFF_PNG, which can compress all pix.
492  * </pre>
493  */
494 l_int32
pixcompDetermineFormat(l_int32 comptype,l_int32 d,l_int32 cmapflag,l_int32 * pformat)495 pixcompDetermineFormat(l_int32   comptype,
496                        l_int32   d,
497                        l_int32   cmapflag,
498                        l_int32  *pformat)
499 {
500 
501     PROCNAME("pixcompDetermineFormat");
502 
503     if (!pformat)
504         return ERROR_INT("&format not defined", procName, 1);
505     *pformat = IFF_PNG;  /* init value and default */
506     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
507         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
508         return ERROR_INT("invalid comptype", procName, 1);
509 
510     if (comptype == IFF_DEFAULT) {
511         if (d == 1)
512             *pformat = IFF_TIFF_G4;
513         else if (d == 16)
514             *pformat = IFF_PNG;
515         else if (d >= 8 && !cmapflag)
516             *pformat = IFF_JFIF_JPEG;
517     } else if (comptype == IFF_TIFF_G4 && d == 1) {
518         *pformat = IFF_TIFF_G4;
519     } else if (comptype == IFF_JFIF_JPEG && d >= 8 && !cmapflag) {
520         *pformat = IFF_JFIF_JPEG;
521     }
522 
523     return 0;
524 }
525 
526 
527 /*---------------------------------------------------------------------*
528  *                      Pixcomp conversion to Pix                      *
529  *---------------------------------------------------------------------*/
530 /*!
531  * \brief   pixCreateFromPixcomp()
532  *
533  * \param[in]    pixc
534  * \return  pix, or NULL on error
535  */
536 PIX *
pixCreateFromPixcomp(PIXC * pixc)537 pixCreateFromPixcomp(PIXC  *pixc)
538 {
539 l_int32  w, h, d, cmapinpix, format;
540 PIX     *pix;
541 
542     PROCNAME("pixCreateFromPixcomp");
543 
544     if (!pixc)
545         return (PIX *)ERROR_PTR("pixc not defined", procName, NULL);
546 
547     if ((pix = pixReadMem(pixc->data, pixc->size)) == NULL)
548         return (PIX *)ERROR_PTR("pix not read", procName, NULL);
549     pixSetResolution(pix, pixc->xres, pixc->yres);
550     if (pixc->text)
551         pixSetText(pix, pixc->text);
552 
553         /* Check fields for consistency */
554     pixGetDimensions(pix, &w, &h, &d);
555     if (pixc->w != w) {
556         L_INFO("pix width %d != pixc width %d\n", procName, w, pixc->w);
557         L_ERROR("pix width %d != pixc width\n", procName, w);
558     }
559     if (pixc->h != h)
560         L_ERROR("pix height %d != pixc height\n", procName, h);
561     if (pixc->d != d) {
562         if (pixc->d == 16)  /* we strip 16 --> 8 bpp by default */
563             L_WARNING("pix depth %d != pixc depth 16\n", procName, d);
564         else
565             L_ERROR("pix depth %d != pixc depth\n", procName, d);
566     }
567     cmapinpix = (pixGetColormap(pix) != NULL);
568     if ((cmapinpix && !pixc->cmapflag) || (!cmapinpix && pixc->cmapflag))
569         L_ERROR("pix cmap flag inconsistent\n", procName);
570     format = pixGetInputFormat(pix);
571     if (format != pixc->comptype) {
572         L_ERROR("pix comptype %d not equal to pixc comptype\n",
573                     procName, format);
574     }
575 
576     return pix;
577 }
578 
579 
580 /*---------------------------------------------------------------------*
581  *                Pixacomp creation and destruction                    *
582  *---------------------------------------------------------------------*/
583 /*!
584  * \brief   pixacompCreate()
585  *
586  * \param[in]    n  initial number of ptrs
587  * \return  pixac, or NULL on error
588  */
589 PIXAC *
pixacompCreate(l_int32 n)590 pixacompCreate(l_int32  n)
591 {
592 PIXAC  *pixac;
593 
594     PROCNAME("pixacompCreate");
595 
596     if (n <= 0)
597         n = INITIAL_PTR_ARRAYSIZE;
598 
599     if ((pixac = (PIXAC *)LEPT_CALLOC(1, sizeof(PIXAC))) == NULL)
600         return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
601     pixac->n = 0;
602     pixac->nalloc = n;
603     pixac->offset = 0;
604 
605     if ((pixac->pixc = (PIXC **)LEPT_CALLOC(n, sizeof(PIXC *))) == NULL) {
606         pixacompDestroy(&pixac);
607         return (PIXAC *)ERROR_PTR("pixc ptrs not made", procName, NULL);
608     }
609     if ((pixac->boxa = boxaCreate(n)) == NULL) {
610         pixacompDestroy(&pixac);
611         return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
612     }
613 
614     return pixac;
615 }
616 
617 
618 /*!
619  * \brief   pixacompCreateWithInit()
620  *
621  * \param[in]    n  initial number of ptrs
622  * \param[in]    offset difference: accessor index - pixacomp array index
623  * \param[in]    pix [optional] initialize each ptr in pixacomp to this pix;
624  *                   can be NULL
625  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
626  * \return  pixac, or NULL on error
627  *
628  * <pre>
629  * Notes:
630  *      (1) Initializes a pixacomp to be fully populated with %pix,
631  *          compressed using %comptype.  If %pix == NULL, %comptype
632  *          is ignored.
633  *      (2) Typically, the array is initialized with a tiny pix.
634  *          This is most easily done by setting %pix == NULL, causing
635  *          initialization of each array element with a tiny placeholder
636  *          pix (w = h = d = 1), using comptype = IFF_TIFF_G4 .
637  *      (3) Example usage:
638  *            // Generate pixacomp for pages 30 - 49.  This has an array
639  *            // size of 20 and the page number offset is 30.
640  *            PixaComp *pixac = pixacompCreateWithInit(20, 30, NULL,
641  *                                                     IFF_TIFF_G4);
642  *            // Now insert png-compressed images into the initialized array
643  *            for (pageno = 30; pageno < 50; pageno++) {
644  *                Pix *pixt = ...   // derived from image[pageno]
645  *                if (pixt)
646  *                    pixacompReplacePix(pixac, pageno, pixt, IFF_PNG);
647  *                pixDestroy(&pixt);
648  *            }
649  *          The result is a pixac with 20 compressed strings, and with
650  *          selected pixt replacing the placeholders.
651  *          To extract the image for page 38, which is decompressed
652  *          from element 8 in the array, use:
653  *            pixt = pixacompGetPix(pixac, 38);
654  * </pre>
655  */
656 PIXAC *
pixacompCreateWithInit(l_int32 n,l_int32 offset,PIX * pix,l_int32 comptype)657 pixacompCreateWithInit(l_int32  n,
658                        l_int32  offset,
659                        PIX     *pix,
660                        l_int32  comptype)
661 {
662 l_int32  i;
663 PIX     *pixt;
664 PIXC    *pixc;
665 PIXAC   *pixac;
666 
667     PROCNAME("pixacompCreateWithInit");
668 
669     if (n <= 0)
670         return (PIXAC *)ERROR_PTR("n must be > 0", procName, NULL);
671     if (pix) {
672         if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
673             comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
674             return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
675     } else {
676         comptype = IFF_TIFF_G4;
677     }
678     if (offset < 0) {
679         L_WARNING("offset < 0; setting to 0\n", procName);
680         offset = 0;
681     }
682 
683     if ((pixac = pixacompCreate(n)) == NULL)
684         return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
685     pixacompSetOffset(pixac, offset);
686     if (pix)
687         pixt = pixClone(pix);
688     else
689         pixt = pixCreate(1, 1, 1);
690     for (i = 0; i < n; i++) {
691         pixc = pixcompCreateFromPix(pixt, comptype);
692         pixacompAddPixcomp(pixac, pixc, L_INSERT);
693     }
694     pixDestroy(&pixt);
695 
696     return pixac;
697 }
698 
699 
700 /*!
701  * \brief   pixacompCreateFromPixa()
702  *
703  * \param[in]    pixa
704  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
705  * \param[in]    accesstype L_COPY, L_CLONE, L_COPY_CLONE
706  * \return  0 if OK, 1 on error
707  *
708  * <pre>
709  * Notes:
710  *      (1) If %format == IFF_DEFAULT, the conversion format for each
711  *          image is chosen automatically.  Otherwise, we use the
712  *          specified format unless it can't be done (e.g., jpeg
713  *          for a 1, 2 or 4 bpp pix, or a pix with a colormap),
714  *          in which case we use the default (assumed best) compression.
715  *      (2) %accesstype is used to extract a boxa from %pixa.
716  *      (3) To compress jpeg with a quality other than the default (75), use
717  *             l_jpegSetQuality()
718  * </pre>
719  */
720 PIXAC *
pixacompCreateFromPixa(PIXA * pixa,l_int32 comptype,l_int32 accesstype)721 pixacompCreateFromPixa(PIXA    *pixa,
722                        l_int32  comptype,
723                        l_int32  accesstype)
724 {
725 l_int32  i, n;
726 BOXA    *boxa;
727 PIX     *pix;
728 PIXAC   *pixac;
729 
730     PROCNAME("pixacompCreateFromPixa");
731 
732     if (!pixa)
733         return (PIXAC *)ERROR_PTR("pixa not defined", procName, NULL);
734     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
735         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
736         return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
737     if (accesstype != L_COPY && accesstype != L_CLONE &&
738         accesstype != L_COPY_CLONE)
739         return (PIXAC *)ERROR_PTR("invalid accesstype", procName, NULL);
740 
741     n = pixaGetCount(pixa);
742     if ((pixac = pixacompCreate(n)) == NULL)
743         return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
744     for (i = 0; i < n; i++) {
745         pix = pixaGetPix(pixa, i, L_CLONE);
746         pixacompAddPix(pixac, pix, comptype);
747         pixDestroy(&pix);
748     }
749     if ((boxa = pixaGetBoxa(pixa, accesstype)) != NULL) {
750         boxaDestroy(&pixac->boxa);
751         pixac->boxa = boxa;
752     }
753 
754     return pixac;
755 }
756 
757 
758 /*!
759  * \brief   pixacompCreateFromFiles()
760  *
761  * \param[in]    dirname
762  * \param[in]    substr [optional] substring filter on filenames; can be null
763  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
764  * \return  pixac, or NULL on error
765  *
766  * <pre>
767  * Notes:
768  *      (1) %dirname is the full path for the directory.
769  *      (2) %substr is the part of the file name (excluding
770  *          the directory) that is to be matched.  All matching
771  *          filenames are read into the Pixa.  If substr is NULL,
772  *          all filenames are read into the Pixa.
773  *      (3) Use %comptype == IFF_DEFAULT to have the compression
774  *          type automatically determined for each file.
775  *      (4) If the comptype is invalid for a file, the default will
776  *          be substituted.
777  * </pre>
778  */
779 PIXAC *
pixacompCreateFromFiles(const char * dirname,const char * substr,l_int32 comptype)780 pixacompCreateFromFiles(const char  *dirname,
781                         const char  *substr,
782                         l_int32      comptype)
783 {
784 PIXAC    *pixac;
785 SARRAY   *sa;
786 
787     PROCNAME("pixacompCreateFromFiles");
788 
789     if (!dirname)
790         return (PIXAC *)ERROR_PTR("dirname not defined", procName, NULL);
791     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
792         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
793         return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
794 
795     if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL)
796         return (PIXAC *)ERROR_PTR("sa not made", procName, NULL);
797     pixac = pixacompCreateFromSA(sa, comptype);
798     sarrayDestroy(&sa);
799     return pixac;
800 }
801 
802 
803 /*!
804  * \brief   pixacompCreateFromSA()
805  *
806  * \param[in]    sa full pathnames for all files
807  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
808  * \return  pixac, or NULL on error
809  *
810  * <pre>
811  * Notes:
812  *      (1) Use %comptype == IFF_DEFAULT to have the compression
813  *          type automatically determined for each file.
814  *      (2) If the comptype is invalid for a file, the default will
815  *          be substituted.
816  * </pre>
817  */
818 PIXAC *
pixacompCreateFromSA(SARRAY * sa,l_int32 comptype)819 pixacompCreateFromSA(SARRAY  *sa,
820                      l_int32  comptype)
821 {
822 char    *str;
823 l_int32  i, n;
824 PIXC    *pixc;
825 PIXAC   *pixac;
826 
827     PROCNAME("pixacompCreateFromSA");
828 
829     if (!sa)
830         return (PIXAC *)ERROR_PTR("sarray not defined", procName, NULL);
831     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
832         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
833         return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
834 
835     n = sarrayGetCount(sa);
836     pixac = pixacompCreate(n);
837     for (i = 0; i < n; i++) {
838         str = sarrayGetString(sa, i, L_NOCOPY);
839         if ((pixc = pixcompCreateFromFile(str, comptype)) == NULL) {
840             L_ERROR("pixc not read from file: %s\n", procName, str);
841             continue;
842         }
843         pixacompAddPixcomp(pixac, pixc, L_INSERT);
844     }
845     return pixac;
846 }
847 
848 
849 /*!
850  * \brief   pixacompDestroy()
851  *
852  * \param[in,out]   ppixac to be nulled
853  * \return  void
854  *
855  * <pre>
856  * Notes:
857  *      (1) Always nulls the input ptr.
858  * </pre>
859  */
860 void
pixacompDestroy(PIXAC ** ppixac)861 pixacompDestroy(PIXAC  **ppixac)
862 {
863 l_int32  i;
864 PIXAC   *pixac;
865 
866     PROCNAME("pixacompDestroy");
867 
868     if (ppixac == NULL) {
869         L_WARNING("ptr address is NULL!\n", procName);
870         return;
871     }
872 
873     if ((pixac = *ppixac) == NULL)
874         return;
875 
876     for (i = 0; i < pixac->n; i++)
877         pixcompDestroy(&pixac->pixc[i]);
878     LEPT_FREE(pixac->pixc);
879     boxaDestroy(&pixac->boxa);
880     LEPT_FREE(pixac);
881 
882     *ppixac = NULL;
883     return;
884 }
885 
886 
887 /*---------------------------------------------------------------------*
888  *                          Pixacomp addition                          *
889  *---------------------------------------------------------------------*/
890 /*!
891  * \brief   pixacompAddPix()
892  *
893  * \param[in]    pixac
894  * \param[in]    pix  to be added
895  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
896  * \return  0 if OK; 1 on error
897  *
898  * <pre>
899  * Notes:
900  *      (1) The array is filled up to the (n-1)-th element, and this
901  *          converts the input pix to a pixc and adds it at
902  *          the n-th position.
903  *      (2) The pixc produced from the pix is owned by the pixac.
904  *          The input pix is not affected.
905  * </pre>
906  */
907 l_int32
pixacompAddPix(PIXAC * pixac,PIX * pix,l_int32 comptype)908 pixacompAddPix(PIXAC   *pixac,
909                PIX     *pix,
910                l_int32  comptype)
911 {
912 l_int32  cmapflag, format;
913 PIXC    *pixc;
914 
915     PROCNAME("pixacompAddPix");
916 
917     if (!pixac)
918         return ERROR_INT("pixac not defined", procName, 1);
919     if (!pix)
920         return ERROR_INT("pix not defined", procName, 1);
921     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
922         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
923         return ERROR_INT("invalid format", procName, 1);
924 
925     cmapflag = pixGetColormap(pix) ? 1 : 0;
926     pixcompDetermineFormat(comptype, pixGetDepth(pix), cmapflag, &format);
927     if ((pixc = pixcompCreateFromPix(pix, format)) == NULL)
928         return ERROR_INT("pixc not made", procName, 1);
929     pixacompAddPixcomp(pixac, pixc, L_INSERT);
930     return 0;
931 }
932 
933 
934 /*!
935  * \brief   pixacompAddPixcomp()
936  *
937  * \param[in]    pixac
938  * \param[in]    pixc  to be added by insertion
939  * \param[in]    copyflag L_INSERT, L_COPY
940  * \return  0 if OK; 1 on error
941  *
942  * <pre>
943  * Notes:
944  *      (1) Anything added to a pixac is owned by the pixac.
945  *          So do not L_INSERT a pixc that is owned by another pixac,
946  *          or destroy a pixc that has been L_INSERTed.
947  * </pre>
948  */
949 l_int32
pixacompAddPixcomp(PIXAC * pixac,PIXC * pixc,l_int32 copyflag)950 pixacompAddPixcomp(PIXAC   *pixac,
951                    PIXC    *pixc,
952                    l_int32  copyflag)
953 {
954 l_int32  n;
955 
956     PROCNAME("pixacompAddPixcomp");
957 
958     if (!pixac)
959         return ERROR_INT("pixac not defined", procName, 1);
960     if (!pixc)
961         return ERROR_INT("pixc not defined", procName, 1);
962     if (copyflag != L_INSERT && copyflag != L_COPY)
963         return ERROR_INT("invalid copyflag", procName, 1);
964 
965     n = pixac->n;
966     if (n >= pixac->nalloc)
967         pixacompExtendArray(pixac);
968     if (copyflag == L_INSERT)
969         pixac->pixc[n] = pixc;
970     else  /* L_COPY */
971         pixac->pixc[n] = pixcompCopy(pixc);
972     pixac->n++;
973 
974     return 0;
975 }
976 
977 
978 /*!
979  * \brief   pixacompExtendArray()
980  *
981  * \param[in]    pixac
982  * \return  0 if OK; 1 on error
983  *
984  * <pre>
985  * Notes:
986  *      (1) We extend the boxa array simultaneously.  This is
987  *          necessary in case we are NOT adding boxes simultaneously
988  *          with adding pixc.  We always want the sizes of the
989  *          pixac and boxa ptr arrays to be equal.
990  * </pre>
991  */
992 static l_int32
pixacompExtendArray(PIXAC * pixac)993 pixacompExtendArray(PIXAC  *pixac)
994 {
995     PROCNAME("pixacompExtendArray");
996 
997     if (!pixac)
998         return ERROR_INT("pixac not defined", procName, 1);
999 
1000     if ((pixac->pixc = (PIXC **)reallocNew((void **)&pixac->pixc,
1001                             sizeof(PIXC *) * pixac->nalloc,
1002                             2 * sizeof(PIXC *) * pixac->nalloc)) == NULL)
1003         return ERROR_INT("new ptr array not returned", procName, 1);
1004     pixac->nalloc = 2 * pixac->nalloc;
1005     boxaExtendArray(pixac->boxa);
1006     return 0;
1007 }
1008 
1009 
1010 /*!
1011  * \brief   pixacompReplacePix()
1012  *
1013  * \param[in]    pixac
1014  * \param[in]    index caller's view of index within pixac; includes offset
1015  * \param[in]    pix  owned by the caller
1016  * \param[in]    comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
1017  * \return  0 if OK; 1 on error
1018  *
1019  * <pre>
1020  * Notes:
1021  *      (1) The %index includes the offset, which must be subtracted
1022  *          to get the actual index into the ptr array.
1023  *      (2) The input %pix is converted to a pixc, which is then inserted
1024  *          into the pixac.
1025  * </pre>
1026  */
1027 l_int32
pixacompReplacePix(PIXAC * pixac,l_int32 index,PIX * pix,l_int32 comptype)1028 pixacompReplacePix(PIXAC   *pixac,
1029                    l_int32  index,
1030                    PIX     *pix,
1031                    l_int32  comptype)
1032 {
1033 l_int32  n, aindex;
1034 PIXC    *pixc;
1035 
1036     PROCNAME("pixacompReplacePix");
1037 
1038     if (!pixac)
1039         return ERROR_INT("pixac not defined", procName, 1);
1040     n = pixacompGetCount(pixac);
1041     aindex = index - pixac->offset;
1042     if (aindex < 0 || aindex >= n)
1043         return ERROR_INT("array index out of bounds", procName, 1);
1044     if (!pix)
1045         return ERROR_INT("pix not defined", procName, 1);
1046     if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
1047         comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
1048         return ERROR_INT("invalid format", procName, 1);
1049 
1050     pixc = pixcompCreateFromPix(pix, comptype);
1051     pixacompReplacePixcomp(pixac, index, pixc);
1052     return 0;
1053 }
1054 
1055 
1056 /*!
1057  * \brief   pixacompReplacePixcomp()
1058  *
1059  * \param[in]    pixac
1060  * \param[in]    index caller's view of index within pixac; includes offset
1061  * \param[in]    pixc  to replace existing one, which is destroyed
1062  * \return  0 if OK; 1 on error
1063  *
1064  * <pre>
1065  * Notes:
1066  *      (1) The %index includes the offset, which must be subtracted
1067  *          to get the actual index into the ptr array.
1068  *      (2) The inserted %pixc is now owned by the pixac.  The caller
1069  *          must not destroy it.
1070  * </pre>
1071  */
1072 l_int32
pixacompReplacePixcomp(PIXAC * pixac,l_int32 index,PIXC * pixc)1073 pixacompReplacePixcomp(PIXAC   *pixac,
1074                        l_int32  index,
1075                        PIXC    *pixc)
1076 {
1077 l_int32  n, aindex;
1078 PIXC    *pixct;
1079 
1080     PROCNAME("pixacompReplacePixcomp");
1081 
1082     if (!pixac)
1083         return ERROR_INT("pixac not defined", procName, 1);
1084     n = pixacompGetCount(pixac);
1085     aindex = index - pixac->offset;
1086     if (aindex < 0 || aindex >= n)
1087         return ERROR_INT("array index out of bounds", procName, 1);
1088     if (!pixc)
1089         return ERROR_INT("pixc not defined", procName, 1);
1090 
1091     pixct = pixacompGetPixcomp(pixac, index, L_NOCOPY);  /* use %index */
1092     pixcompDestroy(&pixct);
1093     pixac->pixc[aindex] = pixc;  /* replace; use array index */
1094 
1095     return 0;
1096 }
1097 
1098 
1099 /*!
1100  * \brief   pixacompAddBox()
1101  *
1102  * \param[in]    pixac
1103  * \param[in]    box
1104  * \param[in]    copyflag L_INSERT, L_COPY
1105  * \return  0 if OK, 1 on error
1106  */
1107 l_int32
pixacompAddBox(PIXAC * pixac,BOX * box,l_int32 copyflag)1108 pixacompAddBox(PIXAC   *pixac,
1109                BOX     *box,
1110                l_int32  copyflag)
1111 {
1112     PROCNAME("pixacompAddBox");
1113 
1114     if (!pixac)
1115         return ERROR_INT("pixac not defined", procName, 1);
1116     if (!box)
1117         return ERROR_INT("box not defined", procName, 1);
1118     if (copyflag != L_INSERT && copyflag != L_COPY)
1119         return ERROR_INT("invalid copyflag", procName, 1);
1120 
1121     boxaAddBox(pixac->boxa, box, copyflag);
1122     return 0;
1123 }
1124 
1125 
1126 /*---------------------------------------------------------------------*
1127  *                         Pixacomp accessors                          *
1128  *---------------------------------------------------------------------*/
1129 /*!
1130  * \brief   pixacompGetCount()
1131  *
1132  * \param[in]    pixac
1133  * \return  count, or 0 if no pixa
1134  */
1135 l_int32
pixacompGetCount(PIXAC * pixac)1136 pixacompGetCount(PIXAC  *pixac)
1137 {
1138     PROCNAME("pixacompGetCount");
1139 
1140     if (!pixac)
1141         return ERROR_INT("pixac not defined", procName, 0);
1142 
1143     return pixac->n;
1144 }
1145 
1146 
1147 /*!
1148  * \brief   pixacompGetPixcomp()
1149  *
1150  * \param[in]    pixac
1151  * \param[in]    index caller's view of index within pixac; includes offset
1152  * \param[in]    copyflag L_NOCOPY, L_COPY
1153  * \return  pixc, or NULL on error
1154  *
1155  * <pre>
1156  * Notes:
1157  *      (1) The %index includes the offset, which must be subtracted
1158  *          to get the actual index into the ptr array.
1159  *      (2) If copyflag == L_NOCOPY, the pixc is owned by %pixac; do
1160  *          not destroy.
1161  * </pre>
1162  */
1163 PIXC *
pixacompGetPixcomp(PIXAC * pixac,l_int32 index,l_int32 copyflag)1164 pixacompGetPixcomp(PIXAC   *pixac,
1165                    l_int32  index,
1166                    l_int32  copyflag)
1167 {
1168 l_int32  aindex;
1169 
1170     PROCNAME("pixacompGetPixcomp");
1171 
1172     if (!pixac)
1173         return (PIXC *)ERROR_PTR("pixac not defined", procName, NULL);
1174     if (copyflag != L_NOCOPY && copyflag != L_COPY)
1175         return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL);
1176     aindex = index - pixac->offset;
1177     if (aindex < 0 || aindex >= pixac->n)
1178         return (PIXC *)ERROR_PTR("array index not valid", procName, NULL);
1179 
1180     if (copyflag == L_NOCOPY)
1181         return pixac->pixc[aindex];
1182     else  /* L_COPY */
1183         return pixcompCopy(pixac->pixc[aindex]);
1184 }
1185 
1186 
1187 /*!
1188  * \brief   pixacompGetPix()
1189  *
1190  * \param[in]    pixac
1191  * \param[in]    index caller's view of index within pixac; includes offset
1192  * \return  pix, or NULL on error
1193  *
1194  * <pre>
1195  * Notes:
1196  *      (1) The %index includes the offset, which must be subtracted
1197  *          to get the actual index into the ptr array.
1198  * </pre>
1199  */
1200 PIX *
pixacompGetPix(PIXAC * pixac,l_int32 index)1201 pixacompGetPix(PIXAC   *pixac,
1202                l_int32  index)
1203 {
1204 l_int32  aindex;
1205 PIXC    *pixc;
1206 
1207     PROCNAME("pixacompGetPix");
1208 
1209     if (!pixac)
1210         return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
1211     aindex = index - pixac->offset;
1212     if (aindex < 0 || aindex >= pixac->n)
1213         return (PIX *)ERROR_PTR("array index not valid", procName, NULL);
1214 
1215     pixc = pixacompGetPixcomp(pixac, index, L_NOCOPY);
1216     return pixCreateFromPixcomp(pixc);
1217 }
1218 
1219 
1220 /*!
1221  * \brief   pixacompGetPixDimensions()
1222  *
1223  * \param[in]    pixac
1224  * \param[in]    index caller's view of index within pixac; includes offset
1225  * \param[out]   pw, ph, pd [optional]  each can be null
1226  * \return  0 if OK, 1 on error
1227  *
1228  * <pre>
1229  * Notes:
1230  *      (1) The %index includes the offset, which must be subtracted
1231  *          to get the actual index into the ptr array.
1232  * </pre>
1233  */
1234 l_int32
pixacompGetPixDimensions(PIXAC * pixac,l_int32 index,l_int32 * pw,l_int32 * ph,l_int32 * pd)1235 pixacompGetPixDimensions(PIXAC    *pixac,
1236                          l_int32   index,
1237                          l_int32  *pw,
1238                          l_int32  *ph,
1239                          l_int32  *pd)
1240 {
1241 l_int32  aindex;
1242 PIXC    *pixc;
1243 
1244     PROCNAME("pixacompGetPixDimensions");
1245 
1246     if (!pixac)
1247         return ERROR_INT("pixac not defined", procName, 1);
1248     aindex = index - pixac->offset;
1249     if (aindex < 0 || aindex >= pixac->n)
1250         return ERROR_INT("array index not valid", procName, 1);
1251 
1252     if ((pixc = pixac->pixc[aindex]) == NULL)
1253         return ERROR_INT("pixc not found!", procName, 1);
1254     pixcompGetDimensions(pixc, pw, ph, pd);
1255     return 0;
1256 }
1257 
1258 
1259 /*!
1260  * \brief   pixacompGetBoxa()
1261  *
1262  * \param[in]    pixac
1263  * \param[in]    accesstype  L_COPY, L_CLONE, L_COPY_CLONE
1264  * \return  boxa, or NULL on error
1265  */
1266 BOXA *
pixacompGetBoxa(PIXAC * pixac,l_int32 accesstype)1267 pixacompGetBoxa(PIXAC   *pixac,
1268                 l_int32  accesstype)
1269 {
1270     PROCNAME("pixacompGetBoxa");
1271 
1272     if (!pixac)
1273         return (BOXA *)ERROR_PTR("pixac not defined", procName, NULL);
1274     if (!pixac->boxa)
1275         return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL);
1276     if (accesstype != L_COPY && accesstype != L_CLONE &&
1277         accesstype != L_COPY_CLONE)
1278         return (BOXA *)ERROR_PTR("invalid accesstype", procName, NULL);
1279 
1280     return boxaCopy(pixac->boxa, accesstype);
1281 }
1282 
1283 
1284 /*!
1285  * \brief   pixacompGetBoxaCount()
1286  *
1287  * \param[in]    pixac
1288  * \return  count, or 0 on error
1289  */
1290 l_int32
pixacompGetBoxaCount(PIXAC * pixac)1291 pixacompGetBoxaCount(PIXAC  *pixac)
1292 {
1293     PROCNAME("pixacompGetBoxaCount");
1294 
1295     if (!pixac)
1296         return ERROR_INT("pixac not defined", procName, 0);
1297 
1298     return boxaGetCount(pixac->boxa);
1299 }
1300 
1301 
1302 /*!
1303  * \brief   pixacompGetBox()
1304  *
1305  * \param[in]    pixac
1306  * \param[in]    index caller's view of index within pixac; includes offset
1307  * \param[in]    accesstype  L_COPY or L_CLONE
1308  * \return  box if null, not automatically an error, or NULL on error
1309  *
1310  * <pre>
1311  * Notes:
1312  *      (1) The %index includes the offset, which must be subtracted
1313  *          to get the actual index into the ptr array.
1314  *      (2) There is always a boxa with a pixac, and it is initialized so
1315  *          that each box ptr is NULL.
1316  *      (3) In general, we expect that there is either a box associated
1317  *          with each pixc, or no boxes at all in the boxa.
1318  *      (4) Having no boxes is thus not an automatic error.  Whether it
1319  *          is an actual error is determined by the calling program.
1320  *          If the caller expects to get a box, it is an error; see, e.g.,
1321  *          pixacGetBoxGeometry().
1322  * </pre>
1323  */
1324 BOX *
pixacompGetBox(PIXAC * pixac,l_int32 index,l_int32 accesstype)1325 pixacompGetBox(PIXAC    *pixac,
1326                l_int32   index,
1327                l_int32   accesstype)
1328 {
1329 l_int32  aindex;
1330 BOX     *box;
1331 
1332     PROCNAME("pixacompGetBox");
1333 
1334     if (!pixac)
1335         return (BOX *)ERROR_PTR("pixac not defined", procName, NULL);
1336     if (!pixac->boxa)
1337         return (BOX *)ERROR_PTR("boxa not defined", procName, NULL);
1338     aindex = index - pixac->offset;
1339     if (aindex < 0 || aindex >= pixac->boxa->n)
1340         return (BOX *)ERROR_PTR("array index not valid", procName, NULL);
1341     if (accesstype != L_COPY && accesstype != L_CLONE)
1342         return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL);
1343 
1344     box = pixac->boxa->box[aindex];
1345     if (box) {
1346         if (accesstype == L_COPY)
1347             return boxCopy(box);
1348         else  /* accesstype == L_CLONE */
1349             return boxClone(box);
1350     } else {
1351         return NULL;
1352     }
1353 }
1354 
1355 
1356 /*!
1357  * \brief   pixacompGetBoxGeometry()
1358  *
1359  * \param[in]    pixac
1360  * \param[in]    index caller's view of index within pixac; includes offset
1361  * \param[out]   px, py, pw, ph [optional]  each can be null
1362  * \return  0 if OK, 1 on error
1363  *
1364  * <pre>
1365  * Notes:
1366  *      (1) The %index includes the offset, which must be subtracted
1367  *          to get the actual index into the ptr array.
1368  * </pre>
1369  */
1370 l_int32
pixacompGetBoxGeometry(PIXAC * pixac,l_int32 index,l_int32 * px,l_int32 * py,l_int32 * pw,l_int32 * ph)1371 pixacompGetBoxGeometry(PIXAC    *pixac,
1372                        l_int32   index,
1373                        l_int32  *px,
1374                        l_int32  *py,
1375                        l_int32  *pw,
1376                        l_int32  *ph)
1377 {
1378 l_int32  aindex;
1379 BOX     *box;
1380 
1381     PROCNAME("pixacompGetBoxGeometry");
1382 
1383     if (!pixac)
1384         return ERROR_INT("pixac not defined", procName, 1);
1385     aindex = index - pixac->offset;
1386     if (aindex < 0 || aindex >= pixac->n)
1387         return ERROR_INT("array index not valid", procName, 1);
1388 
1389     if ((box = pixacompGetBox(pixac, aindex, L_CLONE)) == NULL)
1390         return ERROR_INT("box not found!", procName, 1);
1391     boxGetGeometry(box, px, py, pw, ph);
1392     boxDestroy(&box);
1393     return 0;
1394 }
1395 
1396 
1397 /*!
1398  * \brief   pixacompGetOffset()
1399  *
1400  * \param[in]    pixac
1401  * \return  offset, or 0 on error
1402  *
1403  * <pre>
1404  * Notes:
1405  *      (1) The offset is the difference between the caller's view of
1406  *          the index into the array and the actual array index.
1407  *          By default it is 0.
1408  * </pre>
1409  */
1410 l_int32
pixacompGetOffset(PIXAC * pixac)1411 pixacompGetOffset(PIXAC   *pixac)
1412 {
1413     PROCNAME("pixacompGetOffset");
1414 
1415     if (!pixac)
1416         return ERROR_INT("pixac not defined", procName, 0);
1417     return pixac->offset;
1418 }
1419 
1420 
1421 /*!
1422  * \brief   pixacompSetOffset()
1423  *
1424  * \param[in]    pixac
1425  * \param[in]    offset non-negative
1426  * \return  0 if OK, 1 on error
1427  *
1428  * <pre>
1429  * Notes:
1430  *      (1) The offset is the difference between the caller's view of
1431  *          the index into the array and the actual array index.
1432  *          By default it is 0.
1433  * </pre>
1434  */
1435 l_int32
pixacompSetOffset(PIXAC * pixac,l_int32 offset)1436 pixacompSetOffset(PIXAC   *pixac,
1437                   l_int32  offset)
1438 {
1439     PROCNAME("pixacompSetOffset");
1440 
1441     if (!pixac)
1442         return ERROR_INT("pixac not defined", procName, 1);
1443     pixac->offset = L_MAX(0, offset);
1444     return 0;
1445 }
1446 
1447 
1448 /*---------------------------------------------------------------------*
1449  *                      Pixacomp conversion to Pixa                    *
1450  *---------------------------------------------------------------------*/
1451 /*!
1452  * \brief   pixaCreateFromPixacomp()
1453  *
1454  * \param[in]    pixac
1455  * \param[in]    accesstype L_COPY, L_CLONE, L_COPY_CLONE; for boxa
1456  * \return  pixa if OK, or NULL on error
1457  *
1458  * <pre>
1459  * Notes:
1460  *      (1) Because the pixa has no notion of offset, the offset must
1461  *          be set to 0 before the conversion, so that pixacompGetPix()
1462  *          fetches all the pixcomps.  It is reset at the end.
1463  * </pre>
1464  */
1465 PIXA *
pixaCreateFromPixacomp(PIXAC * pixac,l_int32 accesstype)1466 pixaCreateFromPixacomp(PIXAC   *pixac,
1467                        l_int32  accesstype)
1468 {
1469 l_int32  i, n, offset;
1470 PIX     *pix;
1471 PIXA    *pixa;
1472 
1473     PROCNAME("pixaCreateFromPixacomp");
1474 
1475     if (!pixac)
1476         return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL);
1477     if (accesstype != L_COPY && accesstype != L_CLONE &&
1478         accesstype != L_COPY_CLONE)
1479         return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL);
1480 
1481     n = pixacompGetCount(pixac);
1482     offset = pixacompGetOffset(pixac);
1483     pixacompSetOffset(pixac, 0);
1484     if ((pixa = pixaCreate(n)) == NULL)
1485         return (PIXA *)ERROR_PTR("pixa not made", procName, NULL);
1486     for (i = 0; i < n; i++) {
1487         if ((pix = pixacompGetPix(pixac, i)) == NULL) {
1488             L_WARNING("pix %d not made\n", procName, i);
1489             continue;
1490         }
1491         pixaAddPix(pixa, pix, L_INSERT);
1492     }
1493     if (pixa->boxa) {
1494         boxaDestroy(&pixa->boxa);
1495         pixa->boxa = pixacompGetBoxa(pixac, accesstype);
1496     }
1497     pixacompSetOffset(pixac, offset);
1498 
1499     return pixa;
1500 }
1501 
1502 
1503 /*---------------------------------------------------------------------*
1504  *                         Combining pixacomp
1505  *---------------------------------------------------------------------*/
1506 /*!
1507  * \brief   pixacompJoin()
1508  *
1509  * \param[in]    pixacd  dest pixac; add to this one
1510  * \param[in]    pixacs  [optional] source pixac; add from this one
1511  * \param[in]    istart  starting index in pixacs
1512  * \param[in]    iend  ending index in pixacs; use -1 to cat all
1513  * \return  0 if OK, 1 on error
1514  *
1515  * <pre>
1516  * Notes:
1517  *      (1) This appends a clone of each indicated pixc in pixcas to pixcad
1518  *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
1519  *      (3) iend < 0 means 'read to the end'
1520  *      (4) If pixacs is NULL or contains no pixc, this is a no-op.
1521  * </pre>
1522  */
1523 l_int32
pixacompJoin(PIXAC * pixacd,PIXAC * pixacs,l_int32 istart,l_int32 iend)1524 pixacompJoin(PIXAC   *pixacd,
1525              PIXAC   *pixacs,
1526              l_int32  istart,
1527              l_int32  iend)
1528 {
1529 l_int32  i, n, nb;
1530 BOXA    *boxas, *boxad;
1531 PIXC    *pixc;
1532 
1533     PROCNAME("pixacompJoin");
1534 
1535     if (!pixacd)
1536         return ERROR_INT("pixacd not defined", procName, 1);
1537     if (!pixacs || ((n = pixacompGetCount(pixacs)) == 0))
1538         return 0;
1539 
1540     if (istart < 0)
1541         istart = 0;
1542     if (iend < 0 || iend >= n)
1543         iend = n - 1;
1544     if (istart > iend)
1545         return ERROR_INT("istart > iend; nothing to add", procName, 1);
1546 
1547     for (i = istart; i <= iend; i++) {
1548         pixc = pixacompGetPixcomp(pixacs, i, L_NOCOPY);
1549         pixacompAddPixcomp(pixacd, pixc, L_COPY);
1550     }
1551 
1552     boxas = pixacompGetBoxa(pixacs, L_CLONE);
1553     boxad = pixacompGetBoxa(pixacd, L_CLONE);
1554     nb = pixacompGetBoxaCount(pixacs);
1555     iend = L_MIN(iend, nb - 1);
1556     boxaJoin(boxad, boxas, istart, iend);
1557     boxaDestroy(&boxas);  /* just the clones */
1558     boxaDestroy(&boxad);  /* ditto */
1559     return 0;
1560 }
1561 
1562 
1563 /*!
1564  * \brief   pixacompInterleave()
1565  *
1566  * \param[in]    pixac1  first src pixac
1567  * \param[in]    pixac2  second src pixac
1568  * \return  pixacd  interleaved from sources, or NULL on error.
1569  *
1570  * <pre>
1571  * Notes:
1572  *      (1) If the two pixac have different sizes, a warning is issued,
1573  *          and the number of pairs returned is the minimum size.
1574  * </pre>
1575  */
1576 PIXAC *
pixacompInterleave(PIXAC * pixac1,PIXAC * pixac2)1577 pixacompInterleave(PIXAC   *pixac1,
1578                    PIXAC   *pixac2)
1579 {
1580 l_int32  i, n1, n2, n, nb1, nb2;
1581 BOX     *box;
1582 PIXC    *pixc1, *pixc2;
1583 PIXAC   *pixacd;
1584 
1585     PROCNAME("pixacompInterleave");
1586 
1587     if (!pixac1)
1588         return (PIXAC *)ERROR_PTR("pixac1 not defined", procName, NULL);
1589     if (!pixac2)
1590         return (PIXAC *)ERROR_PTR("pixac2 not defined", procName, NULL);
1591     n1 = pixacompGetCount(pixac1);
1592     n2 = pixacompGetCount(pixac2);
1593     n = L_MIN(n1, n2);
1594     if (n == 0)
1595         return (PIXAC *)ERROR_PTR("at least one input pixac is empty",
1596                                    procName, NULL);
1597     if (n1 != n2)
1598         L_WARNING("counts differ: %d != %d\n", procName, n1, n2);
1599 
1600     pixacd = pixacompCreate(2 * n);
1601     nb1 = pixacompGetBoxaCount(pixac1);
1602     nb2 = pixacompGetBoxaCount(pixac2);
1603     for (i = 0; i < n; i++) {
1604         pixc1 = pixacompGetPixcomp(pixac1, i, L_COPY);
1605         pixacompAddPixcomp(pixacd, pixc1, L_INSERT);
1606         if (i < nb1) {
1607             box = pixacompGetBox(pixac1, i, L_COPY);
1608             pixacompAddBox(pixacd, box, L_INSERT);
1609         }
1610         pixc2 = pixacompGetPixcomp(pixac2, i, L_COPY);
1611         pixacompAddPixcomp(pixacd, pixc2, L_INSERT);
1612         if (i < nb2) {
1613             box = pixacompGetBox(pixac2, i, L_COPY);
1614             pixacompAddBox(pixacd, box, L_INSERT);
1615         }
1616     }
1617 
1618     return pixacd;
1619 }
1620 
1621 
1622 /*---------------------------------------------------------------------*
1623  *                       Pixacomp serialized I/O                       *
1624  *---------------------------------------------------------------------*/
1625 /*!
1626  * \brief   pixacompRead()
1627  *
1628  * \param[in]    filename
1629  * \return  pixac, or NULL on error
1630  *
1631  * <pre>
1632  * Notes:
1633  *      (1) Unlike the situation with serialized Pixa, where the image
1634  *          data is stored in png format, the Pixacomp image data
1635  *          can be stored in tiffg4, png and jpg formats.
1636  * </pre>
1637  */
1638 PIXAC *
pixacompRead(const char * filename)1639 pixacompRead(const char  *filename)
1640 {
1641 FILE   *fp;
1642 PIXAC  *pixac;
1643 
1644     PROCNAME("pixacompRead");
1645 
1646     if (!filename)
1647         return (PIXAC *)ERROR_PTR("filename not defined", procName, NULL);
1648 
1649     if ((fp = fopenReadStream(filename)) == NULL)
1650         return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL);
1651     pixac = pixacompReadStream(fp);
1652     fclose(fp);
1653     if (!pixac)
1654         return (PIXAC *)ERROR_PTR("pixac not read", procName, NULL);
1655     return pixac;
1656 }
1657 
1658 
1659 /*!
1660  * \brief   pixacompReadStream()
1661  *
1662  * \param[in]    fp file stream
1663  * \return  pixac, or NULL on error
1664  */
1665 PIXAC *
pixacompReadStream(FILE * fp)1666 pixacompReadStream(FILE  *fp)
1667 {
1668 char      buf[256];
1669 l_uint8  *data;
1670 l_int32   n, offset, i, w, h, d, ignore;
1671 l_int32   comptype, size, cmapflag, version, xres, yres;
1672 BOXA     *boxa;
1673 PIXC     *pixc;
1674 PIXAC    *pixac;
1675 
1676     PROCNAME("pixacompReadStream");
1677 
1678     if (!fp)
1679         return (PIXAC *)ERROR_PTR("stream not defined", procName, NULL);
1680 
1681     if (fscanf(fp, "\nPixacomp Version %d\n", &version) != 1)
1682         return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL);
1683     if (version != PIXACOMP_VERSION_NUMBER)
1684         return (PIXAC *)ERROR_PTR("invalid pixacomp version", procName, NULL);
1685     if (fscanf(fp, "Number of pixcomp = %d\n", &n) != 1)
1686         return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL);
1687     if (fscanf(fp, "Offset of index into array = %d", &offset) != 1)
1688         return (PIXAC *)ERROR_PTR("offset not read", procName, NULL);
1689 
1690     if ((pixac = pixacompCreate(n)) == NULL)
1691         return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
1692     if ((boxa = boxaReadStream(fp)) == NULL) {
1693         pixacompDestroy(&pixac);
1694         return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
1695     }
1696     boxaDestroy(&pixac->boxa);  /* empty */
1697     pixac->boxa = boxa;
1698     pixacompSetOffset(pixac, offset);
1699 
1700     for (i = 0; i < n; i++) {
1701         if (fscanf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n",
1702                    &ignore, &w, &h, &d) != 4) {
1703             pixacompDestroy(&pixac);
1704             return (PIXAC *)ERROR_PTR("size reading", procName, NULL);
1705         }
1706         if (fscanf(fp, "  comptype = %d, size = %d, cmapflag = %d\n",
1707                    &comptype, &size, &cmapflag) != 3) {
1708             pixacompDestroy(&pixac);
1709             return (PIXAC *)ERROR_PTR("comptype/size reading", procName, NULL);
1710         }
1711 
1712            /* Use fgets() and sscanf(); not fscanf(), for the last
1713              * bit of header data before the binary data.  The reason is
1714              * that fscanf throws away white space, and if the binary data
1715              * happens to begin with ascii character(s) that are white
1716              * space, it will swallow them and all will be lost!  */
1717         if (fgets(buf, sizeof(buf), fp) == NULL) {
1718             pixacompDestroy(&pixac);
1719             return (PIXAC *)ERROR_PTR("fgets read fail", procName, NULL);
1720         }
1721         if (sscanf(buf, "  xres = %d, yres = %d\n", &xres, &yres) != 2) {
1722             pixacompDestroy(&pixac);
1723             return (PIXAC *)ERROR_PTR("read fail for res", procName, NULL);
1724         }
1725         if ((data = (l_uint8 *)LEPT_CALLOC(1, size)) == NULL) {
1726             pixacompDestroy(&pixac);
1727             return (PIXAC *)ERROR_PTR("calloc fail for data", procName, NULL);
1728         }
1729         if (fread(data, 1, size, fp) != size) {
1730             pixacompDestroy(&pixac);
1731             LEPT_FREE(data);
1732             return (PIXAC *)ERROR_PTR("error reading data", procName, NULL);
1733         }
1734         fgetc(fp);  /* swallow the ending nl */
1735         pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
1736         pixc->w = w;
1737         pixc->h = h;
1738         pixc->d = d;
1739         pixc->xres = xres;
1740         pixc->yres = yres;
1741         pixc->comptype = comptype;
1742         pixc->cmapflag = cmapflag;
1743         pixc->data = data;
1744         pixc->size = size;
1745         pixacompAddPixcomp(pixac, pixc, L_INSERT);
1746     }
1747     return pixac;
1748 }
1749 
1750 
1751 /*!
1752  * \brief   pixacompReadMem()
1753  *
1754  * \param[in]    data const; pixacomp format
1755  * \param[in]    size of data
1756  * \return  pixac, or NULL on error
1757  *
1758  * <pre>
1759  * Notes:
1760  *      (1) Deseralizes a buffer of pixacomp data into a pixac in memory.
1761  * </pre>
1762  */
1763 PIXAC *
pixacompReadMem(const l_uint8 * data,size_t size)1764 pixacompReadMem(const l_uint8  *data,
1765                 size_t          size)
1766 {
1767 FILE   *fp;
1768 PIXAC  *pixac;
1769 
1770     PROCNAME("pixacompReadMem");
1771 
1772     if (!data)
1773         return (PIXAC *)ERROR_PTR("data not defined", procName, NULL);
1774     if ((fp = fopenReadFromMemory(data, size)) == NULL)
1775         return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL);
1776 
1777     pixac = pixacompReadStream(fp);
1778     fclose(fp);
1779     if (!pixac) L_ERROR("pixac not read\n", procName);
1780     return pixac;
1781 }
1782 
1783 
1784 /*!
1785  * \brief   pixacompWrite()
1786  *
1787  * \param[in]    filename
1788  * \param[in]    pixac
1789  * \return  0 if OK, 1 on error
1790  *
1791  * <pre>
1792  * Notes:
1793  *      (1) Unlike the situation with serialized Pixa, where the image
1794  *          data is stored in png format, the Pixacomp image data
1795  *          can be stored in tiffg4, png and jpg formats.
1796  * </pre>
1797  */
1798 l_int32
pixacompWrite(const char * filename,PIXAC * pixac)1799 pixacompWrite(const char  *filename,
1800               PIXAC       *pixac)
1801 {
1802 l_int32  ret;
1803 FILE    *fp;
1804 
1805     PROCNAME("pixacompWrite");
1806 
1807     if (!filename)
1808         return ERROR_INT("filename not defined", procName, 1);
1809     if (!pixac)
1810         return ERROR_INT("pixacomp not defined", procName, 1);
1811 
1812     if ((fp = fopenWriteStream(filename, "wb")) == NULL)
1813         return ERROR_INT("stream not opened", procName, 1);
1814     ret = pixacompWriteStream(fp, pixac);
1815     fclose(fp);
1816     if (ret)
1817         return ERROR_INT("pixacomp not written to stream", procName, 1);
1818     return 0;
1819 }
1820 
1821 
1822 /*!
1823  * \brief   pixacompWriteStream()
1824  *
1825  * \param[in]    fp file stream
1826  * \param[in]    pixac
1827  * \return  0 if OK, 1 on error
1828  */
1829 l_int32
pixacompWriteStream(FILE * fp,PIXAC * pixac)1830 pixacompWriteStream(FILE   *fp,
1831                     PIXAC  *pixac)
1832 {
1833 l_int32  n, i;
1834 PIXC    *pixc;
1835 
1836     PROCNAME("pixacompWriteStream");
1837 
1838     if (!fp)
1839         return ERROR_INT("stream not defined", procName, 1);
1840     if (!pixac)
1841         return ERROR_INT("pixac not defined", procName, 1);
1842 
1843     n = pixacompGetCount(pixac);
1844     fprintf(fp, "\nPixacomp Version %d\n", PIXACOMP_VERSION_NUMBER);
1845     fprintf(fp, "Number of pixcomp = %d\n", n);
1846     fprintf(fp, "Offset of index into array = %d", pixac->offset);
1847     boxaWriteStream(fp, pixac->boxa);
1848     for (i = 0; i < n; i++) {
1849         if ((pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY))
1850                 == NULL)
1851             return ERROR_INT("pixc not found", procName, 1);
1852         fprintf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n",
1853                 i, pixc->w, pixc->h, pixc->d);
1854         fprintf(fp, "  comptype = %d, size = %lu, cmapflag = %d\n",
1855                 pixc->comptype, (unsigned long)pixc->size, pixc->cmapflag);
1856         fprintf(fp, "  xres = %d, yres = %d\n", pixc->xres, pixc->yres);
1857         fwrite(pixc->data, 1, pixc->size, fp);
1858         fprintf(fp, "\n");
1859     }
1860     return 0;
1861 }
1862 
1863 
1864 /*!
1865  * \brief   pixacompWriteMem()
1866  *
1867  * \param[out]   pdata  serialized data of pixac
1868  * \param[out]   psize  size of serialized data
1869  * \param[in]    pixac
1870  * \return  0 if OK, 1 on error
1871  *
1872  * <pre>
1873  * Notes:
1874  *      (1) Serializes a pixac in memory and puts the result in a buffer.
1875  * </pre>
1876  */
1877 l_int32
pixacompWriteMem(l_uint8 ** pdata,size_t * psize,PIXAC * pixac)1878 pixacompWriteMem(l_uint8  **pdata,
1879                  size_t    *psize,
1880                  PIXAC     *pixac)
1881 {
1882 l_int32  ret;
1883 FILE    *fp;
1884 
1885     PROCNAME("pixacompWriteMem");
1886 
1887     if (pdata) *pdata = NULL;
1888     if (psize) *psize = 0;
1889     if (!pdata)
1890         return ERROR_INT("&data not defined", procName, 1);
1891     if (!psize)
1892         return ERROR_INT("&size not defined", procName, 1);
1893     if (!pixac)
1894         return ERROR_INT("&pixac not defined", procName, 1);
1895 
1896 #if HAVE_FMEMOPEN
1897     if ((fp = open_memstream((char **)pdata, psize)) == NULL)
1898         return ERROR_INT("stream not opened", procName, 1);
1899     ret = pixacompWriteStream(fp, pixac);
1900 #else
1901     L_INFO("work-around: writing to a temp file\n", procName);
1902   #ifdef _WIN32
1903     if ((fp = fopenWriteWinTempfile()) == NULL)
1904         return ERROR_INT("tmpfile stream not opened", procName, 1);
1905   #else
1906     if ((fp = tmpfile()) == NULL)
1907         return ERROR_INT("tmpfile stream not opened", procName, 1);
1908   #endif  /* _WIN32 */
1909     ret = pixacompWriteStream(fp, pixac);
1910     rewind(fp);
1911     *pdata = l_binaryReadStream(fp, psize);
1912 #endif  /* HAVE_FMEMOPEN */
1913     fclose(fp);
1914     return ret;
1915 }
1916 
1917 
1918 /*--------------------------------------------------------------------*
1919  *                         Conversion to pdf                          *
1920  *--------------------------------------------------------------------*/
1921 /*!
1922  * \brief   pixacompConvertToPdf()
1923  *
1924  * \param[in]    pixac containing images all at the same resolution
1925  * \param[in]    res override the resolution of each input image, in ppi;
1926  *                   use 0 to respect the resolution embedded in the input
1927  * \param[in]    scalefactor scaling factor applied to each image; > 0.0
1928  * \param[in]    type encoding type (L_JPEG_ENCODE, L_G4_ENCODE,
1929  *                    L_FLATE_ENCODE, or L_DEFAULT_ENCODE for default
1930  * \param[in]    quality used for JPEG only; 0 for default (75)
1931  * \param[in]    title [optional] pdf title
1932  * \param[in]    fileout pdf file of all images
1933  * \return  0 if OK, 1 on error
1934  *
1935  * <pre>
1936  * Notes:
1937  *      (1) This follows closely the function pixaConvertToPdf() in pdfio.c.
1938  *      (2) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without
1939  *          colormap and many colors, or 32 bpp; FLATE for anything else.
1940  *      (3) The scalefactor must be > 0.0; otherwise it is set to 1.0.
1941  *      (4) Specifying one of the three encoding types for %type forces
1942  *          all images to be compressed with that type.  Use 0 to have
1943  *          the type determined for each image based on depth and whether
1944  *          or not it has a colormap.
1945  *      (5) If all images are jpeg compressed, don't require scaling
1946  *          and have the same resolution, it is much faster to skip
1947  *          transcoding with pixacompFastConvertToPdfData(), and then
1948  *          write the data out to file.
1949  * </pre>
1950  */
1951 l_int32
pixacompConvertToPdf(PIXAC * pixac,l_int32 res,l_float32 scalefactor,l_int32 type,l_int32 quality,const char * title,const char * fileout)1952 pixacompConvertToPdf(PIXAC       *pixac,
1953                      l_int32      res,
1954                      l_float32    scalefactor,
1955                      l_int32      type,
1956                      l_int32      quality,
1957                      const char  *title,
1958                      const char  *fileout)
1959 {
1960 l_uint8  *data;
1961 l_int32   ret;
1962 size_t    nbytes;
1963 
1964     PROCNAME("pixacompConvertToPdf");
1965 
1966     if (!pixac)
1967         return ERROR_INT("pixac not defined", procName, 1);
1968 
1969     ret = pixacompConvertToPdfData(pixac, res, scalefactor, type, quality,
1970                                    title, &data, &nbytes);
1971     if (ret) {
1972         LEPT_FREE(data);
1973         return ERROR_INT("conversion to pdf failed", procName, 1);
1974     }
1975 
1976     ret = l_binaryWrite(fileout, "w", data, nbytes);
1977     LEPT_FREE(data);
1978     if (ret)
1979         L_ERROR("pdf data not written to file\n", procName);
1980     return ret;
1981 }
1982 
1983 
1984 /*!
1985  * \brief   pixacompConvertToPdfData()
1986  *
1987  * \param[in]    pixac containing images all at the same resolution
1988  * \param[in]    res input resolution of all images
1989  * \param[in]    scalefactor scaling factor applied to each image; > 0.0
1990  * \param[in]    type encoding type (L_JPEG_ENCODE, L_G4_ENCODE,
1991  *                    L_FLATE_ENCODE, or L_DEFAULT_ENCODE for default
1992  * \param[in]    quality used for JPEG only; 0 for default (75)
1993  * \param[in]    title [optional] pdf title
1994  * \param[out]   pdata output pdf data (of all images
1995  * \param[out]   pnbytes size of output pdf data
1996  * \return  0 if OK, 1 on error
1997  *
1998  * <pre>
1999  * Notes:
2000  *      (1) See pixacompConvertToPdf().
2001  * </pre>
2002  */
2003 l_int32
pixacompConvertToPdfData(PIXAC * pixac,l_int32 res,l_float32 scalefactor,l_int32 type,l_int32 quality,const char * title,l_uint8 ** pdata,size_t * pnbytes)2004 pixacompConvertToPdfData(PIXAC       *pixac,
2005                          l_int32      res,
2006                          l_float32    scalefactor,
2007                          l_int32      type,
2008                          l_int32      quality,
2009                          const char  *title,
2010                          l_uint8    **pdata,
2011                          size_t      *pnbytes)
2012 {
2013 l_uint8  *imdata;
2014 l_int32   i, n, ret, scaledres, pagetype;
2015 size_t    imbytes;
2016 L_BYTEA  *ba;
2017 PIX      *pixs, *pix;
2018 L_PTRA   *pa_data;
2019 
2020     PROCNAME("pixacompConvertToPdfData");
2021 
2022     if (!pdata)
2023         return ERROR_INT("&data not defined", procName, 1);
2024     *pdata = NULL;
2025     if (!pnbytes)
2026         return ERROR_INT("&nbytes not defined", procName, 1);
2027     *pnbytes = 0;
2028     if (!pixac)
2029         return ERROR_INT("pixac not defined", procName, 1);
2030     if (scalefactor <= 0.0) scalefactor = 1.0;
2031     if (type < L_DEFAULT_ENCODE || type > L_FLATE_ENCODE) {
2032         L_WARNING("invalid compression type; using per-page default\n",
2033                   procName);
2034         type = L_DEFAULT_ENCODE;
2035     }
2036 
2037         /* Generate all the encoded pdf strings */
2038     n = pixacompGetCount(pixac);
2039     pa_data = ptraCreate(n);
2040     for (i = 0; i < n; i++) {
2041         if ((pixs =
2042              pixacompGetPix(pixac, pixacompGetOffset(pixac) + i)) == NULL) {
2043             L_ERROR("pix[%d] not retrieved\n", procName, i);
2044             continue;
2045         }
2046         if (pixGetWidth(pixs) == 1) {  /* used sometimes as placeholders */
2047             L_INFO("placeholder image[%d] has w = 1\n", procName, i);
2048             pixDestroy(&pixs);
2049             continue;
2050         }
2051         if (scalefactor != 1.0)
2052             pix = pixScale(pixs, scalefactor, scalefactor);
2053         else
2054             pix = pixClone(pixs);
2055         pixDestroy(&pixs);
2056         scaledres = (l_int32)(res * scalefactor);
2057         if (type != L_DEFAULT_ENCODE) {
2058             pagetype = type;
2059         } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) {
2060             L_ERROR("encoding type selection failed for pix[%d]\n",
2061                     procName, i);
2062             pixDestroy(&pix);
2063             continue;
2064         }
2065         ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes,
2066                                   0, 0, scaledres, title, NULL, 0);
2067         pixDestroy(&pix);
2068         if (ret) {
2069             L_ERROR("pdf encoding failed for pix[%d]\n", procName, i);
2070             continue;
2071         }
2072         ba = l_byteaInitFromMem(imdata, imbytes);
2073         LEPT_FREE(imdata);
2074         ptraAdd(pa_data, ba);
2075     }
2076     ptraGetActualCount(pa_data, &n);
2077     if (n == 0) {
2078         L_ERROR("no pdf files made\n", procName);
2079         ptraDestroy(&pa_data, FALSE, FALSE);
2080         return 1;
2081     }
2082 
2083         /* Concatenate them */
2084     ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes);
2085 
2086     ptraGetActualCount(pa_data, &n);  /* recalculate in case it changes */
2087     for (i = 0; i < n; i++) {
2088         ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION);
2089         l_byteaDestroy(&ba);
2090     }
2091     ptraDestroy(&pa_data, FALSE, FALSE);
2092     return ret;
2093 }
2094 
2095 
2096 /*!
2097  * \brief   pixacompFastConvertToPdfData()
2098  *
2099  * \param[in]    pixac containing images all at the same resolution
2100  * \param[in]    res input resolution of all images
2101  * \param[in]    title [optional] pdf title
2102  * \param[out]   pdata output pdf data (of all images
2103  * \param[out]   pnbytes size of output pdf data
2104  * \return  0 if OK, 1 on error
2105  *
2106  * <pre>
2107  * Notes:
2108  *      (1) This generates the pdf without transcoding if all the
2109  *          images in %pixac are compressed with jpeg.
2110  *          Images not jpeg compressed are skipped.
2111  *      (2) It assumes all images have the same resolution, and that
2112  *          the resolution embedded in each jpeg file is correct.
2113  * </pre>
2114  */
2115 l_int32
pixacompFastConvertToPdfData(PIXAC * pixac,const char * title,l_uint8 ** pdata,size_t * pnbytes)2116 pixacompFastConvertToPdfData(PIXAC       *pixac,
2117                              const char  *title,
2118                              l_uint8    **pdata,
2119                              size_t      *pnbytes)
2120 {
2121 l_uint8  *imdata;
2122 l_int32   i, n, ret, comptype;
2123 size_t    imbytes;
2124 L_BYTEA  *ba;
2125 PIXC     *pixc;
2126 L_PTRA   *pa_data;
2127 
2128     PROCNAME("pixacompFastConvertToPdfData");
2129 
2130     if (!pdata)
2131         return ERROR_INT("&data not defined", procName, 1);
2132     *pdata = NULL;
2133     if (!pnbytes)
2134         return ERROR_INT("&nbytes not defined", procName, 1);
2135     *pnbytes = 0;
2136     if (!pixac)
2137         return ERROR_INT("pixac not defined", procName, 1);
2138 
2139         /* Generate all the encoded pdf strings */
2140     n = pixacompGetCount(pixac);
2141     pa_data = ptraCreate(n);
2142     for (i = 0; i < n; i++) {
2143         if ((pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY)) == NULL) {
2144             L_ERROR("pixc[%d] not retrieved\n", procName, i);
2145             continue;
2146         }
2147         pixcompGetParameters(pixc, NULL, NULL, &comptype, NULL);
2148         if (comptype != IFF_JFIF_JPEG) {
2149             L_ERROR("pixc[%d] not jpeg compressed\n", procName, i);
2150             continue;
2151         }
2152         ret = pixcompFastConvertToPdfData(pixc, title, &imdata, &imbytes);
2153         if (ret) {
2154             L_ERROR("pdf encoding failed for pixc[%d]\n", procName, i);
2155             continue;
2156         }
2157         ba = l_byteaInitFromMem(imdata, imbytes);
2158         LEPT_FREE(imdata);
2159         ptraAdd(pa_data, ba);
2160     }
2161     ptraGetActualCount(pa_data, &n);
2162     if (n == 0) {
2163         L_ERROR("no pdf files made\n", procName);
2164         ptraDestroy(&pa_data, FALSE, FALSE);
2165         return 1;
2166     }
2167 
2168         /* Concatenate them */
2169     ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes);
2170 
2171         /* Clean up */
2172     ptraGetActualCount(pa_data, &n);  /* recalculate in case it changes */
2173     for (i = 0; i < n; i++) {
2174         ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION);
2175         l_byteaDestroy(&ba);
2176     }
2177     ptraDestroy(&pa_data, FALSE, FALSE);
2178     return ret;
2179 }
2180 
2181 
2182 /*!
2183  * \brief   pixcompFastConvertToPdfData()
2184  *
2185  * \param[in]    pixc   containing images all at the same resolution
2186  * \param[in]    title [optional] pdf title
2187  * \param[out]   pdata output pdf data (of all images
2188  * \param[out]   pnbytes size of output pdf data
2189  * \return  0 if OK, 1 on error
2190  *
2191  * <pre>
2192  * Notes:
2193  *      (1) This generates the pdf without transcoding.
2194  *      (2) It assumes all images are jpeg encoded, have the same
2195  *          resolution, and that the resolution embedded in each
2196  *          jpeg file is correct.  (It is transferred to the pdf
2197  *          via the cid.)
2198  * </pre>
2199  */
2200 static l_int32
pixcompFastConvertToPdfData(PIXC * pixc,const char * title,l_uint8 ** pdata,size_t * pnbytes)2201 pixcompFastConvertToPdfData(PIXC        *pixc,
2202                             const char  *title,
2203                             l_uint8    **pdata,
2204                             size_t      *pnbytes)
2205 {
2206 l_uint8      *data;
2207 L_COMP_DATA  *cid;
2208 
2209     PROCNAME("pixacompFastConvertToPdfData");
2210 
2211     if (!pdata)
2212         return ERROR_INT("&data not defined", procName, 1);
2213     *pdata = NULL;
2214     if (!pnbytes)
2215         return ERROR_INT("&nbytes not defined", procName, 1);
2216     *pnbytes = 0;
2217     if (!pixc)
2218         return ERROR_INT("pixc not defined", procName, 1);
2219 
2220         /* Make a copy of the data */
2221     data = l_binaryCopy(pixc->data, pixc->size);
2222     cid = l_generateJpegDataMem(data, pixc->size, 0);
2223 
2224         /* Note: cid is destroyed, along with data, by this function */
2225     return cidConvertToPdfData(cid, title, pdata, pnbytes);
2226 }
2227 
2228 
2229 /*--------------------------------------------------------------------*
2230  *                        Output for debugging                        *
2231  *--------------------------------------------------------------------*/
2232 /*!
2233  * \brief   pixacompWriteStreamInfo()
2234  *
2235  * \param[in]    fp file stream
2236  * \param[in]    pixac
2237  * \param[in]    text [optional] identifying string; can be null
2238  * \return  0 if OK, 1 on error
2239  */
2240 l_int32
pixacompWriteStreamInfo(FILE * fp,PIXAC * pixac,const char * text)2241 pixacompWriteStreamInfo(FILE        *fp,
2242                         PIXAC       *pixac,
2243                         const char  *text)
2244 {
2245 l_int32  i, n, nboxes;
2246 PIXC    *pixc;
2247 
2248     PROCNAME("pixacompWriteStreamInfo");
2249 
2250     if (!fp)
2251         return ERROR_INT("fp not defined", procName, 1);
2252     if (!pixac)
2253         return ERROR_INT("pixac not defined", procName, 1);
2254 
2255     if (text)
2256         fprintf(fp, "Pixacomp Info for %s:\n", text);
2257     else
2258         fprintf(fp, "Pixacomp Info:\n");
2259     n = pixacompGetCount(pixac);
2260     nboxes = pixacompGetBoxaCount(pixac);
2261     fprintf(fp, "Number of pixcomp: %d\n", n);
2262     fprintf(fp, "Size of pixcomp array alloc: %d\n", pixac->nalloc);
2263     fprintf(fp, "Offset of index into array: %d\n", pixac->offset);
2264     if (nboxes  > 0)
2265         fprintf(fp, "Boxa has %d boxes\n", nboxes);
2266     else
2267         fprintf(fp, "Boxa is empty\n");
2268     for (i = 0; i < n; i++) {
2269         pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY);
2270         pixcompWriteStreamInfo(fp, pixc, NULL);
2271     }
2272     return 0;
2273 }
2274 
2275 
2276 /*!
2277  * \brief   pixcompWriteStreamInfo()
2278  *
2279  * \param[in]    fp file stream
2280  * \param[in]    pixc
2281  * \param[in]    text [optional] identifying string; can be null
2282  * \return  0 if OK, 1 on error
2283  */
2284 l_int32
pixcompWriteStreamInfo(FILE * fp,PIXC * pixc,const char * text)2285 pixcompWriteStreamInfo(FILE        *fp,
2286                        PIXC        *pixc,
2287                        const char  *text)
2288 {
2289     PROCNAME("pixcompWriteStreamInfo");
2290 
2291     if (!fp)
2292         return ERROR_INT("fp not defined", procName, 1);
2293     if (!pixc)
2294         return ERROR_INT("pixc not defined", procName, 1);
2295 
2296     if (text)
2297         fprintf(fp, "  Pixcomp Info for %s:", text);
2298     else
2299         fprintf(fp, "  Pixcomp Info:");
2300     fprintf(fp, " width = %d, height = %d, depth = %d\n",
2301             pixc->w, pixc->h, pixc->d);
2302     fprintf(fp, "    xres = %d, yres = %d, size in bytes = %lu\n",
2303             pixc->xres, pixc->yres, (unsigned long)pixc->size);
2304     if (pixc->cmapflag)
2305         fprintf(fp, "    has colormap\n");
2306     else
2307         fprintf(fp, "    no colormap\n");
2308     if (pixc->comptype < NumImageFileFormatExtensions) {
2309         fprintf(fp, "    comptype = %s (%d)\n",
2310                 ImageFileFormatExtensions[pixc->comptype], pixc->comptype);
2311     } else {
2312         fprintf(fp, "    Error!! Invalid comptype index: %d\n", pixc->comptype);
2313     }
2314     return 0;
2315 }
2316 
2317 
2318 /*!
2319  * \brief   pixacompDisplayTiledAndScaled()
2320  *
2321  * \param[in]    pixac
2322  * \param[in]    outdepth output depth: 1, 8 or 32 bpp
2323  * \param[in]    tilewidth each pix is scaled to this width
2324  * \param[in]    ncols number of tiles in each row
2325  * \param[in]    background 0 for white, 1 for black; this is the color
2326  *                 of the spacing between the images
2327  * \param[in]    spacing  between images, and on outside
2328  * \param[in]    border width of additional black border on each image;
2329  *                      use 0 for no border
2330  * \return  pix of tiled images, or NULL on error
2331  *
2332  * <pre>
2333  * Notes:
2334  *      (1) This is the same function as pixaDisplayTiledAndScaled(),
2335  *          except it works on a Pixacomp instead of a Pix.  It is particularly
2336  *          useful for showing the images in a Pixacomp at reduced resolution.
2337  *      (2) See pixaDisplayTiledAndScaled() for details.
2338  * </pre>
2339  */
2340 PIX *
pixacompDisplayTiledAndScaled(PIXAC * pixac,l_int32 outdepth,l_int32 tilewidth,l_int32 ncols,l_int32 background,l_int32 spacing,l_int32 border)2341 pixacompDisplayTiledAndScaled(PIXAC   *pixac,
2342                               l_int32  outdepth,
2343                               l_int32  tilewidth,
2344                               l_int32  ncols,
2345                               l_int32  background,
2346                               l_int32  spacing,
2347                               l_int32  border)
2348 {
2349 PIX   *pixd;
2350 PIXA  *pixa;
2351 
2352     PROCNAME("pixacompDisplayTiledAndScaled");
2353 
2354     if (!pixac)
2355         return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
2356 
2357     if ((pixa = pixaCreateFromPixacomp(pixac, L_COPY)) == NULL)
2358         return (PIX *)ERROR_PTR("pixa not made", procName, NULL);
2359 
2360     pixd = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols,
2361                                      background, spacing, border);
2362     pixaDestroy(&pixa);
2363     return pixd;
2364 }
2365 
2366 
2367 /*!
2368  * \brief   pixacompWriteFiles()
2369  *
2370  * \param[in]    pixac
2371  * \param[in]    subdir (subdirectory of /tmp)
2372  * \return  0 if OK, 1 on error
2373  */
2374 l_int32
pixacompWriteFiles(PIXAC * pixac,const char * subdir)2375 pixacompWriteFiles(PIXAC       *pixac,
2376                    const char  *subdir)
2377 {
2378 char     buf[128];
2379 l_int32  i, n;
2380 PIXC    *pixc;
2381 
2382     PROCNAME("pixacompWriteFiles");
2383 
2384     if (!pixac)
2385         return ERROR_INT("pixac not defined", procName, 1);
2386 
2387     if (lept_mkdir(subdir) > 0)
2388         return ERROR_INT("invalid subdir", procName, 1);
2389 
2390     n = pixacompGetCount(pixac);
2391     for (i = 0; i < n; i++) {
2392         pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY);
2393         snprintf(buf, sizeof(buf), "/tmp/%s/%03d", subdir, i);
2394         pixcompWriteFile(buf, pixc);
2395     }
2396     return 0;
2397 }
2398 
2399 extern const char *ImageFileFormatExtensions[];
2400 
2401 /*!
2402  * \brief   pixcompWriteFile()
2403  *
2404  * \param[in]    rootname
2405  * \param[in]    pixc
2406  * \return  0 if OK, 1 on error
2407  *
2408  * <pre>
2409  * Notes:
2410  *      (1) The compressed data is written to file, and the filename is
2411  *          generated by appending the format extension to %rootname.
2412  * </pre>
2413  */
2414 l_int32
pixcompWriteFile(const char * rootname,PIXC * pixc)2415 pixcompWriteFile(const char  *rootname,
2416                  PIXC        *pixc)
2417 {
2418 char   buf[128];
2419 
2420     PROCNAME("pixcompWriteFile");
2421 
2422     if (!pixc)
2423         return ERROR_INT("pixc not defined", procName, 1);
2424 
2425     snprintf(buf, sizeof(buf), "%s.%s", rootname,
2426              ImageFileFormatExtensions[pixc->comptype]);
2427     l_binaryWrite(buf, "w", pixc->data, pixc->size);
2428     return 0;
2429 }
2430 
2431 
2432