/*====================================================================*
 -  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
 *
 *      Pix creation
 *          PIX          *pixCreate()
 *          PIX          *pixCreateTemplate()
 *          PIX          *pixClone()
 *
 *      Pix destruction
 *          void          pixDestroy()
 *
 *      Pix copy
 *          PIX          *pixCopy()
 *	    l_int32       pixCopyColormap()
 *          l_int32       pixSizesEqual()
 *
 *      Pix accessors
 *          l_int32       pixGetWidth()
 *          l_int32       pixSetWidth()
 *          l_int32       pixGetHeight()
 *          l_int32       pixSetHeight()
 *          l_int32       pixGetDepth()
 *          l_int32       pixSetDepth()
 *          l_int32       pixGetWpl()
 *          l_int32       pixSetWpl()
 *          l_int32       pixGetRefcount()
 *          l_int32       pixChangeRefcount()
 *          l_uint32      pixGetXRes()
 *          l_uint32      pixGetYRes()
 *          l_int32       pixSetXRes()
 *          l_int32       pixSetYRes()
 *          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()
 *          l_int32       pixDestroyColormap()
 *          PIXCMAP      *pixGetColormap()
 *          l_int32       pixSetColormap()
 *          l_uint32     *pixGetData()
 *          l_int32       pixSetData()
 *
 *      Pix debug
 *          l_int32       pixPrintStreamInfo()
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"

    /* Can use this to pad each rasterline.  Not presently necessary */
static const l_int32  EXTRA_WORDS_ON_RIGHT = 0;

#ifndef  NO_CONSOLE_IO
#define   DEBUG    0
#endif  /* ~NO_CONSOLE_IO */


/*--------------------------------------------------------------------*
 *                              Pix Creation                          *
 *--------------------------------------------------------------------*/
/*!
 *  pixCreate() 
 *
 *      Input:  width, height, depth
 *      Return: pixd (with data allocated), or null on error
 */
PIX *
pixCreate(l_int32  width,
	  l_int32  height,
	  l_int32  depth)
{
l_int32    wpl;
PIX       *pixd;
l_uint32  *data;

    PROCNAME("pixCreate");

    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);

	/* So far, there is no need to add an extra word to each
	 * raster line.  But the mechanism is here if needed, and
	 * should have no side effects if enabled, as all ops that
	 * use the word pad bits use the actual image width, not wpl.
	 * When I got valgrind, I found that rasteropGeneralLow() was
	 * making occasional read errors one word off the end
	 * of the data, and adding an extra word to each raster
	 * line was a quick fix, but is currently not necessary. */  
    wpl = (width * depth + 31) / 32 + EXTRA_WORDS_ON_RIGHT;
    pixSetWpl(pixd, wpl);
    if ((data = (l_uint32 *)CALLOC(4 * wpl, height)) == NULL)
	return (PIX *)ERROR_PTR("CALLOC fail for data", procName, NULL);
    pixSetData(pixd, data);
    pixd->refcount = 1;
    pixd->informat = IFF_UNKNOWN;

    return pixd;
}


/*!
 *  pixCreateTemplate()
 *
 *      Input:  pixs
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) Makes a Pix of the same size as the input Pix, allocating
 *          the data array, but without copying the image data from pixs.
 *      (2) Copies the other fields, including colormap if it exists.
 */
