/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = EDDCDITH                                       */
/*                                                                    */
/*   Description     = Display Device Driver minor function handler   */
/*                                                                    */
/*                                                                    */
/*   Function        = Create dither color pattern                    */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/

#define INCL_WINSYS
#define INCL_DDIMISC

#include <eddinclt.h>

#include <eddbcone.h>
#include <eddmcone.h>
#include <eddccone.h>

#include <edddtypt.h>

#include <eddcextf.h>
#include <eddgextf.h>
#include <eddmextf.h>

#include <eddhcone.h>
#include <eddhtype.h>
#include <eddhextf.h>
#include <eddhmacr.h>

#include <hwaccess.h>

#include <fudgepal.h>

extern COLORTABLETYPE        DirectSpecialColorTable[];
extern BitmapHeader          NewPatternTable[];
extern HDC                   ColorTableRealized;
extern BOOL                  DitheredPattern;
extern BOOL                  SystemDithering;
extern RGB2                  LastRGBDithered;
extern PRGB2                 DirectDeviceDefaultPalette;
extern PRGB2                 MemoryDeviceDefaultPalette;
PRGB2                        LastPaletteDithered;

extern DDTType  DDT;
extern BOOL     fRealizeSupported;

typedef BYTE    DitherMatrix[DITHMATRIX_XSIZE][DITHMATRIX_YSIZE];
extern BYTE     ColorPat[DITHMATRIX_XSIZE][DITHMATRIX_YSIZE];
extern BYTE     MonoPat[MONODITH_XSIZE*MONODITH_YSIZE];
extern BYTE     dithermatrix[DITHMATRIX_XSIZE][DITHMATRIX_YSIZE];

extern BYTE     mono_table[];
extern BYTE     GreyDither[32];
extern BYTE     GreyToIndex[4];

extern SHORT    softDrawInUse;
extern BOOL     fDitherInVRAM;

extern ULONG    ulDirectDeviceDefaultPaletteSize;
extern USHORT   SizeOfHWPalette;


/**********************************************************************/
/* Define these function prototypes here because they are forward     */
/* referenced but only used within this module.                       */
/**********************************************************************/
VOID SetUpMonoDither(RGB2);
VOID SetUpNonMonoDither(RGB2);


/**********************************************************************/
/* This assembler routine can be replaced by these 2 calls.           */
/* (An optimization may be to combine them as just one call since     */
/* we actually know that ColorPat and MonoPat are together in memory) */
/**********************************************************************/
#define eddh_BltDitheredToVRAM()                                       \
    CopyMemoryToVRAM (ColorPat,                                        \
                      NewPatternTable[PATSYM_DITHERED].BMPhys,         \
                      NewPatternTable[PATSYM_DITHERED].Info.HWWidth,   \
                      NewPatternTable[PATSYM_DITHERED].Info.HWHeight,  \
                      NewPatternTable[PATSYM_DITHERED].Info.HWFormat); \
    CopyMemoryToVRAM (MonoPat,                                         \
                      NewPatternTable[PATSYM_MONODITH].BMPhys,         \
                      NewPatternTable[PATSYM_MONODITH].Info.HWWidth,   \
                      NewPatternTable[PATSYM_MONODITH].Info.HWHeight,  \
                      NewPatternTable[PATSYM_MONODITH].Info.HWFormat);



