//////////////////////////////////////////////////////////////////////////////
//
//  DITH666.C  - full color dither (to a palette with 6 red, 6 green 6 blue
//               levels)
//
//  NOTE this file contains the 'C' code and DITH666A.ASM has the ASM code.
//
//  This file does the following dithering
//
//      32bpp   -> 8bpp
//      24bpp   -> 8bpp
//      16bpp   -> 8bpp
//
//      8bpp    -> 4bpp     N/I
//      16bpp   -> 4bpp     N/I
//      24bpp   -> 4bpp     N/I
//      32bpp   -> 4bpp     N/I
//
//////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <windowsx.h>
#include "drawdibi.h"
#include "dither.h"

#ifdef _WIN32 // 'C' code for Win32
#define USE_C
#endif

#include "dith666.h"

int         giDitherTableUsage = 0;
LPVOID      glpDitherTable;

STATICFN void Get666Colors(LPBITMAPINFOHEADER lpbi);

//////////////////////////////////////////////////////////////////////////////
//
//
//////////////////////////////////////////////////////////////////////////////

void FAR PASCAL Dither16(LPBITMAPINFOHEADER,LPVOID,int,int,int,int,LPBITMAPINFOHEADER,LPVOID,int,int,LPVOID);
void FAR PASCAL Dither24(LPBITMAPINFOHEADER,LPVOID,int,int,int,int,LPBITMAPINFOHEADER,LPVOID,int,int,LPVOID);
void FAR PASCAL Dither32(LPBITMAPINFOHEADER,LPVOID,int,int,int,int,LPBITMAPINFOHEADER,LPVOID,int,int,LPVOID);

//////////////////////////////////////////////////////////////////////////////
//
//   DitherTableInit()
//
//////////////////////////////////////////////////////////////////////////////

STATICFN LPVOID DitherTableInit()
{
#ifdef DEBUG
    DWORD time = timeGetTime();
#endif

#ifdef XDEBUG
    int X,Y;
    char aBuffer[100];
    char far *pBuffer = aBuffer;

    GetProfileString("DrawDib", "Matrix5", "", aBuffer, sizeof(aBuffer));

    if (aBuffer[0])
    {
        for(Y = 0;Y < 4;Y++)
        {
            for(X = 0;X < 4;X++)
            {
                while(!isdigit(*pBuffer))
                {
                        pBuffer++;
                }

                aHalftone4x4_5[X][Y] = *pBuffer - '0';
                pBuffer++;
            }
        }
    }
#endif

    if (aHalftone8[0][0][0][0] == (BYTE)-1)
    {
        int i,x,y;

        for (x=0; x<4; x++)
            for (y=0; y<4; y++)
                for (i=0; i<256; i++)
                    aHalftone8[0][x][y][i] = (i/51 + (i%51 > aHalftone4x4[x][y]));

        for (x=0; x<4; x++)
            for (y=0; y<4; y++)
                for (i=0; i<256; i++)
                    aHalftone8[1][x][y][i] = 6 * (i/51 + (i%51 > aHalftone4x4[x][y]));

        for (x=0; x<4; x++)
            for (y=0; y<4; y++)
                for (i=0; i<256; i++)
                    aHalftone8[2][x][y][i] = 36 * (i/51 + (i%51 > aHalftone4x4[x][y]));
    }

#ifdef USE_C
    if (aHalftone5[0][0][0][0] == (BYTE)-1)
    {
        int i,x,y,z,n;

        for (x=0; x<4; x++)
            for (y=0; y<4; y++)
                for (z=0; z<256; z++) {
                    n = (z >> 2) & 0x1F;
                    i = n > 0 ? n-1 : 0;
                    aHalftone5[0][x][y][z] = (i/6 + (i%6 > aHalftone4x4_5[x][y]));
                }

        for (x=0; x<4; x++)
            for (y=0; y<4; y++)
                for (z=0; z<256; z++) {
                    n = (z & 0x1F);
                    i = n > 0 ? n-1 : 0;
                    aHalftone5[1][x][y][z] = 6 * (i/6 + (i%6 > aHalftone4x4_5[x][y]));
                }

        for (x=0; x<4; x++)
            for (y=0; y<4; y++)
                for (z=0; z<256; z++) {
                    n = z & 0x1F;
                    i = n > 0 ? n-1 : 0;
                    aHalftone5[2][x][y][z] = 36 * (i/6 + (i%6 > aHalftone4x4_5[x][y]));
                }
    }
#endif

    DPF(("DitherTableInit() took %ldms", timeGetTime() - time));

    return NULL;
}