PIX *
pixCreateTemplate(PIX  *pixs)
{
l_int32  w, h, d;
PIX     *pixd;

    PROCNAME("pixCreateTemplate");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);

    w = pixGetWidth(pixs);
    h = pixGetHeight(pixs);
    d = pixGetDepth(pixs);

    if ((pixd = pixCreate(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;
}


/*!
 *  pixClone()
 *
 *      Input:  pix
 *      Return: same pix (ptr), or null on error
 *
 *  Note: Why is this here?  We make a "clone", which
 *        is just another handle to an existing pix, because
 *        (1) images can be large and hence expensive to copy,
 *        (2) extra handles to a data structure that are made
 *        without some control are dangerous because if you
 *        have two (say), how do you know which one should be
 *        used to destroy the pix, and, conversely, if you
 *        destroy the pix, how do you remember that the other
 *        handle is no longer valid?  This is solved by a
 *        simple protocol: (1) whenever you want a new handle
 *        to an existing image, call pixClone(), which just
 *        bumps a ref count, and (2) always call pixDestroy()
 *        on ALL handles, which 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 <can 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)
{
l_uint32  *data;
char      *text;
PIX       *pix;

    PROCNAME("pixDestroy");

    if (!ppix) {
	L_WARNING("ptr address is null!", procName);
        return;
    }

    if ((pix = *ppix) == NULL)
	return;

	/* Decrement the ref count.  If it is 0, destroy the pix. */
    pixChangeRefcount(pix, -1);
    if (pixGetRefcount(pix) <= 0) {
        if ((data = pixGetData(pix)))
            FREE((void *)data);
        if ((text = pixGetText(pix)))
            FREE((void *)text);
        pixDestroyColormap(pix);
        FREE((void *)pix);
    }

    *ppix = NULL;
    return;
}


/*-------------------------------------------------------------------------*
 *                                 Pix Copy                                *
 *-------------------------------------------------------------------------*/
/*!
 *  pixCopy()
 *
 *      Input:  pixd (<optional>)
 *              pixs
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) If pixd = NULL, this makes a new copy, with refcount of 1.
 *          If pixd != NULL, this makes sure pixs and pixd are the same
 *          size, and then copies the image data, leaving the refcounts
 *          of pixs and pixd unchanged.
 *      (2) 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, 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;
    }

	/* Programmer error! */
    if (pixs == pixd)
        return (PIX *)ERROR_PTR("image data copied to itself", procName, pixd);

        /* Check sizes */
    if (!pixSizesEqual(pixs, pixd))
	    return (PIX *)ERROR_PTR("pix sizes not equal", procName, pixd);
    pixCopyColormap(pixd, pixs);
    pixCopyResolution(pixd, pixs);
    pixCopyInputFormat(pixd, pixs);
    pixCopyText(pixd, pixs);

	/* Copy the data */
    datas = pixGetData(pixs);
    datad = pixGetData(pixd);
    memcpy((char*)datad, (char*)datas, bytes);

    return pixd;
}


/*!
 *  pixCopyColormap()
 *
 *      Input:  src and dest Pix
 *      Return: 0 if OK, 1 on error
 */
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 ((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)
        return ERROR_INT("pix1 not defined", procName, 0);
    if (!pix2)
        return ERROR_INT("pix2 not defined", procName, 0);

    if ((pixGetWidth(pix1) != pixGetWidth(pix2)) ||
        (pixGetHeight(pix1) != pixGetHeight(pix2)) ||
        (pixGetDepth(pix1) != pixGetDepth(pix2))) {
#if DEBUG
	fprintf(stderr, "w1 = %d, w2 = %d, h1 = %d, h2 = %d, d1 = %d, d2 = %d\n",
	                 pixGetWidth(pix1), pixGetWidth(pix2),
	                 pixGetHeight(pix1), pixGetHeight(pix2),
	                 pixGetDepth(pix1), pixGetDepth(pix2));
#endif /* DEBUG */
	return 0;
    }
    else
	return 1;
}



/*--------------------------------------------------------------------*
 *                                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;
}


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_uint32  
pixGetYRes(PIX  *pix)
{
    PROCNAME("pixGetYRes");

    if (!pix)
	return ERROR_INT("pix not defined", procName, 0);

    return pix->yres;
}


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_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);

    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  
pixSetYRes(PIX      *pix,
	   l_uint32  res)
{
    PROCNAME("pixSetYRes");

    if (!pix)
	return ERROR_INT("pix not defined", procName, 1);

    pix->yres = res;
    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);

    pixSetInputFormat(pixd, pixGetInputFormat(pixs));
    return 0;
}


char *  
pixGetText(PIX  *pix)
{
    PROCNAME("pixGetText");

    if (!pix)
	return (char *)ERROR_PTR("pix not defined", procName, NULL);

    return pix->text;
}


/*!
 *  pixSetText()
 *
 *      Input:  pix
 *              textstring
 *      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((void *)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);

    pixSetText(pixd, pixGetText(pixs));
    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)
        return 0;

    pixcmapDestroy(&cmap);
    pix->colormap = NULL;
    return 0;
}
	

PIXCMAP *  
pixGetColormap(PIX  *pix)
{
    PROCNAME("pixGetColormap");

    if (!pix)
	return (PIXCMAP *)ERROR_PTR("pix not defined", procName, NULL);

    return pix->colormap;
}


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;
}


l_uint32 *
pixGetData(PIX  *pix)
{
    PROCNAME("pixGetData");

    if (!pix)
	return (l_uint32 *)ERROR_PTR("pix not defined", procName, NULL);

    return pix->data;
}


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;
}


/*--------------------------------------------------------------------*
 *                    Print output for debugging                      *
 *--------------------------------------------------------------------*/
/*!
 *  pixPrintStreamInfo()
 *
 *      Input:  stream
 *              pix
 *              text (an identifying string that is printed)
 *      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)
        return ERROR_INT("text not defined", procName, 1);

    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;
}

