Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/fpix1.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/fpix1.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1972 @@ +/*====================================================================* + - 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 fpix1.c + * <pre> + * + * --------------------------------------------------- + * This file has these FPix, FPixa and DPix utilities: + * - creation and destruction + * - accessors + * - serialization and deserialization + * --------------------------------------------------- + * + * FPix Create/copy/destroy + * FPIX *fpixCreate() + * FPIX *fpixCreateTemplate() + * FPIX *fpixClone() + * FPIX *fpixCopy() + * void fpixDestroy() + * + * FPix accessors + * l_int32 fpixGetDimensions() + * l_int32 fpixSetDimensions() + * l_int32 fpixGetWpl() + * l_int32 fpixSetWpl() + * l_int32 fpixGetResolution() + * l_int32 fpixSetResolution() + * l_int32 fpixCopyResolution() + * l_float32 *fpixGetData() + * l_int32 fpixSetData() + * l_int32 fpixGetPixel() + * l_int32 fpixSetPixel() + * + * FPixa Create/copy/destroy + * FPIXA *fpixaCreate() + * FPIXA *fpixaCopy() + * void fpixaDestroy() + * + * FPixa addition + * l_int32 fpixaAddFPix() + * static l_int32 fpixaExtendArray() + * static l_int32 fpixaExtendArrayToSize() + * + * FPixa accessors + * l_int32 fpixaGetCount() + * FPIX *fpixaGetFPix() + * l_int32 fpixaGetFPixDimensions() + * l_float32 *fpixaGetData() + * l_int32 fpixaGetPixel() + * l_int32 fpixaSetPixel() + * + * DPix Create/copy/destroy + * DPIX *dpixCreate() + * DPIX *dpixCreateTemplate() + * DPIX *dpixClone() + * DPIX *dpixCopy() + * void dpixDestroy() + * + * DPix accessors + * l_int32 dpixGetDimensions() + * l_int32 dpixSetDimensions() + * l_int32 dpixGetWpl() + * l_int32 dpixSetWpl() + * l_int32 dpixGetResolution() + * l_int32 dpixSetResolution() + * l_int32 dpixCopyResolution() + * l_float64 *dpixGetData() + * l_int32 dpixSetData() + * l_int32 dpixGetPixel() + * l_int32 dpixSetPixel() + * + * FPix serialized I/O + * FPIX *fpixRead() + * FPIX *fpixReadStream() + * FPIX *fpixReadMem() + * l_int32 fpixWrite() + * l_int32 fpixWriteStream() + * l_int32 fpixWriteMem() + * FPIX *fpixEndianByteSwap() + * + * DPix serialized I/O + * DPIX *dpixRead() + * DPIX *dpixReadStream() + * DPIX *dpixReadMem() + * l_int32 dpixWrite() + * l_int32 dpixWriteStream() + * l_int32 dpixWriteMem() + * DPIX *dpixEndianByteSwap() + * + * Print FPix (subsampled, for debugging) + * l_int32 fpixPrintStream() + * </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 MaxPtrArraySize = 100000; +static const size_t InitialPtrArraySize = 20; /*!< n'importe quoi */ + + /* Static functions */ +static l_int32 fpixaExtendArray(FPIXA *fpixa); +static l_int32 fpixaExtendArrayToSize(FPIXA *fpixa, l_int32 size); + +/*--------------------------------------------------------------------* + * FPix Create/copy/destroy * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixCreate() + * + * \param[in] width, height + * \return fpixd with data allocated and initialized to 0, or NULL on error + * + * <pre> + * Notes: + * (1) Makes a FPix of specified size, with the data array + * allocated and initialized to 0. + * (2) The number of pixels must be less than 2^29. + * </pre> + */ +FPIX * +fpixCreate(l_int32 width, + l_int32 height) +{ +l_float32 *data; +l_uint64 npix64; +FPIX *fpixd; + + if (width <= 0) + return (FPIX *)ERROR_PTR("width must be > 0", __func__, NULL); + if (height <= 0) + return (FPIX *)ERROR_PTR("height must be > 0", __func__, NULL); + + /* Avoid overflow in malloc arg, malicious or otherwise */ + npix64 = (l_uint64)width * (l_uint64)height; /* # of 4-byte pixels */ + if (npix64 >= (1LL << 29)) { + L_ERROR("requested w = %d, h = %d\n", __func__, width, height); + return (FPIX *)ERROR_PTR("requested bytes >= 2^31", __func__, NULL); + } + + fpixd = (FPIX *)LEPT_CALLOC(1, sizeof(FPIX)); + fpixSetDimensions(fpixd, width, height); + fpixSetWpl(fpixd, width); /* 4-byte words */ + fpixd->refcount = 1; + + data = (l_float32 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float32)); + if (!data) { + fpixDestroy(&fpixd); + return (FPIX *)ERROR_PTR("calloc fail for data", __func__, NULL); + } + fpixSetData(fpixd, data); + return fpixd; +} + + +/*! + * \brief fpixCreateTemplate() + * + * \param[in] fpixs + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) Makes a FPix of the same size as the input FPix, with the + * data array allocated and initialized to 0. + * (2) Copies the resolution. + * </pre> + */ +FPIX * +fpixCreateTemplate(FPIX *fpixs) +{ +l_int32 w, h; +FPIX *fpixd; + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL); + + fpixGetDimensions(fpixs, &w, &h); + if ((fpixd = fpixCreate(w, h)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL); + fpixCopyResolution(fpixd, fpixs); + return fpixd; +} + + +/*! + * \brief fpixClone() + * + * \param[in] fpix + * \return same fpix ptr, or NULL on error + * + * <pre> + * Notes: + * (1) See pixClone() for definition and usage. + * </pre> + */ +FPIX * +fpixClone(FPIX *fpix) +{ + if (!fpix) + return (FPIX *)ERROR_PTR("fpix not defined", __func__, NULL); + ++fpix->refcount; + + return fpix; +} + + +/*! + * \brief fpixCopy() + * + * \param[in] fpixs + * \return fpixd, or NULL on error + */ +FPIX * +fpixCopy(FPIX *fpixs) +{ +l_int32 w, h, bytes; +l_float32 *datas, *datad; +FPIX *fpixd; + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL); + + /* Total bytes in image data */ + fpixGetDimensions(fpixs, &w, &h); + bytes = 4 * w * h; + + if ((fpixd = fpixCreateTemplate(fpixs)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL); + datas = fpixGetData(fpixs); + datad = fpixGetData(fpixd); + memcpy(datad, datas, bytes); + return fpixd; +} + + +/*! + * \brief fpixDestroy() + * + * \param[in,out] pfpix will be set to null before returning + * \return void + * + * <pre> + * Notes: + * (1) Decrements the ref count and, if 0, destroys the fpix. + * (2) Always nulls the input ptr. + * </pre> + */ +void +fpixDestroy(FPIX **pfpix) +{ +l_float32 *data; +FPIX *fpix; + + if (!pfpix) { + L_WARNING("ptr address is null!\n", __func__); + return; + } + + if ((fpix = *pfpix) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the fpix. */ + if (--fpix->refcount == 0) { + if ((data = fpixGetData(fpix)) != NULL) + LEPT_FREE(data); + LEPT_FREE(fpix); + } + *pfpix = NULL; +} + + +/*--------------------------------------------------------------------* + * FPix Accessors * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixGetDimensions() + * + * \param[in] fpix + * \param[out] pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +fpixGetDimensions(FPIX *fpix, + l_int32 *pw, + l_int32 *ph) +{ + if (!pw && !ph) + return ERROR_INT("no return val requested", __func__, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + if (pw) *pw = fpix->w; + if (ph) *ph = fpix->h; + return 0; +} + + +/*! + * \brief fpixSetDimensions() + * + * \param[in] fpix + * \param[in] w, h + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetDimensions(FPIX *fpix, + l_int32 w, + l_int32 h) +{ + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + fpix->w = w; + fpix->h = h; + return 0; +} + + +/*! + * \brief fpixGetWpl() + * + * \param[in] fpix + * \return wpl, or 0 on error + */ +l_int32 +fpixGetWpl(FPIX *fpix) +{ + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 0); + return fpix->wpl; +} + + +/*! + * \brief fpixSetWpl() + * + * \param[in] fpix + * \param[in] wpl + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetWpl(FPIX *fpix, + l_int32 wpl) +{ + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + fpix->wpl = wpl; + return 0; +} + + +/*! + * \brief fpixGetResolution() + * + * \param[in] fpix + * \param[out] pxres, pyres [optional] x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +fpixGetResolution(FPIX *fpix, + l_int32 *pxres, + l_int32 *pyres) +{ + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + if (pxres) *pxres = fpix->xres; + if (pyres) *pyres = fpix->yres; + return 0; +} + + +/*! + * \brief fpixSetResolution() + * + * \param[in] fpix + * \param[in] xres, yres x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetResolution(FPIX *fpix, + l_int32 xres, + l_int32 yres) +{ + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + fpix->xres = xres; + fpix->yres = yres; + return 0; +} + + +/*! + * \brief fpixCopyResolution() + * + * \param[in] fpixd, fpixs + * \return 0 if OK, 1 on error + */ +l_ok +fpixCopyResolution(FPIX *fpixd, + FPIX *fpixs) +{ +l_int32 xres, yres; + if (!fpixs || !fpixd) + return ERROR_INT("fpixs and fpixd not both defined", __func__, 1); + + fpixGetResolution(fpixs, &xres, &yres); + fpixSetResolution(fpixd, xres, yres); + return 0; +} + + +/*! + * \brief fpixGetData() + * + * \param[in] fpix + * \return ptr to fpix data, or NULL on error + */ +l_float32 * +fpixGetData(FPIX *fpix) +{ + if (!fpix) + return (l_float32 *)ERROR_PTR("fpix not defined", __func__, NULL); + return fpix->data; +} + + +/*! + * \brief fpixSetData() + * + * \param[in] fpix + * \param[in] data + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetData(FPIX *fpix, + l_float32 *data) +{ + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + fpix->data = data; + return 0; +} + + +/*! + * \brief fpixGetPixel() + * + * \param[in] fpix + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +fpixGetPixel(FPIX *fpix, + l_int32 x, + l_int32 y, + l_float32 *pval) +{ +l_int32 w, h; + + if (!pval) + return ERROR_INT("pval not defined", __func__, 1); + *pval = 0.0; + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + fpixGetDimensions(fpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *pval = *(fpix->data + y * w + x); + return 0; +} + + +/*! + * \brief fpixSetPixel() + * + * \param[in] fpix + * \param[in] x,y pixel coords + * \param[in] val pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +fpixSetPixel(FPIX *fpix, + l_int32 x, + l_int32 y, + l_float32 val) +{ +l_int32 w, h; + + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + fpixGetDimensions(fpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *(fpix->data + y * w + x) = val; + return 0; +} + + +/*--------------------------------------------------------------------* + * FPixa Create/copy/destroy * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixaCreate() + * + * \param[in] n initial number of ptrs + * \return fpixa, or NULL on error + */ +FPIXA * +fpixaCreate(l_int32 n) +{ +FPIXA *fpixa; + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + fpixa = (FPIXA *)LEPT_CALLOC(1, sizeof(FPIXA)); + fpixa->n = 0; + fpixa->nalloc = n; + fpixa->refcount = 1; + fpixa->fpix = (FPIX **)LEPT_CALLOC(n, sizeof(FPIX *)); + return fpixa; +} + + +/*! + * \brief fpixaCopy() + * + * \param[in] fpixa + * \param[in] copyflag L_COPY, L_CLODE or L_COPY_CLONE + * \return new fpixa, or NULL on error + * + * <pre> + * Notes: + * copyflag may be one of + * ~ L_COPY makes a new fpixa and copies each fpix + * ~ L_CLONE gives a new ref-counted handle to the input fpixa + * ~ L_COPY_CLONE makes a new fpixa with clones of all fpix + * </pre> + */ +FPIXA * +fpixaCopy(FPIXA *fpixa, + l_int32 copyflag) +{ +l_int32 i; +FPIX *fpixc; +FPIXA *fpixac; + + if (!fpixa) + return (FPIXA *)ERROR_PTR("fpixa not defined", __func__, NULL); + + if (copyflag == L_CLONE) { + ++fpixa->refcount; + return fpixa; + } + + if (copyflag != L_COPY && copyflag != L_COPY_CLONE) + return (FPIXA *)ERROR_PTR("invalid copyflag", __func__, NULL); + + if ((fpixac = fpixaCreate(fpixa->n)) == NULL) + return (FPIXA *)ERROR_PTR("fpixac not made", __func__, NULL); + for (i = 0; i < fpixa->n; i++) { + if (copyflag == L_COPY) + fpixc = fpixaGetFPix(fpixa, i, L_COPY); + else /* copy-clone */ + fpixc = fpixaGetFPix(fpixa, i, L_CLONE); + fpixaAddFPix(fpixac, fpixc, L_INSERT); + } + + return fpixac; +} + + +/*! + * \brief fpixaDestroy() + * + * \param[in,out] pfpixa will be set to null before returning + * \return void + * + * <pre> + * Notes: + * (1) Decrements the ref count and, if 0, destroys the fpixa. + * (2) Always nulls the input ptr. + * </pre> + */ +void +fpixaDestroy(FPIXA **pfpixa) +{ +l_int32 i; +FPIXA *fpixa; + + if (pfpixa == NULL) { + L_WARNING("ptr address is NULL!\n", __func__); + return; + } + + if ((fpixa = *pfpixa) == NULL) + return; + + /* Decrement the refcount. If it is 0, destroy the pixa. */ + if (--fpixa->refcount == 0) { + for (i = 0; i < fpixa->n; i++) + fpixDestroy(&fpixa->fpix[i]); + LEPT_FREE(fpixa->fpix); + LEPT_FREE(fpixa); + } + *pfpixa = NULL; +} + + +/*--------------------------------------------------------------------* + * FPixa addition * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixaAddFPix() + * + * \param[in] fpixa + * \param[in] fpix to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK; 1 on error + */ +l_ok +fpixaAddFPix(FPIXA *fpixa, + FPIX *fpix, + l_int32 copyflag) +{ +l_int32 n; +FPIX *fpixc; + + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + if (copyflag == L_INSERT) + fpixc = fpix; + else if (copyflag == L_COPY) + fpixc = fpixCopy(fpix); + else if (copyflag == L_CLONE) + fpixc = fpixClone(fpix); + else + return ERROR_INT("invalid copyflag", __func__, 1); + if (!fpixc) + return ERROR_INT("fpixc not made", __func__, 1); + + n = fpixaGetCount(fpixa); + if (n >= fpixa->nalloc) { + if (fpixaExtendArray(fpixa)) { + if (copyflag != L_INSERT) + fpixDestroy(&fpixc); + return ERROR_INT("extension failed", __func__, 1); + } + } + fpixa->fpix[n] = fpixc; + fpixa->n++; + return 0; +} + + +/*! + * \brief fpixaExtendArray() + * + * \param[in] fpixa + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) Doubles the size of the fpixa ptr array. + * (2) The max number of fpix ptrs is 100000. + * </pre> + */ +static l_int32 +fpixaExtendArray(FPIXA *fpixa) +{ + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + + return fpixaExtendArrayToSize(fpixa, 2 * fpixa->nalloc); +} + + +/*! + * \brief fpixaExtendArrayToSize() + * + * \param[in] fpixa + * \param[in] size new ptr array size + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) If necessary, reallocs new fpix ptr array to %size. + * (2) The max number of fpix ptrs is 100K. + * </pre> + */ +static l_int32 +fpixaExtendArrayToSize(FPIXA *fpixa, + l_int32 size) +{ +size_t oldsize, newsize; + + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + if (fpixa->nalloc > MaxPtrArraySize) /* belt & suspenders */ + return ERROR_INT("fpixa has too many ptrs", __func__, 1); + if (size > MaxPtrArraySize) + return ERROR_INT("size > 100K ptrs; too large", __func__, 1); + if (size <= fpixa->nalloc) { + L_INFO("size too small; no extension\n", __func__); + return 0; + } + + oldsize = fpixa->nalloc * sizeof(FPIX *); + newsize = size * sizeof(FPIX *); + if ((fpixa->fpix = (FPIX **)reallocNew((void **)&fpixa->fpix, + oldsize, newsize)) == NULL) + return ERROR_INT("new ptr array not returned", __func__, 1); + fpixa->nalloc = size; + return 0; +} + + +/*--------------------------------------------------------------------* + * FPixa accessors * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixaGetCount() + * + * \param[in] fpixa + * \return count, or 0 if no pixa + */ +l_int32 +fpixaGetCount(FPIXA *fpixa) +{ + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 0); + + return fpixa->n; +} + + +/*! + * \brief fpixaGetFPix() + * + * \param[in] fpixa + * \param[in] index to the index-th fpix + * \param[in] accesstype L_COPY or L_CLONE + * \return fpix, or NULL on error + */ +FPIX * +fpixaGetFPix(FPIXA *fpixa, + l_int32 index, + l_int32 accesstype) +{ + if (!fpixa) + return (FPIX *)ERROR_PTR("fpixa not defined", __func__, NULL); + if (index < 0 || index >= fpixa->n) + return (FPIX *)ERROR_PTR("index not valid", __func__, NULL); + + if (accesstype == L_COPY) + return fpixCopy(fpixa->fpix[index]); + else if (accesstype == L_CLONE) + return fpixClone(fpixa->fpix[index]); + else + return (FPIX *)ERROR_PTR("invalid accesstype", __func__, NULL); +} + + +/*! + * \brief fpixaGetFPixDimensions() + * + * \param[in] fpixa + * \param[in] index to the index-th box + * \param[out] pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +fpixaGetFPixDimensions(FPIXA *fpixa, + l_int32 index, + l_int32 *pw, + l_int32 *ph) +{ +FPIX *fpix; + + if (!pw && !ph) + return ERROR_INT("no return val requested", __func__, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + if (index < 0 || index >= fpixa->n) + return ERROR_INT("index not valid", __func__, 1); + + if ((fpix = fpixaGetFPix(fpixa, index, L_CLONE)) == NULL) + return ERROR_INT("fpix not found!", __func__, 1); + fpixGetDimensions(fpix, pw, ph); + fpixDestroy(&fpix); + return 0; +} + + +/*! + * \brief fpixaGetData() + * + * \param[in] fpixa + * \param[in] index into fpixa array + * \return data not a copy, or NULL on error + */ +l_float32 * +fpixaGetData(FPIXA *fpixa, + l_int32 index) +{ +l_int32 n; +l_float32 *data; +FPIX *fpix; + + if (!fpixa) + return (l_float32 *)ERROR_PTR("fpixa not defined", __func__, NULL); + n = fpixaGetCount(fpixa); + if (index < 0 || index >= n) + return (l_float32 *)ERROR_PTR("invalid index", __func__, NULL); + + fpix = fpixaGetFPix(fpixa, index, L_CLONE); + data = fpixGetData(fpix); + fpixDestroy(&fpix); + return data; +} + + +/*! + * \brief fpixaGetPixel() + * + * \param[in] fpixa + * \param[in] index into fpixa array + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 on error + */ +l_ok +fpixaGetPixel(FPIXA *fpixa, + l_int32 index, + l_int32 x, + l_int32 y, + l_float32 *pval) +{ +l_int32 n, ret; +FPIX *fpix; + + if (!pval) + return ERROR_INT("pval not defined", __func__, 1); + *pval = 0.0; + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + n = fpixaGetCount(fpixa); + if (index < 0 || index >= n) + return ERROR_INT("invalid index into fpixa", __func__, 1); + + fpix = fpixaGetFPix(fpixa, index, L_CLONE); + ret = fpixGetPixel(fpix, x, y, pval); + fpixDestroy(&fpix); + return ret; +} + + +/*! + * \brief fpixaSetPixel() + * + * \param[in] fpixa + * \param[in] index into fpixa array + * \param[in] x,y pixel coords + * \param[in] val pixel value + * \return 0 if OK; 1 on error + */ +l_ok +fpixaSetPixel(FPIXA *fpixa, + l_int32 index, + l_int32 x, + l_int32 y, + l_float32 val) +{ +l_int32 n, ret; +FPIX *fpix; + + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + n = fpixaGetCount(fpixa); + if (index < 0 || index >= n) + return ERROR_INT("invalid index into fpixa", __func__, 1); + + fpix = fpixaGetFPix(fpixa, index, L_CLONE); + ret = fpixSetPixel(fpix, x, y, val); + fpixDestroy(&fpix); + return ret; +} + + +/*--------------------------------------------------------------------* + * DPix Create/copy/destroy * + *--------------------------------------------------------------------*/ +/*! + * \brief dpixCreate() + * + * \param[in] width, height + * \return dpix with data allocated and initialized to 0, or NULL on error + * + * <pre> + * Notes: + * (1) Makes a DPix of specified size, with the data array + * allocated and initialized to 0. + * (2) The number of pixels must be less than 2^28. + * </pre> + */ +DPIX * +dpixCreate(l_int32 width, + l_int32 height) +{ +l_float64 *data; +l_uint64 npix64; +DPIX *dpix; + + if (width <= 0) + return (DPIX *)ERROR_PTR("width must be > 0", __func__, NULL); + if (height <= 0) + return (DPIX *)ERROR_PTR("height must be > 0", __func__, NULL); + + /* Avoid overflow in malloc arg, malicious or otherwise */ + npix64 = (l_uint64)width * (l_uint64)height; /* # of 8 byte pixels */ + if (npix64 >= (1LL << 28)) { + L_ERROR("requested w = %d, h = %d\n", __func__, width, height); + return (DPIX *)ERROR_PTR("requested bytes >= 2^31", __func__, NULL); + } + + dpix = (DPIX *)LEPT_CALLOC(1, sizeof(DPIX)); + dpixSetDimensions(dpix, width, height); + dpixSetWpl(dpix, width); /* 8 byte words */ + dpix->refcount = 1; + + data = (l_float64 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float64)); + if (!data) { + dpixDestroy(&dpix); + return (DPIX *)ERROR_PTR("calloc fail for data", __func__, NULL); + } + dpixSetData(dpix, data); + return dpix; +} + + +/*! + * \brief dpixCreateTemplate() + * + * \param[in] dpixs + * \return dpixd, or NULL on error + * + * <pre> + * Notes: + * (1) Makes a DPix of the same size as the input DPix, with the + * data array allocated and initialized to 0. + * (2) Copies the resolution. + * </pre> + */ +DPIX * +dpixCreateTemplate(DPIX *dpixs) +{ +l_int32 w, h; +DPIX *dpixd; + + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", __func__, NULL); + + dpixGetDimensions(dpixs, &w, &h); + dpixd = dpixCreate(w, h); + dpixCopyResolution(dpixd, dpixs); + return dpixd; +} + + +/*! + * \brief dpixClone() + * + * \param[in] dpix + * \return same dpix ptr, or NULL on error + * + * <pre> + * Notes: + * (1) See pixClone() for definition and usage. + * </pre> + */ +DPIX * +dpixClone(DPIX *dpix) +{ + if (!dpix) + return (DPIX *)ERROR_PTR("dpix not defined", __func__, NULL); + ++dpix->refcount; + return dpix; +} + + +/*! + * \brief dpixCopy() + * + * \param[in] dpixs + * \return dpixd, or NULL on error + */ +DPIX * +dpixCopy(DPIX *dpixs) +{ +l_int32 w, h, bytes; +l_float64 *datas, *datad; +DPIX *dpixd; + + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", __func__, NULL); + + /* Total bytes in image data */ + dpixGetDimensions(dpixs, &w, &h); + bytes = 8 * w * h; + + if ((dpixd = dpixCreateTemplate(dpixs)) == NULL) + return (DPIX *)ERROR_PTR("dpixd not made", __func__, NULL); + datas = dpixGetData(dpixs); + datad = dpixGetData(dpixd); + memcpy(datad, datas, bytes); + return dpixd; +} + + +/*! + * \brief dpixDestroy() + * + * \param[in,out] pdpix will be set to null before returning + * \return void + * + * <pre> + * Notes: + * (1) Decrements the ref count and, if 0, destroys the dpix. + * (2) Always nulls the input ptr. + * </pre> + */ +void +dpixDestroy(DPIX **pdpix) +{ +l_float64 *data; +DPIX *dpix; + + if (!pdpix) { + L_WARNING("ptr address is null!\n", __func__); + return; + } + + if ((dpix = *pdpix) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the dpix. */ + if (--dpix->refcount == 0) { + if ((data = dpixGetData(dpix)) != NULL) + LEPT_FREE(data); + LEPT_FREE(dpix); + } + *pdpix = NULL; +} + + +/*--------------------------------------------------------------------* + * DPix Accessors * + *--------------------------------------------------------------------*/ +/*! + * \brief dpixGetDimensions() + * + * \param[in] dpix + * \param[out] pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +dpixGetDimensions(DPIX *dpix, + l_int32 *pw, + l_int32 *ph) +{ + if (!pw && !ph) + return ERROR_INT("no return val requested", __func__, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + if (pw) *pw = dpix->w; + if (ph) *ph = dpix->h; + return 0; +} + + +/*! + * \brief dpixSetDimensions() + * + * \param[in] dpix + * \param[in] w, h + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetDimensions(DPIX *dpix, + l_int32 w, + l_int32 h) +{ + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + dpix->w = w; + dpix->h = h; + return 0; +} + + +/*! + * \brief dpixGetWpl() + * + * \param[in] dpix + * \return wpl, or 0 on error + */ +l_int32 +dpixGetWpl(DPIX *dpix) +{ + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 0); + return dpix->wpl; +} + + +/*! + * \brief dpixSetWpl() + * + * \param[in] dpix + * \param[in] wpl + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetWpl(DPIX *dpix, + l_int32 wpl) +{ + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + + dpix->wpl = wpl; + return 0; +} + + +/*! + * \brief dpixGetResolution() + * + * \param[in] dpix + * \param[out] pxres, pyres [optional] x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +dpixGetResolution(DPIX *dpix, + l_int32 *pxres, + l_int32 *pyres) +{ + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + if (pxres) *pxres = dpix->xres; + if (pyres) *pyres = dpix->yres; + return 0; +} + + +/*! + * \brief dpixSetResolution() + * + * \param[in] dpix + * \param[in] xres, yres x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetResolution(DPIX *dpix, + l_int32 xres, + l_int32 yres) +{ + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + + dpix->xres = xres; + dpix->yres = yres; + return 0; +} + + +/*! + * \brief dpixCopyResolution() + * + * \param[in] dpixd, dpixs + * \return 0 if OK, 1 on error + */ +l_ok +dpixCopyResolution(DPIX *dpixd, + DPIX *dpixs) +{ +l_int32 xres, yres; + if (!dpixs || !dpixd) + return ERROR_INT("dpixs and dpixd not both defined", __func__, 1); + + dpixGetResolution(dpixs, &xres, &yres); + dpixSetResolution(dpixd, xres, yres); + return 0; +} + + +/*! + * \brief dpixGetData() + * + * \param[in] dpix + * \return ptr to dpix data, or NULL on error + */ +l_float64 * +dpixGetData(DPIX *dpix) +{ + if (!dpix) + return (l_float64 *)ERROR_PTR("dpix not defined", __func__, NULL); + return dpix->data; +} + + +/*! + * \brief dpixSetData() + * + * \param[in] dpix + * \param[in] data + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetData(DPIX *dpix, + l_float64 *data) +{ + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + + dpix->data = data; + return 0; +} + + +/*! + * \brief dpixGetPixel() + * + * \param[in] dpix + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +dpixGetPixel(DPIX *dpix, + l_int32 x, + l_int32 y, + l_float64 *pval) +{ +l_int32 w, h; + + if (!pval) + return ERROR_INT("pval not defined", __func__, 1); + *pval = 0.0; + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + + dpixGetDimensions(dpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *pval = *(dpix->data + y * w + x); + return 0; +} + + +/*! + * \brief dpixSetPixel() + * + * \param[in] dpix + * \param[in] x,y pixel coords + * \param[in] val pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +dpixSetPixel(DPIX *dpix, + l_int32 x, + l_int32 y, + l_float64 val) +{ +l_int32 w, h; + + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + + dpixGetDimensions(dpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *(dpix->data + y * w + x) = val; + return 0; +} + + +/*--------------------------------------------------------------------* + * FPix serialized I/O * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixRead() + * + * \param[in] filename + * \return fpix, or NULL on error + */ +FPIX * +fpixRead(const char *filename) +{ +FILE *fp; +FPIX *fpix; + + if (!filename) + return (FPIX *)ERROR_PTR("filename not defined", __func__, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (FPIX *)ERROR_PTR_1("stream not opened", + filename, __func__, NULL); + fpix = fpixReadStream(fp); + fclose(fp); + if (!fpix) + return (FPIX *)ERROR_PTR_1("fpix not read", filename, __func__, NULL); + return fpix; +} + + +/*! + * \brief fpixReadStream() + * + * \param[in] fp file stream + * \return fpix, or NULL on error + */ +FPIX * +fpixReadStream(FILE *fp) +{ +char buf[256]; +l_int32 w, h, nbytes, xres, yres, version; +l_float32 *data; +FPIX *fpix; + + if (!fp) + return (FPIX *)ERROR_PTR("stream not defined", __func__, NULL); + + if (fscanf(fp, "\nFPix Version %d\n", &version) != 1) + return (FPIX *)ERROR_PTR("not a fpix file", __func__, NULL); + if (version != FPIX_VERSION_NUMBER) + return (FPIX *)ERROR_PTR("invalid fpix version", __func__, NULL); + if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3) + return (FPIX *)ERROR_PTR("read fail for data size", __func__, NULL); + + /* Use fgets() and sscanf(); not fscanf(), for the last + * bit of header data before the float data. The reason is + * that fscanf throws away white space, and if the float data + * happens to begin with ascii character(s) that are white + * space, it will swallow them and all will be lost! */ + if (fgets(buf, sizeof(buf), fp) == NULL) + return (FPIX *)ERROR_PTR("fgets read fail", __func__, NULL); + if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2) + return (FPIX *)ERROR_PTR("read fail for xres, yres", __func__, NULL); + + if ((fpix = fpixCreate(w, h)) == NULL) + return (FPIX *)ERROR_PTR("fpix not made", __func__, NULL); + fpixSetResolution(fpix, xres, yres); + data = fpixGetData(fpix); + if (fread(data, 1, nbytes, fp) != nbytes) { + fpixDestroy(&fpix); + return (FPIX *)ERROR_PTR("read error for nbytes", __func__, NULL); + } + fgetc(fp); /* ending nl */ + + /* Convert to little-endian if necessary */ + fpixEndianByteSwap(fpix, fpix); + return fpix; +} + + +/*! + * \brief fpixReadMem() + * + * \param[in] data of serialized fpix + * \param[in] size of data in bytes + * \return fpix, or NULL on error + */ +FPIX * +fpixReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +FPIX *fpix; + + if (!data) + return (FPIX *)ERROR_PTR("data not defined", __func__, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (FPIX *)ERROR_PTR("stream not opened", __func__, NULL); + + fpix = fpixReadStream(fp); + fclose(fp); + if (!fpix) L_ERROR("fpix not read\n", __func__); + return fpix; +} + + +/*! + * \brief fpixWrite() + * + * \param[in] filename + * \param[in] fpix + * \return 0 if OK, 1 on error + */ +l_ok +fpixWrite(const char *filename, + FPIX *fpix) +{ +l_int32 ret; +FILE *fp; + + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT_1("stream not opened", filename, __func__, 1); + ret = fpixWriteStream(fp, fpix); + fclose(fp); + if (ret) + return ERROR_INT_1("fpix not written to stream", filename, __func__, 1); + return 0; +} + + +/*! + * \brief fpixWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] fpix + * \return 0 if OK, 1 on error + */ +l_ok +fpixWriteStream(FILE *fp, + FPIX *fpix) +{ +l_int32 w, h, xres, yres; +l_uint32 nbytes; +l_float32 *data; +FPIX *fpixt; + + if (!fp) + return ERROR_INT("stream not defined", __func__, 1); + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + + /* Convert to little-endian if necessary */ + fpixt = fpixEndianByteSwap(NULL, fpix); + + fpixGetDimensions(fpixt, &w, &h); + data = fpixGetData(fpixt); + nbytes = sizeof(l_float32) * w * h; + fpixGetResolution(fpixt, &xres, &yres); + fprintf(fp, "\nFPix Version %d\n", FPIX_VERSION_NUMBER); + fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes); + fprintf(fp, "xres = %d, yres = %d\n", xres, yres); + fwrite(data, 1, nbytes, fp); + fprintf(fp, "\n"); + + fpixDestroy(&fpixt); + return 0; +} + + +/*! + * \brief fpixWriteMem() + * + * \param[out] pdata data of serialized fpix + * \param[out] psize size of returned data + * \param[in] fpix + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Serializes a fpix in memory and puts the result in a buffer. + * </pre> + */ +l_ok +fpixWriteMem(l_uint8 **pdata, + size_t *psize, + FPIX *fpix) +{ +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 (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", __func__, 1); + ret = fpixWriteStream(fp, fpix); + 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 = fpixWriteStream(fp, fpix); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); + fclose(fp); +#endif /* HAVE_FMEMOPEN */ + return ret; +} + + +/*! + * \brief fpixEndianByteSwap() + * + * \param[in] fpixd [optional] can be either NULL, or equal to fpixs + * \param[in] fpixs + * \return fpixd always + * + * <pre> + * Notes: + * (1) On big-endian hardware, this does byte-swapping on each of + * the 4-byte floats in the fpix data. On little-endians, + * the data is unchanged. This is used for serialization + * of fpix; the data is serialized in little-endian byte + * order because most hardware is little-endian. + * (2) The operation can be either in-place or, if fpixd == NULL, + * a new fpix is made. If not in-place, caller must catch + * the returned pointer. + * </pre> + */ +FPIX * +fpixEndianByteSwap(FPIX *fpixd, + FPIX *fpixs) +{ + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", __func__, fpixd); + if (fpixd && (fpixs != fpixd)) + return (FPIX *)ERROR_PTR("fpixd != fpixs", __func__, fpixd); + +#ifdef L_BIG_ENDIAN + { + l_uint32 *data; + l_int32 i, j, w, h; + l_uint32 word; + + fpixGetDimensions(fpixs, &w, &h); + if (!fpixd) + fpixd = fpixCopy(fpixs); + + data = (l_uint32 *)fpixGetData(fpixd); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++, data++) { + word = *data; + *data = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + return fpixd; + } +#else /* L_LITTLE_ENDIAN */ + + if (fpixd) + return fpixd; /* no-op */ + else + return fpixClone(fpixs); + +#endif /* L_BIG_ENDIAN */ +} + + +/*--------------------------------------------------------------------* + * DPix serialized I/O * + *--------------------------------------------------------------------*/ +/*! + * \brief dpixRead() + * + * \param[in] filename + * \return dpix, or NULL on error + */ +DPIX * +dpixRead(const char *filename) +{ +FILE *fp; +DPIX *dpix; + + if (!filename) + return (DPIX *)ERROR_PTR("filename not defined", __func__, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (DPIX *)ERROR_PTR_1("stream not opened", + filename, __func__, NULL); + dpix = dpixReadStream(fp); + fclose(fp); + if (!dpix) + return (DPIX *)ERROR_PTR_1("dpix not read", filename, __func__, NULL); + return dpix; +} + + +/*! + * \brief dpixReadStream() + * + * \param[in] fp file stream + * \return dpix, or NULL on error + */ +DPIX * +dpixReadStream(FILE *fp) +{ +char buf[256]; +l_int32 w, h, nbytes, version, xres, yres; +l_float64 *data; +DPIX *dpix; + + if (!fp) + return (DPIX *)ERROR_PTR("stream not defined", __func__, NULL); + + if (fscanf(fp, "\nDPix Version %d\n", &version) != 1) + return (DPIX *)ERROR_PTR("not a dpix file", __func__, NULL); + if (version != DPIX_VERSION_NUMBER) + return (DPIX *)ERROR_PTR("invalid dpix version", __func__, NULL); + if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3) + return (DPIX *)ERROR_PTR("read fail for data size", __func__, NULL); + + /* Use fgets() and sscanf(); not fscanf(), for the last + * bit of header data before the float data. The reason is + * that fscanf throws away white space, and if the float data + * happens to begin with ascii character(s) that are white + * space, it will swallow them and all will be lost! */ + if (fgets(buf, sizeof(buf), fp) == NULL) + return (DPIX *)ERROR_PTR("fgets read fail", __func__, NULL); + if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2) + return (DPIX *)ERROR_PTR("read fail for xres, yres", __func__, NULL); + + if ((dpix = dpixCreate(w, h)) == NULL) + return (DPIX *)ERROR_PTR("dpix not made", __func__, NULL); + dpixSetResolution(dpix, xres, yres); + data = dpixGetData(dpix); + if (fread(data, 1, nbytes, fp) != nbytes) { + dpixDestroy(&dpix); + return (DPIX *)ERROR_PTR("read error for nbytes", __func__, NULL); + } + fgetc(fp); /* ending nl */ + + /* Convert to little-endian if necessary */ + dpixEndianByteSwap(dpix, dpix); + return dpix; +} + + +/*! + * \brief dpixReadMem() + * + * \param[in] data of serialized dpix + * \param[in] size of data in bytes + * \return dpix, or NULL on error + */ +DPIX * +dpixReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +DPIX *dpix; + + if (!data) + return (DPIX *)ERROR_PTR("data not defined", __func__, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (DPIX *)ERROR_PTR("stream not opened", __func__, NULL); + + dpix = dpixReadStream(fp); + fclose(fp); + if (!dpix) L_ERROR("dpix not read\n", __func__); + return dpix; +} + + +/*! + * \brief dpixWrite() + * + * \param[in] filename + * \param[in] dpix + * \return 0 if OK, 1 on error + */ +l_ok +dpixWrite(const char *filename, + DPIX *dpix) +{ +l_int32 ret; +FILE *fp; + + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT_1("stream not opened", filename, __func__, 1); + ret = dpixWriteStream(fp, dpix); + fclose(fp); + if (ret) + return ERROR_INT_1("dpix not written to stream", filename, __func__, 1); + return 0; +} + + +/*! + * \brief dpixWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] dpix + * \return 0 if OK, 1 on error + */ +l_ok +dpixWriteStream(FILE *fp, + DPIX *dpix) +{ +l_int32 w, h, xres, yres; +l_uint32 nbytes; +l_float64 *data; +DPIX *dpixt; + + if (!fp) + return ERROR_INT("stream not defined", __func__, 1); + if (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + + /* Convert to little-endian if necessary */ + dpixt = dpixEndianByteSwap(NULL, dpix); + + dpixGetDimensions(dpixt, &w, &h); + dpixGetResolution(dpixt, &xres, &yres); + data = dpixGetData(dpixt); + nbytes = sizeof(l_float64) * w * h; + fprintf(fp, "\nDPix Version %d\n", DPIX_VERSION_NUMBER); + fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes); + fprintf(fp, "xres = %d, yres = %d\n", xres, yres); + fwrite(data, 1, nbytes, fp); + fprintf(fp, "\n"); + + dpixDestroy(&dpixt); + return 0; +} + + +/*! + * \brief dpixWriteMem() + * + * \param[out] pdata data of serialized dpix + * \param[out] psize size of returned data + * \param[in] dpix + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Serializes a dpix in memory and puts the result in a buffer. + * </pre> + */ +l_ok +dpixWriteMem(l_uint8 **pdata, + size_t *psize, + DPIX *dpix) +{ +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 (!dpix) + return ERROR_INT("dpix not defined", __func__, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", __func__, 1); + ret = dpixWriteStream(fp, dpix); + fputc('\0', fp); + fclose(fp); + *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 = dpixWriteStream(fp, dpix); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); + fclose(fp); +#endif /* HAVE_FMEMOPEN */ + return ret; +} + + +/*! + * \brief dpixEndianByteSwap() + * + * \param[in] dpixd [optional] can be either NULL, or equal to dpixs + * \param[in] dpixs + * \return dpixd always + * + * <pre> + * Notes: + * (1) On big-endian hardware, this does byte-swapping on each of + * the 4-byte words in the dpix data. On little-endians, + * the data is unchanged. This is used for serialization + * of dpix; the data is serialized in little-endian byte + * order because most hardware is little-endian. + * (2) The operation can be either in-place or, if dpixd == NULL, + * a new dpix is made. If not in-place, caller must catch + * the returned pointer. + * </pre> + */ +DPIX * +dpixEndianByteSwap(DPIX *dpixd, + DPIX *dpixs) +{ + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", __func__, dpixd); + if (dpixd && (dpixs != dpixd)) + return (DPIX *)ERROR_PTR("dpixd != dpixs", __func__, dpixd); + +#ifdef L_BIG_ENDIAN + { + l_uint32 *data; + l_int32 i, j, w, h; + l_uint32 word; + + dpixGetDimensions(dpixs, &w, &h); + if (!dpixd) + dpixd = dpixCopy(dpixs); + + data = (l_uint32 *)dpixGetData(dpixd); + for (i = 0; i < h; i++) { + for (j = 0; j < 2 * w; j++, data++) { + word = *data; + *data = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + return dpixd; + } +#else /* L_LITTLE_ENDIAN */ + + if (dpixd) + return dpixd; /* no-op */ + else + return dpixClone(dpixs); + +#endif /* L_BIG_ENDIAN */ +} + + +/*--------------------------------------------------------------------* + * Print FPix (subsampled, for debugging) * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixPrintStream() + * + * \param[in] fp file stream + * \param[in] fpix + * \param[in] factor for subsampling + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Subsampled printout of fpix for debugging. + * </pre> + */ +l_ok +fpixPrintStream(FILE *fp, + FPIX *fpix, + l_int32 factor) +{ +l_int32 i, j, w, h, count; +l_float32 val; + + if (!fp) + return ERROR_INT("stream not defined", __func__, 1); + if (!fpix) + return ERROR_INT("fpix not defined", __func__, 1); + if (factor < 1) + return ERROR_INT("sampling factor < 1f", __func__, 1); + + fpixGetDimensions(fpix, &w, &h); + fprintf(fp, "\nFPix: w = %d, h = %d\n", w, h); + for (i = 0; i < h; i += factor) { + for (count = 0, j = 0; j < w; j += factor, count++) { + fpixGetPixel(fpix, j, i, &val); + fprintf(fp, "val[%d, %d] = %f ", i, j, val); + if ((count + 1) % 3 == 0) fprintf(fp, "\n"); + } + if (count % 3) fprintf(fp, "\n"); + } + fprintf(fp, "\n"); + return 0; +}