//////////////////////////////////////////////////////////////////////////////
//
//   DitherInit()
//
//////////////////////////////////////////////////////////////////////////////

LPVOID FAR PASCAL Dither8Init(LPBITMAPINFOHEADER lpbi, LPBITMAPINFOHEADER lpbiOut, DITHERPROC FAR *lpDitherProc, LPVOID lpDitherTable)
{
    UINT x,y,i,r,g,b;
    BYTE FAR *pb;

    Get666Colors(lpbiOut);

    DitherTableInit();

    if (lpDitherTable == NULL)
        lpDitherTable = GlobalAllocPtr(GHND, 256*8*8);

    if (lpDitherTable == NULL)
        return (LPVOID)-1;

    pb = (LPBYTE)lpDitherTable;

    for (y=0; y<8; y++)
    {
        for (i=0; i<256; i++)
        {
            r = ((LPRGBQUAD)(lpbi+1))[i].rgbRed;
            g = ((LPRGBQUAD)(lpbi+1))[i].rgbGreen;
            b = ((LPRGBQUAD)(lpbi+1))[i].rgbBlue;

            for (x=0; x<8; x++)
            {
                *pb++ = DITH8(x,y,r,g,b);
            }
        }
    }

    *lpDitherProc = Dither8;

    return lpDitherTable;
}

//////////////////////////////////////////////////////////////////////////////
//
//   DitherInit()
//
//////////////////////////////////////////////////////////////////////////////

LPVOID FAR PASCAL Dither16Init(LPBITMAPINFOHEADER lpbi, LPBITMAPINFOHEADER lpbiOut, DITHERPROC FAR *lpDitherProc, LPVOID lpDitherTable)
{
    Get666Colors(lpbiOut);

    *lpDitherProc = Dither16;

    DitherTableInit();

#ifndef USE_C
    //
    // we dont need to re-init the dither table, unless it is not ours then
    // we should free it.
    //
    if (lpDitherTable && lpDitherTable != glpDitherTable)
    {
        DitherTerm(lpDitherTable);
        lpDitherTable = NULL;
    }

    //
    // we dont need to re-init table
    //
    if (lpDitherTable != NULL)
        return lpDitherTable;

    if (glpDitherTable)
    {
        giDitherTableUsage++;
        return glpDitherTable;
    }
    else
    {
        //
        //  build a table that maps a RGB555 directly to a palette index
        //  we actualy build 4 tables, we assume a 2x2 dither and build
        //  a table for each position in the matrix.
        //

        UINT x,y,r,g,b;
        BYTE FAR *pb;

#ifdef DEBUG
        DWORD time = timeGetTime();
#endif
        lpDitherTable = GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE, 32768l*4);

        if (lpDitherTable == NULL)
            return (LPVOID)-1;

        glpDitherTable = lpDitherTable;
        giDitherTableUsage = 1;

        for (y=0; y<2; y++)
        {
            if (y == 0)
                pb = (BYTE FAR *)lpDitherTable;
            else
                pb = (BYTE FAR *)((BYTE _huge *)lpDitherTable + 65536);

            for (r=0; r<32; r++)
                for (g=0; g<32; g++)
                    for (b=0; b<32; b++)
                        for (x=0; x<2; x++)
                            *pb++ = DITH31(x,y,r,g,b);
        }

        DPF(("Dither16Init() took %ldms", timeGetTime() - time));
    }
#endif
    return lpDitherTable;
}

//////////////////////////////////////////////////////////////////////////////
//
//   DitherTerm()
//
//////////////////////////////////////////////////////////////////////////////

void FAR PASCAL Dither16Term(LPVOID lpDitherTable)
{
    if (giDitherTableUsage == 0 || --giDitherTableUsage > 0)
        return;

    if (glpDitherTable)
    {
        GlobalFreePtr(glpDitherTable);
        glpDitherTable = NULL;
    }
}

