/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: ilgraybi.c /main/6 1996/10/30 11:09:02 drk $ */
/**---------------------------------------------------------------------
***	
***    (c)Copyright 1991 Hewlett-Packard Co.
***    
***                             RESTRICTED RIGHTS LEGEND
***    Use, duplication, or disclosure by the U.S. Government is subject to
***    restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in
***    Technical Data and Computer Software clause in DFARS 252.227-7013.
***                             Hewlett-Packard Company
***                             3000 Hanover Street
***                             Palo Alto, CA 94304 U.S.A.
***    Rights for non-DOD U.S. Government Departments and Agencies are as set
***    forth in FAR 52.227-19(c)(1,2).
***
***-------------------------------------------------------------------*/

#include <stdlib.h>

#include "ilint.h"
#include "ilpipelem.h"
#include "ilconvert.h"
#include "ilerrors.h"

#ifndef LSB_BIT_ORDER
# define flip_bits(start,n)      /*EMPTY*/
#else
extern void flip_bits(ilPtr start, unsigned n);

extern void
flip_bits(ilPtr start,
          unsigned n )
{
	int	i;
	unsigned char   c;

	for (i = 0; i < n; i++) {
	    c = *start;
	    *start++ = ilBitReverseTable[c];
	}
}
#endif

/*  =========================== BITONAL DITHER CODE =============================== */ 

        /*  Private data for error diffusion to bitonal, inited by Init() function. */
typedef struct {
    long            width;                  /* width of src/dst images */
    long            srcRowBytes;            /* bytes/row of src image */
    ilPtr           pSrcPixels;             /* ptr to start of src pixels */
    long            dstRowBytes;            /* bytes/row of dst image */
    ilPtr           pDstPixels;             /* ptr to start of dst pixels */
    ilBool          blackIsZero;            /* src image: true if 0 is black, else white */
    short          *pError1, *pError2;      /* ptr to error accumulator buffers */
    } ilBiDiffusionPrivRec, *ilBiDiffusionPrivPtr;


        /*  Init() function: malloc and zero error accumulator buffers. */
static ilError ilInitBiDiffusion (
    ilBiDiffusionPrivPtr pPriv,
    ilImageInfo        *pSrcImage,
    ilImageInfo        *pDstImage
    )
{
    pPriv->width = pSrcImage->width;
    pPriv->srcRowBytes = pSrcImage->plane[0].nBytesPerRow;
    pPriv->pSrcPixels = pSrcImage->plane[0].pPixels;
    pPriv->dstRowBytes = pDstImage->plane[0].nBytesPerRow;
    pPriv->pDstPixels = pDstImage->plane[0].pPixels;
    pPriv->blackIsZero = pSrcImage->pDes->blackIsZero;

        /*  Create error accumulator buffers, 2 larger than width because the pixels
            above to the left and right are examined.
        */
    pPriv->pError1 = (short *)IL_MALLOC_ZERO (sizeof (short) * (pPriv->width + 2));
    pPriv->pError2 = (short *)IL_MALLOC_ZERO (sizeof (short) * (pPriv->width + 2));
    if (!pPriv->pError1 || !pPriv->pError2)
        return IL_ERROR_MALLOC;

    return IL_OK;
}

        /*  Cleanup() function: dispose error accumulator buffers. */
static ilError ilCleanupBiDiffusion (
    ilBiDiffusionPrivPtr pPriv
    )
{
    if (pPriv->pError1)
        IL_FREE (pPriv->pError1);
    if (pPriv->pError2)
        IL_FREE (pPriv->pError2);
    return IL_OK;
}

        /*  ----------------------- ilDiffuseGrayToBitonal ------------------------- */
        /*  Dithers using error diffusion to bitonal.
            Input image:  IL_DES_GRAY, IL_FORMAT_BYTE.
            Output image: IL_DES_BITONAL, IL_FORMAT_BIT
        */

        /*  Execute() function: dither and pack the given # of src lines.  */
