| /*====================================================================* |
| - Copyright (C) 2001 Leptonica. All rights reserved. |
| - This software is distributed in the hope that it will be |
| - useful, but with NO WARRANTY OF ANY KIND. |
| - No author or distributor accepts responsibility to anyone for the |
| - consequences of using this software, or for whether it serves any |
| - particular purpose or works at all, unless he or she says so in |
| - writing. Everyone is granted permission to copy, modify and |
| - redistribute this source code, for commercial or non-commercial |
| - purposes, with the following restrictions: (1) the origin of this |
| - source code must not be misrepresented; (2) modified versions must |
| - be plainly marked as such; and (3) this notice may not be removed |
| - or altered from any source or modified source distribution. |
| *====================================================================*/ |
| |
| /* |
| * pix1.c |
| * |
| * The pixN.c {N = 1,2,3,4} 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, fg/bg estimation, 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() |
| * |
| * 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 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() |
| * 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 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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "allheaders.h" |
| |
| static void pixFree(PIX *pix); |
| |
| |
| /*-------------------------------------------------------------------------* |
| * Pix Memory Management * |
| * * |
| * These functions give you the freedom to specify at compile or run * |
| * time the allocator and deallocator to be used for pix. It has no * |
| * effect on memory management for other data structs, which are * |
| * controlled by the #defines in environ.h. Likewise, the #defines * |
| * in environ.h have no effect on the pix memory management. * |
| * The default functions are malloc and free. Use setPixMemoryManager() * |
| * to specify other functions to use. * |
| *-------------------------------------------------------------------------*/ |
| struct PixMemoryManager |
| { |
| void *(*allocator)(size_t); |
| void (*deallocator)(void *); |
| }; |
| |
| static struct PixMemoryManager pix_mem_manager = { |
| &malloc, |
| &free |
| }; |
| |
| static void * |
| pix_malloc(size_t size) |
| { |
| #ifndef COMPILER_MSVC |
| return (*pix_mem_manager.allocator)(size); |
| #else /* COMPILER_MSVC */ |
| /* Under MSVC++, pix_mem_manager is initialized after a call |
| * to pix_malloc. Just ignore the custom allocator feature. */ |
| return malloc(size); |
| #endif /* COMPILER_MSVC */ |
| } |
| |
| static void |
| pix_free(void *ptr) |
| { |
| #ifndef COMPILER_MSVC |
| return (*pix_mem_manager.deallocator)(ptr); |
| #else /* COMPILER_MSVC */ |
| /* Under MSVC++, pix_mem_manager is initialized after a call |
| * to pix_malloc. Just ignore the custom allocator feature. */ |
| return free(ptr); |
| #endif /* COMPILER_MSVC */ |
| } |
| |
| /*! |
| * setPixMemoryManager() |
| * |
| * Input: allocator (<optional>; use null to skip) |
| * 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). |
| */ |
| #ifndef COMPILER_MSVC |
| void |
| setPixMemoryManager(void *(allocator(size_t)), |
| void (deallocator(void *))) |
| { |
| if (allocator) pix_mem_manager.allocator = allocator; |
| if (deallocator) pix_mem_manager.deallocator = deallocator; |
| return; |
| } |
| #else /* COMPILER_MSVC */ |
| /* MSVC++ wants type (*fun)(types...) syntax */ |
| void |
| setPixMemoryManager(void *((*allocator)(size_t)), |
| void ((*deallocator)(void *))) |
| { |
| if (allocator) pix_mem_manager.allocator = allocator; |
| if (deallocator) pix_mem_manager.deallocator = deallocator; |
| return; |
| } |
| #endif /* COMPILER_MSVC */ |
| |
| |
| /*--------------------------------------------------------------------* |
| * Pix Creation * |
| *--------------------------------------------------------------------*/ |
| /*! |
| * pixCreate() |
| * |
| * Input: 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, 4 * pixd->wpl * pixd->h); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixCreateNoInit() |
| * |
| * Input: width, height, depth |
| * Return: pixd (with data allocated but not initialized), |
| * or null on error |
| * |
| * Notes: |
| * (1) Must set pad bits to avoid reading unitialized 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"); |
| pixd = pixCreateHeader(width, height, depth); |
| if (!pixd) return NULL; |
| wpl = pixGetWpl(pixd); |
| if ((data = (l_uint32 *)pix_malloc(4 * wpl * height)) == NULL) |
| return (PIX *)ERROR_PTR("pix_malloc fail for data", procName, NULL); |
| pixSetData(pixd, data); |
| pixSetPadBits(pixd, 0); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixCreateTemplate() |
| * |
| * Input: 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(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, 4 * pixd->wpl * pixd->h); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixCreateTemplateNoInit() |
| * |
| * Input: 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(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); |
| pixCopyResolution(pixd, pixs); |
| pixCopyColormap(pixd, pixs); |
| pixCopyText(pixd, pixs); |
| pixCopyInputFormat(pixd, pixs); |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixCreateHeader() |
| * |
| * Input: width, height, depth |
| * Return: pixd (with no data allocated), or null on error |
| */ |
| PIX * |
| pixCreateHeader(l_int32 width, |
| l_int32 height, |
| l_int32 depth) |
| { |
| l_int32 wpl; |
| 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); |
| |
| if ((pixd = (PIX *)CALLOC(1, sizeof(PIX))) == NULL) |
| return (PIX *)ERROR_PTR("CALLOC fail for pixd", procName, NULL); |
| pixSetWidth(pixd, width); |
| pixSetHeight(pixd, height); |
| pixSetDepth(pixd, depth); |
| wpl = (width * depth + 31) / 32; |
| pixSetWpl(pixd, wpl); |
| |
| pixd->refcount = 1; |
| pixd->informat = IFF_UNKNOWN; |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixClone() |
| * |
| * Input: pix |
| * 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 * |
| *--------------------------------------------------------------------*/ |
| /*! |
| * pixDestroy() |
| * |
| * Input: &pix <will be nulled> |
| * 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!", procName); |
| return; |
| } |
| |
| if ((pix = *ppix) == NULL) |
| return; |
| |
| pixFree(pix); |
| *ppix = NULL; |
| return; |
| } |
| |
| |
| /*! |
| * pixFree() |
| * |
| * Input: 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) |
| FREE(text); |
| pixDestroyColormap(pix); |
| FREE(pix); |
| } |
| return; |
| } |
| |
| |
| /*-------------------------------------------------------------------------* |
| * Pix Copy * |
| *-------------------------------------------------------------------------*/ |
| /*! |
| * pixCopy() |
| * |
| * Input: pixd (<optional>; can be null, or equal to pixs, |
| * or different from pixs) |
| * 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 */ |
| PIX *pixs) |
| { |
| l_int32 bytes; |
| l_uint32 *datas, *datad; |
| |
| PROCNAME("pixCopy"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| 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); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| memcpy((char *)datad, (char *)datas, bytes); |
| return pixd; |
| } |
| |
| /* Reallocate image data if sizes are different */ |
| if (pixResizeImageData(pixd, pixs) == 1) |
| return (PIX *)ERROR_PTR("reallocation of data failed", procName, NULL); |
| |
| /* Copy non-image data fields */ |
| pixCopyColormap(pixd, pixs); |
| pixCopyResolution(pixd, pixs); |
| pixCopyInputFormat(pixd, pixs); |
| pixCopyText(pixd, pixs); |
| |
| /* Copy image data */ |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| memcpy((char*)datad, (char*)datas, bytes); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixResizeImageData() |
| * |
| * Input: pixd (gets new uninitialized buffer for image data) |
| * pixs (determines the size of the buffer; not changed) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This removes any existing image data from pixd and |
| * allocates an uninitialized buffer that will hold the |
| * amount of image data that is in pixs. |
| */ |
| l_int32 |
| pixResizeImageData(PIX *pixd, |
| 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; |
| |
| pixGetDimensions(pixs, &w, &h, &d); |
| wpl = pixGetWpl(pixs); |
| pixSetWidth(pixd, w); |
| pixSetHeight(pixd, h); |
| pixSetDepth(pixd, d); |
| pixSetWpl(pixd, wpl); |
| bytes = 4 * wpl * h; |
| pixFreeData(pixd); /* free any existing image data */ |
| if ((data = (l_uint32 *)pix_malloc(bytes)) == NULL) |
| return ERROR_INT("pix_malloc fail for data", procName, 1); |
| pixSetData(pixd, data); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixCopyColormap() |
| * |
| * Input: src and dest Pix |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This always destroys any colormap in pixd (except if |
| * the operation is a no-op. |
| */ |
| l_int32 |
| pixCopyColormap(PIX *pixd, |
| PIX *pixs) |
| { |
| PIXCMAP *cmaps, *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 = pixGetColormap(pixs)) == NULL) /* not an error */ |
| return 0; |
| |
| if ((cmapd = pixcmapCopy(cmaps)) == NULL) |
| return ERROR_INT("cmapd not made", procName, 1); |
| pixSetColormap(pixd, cmapd); |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixSizesEqual() |
| * |
| * Input: two pix |
| * Return: 1 if the two pix have same {h, w, d}; 0 otherwise. |
| */ |
| l_int32 |
| pixSizesEqual(PIX *pix1, |
| 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; |
| } |
| |
| |
| /*! |
| * pixTransferAllData() |
| * |
| * Input: pixd (must be different from pixs) |
| * &pixs (will be nulled if refcount goes to 0) |
| * copytext (1 to copy the text field; 0 to skip) |
| * 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: |
| * void function-inplace(PIX **ppix, ...) { |
| * PIX *pixt = function-makenew(*ppix); |
| * pixDestroy(ppix); |
| * *ppix = pixt; |
| * return; |
| * } |
| * 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: |
| * void function-inplace(PIX *pix, ...) { |
| * PIX *pixt = function-makenew(pix); |
| * pixTransferAllData(pix, &pixt); // pixt is destroyed |
| * return; |
| * } |
| * 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_int32 |
| 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((char *)pixGetData(pixd), (char *)pixGetData(pixs), nbytes); |
| pixCopyColormap(pixd, pixs); |
| if (copytext) |
| pixCopyText(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; |
| } |
| |
| |
| |
| /*--------------------------------------------------------------------* |
| * Accessors * |
| *--------------------------------------------------------------------*/ |
| l_int32 |
| pixGetWidth(PIX *pix) |
| { |
| PROCNAME("pixGetWidth"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, UNDEF); |
| |
| 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(PIX *pix) |
| { |
| PROCNAME("pixGetHeight"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, UNDEF); |
| |
| 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(PIX *pix) |
| { |
| PROCNAME("pixGetDepth"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, UNDEF); |
| |
| 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; |
| } |
| |
| |
| /*! |
| * pixGetDimensions() |
| * |
| * Input: pix |
| * &w, &h, &d (<optional return>; each can be null) |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| pixGetDimensions(PIX *pix, |
| l_int32 *pw, |
| l_int32 *ph, |
| l_int32 *pd) |
| { |
| PROCNAME("pixGetDimensions"); |
| |
| 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; |
| } |
| |
| |
| /*! |
| * pixSetDimensions() |
| * |
| * Input: pix |
| * w, h, d (use 0 to skip the setting for any of these) |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| 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; |
| } |
| |
| |
| /*! |
| * pixCopyDimensions() |
| * |
| * Input: pixd |
| * pixd |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| pixCopyDimensions(PIX *pixd, |
| 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 |
| pixGetWpl(PIX *pix) |
| { |
| PROCNAME("pixGetWpl"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, UNDEF); |
| 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(PIX *pix) |
| { |
| PROCNAME("pixGetRefcount"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, UNDEF); |
| 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_uint32 |
| pixGetXRes(PIX *pix) |
| { |
| PROCNAME("pixGetXRes"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, 0); |
| return pix->xres; |
| } |
| |
| |
| l_int32 |
| pixSetXRes(PIX *pix, |
| l_uint32 res) |
| { |
| PROCNAME("pixSetXRes"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, 1); |
| |
| pix->xres = res; |
| return 0; |
| } |
| |
| |
| l_uint32 |
| pixGetYRes(PIX *pix) |
| { |
| PROCNAME("pixGetYRes"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, 0); |
| return pix->yres; |
| } |
| |
| |
| l_int32 |
| pixSetYRes(PIX *pix, |
| l_uint32 res) |
| { |
| PROCNAME("pixSetYRes"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, 1); |
| |
| pix->yres = res; |
| return 0; |
| } |
| |
| |
| /*! |
| * pixGetResolution() |
| * |
| * Input: pix |
| * &xres, &yres (<optional return>; each can be null) |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| pixGetResolution(PIX *pix, |
| l_uint32 *pxres, |
| l_uint32 *pyres) |
| { |
| PROCNAME("pixGetResolution"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, 1); |
| if (pxres) *pxres = pix->xres; |
| if (pyres) *pyres = pix->yres; |
| return 0; |
| } |
| |
| |
| /*! |
| * pixSetResolution() |
| * |
| * Input: pix |
| * xres, yres (use 0 to skip the setting for either of these) |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| pixSetResolution(PIX *pix, |
| l_uint32 xres, |
| l_uint32 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, |
| 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(PIX *pix) |
| { |
| PROCNAME("pixGetInputFormat"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, UNDEF); |
| 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, |
| 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; |
| } |
| |
| |
| /*! |
| * pixGetText() |
| * |
| * Input: pix |
| * Return: ptr to existing text string |
| * |
| * Notes: |
| * (1) The text string belongs to the pix. The caller must |
| * NOT free it! |
| */ |
| char * |
| pixGetText(PIX *pix) |
| { |
| PROCNAME("pixGetText"); |
| |
| if (!pix) |
| return (char *)ERROR_PTR("pix not defined", procName, NULL); |
| return pix->text; |
| } |
| |
| |
| /*! |
| * pixSetText() |
| * |
| * Input: pix |
| * 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_int32 |
| pixSetText(PIX *pix, |
| const char *textstring) |
| { |
| PROCNAME("pixSetText"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, 1); |
| |
| stringReplace(&pix->text, textstring); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixAddText() |
| * |
| * Input: pix |
| * textstring |
| * 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_int32 |
| 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); |
| FREE(newstring); |
| return 0; |
| } |
| |
| |
| l_int32 |
| pixCopyText(PIX *pixd, |
| 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, pixGetText(pixs)); |
| return 0; |
| } |
| |
| |
| PIXCMAP * |
| pixGetColormap(PIX *pix) |
| { |
| PROCNAME("pixGetColormap"); |
| |
| if (!pix) |
| return (PIXCMAP *)ERROR_PTR("pix not defined", procName, NULL); |
| return pix->colormap; |
| } |
| |
| |
| /*! |
| * pixSetColormap() |
| * |
| * Input: pix |
| * 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_int32 |
| pixSetColormap(PIX *pix, |
| PIXCMAP *colormap) |
| { |
| PROCNAME("pixSetColormap"); |
| |
| if (!pix) |
| return ERROR_INT("pix not defined", procName, 1); |
| |
| pixDestroyColormap(pix); |
| pix->colormap = colormap; |
| return 0; |
| } |
| |
| |
| /*! |
| * pixDestroyColormap() |
| * |
| * Input: pix |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| 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; |
| } |
| |
| |
| /*! |
| * pixGetData() |
| * |
| * Notes: |
| * (1) This gives a new handle for the data. The data is still |
| * owned by the pix, so do not call 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; |
| } |
| |
| |
| /*! |
| * pixSetData() |
| * |
| * 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; |
| } |
| |
| |
| /*! |
| * pixExtractData() |
| * |
| * 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((char *)data, (char *)datas, bytes); |
| } |
| |
| return data; |
| } |
| |
| |
| /*! |
| * pixFreeData() |
| * |
| * 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 * |
| *--------------------------------------------------------------------*/ |
| /*! |
| * pixGetLinePtrs() |
| * |
| * Input: pix |
| * &size (<optional return> 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 |
| * ... |
| * FREE(lineg8); // don't forget this |
| */ |
| void ** |
| pixGetLinePtrs(PIX *pix, |
| l_int32 *psize) |
| { |
| l_int32 i, h, wpl; |
| l_uint32 *data; |
| void **lines; |
| |
| PROCNAME("pixGetLinePtrs"); |
| |
| if (!pix) |
| return (void **)ERROR_PTR("pix not defined", procName, NULL); |
| |
| h = pixGetHeight(pix); |
| if ((lines = (void **)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 * |
| *--------------------------------------------------------------------*/ |
| /*! |
| * pixPrintStreamInfo() |
| * |
| * Input: fp (file stream) |
| * pix |
| * text (<optional> identifying string; can be null) |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| pixPrintStreamInfo(FILE *fp, |
| PIX *pix, |
| const char *text) |
| { |
| 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\n", |
| pixGetWidth(pix), pixGetHeight(pix), pixGetDepth(pix)); |
| fprintf(fp, " wpl = %d, data = %p, refcount = %d\n", |
| pixGetWpl(pix), pixGetData(pix), pixGetRefcount(pix)); |
| if ((cmap = pixGetColormap(pix)) != NULL) |
| pixcmapWriteStream(fp, cmap); |
| else |
| fprintf(fp, " no colormap\n"); |
| |
| return 0; |
| } |