//////////////////////////////////////////////////////////////////////////////
//
//   Dither24Init()
//
//////////////////////////////////////////////////////////////////////////////

LPVOID FAR PASCAL Dither24Init(LPBITMAPINFOHEADER lpbi, LPBITMAPINFOHEADER lpbiOut, DITHERPROC FAR *lpDitherProc, LPVOID lpDitherTable)
{
    Get666Colors(lpbiOut);

    *lpDitherProc = Dither24;

    return DitherTableInit();
}

//////////////////////////////////////////////////////////////////////////////
//
//   Dither24Term()
//
//////////////////////////////////////////////////////////////////////////////

void FAR PASCAL Dither24Term(LPVOID lpDitherTable)
{
}

//////////////////////////////////////////////////////////////////////////////
//
//   Dither32Init()
//
//////////////////////////////////////////////////////////////////////////////

LPVOID FAR PASCAL Dither32Init(LPBITMAPINFOHEADER lpbi, LPBITMAPINFOHEADER lpbiOut, DITHERPROC FAR *lpDitherProc, LPVOID lpDitherTable)
{
    Get666Colors(lpbiOut);

    *lpDitherProc = Dither32;

    return DitherTableInit();
}

//////////////////////////////////////////////////////////////////////////////
//
//   Dither32Term()
//
//////////////////////////////////////////////////////////////////////////////

void FAR PASCAL Dither32Term(LPVOID lpDitherTable)
{
}

//////////////////////////////////////////////////////////////////////////////
//
//   GetDithColors() get the dither palette
//
//////////////////////////////////////////////////////////////////////////////

STATICFN void Get666Colors(LPBITMAPINFOHEADER lpbi)
{
    RGBQUAD FAR *prgb = (RGBQUAD FAR *)(((LPBYTE)lpbi) + (UINT)lpbi->biSize);
    int i;

    for (i=0; i<256; i++)
    {
        prgb[i].rgbRed   = pal666[i][0];
        prgb[i].rgbGreen = pal666[i][1];
        prgb[i].rgbBlue  = pal666[i][2];
        prgb[i].rgbReserved = 0;
    }

    lpbi->biClrUsed = 256;
}

#ifdef USE_C

//////////////////////////////////////////////////////////////////////////////
//
//  Dither24   - dither from 24 to 8 using the Table method in 'C' Code
//
//////////////////////////////////////////////////////////////////////////////

void FAR PASCAL Dither24(
    LPBITMAPINFOHEADER biDst,           // --> BITMAPINFO of the dest
    LPVOID             lpDst,           // --> to destination bits
    int                DstX,            // Destination origin - x coordinate
    int                DstY,            // Destination origin - y coordinate
    int                DstXE,           // x extent of the BLT
    int                DstYE,           // y extent of the BLT
    LPBITMAPINFOHEADER biSrc,           // --> BITMAPINFO of the source
    LPVOID             lpSrc,           // --> to source bits
    int                SrcX,            // Source origin - x coordinate
    int                SrcY,            // Source origin - y coordinate
    LPVOID             lpDitherTable)   // dither table.
{
    int x,y;
    BYTE r,g,b;
    UINT wWidthSrc;
    UINT wWidthDst;
    BYTE _huge *pbS;
    BYTE _huge *pbD;

    if (biDst->biBitCount != 8 || biSrc->biBitCount != 24)
        return;

    wWidthSrc = ((UINT)biSrc->biWidth*3+3)&~3;
    wWidthDst = ((UINT)biDst->biWidth+3)&~3;

    pbD = (BYTE _huge *)lpDst + DstX   + (DWORD)(UINT)DstY * (DWORD)wWidthDst;
    pbS = (BYTE _huge *)lpSrc + SrcX*3 + (DWORD)(UINT)SrcY * (DWORD)wWidthSrc;

    wWidthSrc -= DstXE*3;
    wWidthDst -= DstXE;

#define GET24() \
    b = *pbS++; \
    g = *pbS++; \
    r = *pbS++;

    for (y=0; y<DstYE; y++) {

        for (x=0; x<DstXE; x++) {
            GET24(); *pbD++ = DITH8(x,y,r,g,b);
        }

        pbS += wWidthSrc;
        pbD += wWidthDst;
    }
}

