blob: 98db8749a211901a8cd6e40dcefdb1d2a3d90b66 [file] [log] [blame]
/*====================================================================*
- 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;
}