/*====================================================================* - 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 pix1.c *
* * The pixN.c {N = 1,2,3,4,5} files are sorted by the type of operation. * The primary functions in these files are: * * pix1.c: constructors, destructors and field accessors * pix2.c: pixel poking of image, pad and border pixels * pix3.c: masking and logical ops, counting, mirrored tiling * pix4.c: histograms, statistics, fg/bg estimation * pix5.c: property measurements, rectangle extraction * * * This file has the basic constructors, destructors and field accessors * * Pix memory management (allows custom allocator and deallocator) * static void *pix_malloc() * static void pix_free() * void setPixMemoryManager() * * Pix creation * PIX *pixCreate() * PIX *pixCreateNoInit() * PIX *pixCreateTemplate() * PIX *pixCreateTemplateNoInit() * PIX *pixCreateHeader() * PIX *pixClone() * * Pix destruction * void pixDestroy() * static void pixFree() * * Pix copy * PIX *pixCopy() * l_int32 pixResizeImageData() * l_int32 pixCopyColormap() * l_int32 pixSizesEqual() * l_int32 pixTransferAllData() * l_int32 pixSwapAndDestroy() * * Pix accessors * l_int32 pixGetWidth() * l_int32 pixSetWidth() * l_int32 pixGetHeight() * l_int32 pixSetHeight() * l_int32 pixGetDepth() * l_int32 pixSetDepth() * l_int32 pixGetDimensions() * l_int32 pixSetDimensions() * l_int32 pixCopyDimensions() * l_int32 pixGetSpp() * l_int32 pixSetSpp() * l_int32 pixCopySpp() * l_int32 pixGetWpl() * l_int32 pixSetWpl() * l_int32 pixGetRefcount() * l_int32 pixChangeRefcount() * l_uint32 pixGetXRes() * l_int32 pixSetXRes() * l_uint32 pixGetYRes() * l_int32 pixSetYRes() * l_int32 pixGetResolution() * l_int32 pixSetResolution() * l_int32 pixCopyResolution() * l_int32 pixScaleResolution() * l_int32 pixGetInputFormat() * l_int32 pixSetInputFormat() * l_int32 pixCopyInputFormat() * l_int32 pixSetSpecial() * char *pixGetText() * l_int32 pixSetText() * l_int32 pixAddText() * l_int32 pixCopyText() * PIXCMAP *pixGetColormap() * l_int32 pixSetColormap() * l_int32 pixDestroyColormap() * l_uint32 *pixGetData() * l_int32 pixSetData() * l_uint32 *pixExtractData() * l_int32 pixFreeData() * * Pix line ptrs * void **pixGetLinePtrs() * * Pix debug * l_int32 pixPrintStreamInfo() * * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * Important notes on direct management of pix image data * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * Custom allocator and deallocator * -------------------------------- * * At the lowest level, you can specify the function that does the * allocation and deallocation of the data field in the pix. * By default, this is malloc and free. However, by calling * setPixMemoryManager(), custom functions can be substituted. * When using this, keep two things in mind: * * (1) Call setPixMemoryManager() before any pix have been allocated * (2) Destroy all pix as usual, in order to prevent leaks. * * In pixalloc.c, we provide an example custom allocator and deallocator. * To use it, you must call pmsCreate() before any pix have been allocated * and pmsDestroy() at the end after all pix have been destroyed. * * * Direct manipulation of the pix data field * ----------------------------------------- * * Memory management of the (image) data field in the pix is * handled differently from that in the colormap or text fields. * For colormap and text, the functions pixSetColormap() and * pixSetText() remove the existing heap data and insert the * new data. For the image data, pixSetData() just reassigns the * data field; any existing data will be lost if there isn't * another handle for it. * * Why is pixSetData() limited in this way? Because the image * data can be very large, we need flexible ways to handle it, * particularly when you want to re-use the data in a different * context without making a copy. Here are some different * things you might want to do: * * (1) Use pixCopy(pixd, pixs) where pixd is not the same size * as pixs. This will remove the data in pixd, allocate a * new data field in pixd, and copy the data from pixs, leaving * pixs unchanged. * * (2) Use pixTransferAllData(pixd, &pixs, ...) to transfer the * data from pixs to pixd without making a copy of it. If * pixs is not cloned, this will do the transfer and destroy pixs. * But if the refcount of pixs is greater than 1, it just copies * the data and decrements the ref count. * * (3) Use pixSwapAndDestroy(pixd, &pixs) to replace pixs by an * existing pixd. This is similar to pixTransferAllData(), but * simpler, in that it never makes any copies and if pixs is * cloned, the other references are not changed by this operation. * * (4) Use pixExtractData() to extract the image data from the pix * without copying if possible. This could be used, for example, * to convert from a pix to some other data structure with minimal * heap allocation. After the data is extracated, the pixels can * be munged and used in another context. However, the danger * here is that the pix might have a refcount > 1, in which case * a copy of the data must be made and the input pix left unchanged. * If there are no clones, the image data can be extracted without * a copy, and the data ptr in the pix must be nulled before * destroying it because the pix will no longer 'own' the data. * * We have provided accessors and functions here that should be * sufficient so that you can do anything you want without * explicitly referencing any of the pix member fields. * * However, to avoid memory smashes and leaks when doing special operations * on the pix data field, look carefully at the behavior of the image * data accessors and keep in mind that when you invoke pixDestroy(), * the pix considers itself the owner of all its heap data. **/ #include
* Notes: * (1) The allocator and deallocator function types, * alloc_fn and dealloc_fn, are defined in pix.h. **/ struct PixMemoryManager { alloc_fn allocator; dealloc_fn deallocator; }; /*! Default Pix memory manager */ static struct PixMemoryManager pix_mem_manager = { &malloc, &free }; static void * pix_malloc(size_t size) { #ifndef _MSC_VER return (*pix_mem_manager.allocator)(size); #else /* _MSC_VER */ /* Under MSVC++, pix_mem_manager is initialized after a call * to pix_malloc. Just ignore the custom allocator feature. */ return malloc(size); #endif /* _MSC_VER */ } static void pix_free(void *ptr) { #ifndef _MSC_VER (*pix_mem_manager.deallocator)(ptr); return; #else /* _MSC_VER */ /* Under MSVC++, pix_mem_manager is initialized after a call * to pix_malloc. Just ignore the custom allocator feature. */ free(ptr); return; #endif /* _MSC_VER */ } /*! * \brief setPixMemoryManager() * * \param[in] allocator [optional] use NULL to skip * \param[in] deallocator [optional] use NULL to skip * \return void * *
* Notes: * (1) Use this to change the alloc and/or dealloc functions; * e.g., setPixMemoryManager(my_malloc, my_free). * (2) The C99 standard (section 6.7.5.3, par. 8) says: * A declaration of a parameter as "function returning type" * shall be adjusted to "pointer to function returning type" * so that it can be in either of these two forms: * (a) type (function-ptr(type, ...)) * (b) type ((*function-ptr)(type, ...)) * because form (a) is implictly converted to form (b), as in the * definition of struct PixMemoryManager above. So, for example, * we should be able to declare either of these: * (a) void *(allocator(size_t)) * (b) void *((*allocator)(size_t)) * However, MSVC++ only accepts the second version. **/ void setPixMemoryManager(alloc_fn allocator, dealloc_fn deallocator) { if (allocator) pix_mem_manager.allocator = allocator; if (deallocator) pix_mem_manager.deallocator = deallocator; return; } /*--------------------------------------------------------------------* * Pix Creation * *--------------------------------------------------------------------*/ /*! * \brief pixCreate() * * \param[in] width, height, depth * \return pixd with data allocated and initialized to 0, * or NULL on error */ PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth) { PIX *pixd; PROCNAME("pixCreate"); if ((pixd = pixCreateNoInit(width, height, depth)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h); return pixd; } /*! * \brief pixCreateNoInit() * * \param[in] width, height, depth * \return pixd with data allocated but not initialized, * or NULL on error * *
* Notes: * (1) Must set pad bits to avoid reading uninitialized data, because * some optimized routines (e.g., pixConnComp()) read from pad bits. **/ PIX * pixCreateNoInit(l_int32 width, l_int32 height, l_int32 depth) { l_int32 wpl; PIX *pixd; l_uint32 *data; PROCNAME("pixCreateNoInit"); if ((pixd = pixCreateHeader(width, height, depth)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); wpl = pixGetWpl(pixd); if ((data = (l_uint32 *)pix_malloc(4LL * wpl * height)) == NULL) { pixDestroy(&pixd); return (PIX *)ERROR_PTR("pix_malloc fail for data", procName, NULL); } pixSetData(pixd, data); pixSetPadBits(pixd, 0); return pixd; } /*! * \brief pixCreateTemplate() * * \param[in] pixs * \return pixd, or NULL on error * *
* Notes: * (1) Makes a Pix of the same size as the input Pix, with the * data array allocated and initialized to 0. * (2) Copies the other fields, including colormap if it exists. **/ PIX * pixCreateTemplate(const PIX *pixs) { PIX *pixd; PROCNAME("pixCreateTemplate"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h); return pixd; } /*! * \brief pixCreateTemplateNoInit() * * \param[in] pixs * \return pixd, or NULL on error * *
* Notes: * (1) Makes a Pix of the same size as the input Pix, with * the data array allocated but not initialized to 0. * (2) Copies the other fields, including colormap if it exists. **/ PIX * pixCreateTemplateNoInit(const PIX *pixs) { l_int32 w, h, d; PIX *pixd; PROCNAME("pixCreateTemplateNoInit"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if ((pixd = pixCreateNoInit(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopySpp(pixd, pixs); pixCopyResolution(pixd, pixs); pixCopyColormap(pixd, pixs); pixCopyText(pixd, pixs); pixCopyInputFormat(pixd, pixs); return pixd; } /*! * \brief pixCreateHeader() * * \param[in] width, height, depth * \return pixd with no data allocated, or NULL on error * *
* Notes: * (1) It is assumed that all 32 bit pix have 3 spp. If there is * a valid alpha channel, this will be set to 4 spp later. * (2) All pixCreate*() functions call pixCreateHeader(). If the number of bytes to be allocated is larger than the * maximum value in an int32, we can get overflow, resulting * in a smaller amount of memory actually being allocated. * Later, an attempt to access memory that wasn't allocated will * cause a crash. So to avoid crashing a program (or worse) * with bad (or malicious) input, we limit the requested * allocation of image data in a typesafe way. **/ PIX * pixCreateHeader(l_int32 width, l_int32 height, l_int32 depth) { l_int32 wpl; l_uint64 wpl64, bignum; PIX *pixd; PROCNAME("pixCreateHeader"); if ((depth != 1) && (depth != 2) && (depth != 4) && (depth != 8) && (depth != 16) && (depth != 24) && (depth != 32)) return (PIX *)ERROR_PTR("depth must be {1, 2, 4, 8, 16, 24, 32}", procName, NULL); if (width <= 0) return (PIX *)ERROR_PTR("width must be > 0", procName, NULL); if (height <= 0) return (PIX *)ERROR_PTR("height must be > 0", procName, NULL); /* Avoid overflow in malloc, malicious or otherwise */ wpl64 = ((l_uint64)width * (l_uint64)depth + 31) / 32; if (wpl64 > ((1LL << 29) - 1)) { L_ERROR("requested w = %d, h = %d, d = %d\n", procName, width, height, depth); return (PIX *)ERROR_PTR("wpl >= 2^29", procName, NULL); } wpl = (l_int32)wpl64; bignum = 4LL * wpl * height; /* number of bytes to be requested */ if (bignum > ((1LL << 31) - 1)) { L_ERROR("requested w = %d, h = %d, d = %d\n", procName, width, height, depth); return (PIX *)ERROR_PTR("requested bytes >= 2^31", procName, NULL); } pixd = (PIX *)LEPT_CALLOC(1, sizeof(PIX)); pixSetWidth(pixd, width); pixSetHeight(pixd, height); pixSetDepth(pixd, depth); pixSetWpl(pixd, wpl); if (depth == 24 || depth == 32) pixSetSpp(pixd, 3); else pixSetSpp(pixd, 1); pixd->refcount = 1; pixd->informat = IFF_UNKNOWN; return pixd; } /*! * \brief pixClone() * * \param[in] pixs * \return same pix ptr, or NULL on error * *
* Notes: * (1) A "clone" is simply a handle (ptr) to an existing pix. * It is implemented because (a) images can be large and * hence expensive to copy, and (b) extra handles to a data * structure need to be made with a simple policy to avoid * both double frees and memory leaks. Pix are reference * counted. The side effect of pixClone() is an increase * by 1 in the ref count. * (2) The protocol to be used is: * (a) Whenever you want a new handle to an existing image, * call pixClone(), which just bumps a ref count. * (b) Always call pixDestroy() on all handles. This * decrements the ref count, nulls the handle, and * only destroys the pix when pixDestroy() has been * called on all handles. **/ PIX * pixClone(PIX *pixs) { PROCNAME("pixClone"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixChangeRefcount(pixs, 1); return pixs; } /*--------------------------------------------------------------------* * Pix Destruction * *--------------------------------------------------------------------*/ /*! * \brief pixDestroy() * * \param[in,out] ppix will be set to null before returning * \return void * *
* Notes: * (1) Decrements the ref count and, if 0, destroys the pix. * (2) Always nulls the input ptr. **/ void pixDestroy(PIX **ppix) { PIX *pix; PROCNAME("pixDestroy"); if (!ppix) { L_WARNING("ptr address is null!\n", procName); return; } if ((pix = *ppix) == NULL) return; pixFree(pix); *ppix = NULL; return; } /*! * \brief pixFree() * * \param[in] pix * \return void * *
* Notes: * (1) Decrements the ref count and, if 0, destroys the pix. **/ static void pixFree(PIX *pix) { l_uint32 *data; char *text; if (!pix) return; pixChangeRefcount(pix, -1); if (pixGetRefcount(pix) <= 0) { if ((data = pixGetData(pix)) != NULL) pix_free(data); if ((text = pixGetText(pix)) != NULL) LEPT_FREE(text); pixDestroyColormap(pix); LEPT_FREE(pix); } return; } /*-------------------------------------------------------------------------* * Pix Copy * *-------------------------------------------------------------------------*/ /*! * \brief pixCopy() * * \param[in] pixd [optional] can be null, equal to pixs, * different from pixs * \param[in] pixs * \return pixd, or NULL on error * *
* Notes: * (1) There are three cases: * (a) pixd == null (makes a new pix; refcount = 1) * (b) pixd == pixs (no-op) * (c) pixd != pixs (data copy; no change in refcount) * If the refcount of pixd > 1, case (c) will side-effect * these handles. * (2) The general pattern of use is: * pixd = pixCopy(pixd, pixs); * This will work for all three cases. * For clarity when the case is known, you can use: * (a) pixd = pixCopy(NULL, pixs); * (c) pixCopy(pixd, pixs); * (3) For case (c), we check if pixs and pixd are the same * size (w,h,d). If so, the data is copied directly. * Otherwise, the data is reallocated to the correct size * and the copy proceeds. The refcount of pixd is unchanged. * (4) This operation, like all others that may involve a pre-existing * pixd, will side-effect any existing clones of pixd. **/ PIX * pixCopy(PIX *pixd, /* can be null */ const PIX *pixs) { l_int32 bytes; PROCNAME("pixCopy"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); if (pixs == pixd) return pixd; /* Total bytes in image data */ bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); /* If we're making a new pix ... */ if (!pixd) { if ((pixd = pixCreateTemplate(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); memcpy(pixd->data, pixs->data, bytes); return pixd; } /* Reallocate image data if sizes are different. If this fails, * pixd hasn't been changed. But we want to signal that the copy * failed, so return NULL. This will cause a memory leak if the * return ptr is assigned to pixd, but that is preferred to proceeding * with an incorrect pixd, and in any event this use case of * pixCopy() -- reallocating into an existing pix -- is infrequent. */ if (pixResizeImageData(pixd, pixs) == 1) return (PIX *)ERROR_PTR("reallocation of data failed", procName, NULL); /* Copy non-image data fields */ pixCopyColormap(pixd, pixs); pixCopySpp(pixd, pixs); pixCopyResolution(pixd, pixs); pixCopyInputFormat(pixd, pixs); pixCopyText(pixd, pixs); /* Copy image data */ memcpy(pixd->data, pixs->data, bytes); return pixd; } /*! * \brief pixResizeImageData() * * \param[in] pixd gets new uninitialized buffer for image data * \param[in] pixs determines the size of the buffer; not changed * \return 0 if OK, 1 on error * *
* Notes: * (1) If the sizes of data in pixs and pixd are unequal, this * frees the existing image data in pixd and allocates * an uninitialized buffer that will hold the required amount * of image data in pixs. The image data from pixs is not * copied into the new buffer. * (2) On failure to allocate, pixd is unchanged. **/ l_ok pixResizeImageData(PIX *pixd, const PIX *pixs) { l_int32 w, h, d, wpl, bytes; l_uint32 *data; PROCNAME("pixResizeImageData"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixSizesEqual(pixs, pixd)) /* nothing to do */ return 0; /* Make sure we can copy the data */ pixGetDimensions(pixs, &w, &h, &d); wpl = pixGetWpl(pixs); bytes = 4 * wpl * h; if ((data = (l_uint32 *)pix_malloc(bytes)) == NULL) return ERROR_INT("pix_malloc fail for data", procName, 1); /* OK, do it */ pixSetWidth(pixd, w); pixSetHeight(pixd, h); pixSetDepth(pixd, d); pixSetWpl(pixd, wpl); pixFreeData(pixd); /* free any existing image data */ pixSetData(pixd, data); /* set the uninitialized memory buffer */ pixCopyResolution(pixd, pixs); return 0; } /*! * \brief pixCopyColormap() * * \param[in] pixd * \param[in] pixs copies the colormap to %pixd * \return 0 if OK, 1 on error * *
* Notes: * (1) This destroys the colormap in pixd, unless the operation is a no-op **/ l_ok pixCopyColormap(PIX *pixd, const PIX *pixs) { l_int32 valid; const PIXCMAP *cmaps; PIXCMAP *cmapd; PROCNAME("pixCopyColormap"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixDestroyColormap(pixd); if ((cmaps = pixs->colormap) == NULL) /* not an error */ return 0; pixcmapIsValid(cmaps, &valid); if (!valid) return ERROR_INT("cmap not valid", procName, 1); if ((cmapd = pixcmapCopy(cmaps)) == NULL) return ERROR_INT("cmapd not made", procName, 1); pixSetColormap(pixd, cmapd); return 0; } /*! * \brief pixSizesEqual() * * \param[in] pix1, pix2 * \return 1 if the two pix have same {h, w, d}; 0 otherwise. */ l_int32 pixSizesEqual(const PIX *pix1, const PIX *pix2) { PROCNAME("pixSizesEqual"); if (!pix1 || !pix2) return ERROR_INT("pix1 and pix2 not both defined", procName, 0); if (pix1 == pix2) return 1; if ((pixGetWidth(pix1) != pixGetWidth(pix2)) || (pixGetHeight(pix1) != pixGetHeight(pix2)) || (pixGetDepth(pix1) != pixGetDepth(pix2))) return 0; else return 1; } /*! * \brief pixTransferAllData() * * \param[in] pixd must be different from pixs * \param[in,out] ppixs will be nulled if refcount goes to 0 * \param[in] copytext 1 to copy the text field; 0 to skip * \param[in] copyformat 1 to copy the informat field; 0 to skip * \return 0 if OK, 1 on error * *
* Notes: * (1) This does a complete data transfer from pixs to pixd, * followed by the destruction of pixs (refcount permitting). * (2) If the refcount of pixs is 1, pixs is destroyed. Otherwise, * the data in pixs is copied (rather than transferred) to pixd. * (3) This operation, like all others with a pre-existing pixd, * will side-effect any existing clones of pixd. The pixd * refcount does not change. * (4) When might you use this? Suppose you have an in-place Pix * function (returning void) with the typical signature: * void function-inplace(PIX *pix, ...) * where "..." are non-pointer input parameters, and suppose * further that you sometimes want to return an arbitrary Pix * in place of the input Pix. There are two ways you can do this: * (a) The straightforward way is to change the function * signature to take the address of the Pix ptr: * \code * void function-inplace(PIX **ppix, ...) { * PIX *pixt = function-makenew(*ppix); * pixDestroy(ppix); * *ppix = pixt; * return; * } * \endcode * Here, the input and returned pix are different, as viewed * by the calling function, and the inplace function is * expected to destroy the input pix to avoid a memory leak. * (b) Keep the signature the same and use pixTransferAllData() * to return the new Pix in the input Pix struct: * \code * void function-inplace(PIX *pix, ...) { * PIX *pixt = function-makenew(pix); * pixTransferAllData(pix, &pixt, 0, 0); * // pixDestroy() is called on pixt * return; * } * \endcode * Here, the input and returned pix are the same, as viewed * by the calling function, and the inplace function must * never destroy the input pix, because the calling function * maintains an unchanged handle to it. **/ l_ok pixTransferAllData(PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat) { l_int32 nbytes; PIX *pixs; PROCNAME("pixTransferAllData"); if (!ppixs) return ERROR_INT("&pixs not defined", procName, 1); if ((pixs = *ppixs) == NULL) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) /* no-op */ return ERROR_INT("pixd == pixs", procName, 1); if (pixGetRefcount(pixs) == 1) { /* transfer the data, cmap, text */ pixFreeData(pixd); /* dealloc any existing data */ pixSetData(pixd, pixGetData(pixs)); /* transfer new data from pixs */ pixs->data = NULL; /* pixs no longer owns data */ pixSetColormap(pixd, pixGetColormap(pixs)); /* frees old; sets new */ pixs->colormap = NULL; /* pixs no longer owns colormap */ if (copytext) { pixSetText(pixd, pixGetText(pixs)); pixSetText(pixs, NULL); } } else { /* preserve pixs by making a copy of the data, cmap, text */ pixResizeImageData(pixd, pixs); nbytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); memcpy(pixGetData(pixd), pixGetData(pixs), nbytes); pixCopyColormap(pixd, pixs); if (copytext) pixCopyText(pixd, pixs); } pixCopySpp(pixd, pixs); pixCopyResolution(pixd, pixs); pixCopyDimensions(pixd, pixs); if (copyformat) pixCopyInputFormat(pixd, pixs); /* This will destroy pixs if data was transferred; * otherwise, it just decrements its refcount. */ pixDestroy(ppixs); return 0; } /*! * \brief pixSwapAndDestroy() * * \param[out] ppixd [optional] input pixd can be null, * and it must be different from pixs * \param[in,out] ppixs will be nulled after the swap * \return 0 if OK, 1 on error * *
* Notes: * (1) Simple operation to change the handle name safely. * After this operation, the original image in pixd has * been destroyed, pixd points to what was pixs, and * the input pixs ptr has been nulled. * (2) This works safely whether or not pixs and pixd are cloned. * If pixs is cloned, the other handles still point to * the original image, with the ref count reduced by 1. * (3) Usage example: * \code * Pix *pix1 = pixRead("..."); * Pix *pix2 = function(pix1, ...); * pixSwapAndDestroy(&pix1, &pix2); * pixDestroy(&pix1); // holds what was in pix2 * \endcode * Example with clones ([] shows ref count of image generated * by the function): * \code * Pix *pixs = pixRead("..."); * Pix *pix1 = pixClone(pixs); * Pix *pix2 = function(pix1, ...); [1] * Pix *pix3 = pixClone(pix2); [1] --> [2] * pixSwapAndDestroy(&pix1, &pix2); * pixDestroy(&pixs); // still holds read image * pixDestroy(&pix1); // holds what was in pix2 [2] --> [1] * pixDestroy(&pix3); // holds what was in pix2 [1] --> [0] * \endcode **/ l_ok pixSwapAndDestroy(PIX **ppixd, PIX **ppixs) { PROCNAME("pixSwapAndDestroy"); if (!ppixd) return ERROR_INT("&pixd not defined", procName, 1); if (!ppixs) return ERROR_INT("&pixs not defined", procName, 1); if (*ppixs == NULL) return ERROR_INT("pixs not defined", procName, 1); if (ppixs == ppixd) /* no-op */ return ERROR_INT("&pixd == &pixs", procName, 1); pixDestroy(ppixd); *ppixd = pixClone(*ppixs); pixDestroy(ppixs); return 0; } /*--------------------------------------------------------------------* * Accessors * *--------------------------------------------------------------------*/ l_int32 pixGetWidth(const PIX *pix) { PROCNAME("pixGetWidth"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->w; } l_int32 pixSetWidth(PIX *pix, l_int32 width) { PROCNAME("pixSetWidth"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (width < 0) { pix->w = 0; return ERROR_INT("width must be >= 0", procName, 1); } pix->w = width; return 0; } l_int32 pixGetHeight(const PIX *pix) { PROCNAME("pixGetHeight"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->h; } l_int32 pixSetHeight(PIX *pix, l_int32 height) { PROCNAME("pixSetHeight"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (height < 0) { pix->h = 0; return ERROR_INT("h must be >= 0", procName, 1); } pix->h = height; return 0; } l_int32 pixGetDepth(const PIX *pix) { PROCNAME("pixGetDepth"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->d; } l_int32 pixSetDepth(PIX *pix, l_int32 depth) { PROCNAME("pixSetDepth"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (depth < 1) return ERROR_INT("d must be >= 1", procName, 1); pix->d = depth; return 0; } /*! * \brief pixGetDimensions() * * \param[in] pix * \param[out] pw, ph, pd [optional] each can be null * \return 0 if OK, 1 on error */ l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd) { PROCNAME("pixGetDimensions"); if (pw) *pw = 0; if (ph) *ph = 0; if (pd) *pd = 0; if (!pix) return ERROR_INT("pix not defined", procName, 1); if (pw) *pw = pix->w; if (ph) *ph = pix->h; if (pd) *pd = pix->d; return 0; } /*! * \brief pixSetDimensions() * * \param[in] pix * \param[in] w, h, d use 0 to skip the setting for any of these * \return 0 if OK, 1 on error */ l_ok pixSetDimensions(PIX *pix, l_int32 w, l_int32 h, l_int32 d) { PROCNAME("pixSetDimensions"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (w > 0) pixSetWidth(pix, w); if (h > 0) pixSetHeight(pix, h); if (d > 0) pixSetDepth(pix, d); return 0; } /*! * \brief pixCopyDimensions() * * \param[in] pixd * \param[in] pixs * \return 0 if OK, 1 on error */ l_ok pixCopyDimensions(PIX *pixd, const PIX *pixs) { PROCNAME("pixCopyDimensions"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixSetWidth(pixd, pixGetWidth(pixs)); pixSetHeight(pixd, pixGetHeight(pixs)); pixSetDepth(pixd, pixGetDepth(pixs)); pixSetWpl(pixd, pixGetWpl(pixs)); return 0; } l_int32 pixGetSpp(const PIX *pix) { PROCNAME("pixGetSpp"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->spp; } /* * \brief pixSetSpp() * * \param[in] pix * \param[in] spp 1, 3 or 4 samples * \return 0 if OK, 1 on error * *
* Notes: * (1) For a 32 bpp pix, this can be used to ignore the * alpha sample (spp == 3) or to use it (spp == 4). * For example, to write a spp == 4 image without the alpha * sample (as an rgb pix), call pixSetSpp(pix, 3) and * then write it out as a png. **/ l_int32 pixSetSpp(PIX *pix, l_int32 spp) { PROCNAME("pixSetSpp"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (spp < 1) return ERROR_INT("spp must be >= 1", procName, 1); pix->spp = spp; return 0; } /*! * \brief pixCopySpp() * * \param[in] pixd * \param[in] pixs * \return 0 if OK, 1 on error */ l_ok pixCopySpp(PIX *pixd, const PIX *pixs) { PROCNAME("pixCopySpp"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixSetSpp(pixd, pixGetSpp(pixs)); return 0; } l_int32 pixGetWpl(const PIX *pix) { PROCNAME("pixGetWpl"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->wpl; } l_int32 pixSetWpl(PIX *pix, l_int32 wpl) { PROCNAME("pixSetWpl"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pix->wpl = wpl; return 0; } l_int32 pixGetRefcount(const PIX *pix) { PROCNAME("pixGetRefcount"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->refcount; } l_int32 pixChangeRefcount(PIX *pix, l_int32 delta) { PROCNAME("pixChangeRefcount"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pix->refcount += delta; return 0; } l_int32 pixGetXRes(const PIX *pix) { PROCNAME("pixGetXRes"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->xres; } l_int32 pixSetXRes(PIX *pix, l_int32 res) { PROCNAME("pixSetXRes"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pix->xres = res; return 0; } l_int32 pixGetYRes(const PIX *pix) { PROCNAME("pixGetYRes"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->yres; } l_int32 pixSetYRes(PIX *pix, l_int32 res) { PROCNAME("pixSetYRes"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pix->yres = res; return 0; } /*! * \brief pixGetResolution() * * \param[in] pix * \param[out] pxres, pyres [optional] each can be null * \return 0 if OK, 1 on error */ l_ok pixGetResolution(const PIX *pix, l_int32 *pxres, l_int32 *pyres) { PROCNAME("pixGetResolution"); if (pxres) *pxres = 0; if (pyres) *pyres = 0; if (!pxres && !pyres) return ERROR_INT("no output requested", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (pxres) *pxres = pix->xres; if (pyres) *pyres = pix->yres; return 0; } /*! * \brief pixSetResolution() * * \param[in] pix * \param[in] xres, yres use 0 to skip setting a value for either of these * \return 0 if OK, 1 on error */ l_ok pixSetResolution(PIX *pix, l_int32 xres, l_int32 yres) { PROCNAME("pixSetResolution"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (xres > 0) pix->xres = xres; if (yres > 0) pix->yres = yres; return 0; } l_int32 pixCopyResolution(PIX *pixd, const PIX *pixs) { PROCNAME("pixCopyResolution"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixSetXRes(pixd, pixGetXRes(pixs)); pixSetYRes(pixd, pixGetYRes(pixs)); return 0; } l_int32 pixScaleResolution(PIX *pix, l_float32 xscale, l_float32 yscale) { PROCNAME("pixScaleResolution"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (pix->xres != 0 && pix->yres != 0) { pix->xres = (l_uint32)(xscale * (l_float32)(pix->xres) + 0.5); pix->yres = (l_uint32)(yscale * (l_float32)(pix->yres) + 0.5); } return 0; } l_int32 pixGetInputFormat(const PIX *pix) { PROCNAME("pixGetInputFormat"); if (!pix) return ERROR_INT("pix not defined", procName, 0); return pix->informat; } l_int32 pixSetInputFormat(PIX *pix, l_int32 informat) { PROCNAME("pixSetInputFormat"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pix->informat = informat; return 0; } l_int32 pixCopyInputFormat(PIX *pixd, const PIX *pixs) { PROCNAME("pixCopyInputFormat"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixSetInputFormat(pixd, pixGetInputFormat(pixs)); return 0; } l_int32 pixSetSpecial(PIX *pix, l_int32 special) { PROCNAME("pixSetSpecial"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pix->special = special; return 0; } /*! * \brief pixGetText() * * \param[in] pix * \return ptr to existing text string * *
* Notes: * (1) The text string belongs to the pix: * * the caller must NOT free it * * it must not be used after the pix is destroyed **/ char * pixGetText(PIX *pix) { PROCNAME("pixGetText"); if (!pix) return (char *)ERROR_PTR("pix not defined", procName, NULL); return pix->text; } /*! * \brief pixSetText() * * \param[in] pix * \param[in] textstring can be null * \return 0 if OK, 1 on error * *
* Notes: * (1) This removes any existing textstring and puts a copy of * the input textstring there. **/ l_ok pixSetText(PIX *pix, const char *textstring) { PROCNAME("pixSetText"); if (!pix) return ERROR_INT("pix not defined", procName, 1); stringReplace(&pix->text, textstring); return 0; } /*! * \brief pixAddText() * * \param[in] pix * \param[in] textstring can be null * \return 0 if OK, 1 on error * *
* Notes: * (1) This adds the new textstring to any existing text. * (2) Either or both the existing text and the new text * string can be null. **/ l_ok pixAddText(PIX *pix, const char *textstring) { char *newstring; PROCNAME("pixAddText"); if (!pix) return ERROR_INT("pix not defined", procName, 1); newstring = stringJoin(pixGetText(pix), textstring); stringReplace(&pix->text, newstring); LEPT_FREE(newstring); return 0; } l_int32 pixCopyText(PIX *pixd, const PIX *pixs) { PROCNAME("pixCopyText"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixSetText(pixd, pixs->text); return 0; } PIXCMAP * pixGetColormap(PIX *pix) { PROCNAME("pixGetColormap"); if (!pix) return (PIXCMAP *)ERROR_PTR("pix not defined", procName, NULL); return pix->colormap; } /*! * \brief pixSetColormap() * * \param[in] pix * \param[in] colormap to be assigned * \return 0 if OK, 1 on error. * *
* Notes: * (1) Unlike with the pix data field, pixSetColormap() destroys * any existing colormap before assigning the new one. * Because colormaps are not ref counted, it is important that * the new colormap does not belong to any other pix. **/ l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap) { PROCNAME("pixSetColormap"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pixDestroyColormap(pix); pix->colormap = colormap; return 0; } /*! * \brief pixDestroyColormap() * * \param[in] pix * \return 0 if OK, 1 on error */ l_ok pixDestroyColormap(PIX *pix) { PIXCMAP *cmap; PROCNAME("pixDestroyColormap"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if ((cmap = pix->colormap) != NULL) { pixcmapDestroy(&cmap); pix->colormap = NULL; } return 0; } /*! * \brief pixGetData() * * \param[in] pix * \return ptr to image data * *
* Notes: * (1) This gives a new handle for the data. The data is still * owned by the pix, so do not call LEPT_FREE() on it. **/ l_uint32 * pixGetData(PIX *pix) { PROCNAME("pixGetData"); if (!pix) return (l_uint32 *)ERROR_PTR("pix not defined", procName, NULL); return pix->data; } /*! * \brief pixSetData() * * \param[in] pix * \param[in] data * \return 0 if OK, 1 on error * *
* Notes: * (1) This does not free any existing data. To free existing * data, use pixFreeData() before pixSetData(). **/ l_int32 pixSetData(PIX *pix, l_uint32 *data) { PROCNAME("pixSetData"); if (!pix) return ERROR_INT("pix not defined", procName, 1); pix->data = data; return 0; } /*! * \brief pixExtractData() * * \param[in] pix * \return ptr to data, or null on error * *
* Notes: * (1) This extracts the pix image data for use in another context. * The caller still needs to use pixDestroy() on the input pix. * (2) If refcount == 1, the data is extracted and the * pix->data ptr is set to NULL. * (3) If refcount > 1, this simply returns a copy of the data, * using the pix allocator, and leaving the input pix unchanged. **/ l_uint32 * pixExtractData(PIX *pixs) { l_int32 count, bytes; l_uint32 *data, *datas; PROCNAME("pixExtractData"); if (!pixs) return (l_uint32 *)ERROR_PTR("pixs not defined", procName, NULL); count = pixGetRefcount(pixs); if (count == 1) { /* extract */ data = pixGetData(pixs); pixSetData(pixs, NULL); } else { /* refcount > 1; copy */ bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); datas = pixGetData(pixs); if ((data = (l_uint32 *)pix_malloc(bytes)) == NULL) return (l_uint32 *)ERROR_PTR("data not made", procName, NULL); memcpy(data, datas, bytes); } return data; } /*! * \brief pixFreeData() * * \param[in] pix * \return 0 if OK, 1 on error * *
* Notes: * (1) This frees the data and sets the pix data ptr to null. * It should be used before pixSetData() in the situation where * you want to free any existing data before doing * a subsequent assignment with pixSetData(). **/ l_int32 pixFreeData(PIX *pix) { l_uint32 *data; PROCNAME("pixFreeData"); if (!pix) return ERROR_INT("pix not defined", procName, 1); if ((data = pixGetData(pix)) != NULL) { pix_free(data); pix->data = NULL; } return 0; } /*--------------------------------------------------------------------* * Pix line ptrs * *--------------------------------------------------------------------*/ /*! * \brief pixGetLinePtrs() * * \param[in] pix * \param[out] psize [optional] array size, which is the pix height * \return array of line ptrs, or NULL on error * *
* Notes: * (1) This is intended to be used for fast random pixel access. * For example, for an 8 bpp image, * val = GET_DATA_BYTE(lines8[i], j); * is equivalent to, but much faster than, * pixGetPixel(pix, j, i, &val); * (2) How much faster? For 1 bpp, it's from 6 to 10x faster. * For 8 bpp, it's an amazing 30x faster. So if you are * doing random access over a substantial part of the image, * use this line ptr array. * (3) When random access is used in conjunction with a stack, * queue or heap, the overall computation time depends on * the operations performed on each struct that is popped * or pushed, and whether we are using a priority queue (O(logn)) * or a queue or stack (O(1)). For example, for maze search, * the overall ratio of time for line ptrs vs. pixGet/Set* is * Maze type Type Time ratio * binary queue 0.4 * gray heap (priority queue) 0.6 * (4) Because this returns a void** and the accessors take void*, * the compiler cannot check the pointer types. It is * strongly recommended that you adopt a naming scheme for * the returned ptr arrays that indicates the pixel depth. * (This follows the original intent of Simonyi's "Hungarian" * application notation, where naming is used proactively * to make errors visibly obvious.) By doing this, you can * tell by inspection if the correct accessor is used. * For example, for an 8 bpp pixg: * void **lineg8 = pixGetLinePtrs(pixg, NULL); * val = GET_DATA_BYTE(lineg8[i], j); // fast access; BYTE, 8 * ... * LEPT_FREE(lineg8); // don't forget this * (5) These are convenient for accessing bytes sequentially in an * 8 bpp grayscale image. People who write image processing code * on 8 bpp images are accustomed to grabbing pixels directly out * of the raster array. Note that for little endians, you first * need to reverse the byte order in each 32-bit word. * Here's a typical usage pattern: * pixEndianByteSwap(pix); // always safe; no-op on big-endians * l_uint8 **lineptrs = (l_uint8 **)pixGetLinePtrs(pix, NULL); * pixGetDimensions(pix, &w, &h, NULL); * for (i = 0; i < h; i++) { * l_uint8 *line = lineptrs[i]; * for (j = 0; j < w; j++) { * val = line[j]; * ... * } * } * pixEndianByteSwap(pix); // restore big-endian order * LEPT_FREE(lineptrs); * This can be done even more simply as follows: * l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h); * for (i = 0; i < h; i++) { * l_uint8 *line = lineptrs[i]; * for (j = 0; j < w; j++) { * val = line[j]; * ... * } * } * pixCleanupByteProcessing(pix, lineptrs); **/ void ** pixGetLinePtrs(PIX *pix, l_int32 *psize) { l_int32 i, h, wpl; l_uint32 *data; void **lines; PROCNAME("pixGetLinePtrs"); if (psize) *psize = 0; if (!pix) return (void **)ERROR_PTR("pix not defined", procName, NULL); h = pixGetHeight(pix); if (psize) *psize = h; if ((lines = (void **)LEPT_CALLOC(h, sizeof(void *))) == NULL) return (void **)ERROR_PTR("lines not made", procName, NULL); wpl = pixGetWpl(pix); data = pixGetData(pix); for (i = 0; i < h; i++) lines[i] = (void *)(data + i * wpl); return lines; } /*--------------------------------------------------------------------* * Print output for debugging * *--------------------------------------------------------------------*/ extern const char *ImageFileFormatExtensions[]; /*! * \brief pixPrintStreamInfo() * * \param[in] fp file stream * \param[in] pix * \param[in] text [optional] identifying string; can be null * \return 0 if OK, 1 on error */ l_ok pixPrintStreamInfo(FILE *fp, const PIX *pix, const char *text) { l_int32 informat; const PIXCMAP *cmap; PROCNAME("pixPrintStreamInfo"); if (!fp) return ERROR_INT("fp not defined", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (text) fprintf(fp, " Pix Info for %s:\n", text); fprintf(fp, " width = %d, height = %d, depth = %d, spp = %d\n", pixGetWidth(pix), pixGetHeight(pix), pixGetDepth(pix), pixGetSpp(pix)); fprintf(fp, " wpl = %d, data = %p, refcount = %d\n", pixGetWpl(pix), pix->data, pixGetRefcount(pix)); fprintf(fp, " xres = %d, yres = %d\n", pixGetXRes(pix), pixGetYRes(pix)); if ((cmap = pix->colormap) != NULL) pixcmapWriteStream(fp, cmap); else fprintf(fp, " no colormap\n"); informat = pixGetInputFormat(pix); fprintf(fp, " input format: %d (%s)\n", informat, ImageFileFormatExtensions[informat]); if (pix->text != NULL) fprintf(fp, " text: %s\n", pix->text); return 0; }