//////////////////////////////////////////////////////////////////////////////
//
//  Dither32  - dither from 32 to 8 using the Table method in 'C' Code
//
//////////////////////////////////////////////////////////////////////////////

void FAR PASCAL Dither32(
    LPBITMAPINFOHEADER biDst,           // --> BITMAPINFO of the dest
    LPVOID             lpDst,           // --> to destination bits
    int                DstX,            // Destination origin - x coordinate
    int                DstY,            // Destination origin - y coordinate
    int                DstXE,           // x extent of the BLT
    int                DstYE,           // y extent of the BLT
    LPBITMAPINFOHEADER biSrc,           // --> BITMAPINFO of the source
    LPVOID             lpSrc,           // --> to source bits
    int                SrcX,            // Source origin - x coordinate
    int                SrcY,            // Source origin - y coordinate
    LPVOID             lpDitherTable)   // dither table.
{
    int x,y;
    BYTE r,g,b;
    UINT wWidthSrc;
    UINT wWidthDst;
    BYTE _huge *pbS;
    BYTE _huge *pbD;

    if (biDst->biBitCount != 8 || biSrc->biBitCount != 32)
        return;

    wWidthSrc = ((UINT)biSrc->biWidth*4+3)&~3;
    wWidthDst = ((UINT)biDst->biWidth+3)&~3;

    pbD = (BYTE _huge *)lpDst + DstX   + (DWORD)(UINT)DstY * (DWORD)wWidthDst;
    pbS = (BYTE _huge *)lpSrc + SrcX*4 + (DWORD)(UINT)SrcY * (DWORD)wWidthSrc;

    wWidthSrc -= DstXE*4;
    wWidthDst -= DstXE;

#define GET32() \
    b = *pbS++; \
    g = *pbS++; \
    r = *pbS++; \
    pbS++;

    for (y=0; y<DstYE; y++) {

        for (x=0; x<DstXE; x++)
        {
            GET32();
            *pbD++ = DITH8(x,y,r,g,b);
        }

        pbS += wWidthSrc;
        pbD += wWidthDst;
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Dither16  - dither from 16 to 8 using the Table method in 'C' Code
//
//////////////////////////////////////////////////////////////////////////////

void FAR PASCAL Dither16(
    LPBITMAPINFOHEADER biDst,           // --> BITMAPINFO of the dest
    LPVOID             lpDst,           // --> to destination bits
    int                DstX,            // Destination origin - x coordinate
    int                DstY,            // Destination origin - y coordinate
    int                DstXE,           // x extent of the BLT
    int                DstYE,           // y extent of the BLT
    LPBITMAPINFOHEADER biSrc,           // --> BITMAPINFO of the source
    LPVOID             lpSrc,           // --> to source bits
    int                SrcX,            // Source origin - x coordinate
    int                SrcY,            // Source origin - y coordinate
    LPVOID             lpDitherTable)   // dither table.
{
    int x,y;
    WORD w;
    UINT wWidthSrc;
    UINT wWidthDst;
    BYTE _huge *pbS;
    BYTE _huge *pbD;

    if (biDst->biBitCount != 8 || biSrc->biBitCount != 16)
        return;

    wWidthSrc = ((UINT)biSrc->biWidth*2+3)&~3;
    wWidthDst = ((UINT)biDst->biWidth+3)&~3;

    pbD = (BYTE _huge *)lpDst + DstX   + (DWORD)(UINT)DstY * (DWORD)wWidthDst;
    pbS = (BYTE _huge *)lpSrc + SrcX*2 + (DWORD)(UINT)SrcY * (DWORD)wWidthSrc;

    wWidthSrc -= DstXE*2;
    wWidthDst -= DstXE;

#define GET16() \
    w = *((WORD _huge *)pbS)++;

    for (y=0; y<DstYE; y++) {

        for (x=0; x<DstXE; x++)
        {
            GET16();
            *pbD++ = DITH5(x,y,w);
        }

        pbS += wWidthSrc;
        pbD += wWidthDst;
    }
}

#endif