static ilError ilExecuteBiDiffusion (
    ilExecuteData          *pData,
    long                    dstLine,
    long                   *pNLines             /* ignored on input */
    )
{
#ifdef LSB_BIT_ORDER
# define SPECIAL_MASK_BIT    0x00000001          /* for LSB bit order */
# define SHIFT_MASK(m)       ((m) <<= 1)
#else
# define SPECIAL_MASK_BIT    0x80000000          /* for MSB bit order */
# define SHIFT_MASK(m)       ((m) >>= 1)
#endif
ilBiDiffusionPrivPtr        pPriv;
long                        nLinesM1, nPixelsM1Init, nPixelsM1;
short             *pPrevError, *pError;
int                errorAcc, invert;
long                        srcRowBytes, dstRowBytes;
ilPtr                       pSrcLine, pDstLine;
ilPtr              pSrc;
CARD32             mask, *pDst;
CARD32			    outLong;

    pPriv = (ilBiDiffusionPrivPtr)pData->pPrivate;
    srcRowBytes = pPriv->srcRowBytes;
    pSrcLine = pPriv->pSrcPixels + pData->srcLine * srcRowBytes;
    dstRowBytes = pPriv->dstRowBytes;
    pDstLine = pPriv->pDstPixels + dstLine * dstRowBytes;

    nLinesM1 = *pNLines;
    if (nLinesM1 <= 0)
        return IL_OK;
    nLinesM1--;

    nPixelsM1Init = pPriv->width;
    if (nPixelsM1Init <= 0)
        return IL_OK;
    nPixelsM1Init--;

        /*  Set "invert" to 0 if blackIsZero, else to 0xff, and xor each src pixel
            with invert.  Thus if 0 is white, the xor turns 0 into 255, 1 into 254, etc.
        */
    invert = (pPriv->blackIsZero) ? 0 : 0xff;

        /*  Dither: do Floyd-Steinberg dither (error diffusion) requiring src to be
            256 gray levels.  True error diffusion would map:
                (srcLevels-1)/(dstLevels-1) = 255/1
            but we will use 256/1 to make muls/divs easy.  Use the following:
                    1/16    5/16    3/16
                    7/16     x
            where the fractions indicate what portion of the error from the surrounding
            pixels should be added to this pixels value (x) to determine dst pixel to
            write at "x".
        */
    do {
        pSrc = pSrcLine;
        pSrcLine += srcRowBytes;
        pDst = (CARD32 *)pDstLine;
        pDstLine += dstRowBytes;

            /*  Point to error accumulator buffers and switch, so *pError becomes
                *pPrevError for next line.  Bump ptrs by 1: prev scan line error is
                examined above to left and right: need zero extra value there to handle
                beginning/end of line.
            */
        pError = pPriv->pError1;
        pPrevError = pPriv->pError2;
        pPriv->pError1 = pPrevError;
        pPriv->pError2 = pError;
        pError++; 
        pPrevError++;
        errorAcc = 0;                           /* holds error from pixel to the left */

        mask = SPECIAL_MASK_BIT;

        outLong = 0;
        nPixelsM1 = nPixelsM1Init;
        do {
            errorAcc *= 7;                      /* 7 * error to left */
            errorAcc += *(pPrevError - 1);      /* 1 * error above left */
            errorAcc += 5 * *pPrevError++;      /* 5 * error above */
            errorAcc += 3 * *pPrevError;        /* 3 * error above right */
            errorAcc >>= 4;                     /* /16 */
            errorAcc += *pSrc++ ^ invert;       /* + "x"; w/ blackIsZero handled */
            if (errorAcc >= 128)                /* output white: don't set out bit */
                errorAcc -= 255;                /* sub value of white from errorAcc */
            else                                /* output black: errorAcc -= 0 */
                outLong |= mask;
            *pError++ = errorAcc;               /* store error into buffer */
            if (!(SHIFT_MASK(mask))) {
                flip_bits((ilPtr)&outLong, sizeof(outLong));
                *pDst++ = outLong;
                mask = SPECIAL_MASK_BIT;
                outLong = 0;
                }
            } while (--nPixelsM1 >= 0);

        if (mask != SPECIAL_MASK_BIT) {         /* bits left in outLong; write them */
            flip_bits((ilPtr)&outLong, sizeof(outLong));
            *pDst++ = outLong;
	}

    } while (--nLinesM1 >= 0);

    return IL_OK;
}

        /*  Table exported to ilConvert(), declared in /ilc/ilconvert.h . */
