Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/boxbasic.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/boxbasic.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,2288 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + +/*! + * \file boxbasic.c + * <pre> + * + * Basic 'class' functions for box, boxa and boxaa, + * including accessors and serialization. + * + * Box creation, copy, clone, destruction + * BOX *boxCreate() + * BOX *boxCreateValid() + * BOX *boxCopy() + * BOX *boxClone() + * void boxDestroy() + * + * Box accessors + * l_int32 boxGetGeometry() + * l_int32 boxSetGeometry() + * l_int32 boxGetSideLocations() + * l_int32 boxSetSideLocations() + * l_int32 boxIsValid() + * + * Boxa creation, copy, destruction + * BOXA *boxaCreate() + * BOXA *boxaCopy() + * void boxaDestroy() + * + * Boxa array extension + * l_int32 boxaAddBox() + * l_int32 boxaExtendArray() + * l_int32 boxaExtendArrayToSize() + * + * Boxa accessors + * l_int32 boxaGetCount() + * l_int32 boxaGetValidCount() + * BOX *boxaGetBox() + * BOX *boxaGetValidBox() + * NUMA *boxaFindInvalidBoxes() + * l_int32 boxaGetBoxGeometry() + * l_int32 boxaIsFull() + * + * Boxa array modifiers + * l_int32 boxaReplaceBox() + * l_int32 boxaInsertBox() + * l_int32 boxaRemoveBox() + * l_int32 boxaRemoveBoxAndSave() + * BOXA *boxaSaveValid() + * l_int32 boxaInitFull() + * l_int32 boxaClear() + * + * Boxaa creation, copy, destruction + * BOXAA *boxaaCreate() + * BOXAA *boxaaCopy() + * void boxaaDestroy() + * + * Boxaa array extension + * l_int32 boxaaAddBoxa() + * l_int32 boxaaExtendArray() + * l_int32 boxaaExtendArrayToSize() + * + * Boxaa accessors + * l_int32 boxaaGetCount() + * l_int32 boxaaGetBoxCount() + * BOXA *boxaaGetBoxa() + * BOX *boxaaGetBox() + * + * Boxaa array modifiers + * l_int32 boxaaInitFull() + * l_int32 boxaaExtendWithInit() + * l_int32 boxaaReplaceBoxa() + * l_int32 boxaaInsertBoxa() + * l_int32 boxaaRemoveBoxa() + * l_int32 boxaaAddBox() + * + * Boxaa serialized I/O + * BOXAA *boxaaReadFromFiles() + * BOXAA *boxaaRead() + * BOXAA *boxaaReadStream() + * BOXAA *boxaaReadMem() + * l_int32 boxaaWrite() + * l_int32 boxaaWriteStream() + * l_int32 boxaaWriteMem() + * + * Boxa serialized I/O + * BOXA *boxaRead() + * BOXA *boxaReadStream() + * BOXA *boxaReadMem() + * l_int32 boxaWriteDebug() + * l_int32 boxaWrite() + * l_int32 boxaWriteStream() + * l_int32 boxaWriteStderr() + * l_int32 boxaWriteMem() + * + * Box print (for debug) + * l_int32 boxPrintStreamInfo() + * + * Most functions use only valid boxes, which are boxes that have both + * width and height > 0. However, a few functions, such as + * boxaGetMedianVals() do not assume that all boxes are valid. For any + * function that can use a boxa with invalid boxes, it is convenient + * to use these accessors: + * boxaGetValidCount() : count of valid boxes + * boxaGetValidBox() : returns NULL for invalid boxes + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include "allheaders.h" +#include "pix_internal.h" + + /* Bounds on array sizes */ +static const size_t MaxBoxaPtrArraySize = 10000000; +static const size_t MaxBoxaaPtrArraySize = 1000000; +static const size_t InitialPtrArraySize = 20; /*!< n'importe quoi */ + +/*---------------------------------------------------------------------* + * Box creation, destruction and copy * + *---------------------------------------------------------------------*/ +/*! + * \brief boxCreate() + * + * \param[in] x, y, w, h + * \return box, or NULL on error + * + * <pre> + * Notes: + * (1) This clips the box to the +quad. If no part of the + * box is in the +quad, this returns NULL. + * (2) We allow you to make a box with w = 0 and/or h = 0. + * This does not represent a valid region, but it is useful + * as a placeholder in a boxa for which the index of the + * box in the boxa is important. This is an atypical + * situation; usually you want to put only valid boxes with + * nonzero width and height in a boxa. If you have a boxa + * with invalid boxes, the accessor boxaGetValidBox() + * will return NULL on each invalid box. + * (3) If you want to create only valid boxes, use boxCreateValid(), + * which returns NULL if either w or h is 0. + * </pre> + */ +BOX * +boxCreate(l_int32 x, + l_int32 y, + l_int32 w, + l_int32 h) +{ +BOX *box; + + if (w < 0 || h < 0) + return (BOX *)ERROR_PTR("w and h not both >= 0", __func__, NULL); + if (x < 0) { /* take part in +quad */ + w = w + x; + x = 0; + if (w <= 0) + return (BOX *)ERROR_PTR("x < 0 and box off +quad", __func__, NULL); + } + if (y < 0) { /* take part in +quad */ + h = h + y; + y = 0; + if (h <= 0) + return (BOX *)ERROR_PTR("y < 0 and box off +quad", __func__, NULL); + } + + box = (BOX *)LEPT_CALLOC(1, sizeof(BOX)); + boxSetGeometry(box, x, y, w, h); + box->refcount = 1; + return box; +} + + +/*! + * \brief boxCreateValid() + * + * \param[in] x, y, w, h + * \return box, or NULL on error + * + * <pre> + * Notes: + * (1) This returns NULL if either w = 0 or h = 0. + * </pre> + */ +BOX * +boxCreateValid(l_int32 x, + l_int32 y, + l_int32 w, + l_int32 h) +{ + if (w <= 0 || h <= 0) + return (BOX *)ERROR_PTR("w and h not both > 0", __func__, NULL); + return boxCreate(x, y, w, h); +} + + +/*! + * \brief boxCopy() + * + * \param[in] box + * \return copy of box, or NULL on error + */ +BOX * +boxCopy(BOX *box) +{ +BOX *boxc; + + if (!box) + return (BOX *)ERROR_PTR("box not defined", __func__, NULL); + + boxc = boxCreate(box->x, box->y, box->w, box->h); + return boxc; +} + + +/*! + * \brief boxClone() + * + * \param[in] box + * \return ptr to same box, or NULL on error + */ +BOX * +boxClone(BOX *box) +{ + + if (!box) + return (BOX *)ERROR_PTR("box not defined", __func__, NULL); + + ++box->refcount; + return box; +} + + +/*! + * \brief boxDestroy() + * + * \param[in,out] pbox will be set to null before returning + * \return void + * + * <pre> + * Notes: + * (1) Decrements the ref count and, if 0, destroys the box. + * (2) Always nulls the input ptr. + * </pre> + */ +void +boxDestroy(BOX **pbox) +{ +BOX *box; + + if (pbox == NULL) { + L_WARNING("ptr address is null!\n", __func__); + return; + } + if ((box = *pbox) == NULL) + return; + + if (--box->refcount == 0) + LEPT_FREE(box); + *pbox = NULL; +} + + +/*---------------------------------------------------------------------* + * Box accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief boxGetGeometry() + * + * \param[in] box + * \param[out] px, py, pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +boxGetGeometry(const BOX *box, + l_int32 *px, + l_int32 *py, + l_int32 *pw, + l_int32 *ph) +{ + if (px) *px = 0; + if (py) *py = 0; + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!box) + return ERROR_INT("box not defined", __func__, 1); + if (px) *px = box->x; + if (py) *py = box->y; + if (pw) *pw = box->w; + if (ph) *ph = box->h; + return 0; +} + + +/*! + * \brief boxSetGeometry() + * + * \param[in] box + * \param[in] x, y, w, h [optional] use -1 to leave unchanged + * \return 0 if OK, 1 on error + */ +l_ok +boxSetGeometry(BOX *box, + l_int32 x, + l_int32 y, + l_int32 w, + l_int32 h) +{ + if (!box) + return ERROR_INT("box not defined", __func__, 1); + if (x != -1) box->x = x; + if (y != -1) box->y = y; + if (w != -1) box->w = w; + if (h != -1) box->h = h; + return 0; +} + + +/*! + * \brief boxGetSideLocations() + * + * \param[in] box + * \param[out] pl, pr, pt, pb [optional] each can be null + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) All returned values are within the box. + * </pre> + */ +l_ok +boxGetSideLocations(const BOX *box, + l_int32 *pl, + l_int32 *pr, + l_int32 *pt, + l_int32 *pb) +{ +l_int32 x, y, w, h; + + if (pl) *pl = 0; + if (pr) *pr = 0; + if (pt) *pt = 0; + if (pb) *pb = 0; + if (!box) + return ERROR_INT("box not defined", __func__, 1); + + boxGetGeometry(box, &x, &y, &w, &h); + if (pl) *pl = x; + if (pr) *pr = x + w - 1; + if (pt) *pt = y; + if (pb) *pb = y + h - 1; + return 0; +} + + +/*! + * \brief boxSetSideLocations() + * + * \param[in] box + * \param[in] l, r, t, b [optional] use -1 to leave unchanged + * \return 0 if OK, 1 on error + */ +l_ok +boxSetSideLocations(BOX *box, + l_int32 l, + l_int32 r, + l_int32 t, + l_int32 b) +{ +l_int32 x, y, w, h; + + if (!box) + return ERROR_INT("box not defined", __func__, 1); + x = (l != -1) ? l : box->x; + w = (r != -1) ? r - x + 1 : box->x + box->w - x; + y = (t != -1) ? t : box->y; + h = (b != -1) ? b - y + 1 : box->y + box->h - y; + boxSetGeometry(box, x, y, w, h); + return 0; +} + + +/*! + * \brief boxIsValid() + * + * \param[in] box + * \param[out] pvalid 1 if valid; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxIsValid(BOX *box, + l_int32 *pvalid) +{ + if (!pvalid) + return ERROR_INT("&valid not defined", __func__, 1); + *pvalid = 0; + if (!box) + return ERROR_INT("box not defined", __func__, 1); + + if (box->w > 0 && box->h > 0) + *pvalid = 1; + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxa creation, destruction, copy, extension * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaCreate() + * + * \param[in] n initial number of ptrs; 0 for default + * \return boxa, or NULL on error + */ +BOXA * +boxaCreate(l_int32 n) +{ +BOXA *boxa; + + if (n <= 0 || n > MaxBoxaPtrArraySize) + n = InitialPtrArraySize; + + boxa = (BOXA *)LEPT_CALLOC(1, sizeof(BOXA)); + boxa->n = 0; + boxa->nalloc = n; + boxa->refcount = 1; + if ((boxa->box = (BOX **)LEPT_CALLOC(n, sizeof(BOX *))) == NULL) { + boxaDestroy(&boxa); + return (BOXA *)ERROR_PTR("boxa ptrs not made", __func__, NULL); + } + return boxa; +} + + +/*! + * \brief boxaCopy() + * + * \param[in] boxa + * \param[in] copyflag L_COPY, L_CLONE, L_COPY_CLONE + * \return new boxa, or NULL on error + * + * <pre> + * Notes: + * (1) See pix.h for description of the copyflag. + * (2) The copy-clone makes a new boxa that holds clones of each box. + * </pre> + */ +BOXA * +boxaCopy(BOXA *boxa, + l_int32 copyflag) +{ +l_int32 i; +BOX *boxc; +BOXA *boxac; + + if (!boxa) + return (BOXA *)ERROR_PTR("boxa not defined", __func__, NULL); + + if (copyflag == L_CLONE) { + boxa->refcount++; + return boxa; + } + + if (copyflag != L_COPY && copyflag != L_COPY_CLONE) + return (BOXA *)ERROR_PTR("invalid copyflag", __func__, NULL); + + if ((boxac = boxaCreate(boxa->nalloc)) == NULL) + return (BOXA *)ERROR_PTR("boxac not made", __func__, NULL); + for (i = 0; i < boxa->n; i++) { + if (copyflag == L_COPY) + boxc = boxaGetBox(boxa, i, L_COPY); + else /* copy-clone */ + boxc = boxaGetBox(boxa, i, L_CLONE); + boxaAddBox(boxac, boxc, L_INSERT); + } + return boxac; +} + + +/*! + * \brief boxaDestroy() + * + * \param[in,out] pboxa will be set to null before returning + * \return void + * + * <pre> + * Notes: + * (1) Decrements the ref count and, if 0, destroys the boxa. + * (2) Always nulls the input ptr. + * </pre> + */ +void +boxaDestroy(BOXA **pboxa) +{ +l_int32 i; +BOXA *boxa; + + if (pboxa == NULL) { + L_WARNING("ptr address is null!\n", __func__); + return; + } + + if ((boxa = *pboxa) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the boxa. */ + if (--boxa->refcount == 0) { + for (i = 0; i < boxa->n; i++) + boxDestroy(&boxa->box[i]); + LEPT_FREE(boxa->box); + LEPT_FREE(boxa); + } + + *pboxa = NULL; +} + + +/*! + * \brief boxaAddBox() + * + * \param[in] boxa + * \param[in] box to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +boxaAddBox(BOXA *boxa, + BOX *box, + l_int32 copyflag) +{ +l_int32 n; +BOX *boxc; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + if (!box) + return ERROR_INT("box not defined", __func__, 1); + + if (copyflag == L_INSERT) + boxc = box; + else if (copyflag == L_COPY) + boxc = boxCopy(box); + else if (copyflag == L_CLONE) + boxc = boxClone(box); + else + return ERROR_INT("invalid copyflag", __func__, 1); + if (!boxc) + return ERROR_INT("boxc not made", __func__, 1); + + n = boxaGetCount(boxa); + if (n >= boxa->nalloc) { + if (boxaExtendArray(boxa)) { + if (copyflag != L_INSERT) + boxDestroy(&boxc); + return ERROR_INT("extension failed", __func__, 1); + } + } + boxa->box[n] = boxc; + boxa->n++; + return 0; +} + + +/*! + * \brief boxaExtendArray() + * + * \param[in] boxa + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) Reallocs with doubled size of ptr array. + * </pre> + */ +l_ok +boxaExtendArray(BOXA *boxa) +{ + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + return boxaExtendArrayToSize(boxa, 2 * boxa->nalloc); +} + + +/*! + * \brief boxaExtendArrayToSize() + * + * \param[in] boxa + * \param[in] size new size of boxa ptr array + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) If necessary, reallocs new boxa ptr array to %size. + * (2) The max number of box ptrs is 10M. + * </pre> + */ +l_ok +boxaExtendArrayToSize(BOXA *boxa, + size_t size) +{ +size_t oldsize, newsize; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + if (boxa->nalloc > MaxBoxaPtrArraySize) /* belt & suspenders */ + return ERROR_INT("boxa has too many ptrs", __func__, 1); + if (size > MaxBoxaPtrArraySize) + return ERROR_INT("size > 10M box ptrs; too large", __func__, 1); + if (size <= boxa->nalloc) { + L_INFO("size too small; no extension\n", __func__); + return 0; + } + + oldsize = boxa->nalloc * sizeof(BOX *); + newsize = size * sizeof(BOX *); + if ((boxa->box = (BOX **)reallocNew((void **)&boxa->box, + oldsize, newsize)) == NULL) + return ERROR_INT("new ptr array not returned", __func__, 1); + boxa->nalloc = size; + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxa accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaGetCount() + * + * \param[in] boxa + * \return count of all boxes; 0 if no boxes or on error + */ +l_int32 +boxaGetCount(const BOXA *boxa) +{ + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 0); + return boxa->n; +} + + +/*! + * \brief boxaGetValidCount() + * + * \param[in] boxa + * \return count of valid boxes; 0 if no valid boxes or on error + */ +l_int32 +boxaGetValidCount(BOXA *boxa) +{ +l_int32 n, i, w, h, count; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 0); + + n = boxaGetCount(boxa); + for (i = 0, count = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + if (w > 0 && h > 0) + count++; + } + return count; +} + + +/*! + * \brief boxaGetBox() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[in] accessflag L_COPY or L_CLONE + * \return box, or NULL on error + */ +BOX * +boxaGetBox(BOXA *boxa, + l_int32 index, + l_int32 accessflag) +{ + if (!boxa) + return (BOX *)ERROR_PTR("boxa not defined", __func__, NULL); + if (index < 0 || index >= boxa->n) + return (BOX *)ERROR_PTR("index not valid", __func__, NULL); + + if (accessflag == L_COPY) + return boxCopy(boxa->box[index]); + else if (accessflag == L_CLONE) + return boxClone(boxa->box[index]); + else + return (BOX *)ERROR_PTR("invalid accessflag", __func__, NULL); +} + + +/*! + * \brief boxaGetValidBox() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[in] accessflag L_COPY or L_CLONE + * \return box, or NULL if box is not valid or on error + * + * <pre> + * Notes: + * (1) This returns NULL for an invalid box in a boxa. + * For a box to be valid, both the width and height must be > 0. + * (2) We allow invalid boxes, with w = 0 or h = 0, as placeholders + * in boxa for which the index of the box in the boxa is important. + * This is an atypical situation; usually you want to put only + * valid boxes in a boxa. + * </pre> + */ +BOX * +boxaGetValidBox(BOXA *boxa, + l_int32 index, + l_int32 accessflag) +{ +l_int32 w, h; +BOX *box; + + if (!boxa) + return (BOX *)ERROR_PTR("boxa not defined", __func__, NULL); + + if ((box = boxaGetBox(boxa, index, accessflag)) == NULL) + return (BOX *)ERROR_PTR("box not returned", __func__, NULL); + boxGetGeometry(box, NULL, NULL, &w, &h); + if (w <= 0 || h <= 0) /* not valid, but not necessarily an error */ + boxDestroy(&box); + return box; +} + + +/*! + * \brief boxaFindInvalidBoxes() + * + * \param[in] boxa + * \return na numa of invalid boxes; NULL if there are none or on error + */ +NUMA * +boxaFindInvalidBoxes(BOXA *boxa) +{ +l_int32 i, n, w, h; +NUMA *na; + + if (!boxa) + return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL); + + n = boxaGetCount(boxa); + if (boxaGetValidCount(boxa) == n) + return NULL; + + na = numaMakeConstant(0, n); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + if (w == 0 || h == 0) + numaSetValue(na, i, 1); + } + return na; +} + + +/*! + * \brief boxaGetBoxGeometry() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[out] px, py, pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +boxaGetBoxGeometry(BOXA *boxa, + l_int32 index, + l_int32 *px, + l_int32 *py, + l_int32 *pw, + l_int32 *ph) +{ +BOX *box; + + if (px) *px = 0; + if (py) *py = 0; + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + if (index < 0 || index >= boxa->n) + return ERROR_INT("index not valid", __func__, 1); + + if ((box = boxaGetBox(boxa, index, L_CLONE)) == NULL) + return ERROR_INT("box not found!", __func__, 1); + boxGetGeometry(box, px, py, pw, ph); + boxDestroy(&box); + return 0; +} + + +/*! + * \brief boxaIsFull() + * + * \param[in] boxa + * \param[out] pfull 1 if boxa is full; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxaIsFull(BOXA *boxa, + l_int32 *pfull) +{ +l_int32 i, n, full; +BOX *box; + + if (!pfull) + return ERROR_INT("&full not defined", __func__, 1); + *pfull = 0; + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + n = boxaGetCount(boxa); + full = 1; + for (i = 0; i < n; i++) { + if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) { + full = 0; + break; + } + boxDestroy(&box); + } + *pfull = full; + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxa array modifiers * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaReplaceBox() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[in] box insert this box to replace existing one + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) In-place replacement of one box; the input %box is now + * owned by the boxa. + * (2) The previous box at that location, if any, is destroyed. + * </pre> + */ +l_ok +boxaReplaceBox(BOXA *boxa, + l_int32 index, + BOX *box) +{ + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + if (index < 0 || index >= boxa->n) + return ERROR_INT("index not valid", __func__, 1); + if (!box) + return ERROR_INT("box not defined", __func__, 1); + + boxDestroy(&(boxa->box[index])); + boxa->box[index] = box; + return 0; +} + + +/*! + * \brief boxaInsertBox() + * + * \param[in] boxa + * \param[in] index location in boxa to insert new value + * \param[in] box new box to be inserted; the boxa now owns it + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This shifts box[i] --> box[i + 1] for all i >= index, + * and then inserts box as box[index]. + * (2) To insert at the beginning of the array, set index = 0. + * (3) To append to the array, it's easier to use boxaAddBox(). + * (4) This should not be used repeatedly to insert into large arrays, + * because the function is O(n). + * </pre> + */ +l_ok +boxaInsertBox(BOXA *boxa, + l_int32 index, + BOX *box) +{ +l_int32 i, n; +BOX **array; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + n = boxaGetCount(boxa); + if (index < 0 || index > n) { + L_ERROR("index %d not in [0,...,%d]\n", __func__, index, n); + return 1; + } + if (!box) + return ERROR_INT("box not defined", __func__, 1); + + if (n >= boxa->nalloc) { + if (boxaExtendArray(boxa)) + return ERROR_INT("extension failed", __func__, 1); + } + array = boxa->box; + boxa->n++; + for (i = n; i > index; i--) + array[i] = array[i - 1]; + array[index] = box; + return 0; +} + + +/*! + * \brief boxaRemoveBox() + * + * \param[in] boxa + * \param[in] index of box to be removed and destroyed + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This removes box[index] and then shifts + * box[i] --> box[i - 1] for all i > index. + * (2) It should not be used repeatedly to remove boxes from + * large arrays, because the function is O(n). + * </pre> + */ +l_ok +boxaRemoveBox(BOXA *boxa, + l_int32 index) +{ + return boxaRemoveBoxAndSave(boxa, index, NULL); +} + + +/*! + * \brief boxaRemoveBoxAndSave() + * + * \param[in] boxa + * \param[in] index of box to be removed + * \param[out] pbox [optional] removed box + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This removes box[index] and then shifts + * box[i] --> box[i - 1] for all i > index. + * (2) It should not be used repeatedly to remove boxes from + * large arrays, because the function is O(n). + * </pre> + */ +l_ok +boxaRemoveBoxAndSave(BOXA *boxa, + l_int32 index, + BOX **pbox) +{ +l_int32 i, n; +BOX **array; + + if (pbox) *pbox = NULL; + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + n = boxaGetCount(boxa); + if (index < 0 || index >= n) { + L_ERROR("index %d not in [0,...,%d]\n", __func__, index, n - 1); + return 1; + } + + if (pbox) + *pbox = boxaGetBox(boxa, index, L_CLONE); + array = boxa->box; + boxDestroy(&array[index]); + for (i = index + 1; i < n; i++) + array[i - 1] = array[i]; + array[n - 1] = NULL; + boxa->n--; + + return 0; +} + + +/*! + * \brief boxaSaveValid() + * + * \param[in] boxas + * \param[in] copyflag L_COPY or L_CLONE + * \return boxad if OK, NULL on error + * + * <pre> + * Notes: + * (1) This makes a copy/clone of each valid box. + * </pre> + */ +BOXA * +boxaSaveValid(BOXA *boxas, + l_int32 copyflag) +{ +l_int32 i, n; +BOX *box; +BOXA *boxad; + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXA *)ERROR_PTR("invalid copyflag", __func__, NULL); + + n = boxaGetCount(boxas); + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxas, i, copyflag)) != NULL) + boxaAddBox(boxad, box, L_INSERT); + } + + return boxad; +} + + +/*! + * \brief boxaInitFull() + * + * \param[in] boxa typically empty + * \param[in] box [optional] to be replicated into the entire ptr array + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This initializes a boxa by filling up the entire box ptr array + * with copies of %box. If %box == NULL, use a placeholder box + * of zero size. Any existing boxes are destroyed. + * After this opepration, the number of boxes is equal to + * the number of allocated ptrs. + * (2) Note that we use boxaReplaceBox() instead of boxaInsertBox(). + * They both have the same effect when inserting into a NULL ptr + * in the boxa ptr array: + * (3) Example usage. This function is useful to prepare for a + * random insertion (or replacement) of boxes into a boxa. + * To randomly insert boxes into a boxa, up to some index "max": + * Boxa *boxa = boxaCreate(max); + * boxaInitFull(boxa, NULL); + * If you want placeholder boxes of non-zero size: + * Boxa *boxa = boxaCreate(max); + * Box *box = boxCreate(...); + * boxaInitFull(boxa, box); + * boxDestroy(&box); + * If we have an existing boxa with a smaller ptr array, it can + * be reused for up to max boxes: + * boxaExtendArrayToSize(boxa, max); + * boxaInitFull(boxa, NULL); + * The initialization allows the boxa to always be properly + * filled, even if all the boxes are not later replaced. + * If you want to know which boxes have been replaced, + * and you initialized with invalid zero-sized boxes, + * use boxaGetValidBox() to return NULL for the invalid boxes. + * </pre> + */ +l_ok +boxaInitFull(BOXA *boxa, + BOX *box) +{ +l_int32 i, n; +BOX *boxt; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + n = boxa->nalloc; + boxa->n = n; + for (i = 0; i < n; i++) { + if (box) + boxt = boxCopy(box); + else + boxt = boxCreate(0, 0, 0, 0); + boxaReplaceBox(boxa, i, boxt); + } + return 0; +} + + +/*! + * \brief boxaClear() + * + * \param[in] boxa + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This destroys all boxes in the boxa, setting the ptrs + * to null. The number of allocated boxes, n, is set to 0. + * </pre> + */ +l_ok +boxaClear(BOXA *boxa) +{ +l_int32 i, n; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) + boxDestroy(&boxa->box[i]); + boxa->n = 0; + return 0; +} + + +/*--------------------------------------------------------------------------* + * Boxaa creation, destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief boxaaCreate() + * + * \param[in] n size of boxa ptr array to be alloc'd; 0 for default + * \return baa, or NULL on error + */ +BOXAA * +boxaaCreate(l_int32 n) +{ +BOXAA *baa; + + if (n <= 0 || n > MaxBoxaaPtrArraySize) + n = InitialPtrArraySize; + + baa = (BOXAA *)LEPT_CALLOC(1, sizeof(BOXAA)); + if ((baa->boxa = (BOXA **)LEPT_CALLOC(n, sizeof(BOXA *))) == NULL) { + boxaaDestroy(&baa); + return (BOXAA *)ERROR_PTR("boxa ptr array not made", __func__, NULL); + } + baa->nalloc = n; + baa->n = 0; + return baa; +} + + +/*! + * \brief boxaaCopy() + * + * \param[in] baas input boxaa to be copied + * \param[in] copyflag L_COPY, L_CLONE + * \return baad new boxaa, composed of copies or clones of the boxa + * in baas, or NULL on error + * + * <pre> + * Notes: + * (1) L_COPY makes a copy of each boxa in baas. + * L_CLONE makes a clone of each boxa in baas. + * </pre> + */ +BOXAA * +boxaaCopy(BOXAA *baas, + l_int32 copyflag) +{ +l_int32 i, n; +BOXA *boxa; +BOXAA *baad; + + if (!baas) + return (BOXAA *)ERROR_PTR("baas not defined", __func__, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXAA *)ERROR_PTR("invalid copyflag", __func__, NULL); + + n = boxaaGetCount(baas); + baad = boxaaCreate(n); + for (i = 0; i < n; i++) { + boxa = boxaaGetBoxa(baas, i, copyflag); + boxaaAddBoxa(baad, boxa, L_INSERT); + } + + return baad; +} + + +/*! + * \brief boxaaDestroy() + * + * \param[in,out] pbaa will be set to null before returning + */ +void +boxaaDestroy(BOXAA **pbaa) +{ +l_int32 i; +BOXAA *baa; + + if (pbaa == NULL) { + L_WARNING("ptr address is NULL!\n", __func__); + return; + } + + if ((baa = *pbaa) == NULL) + return; + + for (i = 0; i < baa->n; i++) + boxaDestroy(&baa->boxa[i]); + LEPT_FREE(baa->boxa); + LEPT_FREE(baa); + *pbaa = NULL; +} + + + +/*--------------------------------------------------------------------------* + * Add Boxa to Boxaa * + *--------------------------------------------------------------------------*/ +/*! + * \brief boxaaAddBoxa() + * + * \param[in] baa + * \param[in] ba to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +boxaaAddBoxa(BOXAA *baa, + BOXA *ba, + l_int32 copyflag) +{ +l_int32 n; +BOXA *bac; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + if (!ba) + return ERROR_INT("ba not defined", __func__, 1); + if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) + return ERROR_INT("invalid copyflag", __func__, 1); + + if (copyflag == L_INSERT) + bac = ba; + else + bac = boxaCopy(ba, copyflag); + + n = boxaaGetCount(baa); + if (n >= baa->nalloc) { + if (boxaaExtendArray(baa)) + return ERROR_INT("extension failed", __func__, 1); + } + baa->boxa[n] = bac; + baa->n++; + return 0; +} + + +/*! + * \brief boxaaExtendArray() + * + * \param[in] baa + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Doubles the size of the boxa ptr array. + * (2) The max number of boxa ptrs is 1 million. + * </pre> + */ +l_ok +boxaaExtendArray(BOXAA *baa) +{ + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + + return boxaaExtendArrayToSize(baa, 2 * baa->nalloc); +} + + +/*! + * \brief boxaaExtendArrayToSize() + * + * \param[in] baa + * \param[in] size new size of boxa array + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) If necessary, reallocs the boxa ptr array to %size. + * (2) %size limited to 1M boxa ptrs. + * </pre> + */ +l_ok +boxaaExtendArrayToSize(BOXAA *baa, + l_int32 size) +{ +size_t oldsize, newsize; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + if (baa->nalloc > MaxBoxaaPtrArraySize) /* belt & suspenders */ + return ERROR_INT("baa has too many ptrs", __func__, 1); + if (size > MaxBoxaaPtrArraySize) + return ERROR_INT("size > 1M boxa ptrs; too large", __func__, 1); + if (size <= baa->nalloc) { + L_INFO("size too small; no extension\n", __func__); + return 0; + } + + oldsize = baa->nalloc * sizeof(BOXA *); + newsize = size * sizeof(BOXA *); + if ((baa->boxa = (BOXA **)reallocNew((void **)&baa->boxa, + oldsize, newsize)) == NULL) + return ERROR_INT("new ptr array not returned", __func__, 1); + baa->nalloc = size; + return 0; +} + + +/*----------------------------------------------------------------------* + * Boxaa accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief boxaaGetCount() + * + * \param[in] baa + * \return count number of boxa, or 0 if no boxa or on error + */ +l_int32 +boxaaGetCount(BOXAA *baa) +{ + if (!baa) + return ERROR_INT("baa not defined", __func__, 0); + return baa->n; +} + + +/*! + * \brief boxaaGetBoxCount() + * + * \param[in] baa + * \return count number of boxes, or 0 if no boxes or on error + */ +l_int32 +boxaaGetBoxCount(BOXAA *baa) +{ +BOXA *boxa; +l_int32 n, sum, i; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 0); + + n = boxaaGetCount(baa); + for (sum = 0, i = 0; i < n; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + sum += boxaGetCount(boxa); + boxaDestroy(&boxa); + } + + return sum; +} + + +/*! + * \brief boxaaGetBoxa() + * + * \param[in] baa + * \param[in] index to the index-th boxa + * \param[in] accessflag L_COPY or L_CLONE + * \return boxa, or NULL on error + */ +BOXA * +boxaaGetBoxa(BOXAA *baa, + l_int32 index, + l_int32 accessflag) +{ +l_int32 n; + + if (!baa) + return (BOXA *)ERROR_PTR("baa not defined", __func__, NULL); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return (BOXA *)ERROR_PTR("index not valid", __func__, NULL); + if (accessflag != L_COPY && accessflag != L_CLONE) + return (BOXA *)ERROR_PTR("invalid accessflag", __func__, NULL); + + return boxaCopy(baa->boxa[index], accessflag); +} + + +/*! + * \brief boxaaGetBox() + * + * \param[in] baa + * \param[in] iboxa index into the boxa array in the boxaa + * \param[in] ibox index into the box array in the boxa + * \param[in] accessflag L_COPY or L_CLONE + * \return box, or NULL on error + */ +BOX * +boxaaGetBox(BOXAA *baa, + l_int32 iboxa, + l_int32 ibox, + l_int32 accessflag) +{ +BOX *box; +BOXA *boxa; + + if ((boxa = boxaaGetBoxa(baa, iboxa, L_CLONE)) == NULL) + return (BOX *)ERROR_PTR("boxa not retrieved", __func__, NULL); + if ((box = boxaGetBox(boxa, ibox, accessflag)) == NULL) + L_ERROR("box not retrieved\n", __func__); + boxaDestroy(&boxa); + return box; +} + + +/*----------------------------------------------------------------------* + * Boxaa array modifiers * + *----------------------------------------------------------------------*/ +/*! + * \brief boxaaInitFull() + * + * \param[in] baa typically empty + * \param[in] boxa to be replicated into the entire ptr array + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This initializes a boxaa by filling up the entire boxa ptr array + * with copies of %boxa. Any existing boxa are destroyed. + * After this operation, the number of boxa is equal to + * the number of allocated ptrs. + * (2) Note that we use boxaaReplaceBoxa() which replaces a boxa, + * instead of boxaaInsertBoxa(), which is O(n) and shifts all + * the boxa pointers from the insertion point to the end. + * (3) Example usage. This function is useful to prepare for a + * random insertion (or replacement) of boxa into a boxaa. + * To randomly insert boxa into a boxaa, up to some index "max": + * Boxaa *baa = boxaaCreate(max); + * // initialize the boxa + * Boxa *boxa = boxaCreate(...); + * ... [optionally fill with boxes] + * boxaaInitFull(baa, boxa); + * A typical use is to initialize the array with empty boxa, + * and to replace only a subset that must be aligned with + * something else, such as a pixa. + * </pre> + */ +l_ok +boxaaInitFull(BOXAA *baa, + BOXA *boxa) +{ +l_int32 i, n; +BOXA *boxat; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + n = baa->nalloc; + baa->n = n; + for (i = 0; i < n; i++) { + boxat = boxaCopy(boxa, L_COPY); + boxaaReplaceBoxa(baa, i, boxat); + } + return 0; +} + + +/*! + * \brief boxaaExtendWithInit() + * + * \param[in] baa + * \param[in] maxindex + * \param[in] boxa to be replicated into the extended ptr array + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This should be used on an existing boxaa that has been + * fully loaded with boxa. It then extends the boxaa, + * loading all the additional ptrs with copies of boxa. + * Typically, boxa will be empty. + * </pre> + */ +l_ok +boxaaExtendWithInit(BOXAA *baa, + l_int32 maxindex, + BOXA *boxa) +{ +l_int32 i, n; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + /* Extend the ptr array if necessary */ + n = boxaaGetCount(baa); + if (maxindex < n) return 0; + if (boxaaExtendArrayToSize(baa, maxindex + 1)) + return ERROR_INT("extension failed", __func__, 1); + + /* Fill the new entries with copies of boxa */ + for (i = n; i <= maxindex; i++) + boxaaAddBoxa(baa, boxa, L_COPY); + return 0; +} + + +/*! + * \brief boxaaReplaceBoxa() + * + * \param[in] baa + * \param[in] index to the index-th boxa + * \param[in] boxa insert and replace any existing one + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Any existing boxa is destroyed, and the input one + * is inserted in its place. + * (2) If the index is invalid, return 1 (error) + * </pre> + */ +l_ok +boxaaReplaceBoxa(BOXAA *baa, + l_int32 index, + BOXA *boxa) +{ +l_int32 n; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", __func__, 1); + + boxaDestroy(&baa->boxa[index]); + baa->boxa[index] = boxa; + return 0; +} + + +/*! + * \brief boxaaInsertBoxa() + * + * \param[in] baa + * \param[in] index location in boxaa to insert new boxa + * \param[in] boxa new boxa to be inserted + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This shifts boxa[i] --> boxa[i + 1] for all i >= index, + * and then inserts boxa as boxa[index]. It is typically used + * when %baa is full of boxa. + * (2) To insert at the beginning of the array, set %index = 0. + * (3) To append to the array, it is equivalent to boxaaAddBoxa(). + * (4) This should not be used repeatedly to insert into large arrays, + * because the function is O(n). + * </pre> + */ +l_ok +boxaaInsertBoxa(BOXAA *baa, + l_int32 index, + BOXA *boxa) +{ +l_int32 i, n; +BOXA **array; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + n = boxaaGetCount(baa); + if (index < 0 || index > n) { + L_ERROR("index %d not in [0,...,%d]\n", __func__, index, n); + return 1; + } + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + if (n >= baa->nalloc) { + if (boxaaExtendArray(baa)) + return ERROR_INT("extension failed", __func__, 1); + } + array = baa->boxa; + baa->n++; + for (i = n; i > index; i--) + array[i] = array[i - 1]; + array[index] = boxa; + return 0; +} + + +/*! + * \brief boxaaRemoveBoxa() + * + * \param[in] baa + * \param[in] index of the boxa to be removed and destroyed + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This removes boxa[index] and then shifts + * boxa[i] --> boxa[i - 1] for all i > index. + * (2) The removed boxaa is destroyed. + * (2) This should not be used repeatedly on large arrays, + * because the function is O(n). + * </pre> + */ +l_ok +boxaaRemoveBoxa(BOXAA *baa, + l_int32 index) +{ +l_int32 i, n; +BOXA **array; + + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", __func__, 1); + + array = baa->boxa; + boxaDestroy(&array[index]); + for (i = index + 1; i < n; i++) + array[i - 1] = array[i]; + array[n - 1] = NULL; + baa->n--; + + return 0; +} + + +/*! + * \brief boxaaAddBox() + * + * \param[in] baa + * \param[in] index of boxa with boxaa + * \param[in] box to be added + * \param[in] accessflag L_INSERT, L_COPY or L_CLONE + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Adds to an existing boxa only. + * </pre> + */ +l_ok +boxaaAddBox(BOXAA *baa, + l_int32 index, + BOX *box, + l_int32 accessflag) +{ +l_int32 n; +BOXA *boxa; + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", __func__, 1); + if (accessflag != L_INSERT && accessflag != L_COPY && accessflag != L_CLONE) + return ERROR_INT("invalid accessflag", __func__, 1); + + boxa = boxaaGetBoxa(baa, index, L_CLONE); + boxaAddBox(boxa, box, accessflag); + boxaDestroy(&boxa); + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxaa serialized I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaaReadFromFiles() + * + * \param[in] dirname directory + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] first 0-based + * \param[in] nfiles use 0 for everything from %first to the end + * \return baa, or NULL on error or if no boxa files are found. + * + * <pre> + * Notes: + * (1) The files must be serialized boxa files (e.g., *.ba). + * If some files cannot be read, warnings are issued. + * (2) Use %substr to filter filenames in the directory. If + * %substr == NULL, this takes all files. + * (3) After filtering, use %first and %nfiles to select + * a contiguous set of files, that have been lexically + * sorted in increasing order. + * </pre> + */ +BOXAA * +boxaaReadFromFiles(const char *dirname, + const char *substr, + l_int32 first, + l_int32 nfiles) +{ +char *fname; +l_int32 i, n; +BOXA *boxa; +BOXAA *baa; +SARRAY *sa; + + if (!dirname) + return (BOXAA *)ERROR_PTR("dirname not defined", __func__, NULL); + + sa = getSortedPathnamesInDirectory(dirname, substr, first, nfiles); + if (!sa || ((n = sarrayGetCount(sa)) == 0)) { + sarrayDestroy(&sa); + return (BOXAA *)ERROR_PTR("no pixa files found", __func__, NULL); + } + + baa = boxaaCreate(n); + for (i = 0; i < n; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + if ((boxa = boxaRead(fname)) == NULL) { + L_ERROR("boxa not read for %d-th file", __func__, i); + continue; + } + boxaaAddBoxa(baa, boxa, L_INSERT); + } + + sarrayDestroy(&sa); + return baa; +} + + +/*! + * \brief boxaaRead() + * + * \param[in] filename + * \return boxaa, or NULL on error + */ +BOXAA * +boxaaRead(const char *filename) +{ +FILE *fp; +BOXAA *baa; + + if (!filename) + return (BOXAA *)ERROR_PTR("filename not defined", __func__, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (BOXAA *)ERROR_PTR_1("stream not opened", + filename, __func__, NULL); + baa = boxaaReadStream(fp); + fclose(fp); + if (!baa) + return (BOXAA *)ERROR_PTR_1("boxaa not read", + filename, __func__, NULL); + return baa; +} + + +/*! + * \brief boxaaReadStream() + * + * \param[in] fp input file stream + * \return boxaa, or NULL on error + * + * <pre> + * Notes: + * (1) It is OK for the boxaa to be empty (n == 0). + * </pre> + */ +BOXAA * +boxaaReadStream(FILE *fp) +{ +l_int32 n, i, x, y, w, h, version; +l_int32 ignore; +BOXA *boxa; +BOXAA *baa; + + if (!fp) + return (BOXAA *)ERROR_PTR("stream not defined", __func__, NULL); + + if (fscanf(fp, "\nBoxaa Version %d\n", &version) != 1) + return (BOXAA *)ERROR_PTR("not a boxaa file", __func__, NULL); + if (version != BOXAA_VERSION_NUMBER) + return (BOXAA *)ERROR_PTR("invalid boxa version", __func__, NULL); + if (fscanf(fp, "Number of boxa = %d\n", &n) != 1) + return (BOXAA *)ERROR_PTR("not a boxaa file", __func__, NULL); + if (n < 0) + return (BOXAA *)ERROR_PTR("num boxa ptrs < 0", __func__, NULL); + if (n > MaxBoxaaPtrArraySize) + return (BOXAA *)ERROR_PTR("too many boxa ptrs", __func__, NULL); + if (n == 0) L_INFO("the boxaa is empty\n", __func__); + + if ((baa = boxaaCreate(n)) == NULL) + return (BOXAA *)ERROR_PTR("boxaa not made", __func__, NULL); + for (i = 0; i < n; i++) { + if (fscanf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d", + &ignore, &x, &y, &w, &h) != 5) { + boxaaDestroy(&baa); + return (BOXAA *)ERROR_PTR("boxa descr not valid", __func__, NULL); + } + if ((boxa = boxaReadStream(fp)) == NULL) { + boxaaDestroy(&baa); + return (BOXAA *)ERROR_PTR("boxa not made", __func__, NULL); + } + boxaaAddBoxa(baa, boxa, L_INSERT); + } + return baa; +} + + +/*! + * \brief boxaaReadMem() + * + * \param[in] data serialization of boxaa; in ascii + * \param[in] size of data in bytes; can use strlen to get it + * \return baa, or NULL on error + */ +BOXAA * +boxaaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +BOXAA *baa; + + if (!data) + return (BOXAA *)ERROR_PTR("data not defined", __func__, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (BOXAA *)ERROR_PTR("stream not opened", __func__, NULL); + + baa = boxaaReadStream(fp); + fclose(fp); + if (!baa) L_ERROR("baa not read\n", __func__); + return baa; +} + + +/*! + * \brief boxaaWrite() + * + * \param[in] filename + * \param[in] baa + * \return 0 if OK, 1 on error + */ +l_ok +boxaaWrite(const char *filename, + BOXAA *baa) +{ +l_int32 ret; +FILE *fp; + + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT_1("stream not opened", filename, __func__, 1); + ret = boxaaWriteStream(fp, baa); + fclose(fp); + if (ret) + return ERROR_INT_1("baa not written to stream", filename, __func__, 1); + return 0; +} + + +/*! + * \brief boxaaWriteStream() + * + * \param[in] fp output file stream + * \param[in] baa + * \return 0 if OK, 1 on error + */ +l_ok +boxaaWriteStream(FILE *fp, + BOXAA *baa) +{ +l_int32 n, i, x, y, w, h; +BOX *box; +BOXA *boxa; + + if (!fp) + return ERROR_INT("stream not defined", __func__, 1); + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + + n = boxaaGetCount(baa); + fprintf(fp, "\nBoxaa Version %d\n", BOXAA_VERSION_NUMBER); + fprintf(fp, "Number of boxa = %d\n", n); + + for (i = 0; i < n; i++) { + if ((boxa = boxaaGetBoxa(baa, i, L_CLONE)) == NULL) + return ERROR_INT("boxa not found", __func__, 1); + boxaGetExtent(boxa, NULL, NULL, &box); + boxGetGeometry(box, &x, &y, &w, &h); + fprintf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d", + i, x, y, w, h); + boxaWriteStream(fp, boxa); + boxDestroy(&box); + boxaDestroy(&boxa); + } + return 0; +} + + +/*! + * \brief boxaaWriteMem() + * + * \param[out] pdata data of serialized boxaa; ascii + * \param[out] psize size of returned data + * \param[in] baa + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Serializes a boxaa in memory and puts the result in a buffer. + * </pre> + */ +l_ok +boxaaWriteMem(l_uint8 **pdata, + size_t *psize, + BOXAA *baa) +{ +l_int32 ret; +FILE *fp; + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", __func__, 1); + if (!psize) + return ERROR_INT("&size not defined", __func__, 1); + if (!baa) + return ERROR_INT("baa not defined", __func__, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", __func__, 1); + ret = boxaaWriteStream(fp, baa); + fputc('\0', fp); + fclose(fp); + if (*psize > 0) *psize = *psize - 1; +#else + L_INFO("no fmemopen API --> work-around: write to temp file\n", __func__); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", __func__, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", __func__, 1); + #endif /* _WIN32 */ + ret = boxaaWriteStream(fp, baa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); + fclose(fp); +#endif /* HAVE_FMEMOPEN */ + return ret; +} + + +/*---------------------------------------------------------------------* + * Boxa serialized I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaRead() + * + * \param[in] filename + * \return boxa, or NULL on error + */ +BOXA * +boxaRead(const char *filename) +{ +FILE *fp; +BOXA *boxa; + + if (!filename) + return (BOXA *)ERROR_PTR("filename not defined", __func__, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (BOXA *)ERROR_PTR_1("stream not opened", + filename, __func__, NULL); + boxa = boxaReadStream(fp); + fclose(fp); + if (!boxa) + return (BOXA *)ERROR_PTR_1("boxa not read", + filename, __func__, NULL); + return boxa; +} + + +/*! + * \brief boxaReadStream() + * + * \param[in] fp input file stream + * \return boxa, or NULL on error + * + * <pre> + * Notes: + * (1) It is OK for the boxa to be empty (n == 0). + * </pre> + */ +BOXA * +boxaReadStream(FILE *fp) +{ +l_int32 n, i, x, y, w, h, version; +l_int32 ignore; +BOX *box; +BOXA *boxa; + + if (!fp) + return (BOXA *)ERROR_PTR("stream not defined", __func__, NULL); + + if (fscanf(fp, "\nBoxa Version %d\n", &version) != 1) + return (BOXA *)ERROR_PTR("not a boxa file", __func__, NULL); + if (version != BOXA_VERSION_NUMBER) + return (BOXA *)ERROR_PTR("invalid boxa version", __func__, NULL); + if (fscanf(fp, "Number of boxes = %d\n", &n) != 1) + return (BOXA *)ERROR_PTR("not a boxa file", __func__, NULL); + if (n < 0) + return (BOXA *)ERROR_PTR("num box ptrs < 0", __func__, NULL); + if (n > MaxBoxaPtrArraySize) + return (BOXA *)ERROR_PTR("too many box ptrs", __func__, NULL); + if (n == 0) L_INFO("the boxa is empty\n", __func__); + + if ((boxa = boxaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("boxa not made", __func__, NULL); + for (i = 0; i < n; i++) { + if (fscanf(fp, " Box[%d]: x = %d, y = %d, w = %d, h = %d\n", + &ignore, &x, &y, &w, &h) != 5) { + boxaDestroy(&boxa); + return (BOXA *)ERROR_PTR("box descr not valid", __func__, NULL); + } + box = boxCreate(x, y, w, h); + boxaAddBox(boxa, box, L_INSERT); + } + return boxa; +} + + +/*! + * \brief boxaReadMem() + * + * \param[in] data serialization of boxa; in ascii + * \param[in] size of data in bytes; can use strlen to get it + * \return boxa, or NULL on error + */ +BOXA * +boxaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +BOXA *boxa; + + if (!data) + return (BOXA *)ERROR_PTR("data not defined", __func__, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (BOXA *)ERROR_PTR("stream not opened", __func__, NULL); + + boxa = boxaReadStream(fp); + fclose(fp); + if (!boxa) L_ERROR("boxa not read\n", __func__); + return boxa; +} + + +/*! + * \brief boxaWriteDebug() + * + * \param[in] filename + * \param[in] boxa + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) Debug version, intended for use in the library when writing + * to files in a temp directory with names that are compiled in. + * This is used instead of boxaWrite() for all such library calls. + * (2) The global variable LeptDebugOK defaults to 0, and can be set + * or cleared by the function setLeptDebugOK(). + * </pre> + */ +l_ok +boxaWriteDebug(const char *filename, + BOXA *boxa) +{ + if (LeptDebugOK) { + return boxaWrite(filename, boxa); + } else { + L_INFO("write to named temp file %s is disabled\n", __func__, filename); + return 0; + } +} + + +/*! + * \brief boxaWrite() + * + * \param[in] filename + * \param[in] boxa + * \return 0 if OK, 1 on error + */ +l_ok +boxaWrite(const char *filename, + BOXA *boxa) +{ +l_int32 ret; +FILE *fp; + + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT_1("stream not opened", filename, __func__, 1); + ret = boxaWriteStream(fp, boxa); + fclose(fp); + if (ret) + return ERROR_INT_1("boxa not written to stream", filename, __func__, 1); + + return 0; +} + + +/*! + * \brief boxaWriteStream() + * + * \param[in] fp file stream; use NULL for stderr + * \param[in] boxa + * \return 0 if OK, 1 on error + */ +l_ok +boxaWriteStream(FILE *fp, + BOXA *boxa) +{ +l_int32 n, i; +BOX *box; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + if (!fp) + return boxaWriteStderr(boxa); + + n = boxaGetCount(boxa); + fprintf(fp, "\nBoxa Version %d\n", BOXA_VERSION_NUMBER); + fprintf(fp, "Number of boxes = %d\n", n); + for (i = 0; i < n; i++) { + if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) + return ERROR_INT("box not found", __func__, 1); + fprintf(fp, " Box[%d]: x = %d, y = %d, w = %d, h = %d\n", + i, box->x, box->y, box->w, box->h); + boxDestroy(&box); + } + return 0; +} + + +/*! + * \brief boxaWriteStderr() + * + * \param[in] boxa + * \return 0 if OK, 1 on error + */ +l_ok +boxaWriteStderr(BOXA *boxa) +{ +l_int32 n, i; +BOX *box; + + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + + n = boxaGetCount(boxa); + lept_stderr("\nBoxa Version %d\n", BOXA_VERSION_NUMBER); + lept_stderr("Number of boxes = %d\n", n); + for (i = 0; i < n; i++) { + if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) + return ERROR_INT("box not found", __func__, 1); + lept_stderr(" Box[%d]: x = %d, y = %d, w = %d, h = %d\n", + i, box->x, box->y, box->w, box->h); + boxDestroy(&box); + } + return 0; +} + + +/*! + * \brief boxaWriteMem() + * + * \param[out] pdata data of serialized boxa; ascii + * \param[out] psize size of returned data + * \param[in] boxa + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Serializes a boxa in memory and puts the result in a buffer. + * </pre> + */ +l_ok +boxaWriteMem(l_uint8 **pdata, + size_t *psize, + BOXA *boxa) +{ +l_int32 ret; +FILE *fp; + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", __func__, 1); + if (!psize) + return ERROR_INT("&size not defined", __func__, 1); + if (!boxa) + return ERROR_INT("boxa not defined", __func__, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", __func__, 1); + ret = boxaWriteStream(fp, boxa); + fputc('\0', fp); + fclose(fp); + if (*psize > 0) *psize = *psize - 1; +#else + L_INFO("no fmemopen API --> work-around: write to temp file\n", __func__); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", __func__, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", __func__, 1); + #endif /* _WIN32 */ + ret = boxaWriteStream(fp, boxa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); + fclose(fp); +#endif /* HAVE_FMEMOPEN */ + return ret; +} + + +/*---------------------------------------------------------------------* + * Debug printing * + *---------------------------------------------------------------------*/ +/*! + * \brief boxPrintStreamInfo() + * + * \param[in] fp file stream; use NULL for stderr + * \param[in] box + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This outputs debug info. Use serialization functions to + * write to file if you want to read the data back. + * </pre> + */ +l_ok +boxPrintStreamInfo(FILE *fp, + BOX *box) +{ + if (!box) + return ERROR_INT("box not defined", __func__, 1); + + if (!fp) { /* output to stderr */ + lept_stderr(" Box: x = %d, y = %d, w = %d, h = %d\n", + box->x, box->y, box->w, box->h); + } else { + fprintf(fp, " Box: x = %d, y = %d, w = %d, h = %d\n", + box->x, box->y, box->w, box->h); + } + return 0; +}