/**********************************************************************/
/* routine to set up a dithered color                                 */
/* returns TRUE if one was set up                                     */
/* (Global variable DitheredPattern is also set to the same as the    */
/* return value).                                                     */
/**********************************************************************/
BOOL eddc_SetDithColourAndPattern(ULONG Color)
{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    RGB2    RGBColor;                   /* RGB value of color         */

    /******************************************************************/
    /* Assume we do not dither, change if we decide to after all...   */
    /******************************************************************/
    DitheredPattern = FALSE;

    /******************************************************************/
    /* use dithering if all of these are satisfied                    */
    /*  1: we are not drawing to a 16bpp bitmap                       */
    /*  2: current pattern is the default (solid)                     */
    /*  3: we are not using a palette manager palette or              */
    /*     we are only drawing to a 1bpp bitmap                       */
    /*  4: dithering is allowed (purecolor not specified)             */
    /*  5: realize is not supported or LCT is not realizable          */
    /*  6: we are drawing to memory or no color table is currently    */
    /*     realized                                                   */
    /*  7: mode is RGB and color is not an actual color or            */
    /*     mode is otherwise and color at this index was not exact    */
    /*                                                                */
    /* We also have a final test which allows us to prevent dithering */
    /* of negative (system) colors by clearing the SystemDithering    */
    /* flag.  This is normally set (and would only be reset via the   */
    /* kernel debugger).                                              */
    /*                                                                */
    /******************************************************************/
    /******************************************************************/
    /* Defect 52760 - We were using the DC bit count as opposed to    */
    /* the actual number of bpp at the hardware level.                */
    /******************************************************************/

    #ifndef   BPP24
    if (   (DDT.BitCount != 16)
    #else
    if (   (DDT.BitCount < 16)
    #endif

        && (pdc->TempPattern == &NewPatternTable[PATSYM_SOLID-1])

        && (   (pdc->DCIColFormat != LCOLF_PALETTE)
            || (pdc->DCISelListEntry->Info.BitCount == 1) )

        && !(pdc->DCIColStatus & LCOL_PURECOLOR)

        && (   (fRealizeSupported == FALSE)
            || !(pdc->DCIColStatus & LCOL_REALIZABLE))

        && ( (pdc->DCIDCType != OD_DIRECT)
            || !ColorTableRealized )

        && (   ((LONG)Color >= 0)
            || SystemDithering )
       )
    {
        /**************************************************************/
        /* First we want to get the RGB we want to dither into        */
        /* RGBColor and check that it does not exist as an exact color*/
        /* already.                                                   */
        /**************************************************************/
        if ( (pdc->DCIColFormat == LCOLF_RGB) && ((LONG)Color >= 0) )
        {
            /**********************************************************/
            /* Search the default palette to see if the color exact   */
            /* color exists.                                          */
            /**********************************************************/
            if (ExactDefaultPhysicalIndex(Color) != PAL_ERROR)
            {
                /******************************************************/
                /* An exact match of the color already exists.        */
                /******************************************************/
                return(FALSE);
            }
            RGBColor = *((PRGB2)&Color);
        }
        else if ((LONG)Color >= 0 && Color < pdc->DCIColTabSize)
        {
            /**********************************************************/
            /* This is a color in the logical color table.            */
            /**********************************************************/
            RGBColor = pdc->DCIColorTable[Color].LogRGB;

            if (((*(PULONG)&(pdc->DCIDeviceDefaultPalette[
                                    pdc->DCIColorTable[Color].PhyIndex]))
                & 0x00FFFFFF) == ((*(PULONG)&RGBColor) & 0x00FFFFFF) )
            {
                /******************************************************/
                /* The physical index specified in the color table    */
                /* found us an exact match of the color.              */
                /******************************************************/
                return(FALSE);
            }
        }
        else if (((LONG)Color <= SYSCLR_HELPHILITE) &&
                 ((LONG)Color > (SYSCLR_HELPHILITE - SYSCLR_CSYSCOLORS)))
        {
            /**********************************************************/
            /* This is a system color                                 */
            /**********************************************************/
            RGBColor = DirectSpecialColorTable[-((LONG)Color)].LogRGB;

            if (((*(PULONG)&(pdc->DCIDeviceDefaultPalette[
                           DirectSpecialColorTable[-((LONG)Color)].PhyIndex]))
                & 0x00FFFFFF) == ((*(PULONG)&RGBColor) & 0x00FFFFFF) )
            {
                /******************************************************/
                /* The physical index specified in the special        */
                /* color table found us an exact match of the color.  */
                /******************************************************/
                return(FALSE);
            }
        }
        else
        {
            /**********************************************************/
            /* this color is either invalid or one of the special     */
            /* low value negative ones which we dont dither           */
            /**********************************************************/
            return(FALSE);
        }

        /**************************************************************/
        /* We must dither the pattern.                                */
        /**************************************************************/
        DitheredPattern = TRUE;

        /**************************************************************/
        /* Just be sure we dont have any odd options bits set.        */
        /**************************************************************/
        RGBColor.fcOptions = 0;

        /**************************************************************/
        /* Even though we are about to set up a pattern in memory     */
        /* which is on occasions bltted to the hardware, there is no  */
        /* need to wait for the hw to complete because it always uses */
        /* the copy in VRAM.  (edd_BltDitheredToVRAM waits for        */
        /* hardware locally.)                                         */
        /**************************************************************/

        if (pdc->DCISelListEntry->Info.BitCount == 1)
        {
            /**********************************************************/
            /* We are at 1 bit per pel so set up a the mono dithered  */
            /* pattern.                                               */
            /**********************************************************/
            SetUpMonoDither(RGBColor);
            fDitherInVRAM = FALSE;
        }
        else if (   (URGB(RGBColor) == URGB(LastRGBDithered))
                 && (pdc->DCIDeviceDefaultPalette == LastPaletteDithered) )
        {
            /**********************************************************/
            /* This RGB is the same as the last RGB we dithered (in   */
            /* color) and we are using the same default palette so we */
            /* can just reuse the existing values in the dither       */
            /* matrix. (Note we only ever dither using one of the     */
            /* device default palettes).                              */
            /**********************************************************/
#ifdef FIREWALLS
            /**********************************************************/
            /* This is a good place to check that the DCs device      */
            /* default palette has been set up correctly.             */
            /**********************************************************/
            if (pdc->DCIDCType == OD_DIRECT)
            {
                if (pdc->DCIDeviceDefaultPalette != DirectDeviceDefaultPalette)
                {
                    haltproc();
                }
            }
            else
            {
                if (pdc->DCIDeviceDefaultPalette != MemoryDeviceDefaultPalette)
                {
                    haltproc();
                }
            }
#endif
        }
        /**************************************************************/
        /* We must do a color dither.                                 */
        /**************************************************************/
        else
        {
            SetUpNonMonoDither(RGBColor);
            LastRGBDithered = RGBColor;
            LastPaletteDithered = pdc->DCIDeviceDefaultPalette;
            fDitherInVRAM = FALSE;
#ifdef FIREWALLS
            /**********************************************************/
            /* This is a good place to check that the DCs device      */
            /* default palette has been set up correctly.             */
            /**********************************************************/
            if (pdc->DCIDCType == OD_DIRECT)
            {
                if (pdc->DCIDeviceDefaultPalette != DirectDeviceDefaultPalette)
                {
                    haltproc();
                }
            }
            else
            {
                if (pdc->DCIDeviceDefaultPalette != MemoryDeviceDefaultPalette)
                {
                    haltproc();
                }
            }
#endif
        }

        /**************************************************************/
        /* If we set up the dither pattern then copy it to VRAM if    */
        /* necessary.                                                 */
        /**************************************************************/
        if (DitheredPattern)
        {
            /**********************************************************/
            /* Only blt to VRAM if we are in hardware drawing mode.   */
            /* Set the fDitherInVRAM flag accordingly so next time    */
            /* if we use the same color then we will know where it is.*/
            /**********************************************************/
            if (!softDrawInUse)
            {
                eddh_BltDitheredToVRAM();
                fDitherInVRAM = TRUE;
            }
        }

    } /* current pattern is the default */

    /******************************************************************/
    /* Return TRUE if we have set up a dither pattern.                */
    /******************************************************************/
    return(DitheredPattern);

} /* eddc_SetDithColourAndPattern */


VOID SetUpMonoDither(RGB2 RGBColor)
{
    ULONG   Grey;
    ULONG   i;

    /******************************************************************/
    /* basically we'll use a color to mono scaling on the original    */
    /* color, then we'll use an appropriate pattern based on that     */
    /* scaling                                                        */
    /******************************************************************/
    /******************************************************************/
    /* the weighting used going from color to mono is                 */
    /*      Red     0.30                                              */
    /*      Green   0.59                                              */
    /*      Blue    0.11                                              */
    /******************************************************************/
    Grey = ((ULONG)RGBColor.bRed   * 30 +
            (ULONG)RGBColor.bGreen * 59 +
            (ULONG)RGBColor.bBlue  * 11   ) / 100;

    /******************************************************************/
    /* currently Grey holds a number 0 - 255                          */
    /* we want it in the range 0 - 64                                 */
    /* formula to include rounding is                                 */
    /*    TRUNC(((g * 64) + 0.5) / 255)                               */
    /* which we rearrange to keep in integer arithmetic.              */
    /******************************************************************/
    Grey = ((Grey * 128) + 255) / 510;

    /******************************************************************/
    /* lookup table only stores values 0 - 32                         */
    /* values for 33-64 are inverse of 31 - 0                         */
    /******************************************************************/
    if (Grey > 32)
    {
        Grey = 64 - Grey;
        for (i = 0; i < MONO_DITHER_BUFFER_SIZE; i++)
        {
            MonoPat[i] = (BYTE)
              ~(mono_table[Grey*8 + MONO_DITHER_BUFFER_SIZE-i-1]);
        }
    }
    else
    {
        for (i = 0; i < MONO_DITHER_BUFFER_SIZE; i++)
        {
            MonoPat[i] = mono_table[Grey*8 + i];
        }
    }
}


ULONG SingleComponentDitherLevels(ULONG ulNumberOfRealLevels,
                                  BYTE  bDesiredLevel)
{
    ULONG   ulDitheredLevel;
    ULONG   ulRealLevel1;
    ULONG   ulRealLevel2;
    ULONG   ulNumberOfLevel2s;
    BYTE    Levels[4];

    /******************************************************************/
    /* We will only dither using two adjacent real levels at once.    */
    /* This gives us a total number of dithered levels of:            */
    /*                                                                */
    /*     dithered levels = 4 * (number of real levels - 1) + 1      */
    /*                                                                */
    /* If we calculate the dithered level in the range of 0 to number */
    /* dithered levels - 1  then:                                     */
    /*                                                                */
    /* The two real levels we will use in the dithered pattern are:   */
    /*      level1 = dithered level / 4                               */
    /*      level2 = level1 + 1                                       */
    /*                                                                */
    /* The proportions they are used in are given by:                 */
    /*      number of level1 pels = 4 - number of level2 pels         */
    /*      number of level2 pels = dithered level % 4                */
    /*                                                                */
    /******************************************************************/

    /******************************************************************/
    /* The current bDesiredLevel value we have is in the range 0 to   */
    /* 255.  We must first scale this to be in the range of 0 to      */
    /* number of dithered levels - 1.                                 */
    /******************************************************************/
    ulDitheredLevel =
      bDesiredLevel * ( 2 * ( 4 * (ulNumberOfRealLevels-1) ) +1 ) / 510;


    /******************************************************************/
    /* Now we have the dithered level in the correct range.  Work out */
    /* the two real levels we are going to use and the proportions in */
    /* which to use them.                                             */
    /******************************************************************/
    ulNumberOfLevel2s = ulDitheredLevel % 4;

    ulRealLevel1 = ulDitheredLevel / 4;
    if (ulNumberOfLevel2s != 0)
    {
        ulRealLevel2 = ulRealLevel1 + 1;
    }

    /******************************************************************/
    /* Now put the two levels into the our array in the appropriate   */
    /* places.                                                        */
    /******************************************************************/
    switch (ulNumberOfLevel2s)
    {
        case 0:
            Levels[3] = (BYTE)ulRealLevel1;
        case 1:
            Levels[2] = (BYTE)ulRealLevel1;
        case 2:
            Levels[1] = (BYTE)ulRealLevel1;
        case 3:
            Levels[0] = (BYTE)ulRealLevel1;
    }
    switch (ulNumberOfLevel2s)
    {
        case 3:
            Levels[1] = (BYTE)ulRealLevel2;
        case 2:
            Levels[2] = (BYTE)ulRealLevel2;
        case 1:
            Levels[3] = (BYTE)ulRealLevel2;
    }

    /******************************************************************/
    /* Return the 4 level values as a ULONG for convenience.          */
    /******************************************************************/
    return( *(PULONG)Levels );
}


VOID SetUpGreyDither(RGB2 RGBColor)
{
    BYTE    bNumberOfRealGreys;
    BYTE    bFirstGreyIndex;
    BYTE    bGreyLevel;
    ULONG   ulPaletteSize;
    ULONG   ulFourGreyLevels;
    ULONG   i;

#ifdef FIREWALLS
    if ((RGBColor.bRed != RGBColor.bBlue) || (RGBColor.bRed != RGBColor.bGreen))
    {
        /**************************************************************/
        /* We have not been passed a grey RGB color.                  */
        /**************************************************************/
        haltproc();
    }
#endif

    /******************************************************************/
    /* We must check which device default palette we are using.  If   */
    /* we are drawing to memory then we will be using the full size   */
    /* 256 entry device default palette.  If we are drawing to the    */
    /* screen then we may be using one of the reduced size device     */
    /* default palettes.                                              */
    /******************************************************************/
    if (pdc->DCIDCType == OD_DIRECT)
    {
        switch (ulDirectDeviceDefaultPaletteSize)
        {
            case 16:
                bFirstGreyIndex = 7;
                bNumberOfRealGreys = 2;
                RGBColor.bRed =
                  (35+(((ULONG)RGBColor.bRed)/4))*((ULONG)RGBColor.bRed)/100;
                ulPaletteSize=16;
                break;
            case 32:
                bFirstGreyIndex = 13;
                bNumberOfRealGreys = 6;
                if (RGBColor.bRed < 204)
                {
                    RGBColor.bRed = ((ULONG)RGBColor.bRed)*9/10;
                }
                else if (RGBColor.bRed < 224)
                {
                    RGBColor.bRed = (9*((ULONG)RGBColor.bRed)/5)-185;
                }
                else
                {
                    RGBColor.bRed = (6*((ULONG)RGBColor.bRed)/5)-51;
                }
                ulPaletteSize=32;
                break;
            case 64:
                bFirstGreyIndex = 30;
                bNumberOfRealGreys = 4;
                if (RGBColor.bRed < 204)
                {
                    RGBColor.bRed =
                     (51+(((ULONG)RGBColor.bRed)/4))*((ULONG)RGBColor.bRed)/100;
                }
                ulPaletteSize=64;
                break;
            case 128:
                bFirstGreyIndex = 60;
                bNumberOfRealGreys = 8;
                ulPaletteSize=128;
                break;
            case 256:
                bFirstGreyIndex = 112;
                bNumberOfRealGreys = 32;
                ulPaletteSize=256;
                break;
            default:
                /******************************************************/
                /* The device default palette size is not 16, 32, 64, */
                /* 128, or 256 entries - something has gone badly     */
                /* wrong!                                             */
                /******************************************************/
#ifdef FIREWALLS
                haltproc();
#endif
                return;
        }

    }
    else
    {
        /**************************************************************/
        /* We are not drawing to the screen so we must use the        */
        /* largest device default palette we can.  If we are at 8     */
        /* bits per pel then this will be the 256 entry full size     */
        /* device default palette.  If we are at 4 bits per pel then  */
        /* this will be the 16 entry (reduced size) device default    */
        /* palette.                                                   */
        /**************************************************************/
        if (DDT.BitCount == 4)
        {
            bFirstGreyIndex = 7;
            bNumberOfRealGreys = 2;
            ulPaletteSize=16;
        }
        else
        {
            bFirstGreyIndex = 112;
            bNumberOfRealGreys = 32;
            ulPaletteSize=256;
        }
    }

    /******************************************************************/
    /* Now call off to find the four grey levels we want to use within*/
    /* the dither matrix. (We will dither with all of the real greys  */
    /* and black and white as well).                                  */
    /******************************************************************/
    ulFourGreyLevels =
        SingleComponentDitherLevels(bNumberOfRealGreys+2, RGBColor.bRed);


    for (i = 0; i<4; i++ )
    {
        bGreyLevel = (BYTE)ulFourGreyLevels;
        ulFourGreyLevels = ulFourGreyLevels>>8;

        /**************************************************************/
        /* We want to convert the grey level into an index into the   */
        /* device default palette.                                    */
        /**************************************************************/
        if (bGreyLevel == (BYTE)0)
        {
            /**********************************************************/
            /* bGreyLevel is black.  This is always entry 0 in the    */
            /* palette.  So we do not need to do anything here.       */
            /**********************************************************/
        }
        else if (bGreyLevel == bNumberOfRealGreys+(BYTE)1)
        {
            /**********************************************************/
            /* bGreyLevel is white.  This is always the top entry in  */
            /* the palette.                                           */
            /**********************************************************/
            bGreyLevel = (BYTE)(SizeOfHWPalette-(USHORT)1);
        }
        else if (bGreyLevel > (bNumberOfRealGreys/(BYTE)2))
        {
            /**********************************************************/
            /* The grey we want is in the top half of the palette.    */
            /**********************************************************/
            bGreyLevel += SizeOfHWPalette - bFirstGreyIndex - bNumberOfRealGreys - (BYTE)1;
        }
        else
        {
            /**********************************************************/
            /* The grey we want is in the bottom half of the palette. */
            /**********************************************************/
            bGreyLevel += bFirstGreyIndex - (BYTE)1;
        }

        /**************************************************************/
        /* The grey level is now converted into an index into the     */
        /* current device default palette. Now we must put the index  */
        /* into the dither matrix.  We want to put these in the       */
        /* matrix in the order (0,0), (1,1), (0,1), (1, 0) to avoid   */
        /* the possibilty of horizontal or vertical lines in the      */
        /* dither.                                                    */
        /**************************************************************/
        ColorPat[i%2][((i+1)/2)%2] = bGreyLevel;
    }

    /******************************************************************/
    /* Translate the dither matrix to the new palette order (see      */
    /* fudgepal.c).                                                   */
    /******************************************************************/
    FudgeDitherPattern(ulPaletteSize);
}

VOID SetUp16ColorDither(RGB2 RGBColor)
{
    ULONG          i,j,temp;           /* loop variables              */
    ULONG          Rpart,Gpart,Bpart;  /* red,green,blue components   */
    BYTE           PelCount,PelsLeft;  /* counters                    */
    RGB2           Pattern[4];         /* temporary holding bay for   */
                                       /* dither RGBs                 */

    /******************************************************************/
    /* Convert the RGB levels to a range from 0 to 8.  (ie 9 levels   */
    /* in total).                                                     */
    /******************************************************************/
    Rpart = (ULONG)(RGBColor.bRed   / 31);
    Gpart = (ULONG)(RGBColor.bGreen / 31);
    Bpart = (ULONG)(RGBColor.bBlue  / 31);

    /******************************************************************/
    /* We can divide the 9 levels (of red, green, and blue            */
    /* components) into combinations of 2, 1, or 0 strength parts     */
    /* (corresponding to FF, 80, and 00), but we can not mix 2 and 1  */
    /* strength components within the same pel.  So we actually have  */
    /* pels which are combinations of 2 and 0 strength components,    */
    /* and pels which are combinations of 1 and 0 strength            */
    /* components.                                                    */
    /**4***************************************************************/
    /******************************************************************/
    /* We will start by initializing the pattern to all 0 strength    */
    /* components (ie.  black) and we can then overwrite with color   */
    /* components later.                                              */
    /******************************************************************/
    Pattern[0].bRed = Pattern[0].bGreen = Pattern[0].bBlue = 0;
    Pattern[1].bRed = Pattern[1].bGreen = Pattern[1].bBlue = 0;
    Pattern[2].bRed = Pattern[2].bGreen = Pattern[2].bBlue = 0;
    Pattern[3].bRed = Pattern[3].bGreen = Pattern[3].bBlue = 0;

    /******************************************************************/
    /* If it is possible to use two 1 pels rather than one 2 pel then */
    /* we should do this because it results in lower contrast within  */
    /* the dither matrix.                                             */
    /******************************************************************/

    /******************************************************************/
    /* Loop round while we have to do at least one 2 strength         */
    /* component.  (ie the number of parts left for one of the        */
    /* components is more than the number of pels left).              */
    /******************************************************************/
    for (PelCount = 0, PelsLeft = 4;
            (PelsLeft != 0)
         && ( (Rpart>PelsLeft)
            ||(Gpart>PelsLeft)
            ||(Bpart>PelsLeft));
        PelCount++, PelsLeft-- )
    {
        /**************************************************************/
        /* At this point we are committed to using 2 strength         */
        /* components for this pel, so the number of pels we have     */
        /* left to do 1 strength components with is now PelsLeft-1.   */
        /**************************************************************/
        if (Rpart>(PelsLeft-(BYTE)1))
        {
            Pattern[PelCount].bRed = 0xFF;
            Rpart = Rpart-(BYTE)2;
        }
        if (Gpart>(PelsLeft-(BYTE)1))
        {
            Pattern[PelCount].bGreen = 0xFF;
            Gpart = Gpart-(BYTE)2;
        }
        if (Bpart>(PelsLeft-(BYTE)1))
        {
            Pattern[PelCount].bBlue = 0xFF;
            Bpart = Bpart-(BYTE)2;
        }
    }
    /******************************************************************/
    /* Now do the loop for any remaing 1 strength components for the  */
    /* remaining pels.                                                */
    /******************************************************************/
    while (PelsLeft!=0)
    {
        if (Rpart!=0)
        {
            Pattern[PelCount].bRed = 0x80;
            Rpart--;
        }
        if (Gpart!=0)
        {
            Pattern[PelCount].bGreen = 0x80;
            Gpart--;
        }
        if (Bpart!=0)
        {
            Pattern[PelCount].bBlue = 0x80;
            Bpart--;
        }
        PelsLeft--;
        PelCount++;
    }

    /******************************************************************/
    /* Each entry in the pattern array now contains an RGB            */
    /* combination that exists in the default palette.  Find that     */
    /* entry and load the dither pattern with the appropriate index.  */
    /* We want to load the pattern in a predictably color order so    */
    /* that we avoid edge join problems where two dithers meet. We    */
    /* will do this by sorting the dither using the index values.     */
    /* (This works quite well because of the arrangement of the       */
    /* colors in the 16 entry device default palette).                */
    /******************************************************************/
    for (i = 0; i < 4; i++)
    {
        ColorPat[i%2][i/2] =
                  (BYTE)ExactDefaultPhysicalIndex(*(PULONG)&Pattern[i]);
#ifdef FIREWALLS
        if (ColorPat[i%2][i/2] == PAL_ERROR)
        {
            /**********************************************************/
            /* The RGB we calculated is not in the device default     */
            /* palette - the assumptions we made were       for some  */
            /* reason. Possibly the palette has been changed in       */
            /* edddata.c or the current palette is not the 16 entry   */
            /* device default palette.                                */
            /**********************************************************/
            haltproc();
        }
#endif
    }
    for (i = 1; i < 4; i++)
    {
        for (j = 3; j >= i; j--)
        {
            if ( ((PBYTE)ColorPat)[j-1] < ((PBYTE)ColorPat)[j] )
            {
                temp = ((PBYTE)ColorPat)[j-1];
                ((PBYTE)ColorPat)[j-1] = ((PBYTE)ColorPat)[j];
                ((PBYTE)ColorPat)[j] = (BYTE)temp;
            }
        }
    }
    temp = ((PBYTE)ColorPat)[1];
    ((PBYTE)ColorPat)[1] = ((PBYTE)ColorPat)[3];
    ((PBYTE)ColorPat)[3] = temp;
}



VOID SetUp32ColorDither(RGB2 RGBColor)
{
    ULONG          i,j,temp;           /* loop variables              */
    ULONG          Rpart,Gpart,Bpart;  /* red,green,blue components   */
    BYTE           PelCount,PelsLeft;  /* counters                    */
    RGB2           Pattern[4];         /* temporary holding bay for   */
                                       /* dither RGBs                 */

    /******************************************************************/
    /* This function is very similar to SetUp16ColorDither because    */
    /* the 32 entry palette is based around the same structure as the */
    /* 16 entry palette.                                              */
    /******************************************************************/

    /******************************************************************/
    /* Convert the supplied RGB levels to a range between 0 and 16.   */
    /* (ie 17 levels in total).                                       */
    /******************************************************************/

    Rpart = ((ULONG)RGBColor.bRed)*2/31;
    Gpart = ((ULONG)RGBColor.bGreen)*2/31;
    Bpart = ((ULONG)RGBColor.bBlue)*2/31;

    /******************************************************************/
    /* We can divide the 16 levels (of red, green, and blue           */
    /* components) into combinations of 4, 3, 2, 1, or 0 strength     */
    /* parts (corresponding to FF, C0, 80, 40, and 00), but we can    */
    /* not mix non-zero strength components within the same pel.  So  */
    /* we actually have pels which are combinations of 4 and 0        */
    /* strength components, and pels which are combinations of 3 and  */
    /* 0 strength components, etc...                                  */
    /******************************************************************/
    /******************************************************************/
    /* We will start by initializing the pattern to all 0 strength    */
    /* components (ie.  black) and we can then overwrite with color   */
    /* components later.                                              */
    /******************************************************************/
    Pattern[0].bRed = Pattern[0].bGreen = Pattern[0].bBlue = 0;
    Pattern[1].bRed = Pattern[1].bGreen = Pattern[1].bBlue = 0;
    Pattern[2].bRed = Pattern[2].bGreen = Pattern[2].bBlue = 0;
    Pattern[3].bRed = Pattern[3].bGreen = Pattern[3].bBlue = 0;

    /******************************************************************/
    /* If it is possible to use two 1 pels rather than one 2 pel then */
    /* we should do this because it results in lower contrast within  */
    /* the dither matrix.                                             */
    /******************************************************************/

    /******************************************************************/
    /* Loop round while we have to do at least one 4 strength         */
    /* component.  ie the number of parts left for one of the         */
    /* components is more than the number of pels left * 3 (the next  */
    /* lower strength component).                                     */
    /******************************************************************/
    for (PelCount = 0, PelsLeft = 4;
            (PelsLeft != 0)
         && ( (Rpart>(PelsLeft*(BYTE)3))
            ||(Gpart>(PelsLeft*(BYTE)3))
            ||(Bpart>(PelsLeft*(BYTE)3)));
        PelCount++, PelsLeft-- )
    {
        /**************************************************************/
        /* At this point we are committed to using 4 strength         */
        /* components for this pel, so the number of pels we have     */
        /* left to do 3 strength components with is now PelsLeft-1.   */
        /**************************************************************/
        if (Rpart>((PelsLeft-(BYTE)1)*(BYTE)3))
        {
            Pattern[PelCount].bRed = 0xFF;
            Rpart = Rpart-(BYTE)4;
        }
        if (Gpart>((PelsLeft-(BYTE)1)*(BYTE)3))
        {
            Pattern[PelCount].bGreen = 0xFF;
            Gpart = Gpart-(BYTE)4;
        }
        if (Bpart>((PelsLeft-(BYTE)1)*(BYTE)3))
        {
            Pattern[PelCount].bBlue = 0xFF;
            Bpart = Bpart-(BYTE)4;
        }
    }

    /******************************************************************/
    /* Loop round the remaining pels while we have to do at least one */
    /* 3 strength component.  ie the number of parts left for one of  */
    /* the components is more than the number of pels left * 2 (the   */
    /* next lower strength component).                                */
    /******************************************************************/
    while (  (PelsLeft != 0)
          && (  (Rpart>(PelsLeft*(BYTE)2))
              ||(Gpart>(PelsLeft*(BYTE)2))
              ||(Bpart>(PelsLeft*(BYTE)2))
             )
          )
    {
        /**************************************************************/
        /* At this point we are committed to using 3 strength         */
        /* components for this pel, so the number of pels we have     */
        /* left to do 2 strength components with is now PelsLeft-1.   */
        /**************************************************************/
        if (Rpart>((PelsLeft-(BYTE)1)*(BYTE)2))
        {
            Pattern[PelCount].bRed = 0xC0;
            Rpart = Rpart-(BYTE)3;
        }
        if (Gpart>((PelsLeft-(BYTE)1)*(BYTE)2))
        {
            Pattern[PelCount].bGreen = 0xC0;
            Gpart = Gpart-(BYTE)3;
        }
        if (Bpart>((PelsLeft-(BYTE)1)*(BYTE)2))
        {
            Pattern[PelCount].bBlue = 0xC0;
            Bpart = Bpart-(BYTE)3;
        }
        PelsLeft--;
        PelCount++;
    }

    /******************************************************************/
    /* Loop round the remaining pels while we have to do at least one */
    /* 2 strength component.  ie the number of parts left for one of  */
    /* the components is more than the number of pels left.           */
    /******************************************************************/
    while (  (PelsLeft != 0)
          && (  (Rpart>PelsLeft)
              ||(Gpart>PelsLeft)
              ||(Bpart>PelsLeft)
             )
          )
    {
        /**************************************************************/
        /* At this point we are committed to using 2 strength         */
        /* components for this pel, so the number of pels we have     */
        /* left to do 1 strength components with is now PelsLeft-1.   */
        /**************************************************************/
        if (Rpart>(PelsLeft-(BYTE)1))
        {
            Pattern[PelCount].bRed = 0x80;
            Rpart = Rpart-(BYTE)2;
        }
        if (Gpart>(PelsLeft-(BYTE)1))
        {
            Pattern[PelCount].bGreen = 0x80;
            Gpart = Gpart-(BYTE)2;
        }
        if (Bpart>(PelsLeft-(BYTE)1))
        {
            Pattern[PelCount].bBlue = 0x80;
            Bpart = Bpart-(BYTE)2;
        }
        PelsLeft--;
        PelCount++;
    }

    /******************************************************************/
    /* Now do the loop for any remaing 1 strength components for the  */
    /* remaining pels.                                                */
    /******************************************************************/
    while (PelsLeft!=0)
    {
        if (Rpart!=0)
        {
            Pattern[PelCount].bRed = 0x40;
            Rpart--;
        }
        if (Gpart!=0)
        {
            Pattern[PelCount].bGreen = 0x40;
            Gpart--;
        }
        if (Bpart!=0)
        {
            Pattern[PelCount].bBlue = 0x40;
            Bpart--;
        }
        PelsLeft--;
        PelCount++;
    }

//    /******************************************************************/
//    /* Each entry in the pattern array now contains an RGB            */
//    /* combination that exists in the default palette.  Find that     */
//    /* entry and load the dither pattern with the appropriate index.  */
//    /* We want to load the pattern in the order [0][0], [1][1],       */
//    /* [0][1], [1],[0] so that we avoid having horizontal/vertical    */
//    /* stripes if possible.                                           */
//    /******************************************************************/
//    for (i = 0; i < 4; i++)
//    {
//        ColorPat[i%2][((i+1)/2)%2] =
//                          (BYTE)NearestDefaultPhysicalIndex(Pattern[i]);
//#ifdef FIREWALLS
//        if (ColorPat[i%2][((i+1)/2)%2] == PAL_ERROR)
//        {
//            /**********************************************************/
//            /* The RGB we calculated is not in the device default     */
//            /* palette - the assumptions we made were       for some  */
//            /* reason. Possibly the palette has been changed in       */
//            /* edddata.c or the current palette is not the 16 entry   */
//            /* device default palette.                                */
//            /**********************************************************/
//            haltproc();
//        }
//#endif
//    }
    /******************************************************************/
    /* Each entry in the pattern array now contains an RGB            */
    /* combination that exists in the default palette.  Find that     */
    /* entry and load the dither pattern with the appropriate index.  */
    /* We want to load the pattern in a predictably color order so    */
    /* that we avoid edge join problems where two dithers meet. We    */
    /* will do this by sorting the dither using the index values.     */
    /* (This works quite well because of the arrangement of the       */
    /* colors in the 32 entry device default palette).                */
    /******************************************************************/
    for (i = 0; i < 4; i++)
    {
        ColorPat[i%2][i/2] =
                  (BYTE)ExactDefaultPhysicalIndex(*(PULONG)&Pattern[i]);
#ifdef FIREWALLS
        if (ColorPat[i%2][i/2] == PAL_ERROR)
        {
            /**********************************************************/
            /* The RGB we calculated is not in the device default     */
            /* palette - the assumptions we made were       for some  */
            /* reason. Possibly the palette has been changed in       */
            /* edddata.c or the current palette is not the 32 entry   */
            /* device default palette.                                */
            /**********************************************************/
            haltproc();
        }
#endif
    }
    for (i = 1; i < 4; i++)
    {
        for (j = 3; j >= i; j--)
        {
            if ( ((PBYTE)ColorPat)[j-1] < ((PBYTE)ColorPat)[j] )
            {
                temp = ((PBYTE)ColorPat)[j-1];
                ((PBYTE)ColorPat)[j-1] = ((PBYTE)ColorPat)[j];
                ((PBYTE)ColorPat)[j] = (BYTE)temp;
            }
        }
    }
    temp = ((PBYTE)ColorPat)[1];
    ((PBYTE)ColorPat)[1] = ((PBYTE)ColorPat)[3];
    ((PBYTE)ColorPat)[3] = temp;
}

VOID SetUpColorDither(RGB2 RGBColor)
{

    BYTE    bRedLevels;
    BYTE    bBlueLevels;
    BYTE    bGreenLevels;
    BYTE    bTotalCombinedLevels;
    BYTE    bRedLevel;
    BYTE    bBlueLevel;
    BYTE    bGreenLevel;
    BYTE    bIndex;
    ULONG   ulPaletteSize;
    ULONG   ulFourRedLevels;
    ULONG   ulFourBlueLevels;
    ULONG   ulFourGreenLevels;
    ULONG   i;

    /******************************************************************/
    /* We must check which device default palette we are using.  If   */
    /* we are drawing to memory then we will be using the full size   */
    /* 256 entry device default palette.  If we are drawing to the    */
    /* screen then we may be using one of the reduced size device     */
    /* default palettes. If we are using either the 16 entry or       */
    /* 32 entry device default palette then we should call off to     */
    /* the special routines to handle dithering with these palettes.  */
    /******************************************************************/
    if (pdc->DCIDCType == OD_DIRECT)
    {
        switch (ulDirectDeviceDefaultPaletteSize)
        {
            case 16:
                SetUp16ColorDither(RGBColor);
                return;
            case 32:
                SetUp32ColorDither(RGBColor);
                return;
            case 64:
                bRedLevels = 4;
                bGreenLevels = 5;
                bBlueLevels = 3;
                ulPaletteSize=64;
                break;
            case 128:
                bRedLevels = 5;
                bGreenLevels = 6;
                bBlueLevels = 4;
                ulPaletteSize=128;
                break;
            case 256:
                bRedLevels = 7;
                bGreenLevels = 8;
                bBlueLevels = 4;
                ulPaletteSize=256;
                break;
            default:
                /******************************************************/
                /* The device default palette size is not 16, 32, 64, */
                /* 128, or 256 entries - something has gone badly     */
                /* wrong!                                             */
                /******************************************************/
#ifdef FIREWALLS
                haltproc();
#endif
                return;
        }

    }
    else
    {
        /**************************************************************/
        /* We are not drawing to the screen so we must use the        */
        /* largest device default palette we can.  If we are at 8     */
        /* bits per pel then this will be the 256 entry full size     */
        /* device default palette.  If we are at 4 bits per pel then  */
        /* this will be the 16 entry (reduced size) device default    */
        /* palette so we should call off to the specific routine      */
        /* to dither with this palette.                               */
        /**************************************************************/
        if (DDT.BitCount == 4)
        {
            SetUp16ColorDither(RGBColor);
            return;
        }
        else
        {
            bRedLevels = 7;
            bGreenLevels = 8;
            bBlueLevels = 4;
            ulPaletteSize=256;
        }
    }

    /******************************************************************/
    /* Now call off to find the four levels of each of the red,       */
    /* green, and blue components we want to use within the dither    */
    /* matrix.                                                        */
    /******************************************************************/
    ulFourRedLevels =
                 SingleComponentDitherLevels(bRedLevels, RGBColor.bRed);
    ulFourBlueLevels =
               SingleComponentDitherLevels(bBlueLevels, RGBColor.bBlue);
    ulFourGreenLevels =
             SingleComponentDitherLevels(bGreenLevels, RGBColor.bGreen);

    /******************************************************************/
    /* Work out the total number of levels resulting in combining     */
    /* all three components.                                          */
    /******************************************************************/
    bTotalCombinedLevels = bRedLevels * bGreenLevels * bBlueLevels;

    for (i = 0; i<4; i++ )
    {
        /**************************************************************/
        /* Get one of each of the three levels. Note that we do not   */
        /* want to pick these up in the same order or we will end up  */
        /* with a high contrast dither matrix.                        */
        /**************************************************************/
        bGreenLevel = ((PBYTE)&ulFourGreenLevels)[i];
        bBlueLevel = ((PBYTE)&ulFourBlueLevels)[(i+1)%4];
        bRedLevel = ((PBYTE)&ulFourRedLevels)[(i+2)%4];

        /**************************************************************/
        /* We want to convert the component levels into an index into */
        /* the device default palette.                                */
        /**************************************************************/
        bIndex =   (bRedLevel * bGreenLevels * bBlueLevels)
                 + (bGreenLevel   * bBlueLevels)
                 + (bBlueLevel);

        /**************************************************************/
        /* If the index is in the top half of the palette then we     */
        /* need to adjust it to allow for the greys which are located */
        /* in the centre of the palette.                              */
        /**************************************************************/
        if (bIndex >= (bTotalCombinedLevels/(BYTE)2))
        {
            bIndex += (BYTE)(SizeOfHWPalette-(USHORT)bTotalCombinedLevels);
        }

        /**************************************************************/
        /* Put the index into the dither matrix.                      */
        /**************************************************************/
        ColorPat[i%2][i/2] = bIndex;
    }

    /******************************************************************/
    /* Translate the dither matrix to the new palette order (see      */
    /* fudgepal.c).                                                   */
    /******************************************************************/
    FudgeDitherPattern(ulPaletteSize);

}



VOID SetUpNonMonoDither(RGB2 RGBColor)
{
    /******************************************************************/
    /* If the RGB passed is a grey then call off to the               */
    /* SetUpGreyDither function to do all the work.                   */
    /******************************************************************/
    if ((RGBColor.bRed == RGBColor.bGreen) &&
        (RGBColor.bRed == RGBColor.bBlue))
    {
        SetUpGreyDither(RGBColor);
    }
    else
    {
        SetUpColorDither(RGBColor);
    }

    #ifndef   _8514
    /******************************************************************/
    /* We assumed 1 pel per byte in the dither matrix but if we are   */
    /* at 4bpp then we get 2 pels per byte so we need to pack the     */
    /* pels into only two bytes.                                      */
    /******************************************************************/
    if (DDT.BitCount == 4)
    {
        /**************************************************************/
        /* Pack the pels into half of the dither matrix.              */
        /**************************************************************/
        ((PBYTE)ColorPat)[0] = (BYTE)
                     (((PBYTE)ColorPat)[0] << 4) | ((PBYTE)ColorPat)[1];
        ((PBYTE)ColorPat)[1] = (BYTE)
                     (((PBYTE)ColorPat)[2] << 4) | ((PBYTE)ColorPat)[3];
    }
    #endif

}