IL_PRIVATE ilConvertRec _ilDiffuseGrayToBitonal = {
    IL_NPF,                                     /* CheckFormat() */
    IL_STD_FORMAT_BYTE,                         /* srcFormatCode */
    IL_NPF,                                     /* AddElement() */
    IL_DES_BITONAL,                             /* pDstDes */
    IL_FORMAT_BIT,                              /* pDstFormat */
    sizeof (ilBiDiffusionPrivRec),              /* nBytesPrivate */
    ilInitBiDiffusion,                          /* Init() */
    ilCleanupBiDiffusion,                       /* Cleanup() */
    IL_NPF,                                     /* Destroy() */
    ilExecuteBiDiffusion                        /* Execute() */
    };


/*  =========================== BITONAL THRESHOLD CODE =============================== */ 

        /*  ----------------------- ilThresholdGrayToBitonal ------------------------- */
        /*  Convert gray to bitonal using a user-defined threshold.
            Input image:  IL_DES_GRAY, IL_FORMAT_BYTE.
            Output image: IL_DES_BITONAL, IL_FORMAT_BIT
        */

        /*  Private data for threshold conversion, inited by Init() function. */
typedef struct {
    CARD32         *pThreshold;             /* ptr to threshold for such gray->bi cvts */
    long            width;                  /* width of src/dst images */
    long            srcRowBytes;            /* bytes/row of src image */
    ilPtr           pSrcPixels;             /* ptr to start of src pixels */
    long            dstRowBytes;            /* bytes/row of dst image */
    ilPtr           pDstPixels;             /* ptr to start of dst pixels */
    ilBool          blackIsZero;            /* true if 0 is black, else is white */
    } ilThresholdPrivRec, *ilThresholdPrivPtr;

        /*  AddElement function: save copy of pThreshold (pOptionData to ilConvert())
            into private.  This ptr is dereferenced every time the pipe is run, allowing
            the caller to change the threshold without reforming the pipe.
        */
static ilError ilAddElementThreshold (
    ilThresholdPrivPtr  pPriv,
    unsigned short     *pPalette,       /* not used */
    CARD32             *pThreshold      /* pOptionData to ilConvert() */
    )
{
    pPriv->pThreshold = pThreshold;
    return IL_OK;
}

        /*  Init() function: init the counter of "y" within private; load image pixel
            address and rowBytes into private for faster reference in Execute().
        */
static ilError ilInitThreshold (
    ilThresholdPrivPtr  pPriv,
    ilImageInfo        *pSrcImage,
    ilImageInfo        *pDstImage
    )
{
    pPriv->width = pSrcImage->width;
    pPriv->srcRowBytes = pSrcImage->plane[0].nBytesPerRow;
    pPriv->pSrcPixels = pSrcImage->plane[0].pPixels;
    pPriv->dstRowBytes = pDstImage->plane[0].nBytesPerRow;
    pPriv->pDstPixels = pDstImage->plane[0].pPixels;

    pPriv->blackIsZero = pSrcImage->pDes->blackIsZero;
    return IL_OK;
}

        /*  Execute() function: dither and pack the given # of src lines.
        */
static ilError ilExecuteThreshold (
    ilExecuteData          *pData,
    long                    dstLine,
    long                   *pNLines             /* ignored on input */
    )
{
#ifdef LSB_BIT_ORDER
# define SPECIAL_MASK_BIT    0x00000001          /* for LSB bit order */
# define SHIFT_MASK(m)       ((m) <<= 1)
#else
# define SPECIAL_MASK_BIT    0x80000000          /* for MSB bit order */
# define SHIFT_MASK(m)       ((m) >>= 1)
#endif
ilThresholdPrivPtr          pPriv;
long                        nLinesM1, nPixelsM1Init;
long                        srcRowBytes, dstRowBytes;
ilPtr                       pSrcLine, pDstLine;
long               nPixelsM1;
ilPtr              pSrc;
CARD32             mask, *pDst;
CARD32			    outLong;
ilByte             threshold;

    pPriv = (ilThresholdPrivPtr)pData->pPrivate;
    threshold = *pPriv->pThreshold;
    srcRowBytes = pPriv->srcRowBytes;
    pSrcLine = pPriv->pSrcPixels + pData->srcLine * srcRowBytes;
    dstRowBytes = pPriv->dstRowBytes;
    pDstLine = pPriv->pDstPixels + dstLine * dstRowBytes;

    nLinesM1 = *pNLines;
    if (nLinesM1 <= 0)
        return IL_OK;;
    nLinesM1--;
    nPixelsM1Init = pPriv->width;
    if (nPixelsM1Init <= 0)
        return IL_OK;
    nPixelsM1Init--;

        /*  For each pixel, compare to the threshold and output black if 
            < threshold (0 = black) or >= threshold (0 = white).
        */
    do {
        pSrc = pSrcLine;
        pSrcLine += srcRowBytes;
        pDst = (CARD32 *)pDstLine;
        pDstLine += dstRowBytes;
        mask = SPECIAL_MASK_BIT;
        outLong = 0;
        nPixelsM1 = nPixelsM1Init;

        if (pPriv->blackIsZero) {
            do {
                if (*pSrc++ < threshold)
                     outLong |= mask;
                if (! SHIFT_MASK(mask)) {
                    flip_bits((ilPtr)&outLong, sizeof(outLong));
                    *pDst++ = outLong;
                    mask = SPECIAL_MASK_BIT;
                    outLong = 0;
                    }
                } while (--nPixelsM1 >= 0);
            }
        else {
            do {
                if (*pSrc++ >= threshold)
                     outLong |= mask;
                if (! SHIFT_MASK(mask)) {
                    flip_bits((ilPtr)&outLong, sizeof(outLong));
                    *pDst++ = outLong;
                    mask = SPECIAL_MASK_BIT;
                    outLong = 0;
                    }
                } while (--nPixelsM1 >= 0);
            }

            /*  If mask != left bit on, some bits in outLong; output them.  Next line.
            */
        if (mask != SPECIAL_MASK_BIT) {
            flip_bits((ilPtr)&outLong, sizeof(outLong));
            *pDst++ = outLong;
          }

        } while (--nLinesM1 >= 0);

    return IL_OK;
}

        /*  Table exported to ilConvert(), declared in /ilc/ilconvert.h .
            Convert using a user-supplied threshold.
            Input image:  IL_DES_GRAY (either form of blackIsZero), IL_FORMAT_BYTE.
            Output image: IL_DES_BITONAL, IL_FORMAT_BIT
        */
IL_PRIVATE ilConvertRec _ilThresholdGrayToBitonal = {
    IL_NPF,                                     /* CheckFormat() */
    IL_STD_FORMAT_BYTE,                         /* srcFormatCode */
    ilAddElementThreshold,                      /* AddElement() */
    IL_DES_BITONAL,                             /* pDstDes */
    IL_FORMAT_BIT,                              /* pDstFormat */
    sizeof (ilThresholdPrivRec),                /* nBytesPrivate */
    ilInitThreshold,                            /* Init() */
    IL_NPF,                                     /* Cleanup() */
    IL_NPF,                                     /* Destroy() */
    ilExecuteThreshold                          /* Execute() */
    };

