/****************************Module*Header*******************************\
* Module Name: ylateobj.cxx
*
* This contains the xlate object constructors, destructors and methods
* An xlateobj is used to translate indexes from one palette to another.
*
* When blting between raster surfaces we create a translate object
* between to speed up the BitBlt.  When the destination surface is
* palette managed we have to do a bit more work.
*
* Created: 18-Nov-1990 14:23:55
* Author: Patrick Haluptzok patrickh
*
* Copyright (c) 1990-1999 Microsoft Corporation
\**************************************************************************/

#include "precomp.hxx"

extern "C" VOID vInitXLATE();

#pragma alloc_text(INIT, vInitXLATE)

// These two variables are for the translate components cacheing of
// translate objects.  There access is restricted to being done only
// after the palette semaphore has been grabbed.

XLATETABLE xlateTable[XLATE_CACHE_SIZE];
ULONG ulTableIndex = 0;

TRANSLATE20 defaultTranslate =
{
    0,
    {0,1,2,3,4,5,6,7,8,9,246,247,248,249,250,251,252,253,254,255}
};

// This is the identity xlate object

XLATE256 xloIdent;

/******************************Public*Routine******************************\
* vInitXLATE
*
* initialize the xlateobj component
*
* History:
*  10-Jan-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

extern "C" VOID vInitXLATE()
{
    RtlZeroMemory(xlateTable, (UINT) XLATE_CACHE_SIZE * sizeof(XLATETABLE));

    xloIdent.iUniq    = XLATE_IDENTITY_UNIQ;
    xloIdent.flXlate  = XO_TRIVIAL;
    xloIdent.iSrcType = 0;
    xloIdent.iDstType = 0;
    xloIdent.cEntries = 256;
    xloIdent.pulXlate = xloIdent.ai;
    xloIdent.iBackSrc = 0;
    xloIdent.iForeDst = 0;
    xloIdent.iBackDst = 0;

    //
    // This may seem hackish but XLATE_CACHE_JOURANL ensures that
    // it doesn't cause anything to get unlocked or freed in
    // the destructor.
    //

    xloIdent.lCacheIndex = XLATE_CACHE_JOURNAL;
    xloIdent.ppalSrc = (PPALETTE) NULL;
    xloIdent.ppalDst = (PPALETTE) NULL;
    xloIdent.ppalDstDC = (PPALETTE) NULL;

    xloIdent.hcmXform = NULL;
    xloIdent.lIcmMode = DC_ICM_OFF;

    UINT uiTemp;

    for (uiTemp = 0; uiTemp < 256; uiTemp++)
    {
        xloIdent.ai[uiTemp] = uiTemp;
    }
}


/******************************Public*Routine******************************\
* EXLATEOBJ::vDelete
*
* Deletes the object.
*
* History:
*  Sun 17-Oct-1993 -by- Patrick Haluptzok [patrickh]
* Kill hmgr references.
*
*  26-Nov-1990 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

VOID EXLATEOBJ::vDelete()
{
    if (pxlate != (PXLATE) NULL)
    {
        if (pxlate->lCacheIndex == XLATE_CACHE_JOURNAL)
        {
            if (ppalSrc())
            {
                bDeletePalette((HPAL) ppalSrc()->hGet());
            }
        }

        VFREEMEM(pxlate);
        pxlate = (PXLATE) NULL;
    }
}


/******************************Public*Routine******************************\
* XLATEMEMOBJ destructor
*
* destructor for xlate memory objects
*
* History:
*  Mon 19-Nov-1990 -by- Patrick Haluptzok [patrickh]
* Wrote it.
\**************************************************************************/

XLATEMEMOBJ::~XLATEMEMOBJ()
{
    if (pxlate != (PXLATE) NULL)
    {
        VFREEMEM(pxlate);
        pxlate = (PXLATE) NULL;
    }
}


/******************************Public*Routine******************************\
* XLATE::vMapNewXlate
*
*   Maps a pxlate from being relative to one palette to being relative to
*   another palette.
*
* Arguments:
*
*   ptrans - Translate to map
*
* History:
*  10-Jan-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

VOID
FASTCALL
XLATE::vMapNewXlate(
    PTRANSLATE ptrans
    )
{
    ULONG ulTemp;
    ULONG ulCount = cEntries;

    if (ptrans == NULL)
    {
        //
        // This is the default palette case
        //

        while (ulCount--)
        {
            ulTemp = ai[ulCount];

            if (ulTemp >= 10)
            {
                ai[ulCount] = ulTemp + 236;
            }

            //
            // else
            //
            //
            // do nothing, the first 10 are a 1 to 1 mapping.
            //
        }
    }
    else
    {
        ULONG *pultemp = ai;

        while (ulCount--)
        {
            *pultemp = (ULONG) ptrans->ajVector[*pultemp];
            pultemp++;

            //
            // ai[ulCount] = (ULONG) ptrans->ajVector[ai[ulCount]];
            //
        }
    }
}


/******************************Public*Routine******************************\
* EXLATEOBJ::bMakeXlate
*
*   This is called by SetDIBitsToDevice for the DIB_PAL_COLORS case.
*   This takes the indices in bmiColors and translates them through
*   a logical palette.
*
* Arguments:
*
*   pusIndices      -
*   palDC           -
*   pSurfDst        -
*   cEntriesInit    -
*   cEntriesMax     -
*
* History:
*  17-Apr-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

BOOL
EXLATEOBJ::bMakeXlate(
    PUSHORT  pusIndices,
    XEPALOBJ palDC,
    SURFACE *pSurfDst,
    ULONG    cEntriesInit,
    ULONG    cEntriesMax
    )
{
    ASSERTGDI(pSurfDst != NULL, "ERROR GDI bMakeXlate1");
    ASSERTGDI(palDC.bValid(), "ERROR bMakeXlate 134");

    XEPALOBJ palDst(pSurfDst->ppal());

    ULONG ulTemp;
    ULONG ulIndex;

    //
    // count of colors in DC
    //

    ULONG cColorsDC;
    PXLATE pXlateTemp;

    ASSERTGDI(palDC.cEntries() != 0, "ERROR 0 entry dc palette isn't allowed");

    //
    // Allocate room for the Xlate.
    //

    pxlate = pXlateTemp = (PXLATE) PALLOCNOZ(sizeof(XLATE) +
                                                (sizeof(ULONG) * cEntriesMax),
                                             'tlxG');

    if (pXlateTemp == (PXLATE)NULL)
    {
        WARNING("GDISRV bMakeXlate failed memory allocation\n");
        return(FALSE);
    }

    pXlateTemp->iUniq       = ulGetNewUniqueness(ulXlatePalUnique);
    pXlateTemp->flXlate     = XO_TABLE;
    pXlateTemp->iSrcType    = pXlateTemp->iDstType = 0;
    pXlateTemp->cEntries    = cEntriesMax;
    pXlateTemp->pulXlate    = pXlateTemp->ai;
    pXlateTemp->iBackSrc    = 0;
    pXlateTemp->iForeDst    = 0;
    pXlateTemp->iBackDst    = 0;
    pXlateTemp->lCacheIndex = XLATE_CACHE_INVALID;
    pXlateTemp->ppalSrc     = (PPALETTE) NULL;
    pXlateTemp->ppalDst     = palDst.ppalGet();
    pXlateTemp->ppalDstDC   = palDC.ppalGet();

    RtlZeroMemory(((ULONG *)pXlateTemp->ai) + cEntriesInit,
                  (UINT) (cEntriesMax - cEntriesInit) * sizeof(ULONG));

    PAL_ULONG palentry;
    PTRANSLATE ptrans;
    cColorsDC = palDC.cEntries();

    //
    // Grab the palette semaphore so you can touch the pxlates.
    //

    SEMOBJ  semo(ghsemPalette);

    //
    // Ok fill in the pxlate correctly.
    //

    if ((!palDst.bValid()) || (palDst.bIsPalManaged()))
    {
        //
        // Do the palette managed cases
        //

        if (palDC.bIsPalDefault())
        {
            for(ulTemp = 0; ulTemp < cEntriesInit; ulTemp++)
            {
                //
                // Get the index into the logical palette
                //

                ulIndex = (ULONG) pusIndices[ulTemp];

                //
                // Make sure it isn't too large
                //

                if (ulIndex >= 20)
                {
                    ulIndex = ulIndex % 20;
                }

                if (ulIndex < 10)
                {
                    pXlateTemp->ai[ulTemp] = ulIndex;
                }
                else
                {
                    pXlateTemp->ai[ulTemp] = ulIndex + 236;
                }
            }
        }
        else if (palDst.bValid() && (palDC.ptransCurrent() != NULL))
        {
            //
            // Map through the current translate.
            //

            ptrans = palDC.ptransCurrent();

            for(ulTemp = 0; ulTemp < cEntriesInit; ulTemp++)
            {
                //
                // Get the index into the logical palette
                //

                ulIndex = (ULONG) pusIndices[ulTemp];

                if (ulIndex >= cColorsDC)
                {
                    ulIndex = ulIndex % cColorsDC;
                }

                pXlateTemp->ai[ulTemp] = (ULONG) ptrans->ajVector[ulIndex];

                ASSERTGDI(pXlateTemp->ai[ulTemp] < 256, "ERROR GDI bMakeXlate6");
            }
        }
        else if ((!palDst.bValid()) && (palDC.ptransFore() != NULL))
        {
            //
            // It's a bitmap, Map through the foreground translate.
            //

            ptrans = palDC.ptransFore();

            for(ulTemp = 0; ulTemp < cEntriesInit; ulTemp++)
            {
                //
                // Get the index into the logical palette
                //

                ulIndex = (ULONG) pusIndices[ulTemp];

                if (ulIndex >= cColorsDC)
                {
                    ulIndex = ulIndex % cColorsDC;
                }

                pXlateTemp->ai[ulTemp] = (ULONG) ptrans->ajVector[ulIndex];

             ASSERTGDI(pXlateTemp->ai[ulTemp] < 256, "ERROR GDI bMakeXlate6");
            }
        }
        else
        {
            //
            // It's palette hasn't been realized.  Grab the palette value and
            // then map into default palette if not PC_EXPLICIT entry.
            //

            for (ulTemp = 0; ulTemp < cEntriesInit; ulTemp++)
            {
                //
                // Get the index into the logical palette
                //

                ulIndex = (ULONG) pusIndices[ulTemp];

                if (ulIndex >= cColorsDC)
                {
                    ulIndex = ulIndex % cColorsDC;
                }

                palentry.ul = palDC.ulEntryGet(ulIndex);

                if (palentry.pal.peFlags == PC_EXPLICIT)
                {
                    //
                    // Get the correct index in the surface palette.
                    //

                    ulIndex = palentry.ul & 0x0000FFFF;

                    if (ulIndex >= 256)
                    {
                        ulIndex = ulIndex % 256;
                    }
                }
                else
                {
                    //
                    // Match against the default palette entries.
                    //

                    ulIndex = ((XEPALOBJ) ppalDefault).ulGetNearestFromPalentry(palentry.pal);

                    if (ulIndex >= 10)
                    {
                        ulIndex = ulIndex + 236;
                    }
                }

                pXlateTemp->ai[ulTemp] = ulIndex;
                ASSERTGDI(pXlateTemp->ai[ulTemp] < 256, "ERROR GDI bMakeXlate6");
            }
        }
    }
    else
    {
        //
        // Find the RGB in the palDC (reaching down to palDst for
        // PC_EXPLICIT entries).  Then find closest match in surface palette.
        //

        ASSERTGDI(palDst.bValid(), "ERROR palDst is not valid");

        for (ulTemp = 0; ulTemp < cEntriesInit; ulTemp++)
        {
            //
            // Get the index into the logical palette
            //

            palentry.ul = (ULONG) pusIndices[ulTemp];

            if (palentry.ul >= cColorsDC)
            {
                palentry.ul = palentry.ul % cColorsDC;
            }

            palentry.pal = palDC.palentryGet(palentry.ul);

            if (palentry.pal.peFlags == PC_EXPLICIT)
            {
                //
                // Get the correct index in the surface palette.
                //

                if (palDst.cEntries())
                {
                    palentry.ul = palentry.ul & 0x0000FFFF;

                    if (palentry.ul >= palDst.cEntries())
                    {
                        palentry.ul = palentry.ul % palDst.cEntries();
                    }
                }
                else
                {
                    //
                    // Well what else can we do ?
                    //

                    palentry.ul = 0;
                }

                pXlateTemp->ai[ulTemp] = palentry.ul;
            }
            else
            {
                pXlateTemp->ai[ulTemp] = palDst.ulGetNearestFromPalentry(palentry.pal);
            }
        }
    }

    pxlate->vCheckForTrivial();
    return(TRUE);
}


//
// This describes the overall logic of surface to surface xlates.
// There are basically n types of xlates to worry about:
//
// 1. XO_TRIVIAL - no translation occurs, identity.
//
// 2. XO_TABLE - look up in a table for the correct table translation.
//
//     a. XO_TO_MONO - look in to table for the correct tranlation,
//        but when getting it out of the cache make sure iBackSrc is the
//        same.  iBackSrc goes to 1, everything else to 0.
//     b. XLATE_FROM_MONO - look into table for the correct translation
//        but when getting it out of the cache make sure iBackDst and
//        iForeDst are both still valid. 1 goes to iBackDst, 0 to iForeDst.
//     c. just plain jane indexed to indexed or rgb translation.
//
// 3. XLATE_RGB_SRC - Have to call XLATEOBJ_iXlate to get it translated.
//
//     a. XO_TO_MONO - we have saved the iBackColor in ai[0]
//     b. just plain jane RGB to indexed.  Grab the RGB, find the closest
//        match in Dst palette.  Lot's of work.
//
// 4. XLATE_RGB_BOTH - Bit mashing time.  Call XLATEOBJ_iXlate
//

/******************************Public*Routine******************************\
* EXLATEOBJ::bInitXlateObj
*
*   Cache aware initializer for Xlates.
*
* Arguments:
*
*   hcmXform - hColorTransform, may be NULL
*   pdc      - pointer to DC object
*   palSrc   - src surface palette
*   palDst   - dst surface palette
*   palSrcDC - src DC palette
*   palDstDC - dst DC palette
*   iForeDst - For Mono->Color this is what a 0 goes to
*   iBackDst - For Mono->Color this is what a 1 goes to
*   iBackSrc - For Color->Mono this is the color that goes to
*              1, all other colors go to 0.
*   flCear   - Used for multi-monitor systems
*
* History:
*  Sun 23-Jun-1991 -by- Patrick Haluptzok [patrickh]
*
\**************************************************************************/

BOOL
EXLATEOBJ::bInitXlateObj(
    HANDLE      hcmXform,
    LONG        lIcmMode,
    XEPALOBJ    palSrc,
    XEPALOBJ    palDst,
    XEPALOBJ    palSrcDC,
    XEPALOBJ    palDstDC,
    ULONG       iForeDst,       
    ULONG       iBackDst,       
    ULONG       iBackSrc,       
    ULONG       flCreate        
    )
{
    //
    // Check if blting from compatible bitmap to screen.  This is a common
    // occurrence on 8bpp devices we want to accelerate.
    //

    if (!palSrc.bValid())
    {
        if (
              !palDst.bValid() ||
              (
                palDst.bIsPalManaged() &&
                (
                  (palDstDC.ptransCurrent() == NULL) ||
                  (palDstDC.ptransCurrent() == palDstDC.ptransFore())
                )
              )
           )
        {
            vMakeIdentity();
            return(TRUE);
        }
    }

    //
    // Check if blting from screen to compatible bitmap.
    //

    if (!palDst.bValid())
    {
        if (palSrc.bIsPalManaged())
        {
            //
            // Blting from screen to memory bitmap.  Check for identity.
            //

            if ((palDstDC.ptransCurrent() == NULL) ||
                (palDstDC.ptransCurrent() == palDstDC.ptransFore()))
            {
                vMakeIdentity();
                return(TRUE);
            }
        }
    }

    ASSERTGDI(palDstDC.bValid(), "7GDIERROR vInitXlateObj");
    ASSERTGDI(palDst.bValid() || palSrc.bValid(), "8GDIERROR vInitXlateObj");

    //
    // Check for the easy identity out.  This check does the right thing for
    // ppal==ppal and equal times of two different ppals.
    //

    if ((palSrc.bValid()) && (palDst.bValid()))
    {
        if (palSrc.ulTime() == palDst.ulTime())
        {
            ASSERTGDI(palSrc.cEntries() == palDst.cEntries(), "Xlateobj matching times, different # of entries");
            vMakeIdentity();
            return(TRUE);
        }
    }

    BOOL bCacheXlate = TRUE;

    if (IS_ICM_ON(lIcmMode) && (hcmXform != NULL))
    {
        //
        // If we enable ICM and have a valid color tranform,
        // don't search from cache and don't insert new Xlate to cache.
        //

        bCacheXlate = FALSE;
    }
    else if ((palSrc.bValid()) && (palDst.bValid()))
    {
        if (bSearchCache(palSrc,    palDst,    palSrcDC,    palDstDC,
                         iForeDst,  iBackDst,  iBackSrc,    flCreate))
        {
            return(TRUE);
        }
    }

    pxlate = CreateXlateObject(hcmXform,
                               lIcmMode,
                               palSrc,
                               palDst,
                               palSrcDC,
                               palDstDC,
                               iForeDst,
                               iBackDst,
                               iBackSrc,
                               flCreate);

    if (pxlate != (PXLATE) NULL)
    {
        if (bCacheXlate     &&
            palSrc.bValid() && 
            palDst.bValid() &&
            (!(pxlate->flPrivate & XLATE_RGB_SRC)))
        {
            vAddToCache(palSrc, palDst, palSrcDC, palDstDC);
        }

        return(TRUE);
    }
    else
    {
        return(FALSE);
    }
}


/******************************Public*Routine******************************\
* CreateXlateObject
*
*   Allocates an xlate sets it up.
*
* Arguments:
*
*   palSrc        - src surface palette
*   palDestSurf   - dst surface palette
*   palSrcDC      - src DC palette
*   palDestDC     - dst DC palette
*   iForeDst      - For Mono->Color this is what a 0 goes to
*   iBackDst      - For Mono->Color this is what a 1 goes to
*   iBackSrc      - For Color->Mono this is the color that goes to
*                   1, all other colors go to 0.
*
* History:
*  02-May-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

PXLATE
CreateXlateObject(
    HANDLE      hcmXform,
    LONG        lIcmMode,
    XEPALOBJ    palSrc,
    XEPALOBJ    palDestSurf,
    XEPALOBJ    palSrcDC,
    XEPALOBJ    palDestDC,
    ULONG       iForeDst,
    ULONG       iBackDst,
    ULONG       iBackSrc,
    ULONG       flCreateFlag
    )
{
    ASSERTGDI(palDestDC.bValid(), "CreateXlateObject recieved bad ppalDstDC, bug in GDI");
    ASSERTGDI(palSrc.bValid() || palDestSurf.bValid(), "CreateXlaetObject recieved bad ppalSrc, bug in GDI");
    ASSERTGDI(palSrc.ppalGet() != palDestSurf.ppalGet(), "Didn't recognize ident quickly");

    //
    // cEntry == 0 means the source palette is an RGB/Bitfields type.
    // An invalid palette means it's a compatible bitmap on a palette managed device.
    //

    ULONG ulTemp;
    ULONG cEntry = palSrc.bValid() ? palSrc.cEntries() : 256;

    //
    // Allocate room for the structure.
    //

    PXLATE pxlate = pCreateXlate(cEntry);

    if (pxlate == NULL)
    {
        WARNING("CreateXlateObject failed pCreateXlate\n");
        return(pxlate);
    }

    //
    // Grab the palette semaphore so you can access the ptrans in palDestDC.
    //

    SEMOBJ  semo(ghsemPalette); // ??? This may not need to be grabbed
                               // ??? since we have the DC's locked that
                               // ??? this palette DC is in.  Investigate.

    //
    // Initialize ICM stuffs.
    //

    pxlate->vCheckForICM(hcmXform,lIcmMode);

    BOOL bCMYKColor = ((pxlate->flXlate & XO_FROM_CMYK) ? TRUE : FALSE);

    //
    // See if we should be matching to the Dest DC palette instead of the
    // surface.  This occurs when the surface is palette managed and the
    // user hasn't selected an RGB palette into his destination DC.
    // Note we check for a valid pxlate in the Destination DC palette.
    // In multi-task OS the user can do things to get the translate in a
    // invalid or NULL state easily.  We match against the reserved palette
    // in this case.
    //

    BOOL bPalManaged = (!palDestSurf.bValid()) || (palDestSurf.bIsPalManaged());

    if (bPalManaged)
    {
        //
        // We are blting to a compatible bitmap or the screen on a palette managed
        // device.  Check that we have a foreground realization already done,
        // otherwise just use the default palette for the DC palette.
        //

        ASSERTGDI(((palDestDC.ptransFore() == NULL) && (palDestDC.ptransCurrent() == NULL)) ||
                  ((palDestDC.ptransFore() != NULL) && (palDestDC.ptransCurrent() != NULL)),
                  "ERROR translates are either both valid or both invalid");

        if (palDestDC.ptransFore() == NULL)
        {
            //
            // Match against the default palette, valid translate is not available.
            //

            palDestDC.ppalSet(ppalDefault);
        }

        if (!(flCreateFlag & XLATE_USE_SURFACE_PAL))
        {
            pxlate->flPrivate |= XLATE_PAL_MANAGED;

            //
            // Set a flag that says whether we are background or foreground.
            //

            if (palDestSurf.bValid())
            {
                pxlate->flPrivate |= XLATE_USE_CURRENT;
            }
        }
    }

    pxlate->ppalSrc   = palSrc.ppalGet();
    pxlate->ppalDst   = palDestSurf.ppalGet();
    pxlate->ppalDstDC = palDestDC.ppalGet();

    PTRANSLATE ptransFore = palDestDC.ptransFore();
    PTRANSLATE ptransCurrent = palDestDC.ptransCurrent();

    //
    // Ok, lets build the translate vector.
    //

    if (
        (!palSrc.bValid()) ||
        (
          (palSrc.bIsPalManaged()) &&
          ((ptransFore == ptransCurrent) || (flCreateFlag & XLATE_USE_FOREGROUND))
        )
       )
    {
        //
        // The source is a compatible bitmap on a palette managed device or the screen
        // with Dst DC palette having been realized in the foreground.
        //
        // We start out assuming an identity blt.
        //

        if (ptransFore == NULL)
        {
            //
            // Match against the default palette since a
            // valid translate is not available.
            //

            palDestDC.ppalSet(ppalDefault);
            pxlate->ppalDstDC = ppalDefault;
            ptransFore = ptransCurrent = (TRANSLATE *) &defaultTranslate;
        }

        ASSERTGDI(cEntry == 256, "ERROR xlate too small");

        for (ulTemp = 0; ulTemp < 256; ulTemp++)
        {
            pxlate->ai[ulTemp] = ulTemp;
        }

        if (!palDestSurf.bValid())
        {
            //
            // Compatible bitmap to compatible bitmap on palette managed
            // device. Both are relevant to the foreground realize of the
            // DestDC palette so the xlate is identity.
            //

            pxlate->flXlate |= XO_TRIVIAL;
        }
        else if ((palDestSurf.bIsPalDibsection()) && (bEqualRGB_In_Palette(palDestSurf, palDestDC)))
        {
            //
            // If you blt from a compatible bitmap to a DIBSECTION it will be identity
            // if the RGB's of both the DC palette and the DIBSECTION's palette are the same.
            // We do this special check so that if they contain duplicates we still get an
            // identity xlate.
            //

            pxlate->flXlate |= XO_TRIVIAL;  // Size wise this will combine with first check

        }
        else if ((palDestSurf.bIsPalDibsection()) && (palSrc.bValid()) && (bEqualRGB_In_Palette(palDestSurf, palSrc)))
        {
            pxlate->flXlate |= XO_TRIVIAL;  // Size wise this will combine with first check
        }
        else if (palDestSurf.bIsPalManaged())
        {
            //
            // Compatible bitmap to the screen on a palette managed device.
            // The compatible bitmaps colors are defined as the foreground
            // realization of the destination DC's palette.  If the Dst
            // DC's palette is realized in the foreground it's identity.
            // Otherwise we translate from the current to the foreground
            // indices.
            //

            if (ptransCurrent == ptransFore)
            {
                //
                // It's in the foreground or not realized yet so it's
                // identity.  Not realized case also hits on default logical palette.
                //

                pxlate->flXlate |= XO_TRIVIAL;
            }
            else
            {
                //
                // It's foreground to current translation.
                //

                ASSERTGDI(ptransFore != ptransCurrent, "Should have been identity, never get here");
                ASSERTGDI(ptransFore != NULL, "ERROR this should not have got here Fore");
                ASSERTGDI(ptransCurrent != NULL, "ERROR this should not have got here Current");

                for (ulTemp = 0; ulTemp < palDestDC.cEntries(); ulTemp++)
                {
                    pxlate->ai[ptransFore->ajVector[ulTemp]] = (ULONG) ptransCurrent->ajVector[ulTemp];
                }

                //
                // Now map the default colors that are really there independant of logical palette.
                //

                if (palDestSurf.bIsNoStatic())
                {
                    //
                    // Only black and white are here.
                    //

                    pxlate->ai[0] = 0;
                    pxlate->ai[255] = 255;
                }
                else if (!palDestSurf.bIsNoStatic256())
                {
                    //
                    // All the 20 holy colors are here.
                    // Fix them up.
                    //

                    for (ulTemp = 0; ulTemp < 10; ulTemp++)
                    {
                        pxlate->ai[ulTemp] = ulTemp;
                        pxlate->ai[ulTemp + 246] = ulTemp + 246;
                    }
                }
            }
        }
        else if (palDestSurf.bIsMonochrome())
        {
            RtlZeroMemory(pxlate->ai, 256 * sizeof(ULONG));

            ulTemp = ulGetNearestIndexFromColorref(palSrc, palSrcDC, iBackSrc);

            ASSERTGDI(ulTemp < 256, "ERROR palSrc invalid - ulGetNearest is > 256");

            pxlate->vSetIndex(ulTemp, 1);
            pxlate->flXlate |= XO_TO_MONO;
            pxlate->iBackSrc = iBackSrc;
        }
        else
        {
            //
            // Blting from palette-managed bitmap to non-palette managed surface.
            //
            // Do all the non-default colors that are realized.
            //

            if (palDestSurf.cEntries() != 256)
            {
                //
                // If the dest is 256 entries we will just leave
                // the identity xlate as the initial state.
                // Otherwise we 0 it out so we don't have a translation
                // index left that's potentially bigger than the
                // destination palette.
                //

                RtlZeroMemory(pxlate->ai, 256 * sizeof(ULONG));
            }

            //
            // We need to fill in where the default colors map.
            //

            for (ulTemp = 0; ulTemp < 10; ulTemp++)
            {
                pxlate->ai[ulTemp] =
                    palDestSurf.ulGetNearestFromPalentry(logDefaultPal.palPalEntry[ulTemp]);
                pxlate->ai[ulTemp + 246] =
                    palDestSurf.ulGetNearestFromPalentry(logDefaultPal.palPalEntry[ulTemp+10]);
            }

            if (flCreateFlag & XLATE_USE_SURFACE_PAL) 
            {
                //
                // Map directly to destination surface palette from source surface palette.
                //

                for (ulTemp = 0; ulTemp < palSrc.cEntries(); ulTemp++)
                {
                   pxlate->ai[ulTemp] = palDestSurf.ulGetNearestFromPalentry(palSrc.palentryGet(ulTemp));
                }

                pxlate->flPrivate |= XLATE_USE_SURFACE_PAL;
            }
            else
            {
                //
                // Go through DC palette and then map to destination surface palette.
                //

                if (ptransFore != NULL)
                {
                    for (ulTemp = 0; ulTemp < palDestDC.cEntries(); ulTemp++)
                    {
                        pxlate->ai[ptransFore->ajVector[ulTemp]] =
                                palDestSurf.ulGetNearestFromPalentry(palDestDC.palentryGet(ulTemp));
                    }
                }
            }
        }
    }
    else if (palSrc.bIsPalDibsection() &&
             ((palDestSurf.bValid() &&
               palDestSurf.bIsPalDibsection() &&
               bEqualRGB_In_Palette(palSrc, palDestSurf)
              ) ||
              (((!palDestSurf.bValid()) ||
                (palDestSurf.bIsPalManaged() && (ptransFore == ptransCurrent))
               ) &&
               bEqualRGB_In_Palette(palSrc, palDestDC)
              )
             )
            )
    {
        //
        // Blting from a Dibsection &&
        // ((To another Dibsection with ident palette) ||
        //  (((To a compatible palette managed bitmap)  ||
        //    (To a palette managed screen with the DstDc's logical palette realized in the foreground)) &&
        //   (the dst logical palette == Dibsection's palette)
        // ))
        //
        // This is identity.  We do this special check so even if there are duplicate
        // colors in the palette we still get identity.
        //

        for (ulTemp = 0; ulTemp < 256; ulTemp++)
        {
            pxlate->ai[ulTemp] = ulTemp;
        }

        pxlate->flXlate |= XO_TRIVIAL;
    }
    else if ((palSrc.bIsPalManaged()) && (!palDestSurf.bValid()))
    {
        //
        // We are blting from the screen to a compatible bitmap on a palette
        // managed device where the screen is realized in the background.
        //

        //
        // Make it identity by default.
        //

        for (ulTemp = 0; ulTemp < 256; ulTemp++)
        {
            pxlate->ai[ulTemp] = ulTemp;
        }

        //
        // We are blting background to foreground.
        // i.e. we are blting current realize to foreground realize.
        //

        ASSERTGDI(pxlate->cEntries == 256, "ERROR xlate too small");
        ASSERTGDI(ptransFore != ptransCurrent, "Should have been identity, never get here");
        ASSERTGDI(ptransFore != NULL, "ERROR this should not have got here Fore");
        ASSERTGDI(ptransCurrent != NULL, "ERROR this should not have got here Current");

        for (ulTemp = 0; ulTemp < palDestDC.cEntries(); ulTemp++)
        {
            pxlate->ai[ptransCurrent->ajVector[ulTemp]] = (ULONG) ptransFore->ajVector[ulTemp];
        }

        //
        // Now map the default colors that are really there independant of logical palette realization.
        //

        if (palSrc.bIsNoStatic())
        {
            //
            // Only black and white are here.
            //

            pxlate->ai[0] = 0;
            pxlate->ai[255] = 255;
        }
        else if (!palSrc.bIsNoStatic256())
        {
            //
            // All the 20 holy colors are here.
            // Fix them up.
            //

            for (ulTemp = 0; ulTemp < 10; ulTemp++)
            {
                pxlate->ai[ulTemp] = ulTemp;
                pxlate->ai[ulTemp + 246] = ulTemp + 246;
            }
        }
    }
    else if (palSrc.bIsMonochrome())
    {
        if (palDestSurf.bIsMonochrome())
        {
            //
            // It's identity blt.
            //

            pxlate->vSetIndex(0, 0);
            pxlate->vSetIndex(1, 1);
        }
        else
        {
            if (bCMYKColor)
            {
                pxlate->vSetIndex(0, iForeDst);
                pxlate->vSetIndex(1, iBackDst);
            }
            else
            {
                pxlate->vSetIndex(0, ulGetNearestIndexFromColorref(palDestSurf,
                                                                   palDestDC,
                                                                   iForeDst));
                pxlate->vSetIndex(1, ulGetNearestIndexFromColorref(palDestSurf,
                                                                   palDestDC,
                                                                   iBackDst));
            }

            //
            // XLATE_FROM_MONO is set so we know to look at colors in
            // cache code for getting a hit.
            //

            pxlate->flPrivate |= XLATE_FROM_MONO;
            pxlate->iForeDst = iForeDst;
            pxlate->iBackDst = iBackDst;
        }
    }
    else if (cEntry == 0)
    {
        ASSERTGDI(palSrc.cEntries() == 0, "ERROR how could this happen");

        //
        // Src is a RGB/Bitfields palette.
        //

        if (palDestSurf.bIsMonochrome())
        {
            //
            // Put the color the Background of the source matches in pulXlate[0]
            //

            pxlate->ai[0] = ulGetNearestIndexFromColorref(palSrc, palSrcDC, iBackSrc);
            pxlate->flXlate |= XO_TO_MONO;
            pxlate->iBackSrc = iBackSrc;

            //
            // The S3 and other snazzy drivers look at this and get the color out
            // of slot 0 when XO_TO_MONO is set to get way better performance in
            // WinBench.
            //

            pxlate->pulXlate = pxlate->ai;
        }
    }
    else
    {
        //
        // Src is a regular indexed palette, it wouldn't have duplicating entries
        // in it so we don't need to check for the Dibsection case.  This is like
        // a palette for a 16 color VGA or a printer or a SetDIBitsToDevice.
        //

        if (palDestSurf.bValid() && palDestSurf.bIsMonochrome())
        {
            RtlZeroMemory(pxlate->ai,(UINT) cEntry * sizeof(*pxlate->ai));
            cEntry = ulGetNearestIndexFromColorref(palSrc, palSrcDC, iBackSrc);
            ASSERTGDI(cEntry < pxlate->cEntries, "ERROR this is not in range");
            pxlate->vSetIndex(cEntry, 1);
            pxlate->flXlate |= XO_TO_MONO;
            pxlate->iBackSrc = iBackSrc;
        }
        else
        {
            if (bCMYKColor)
            {
                //
                // Copy source CMYK color table to index table.
                //

                while(cEntry--)
                {
                    pxlate->ai[cEntry] = palSrc.ulEntryGet(cEntry);
                }
            }
            else
            {
                //
                // If the destination is palette managed, map to logical palette
                // and then translate to physical indices accordingly.  Otherwise
                // map to the physical palette directly.
                //

                PPALETTE ppalTemp;

                if (bPalManaged && !(flCreateFlag & XLATE_USE_SURFACE_PAL))
                {
                    ppalTemp = palDestDC.ppalGet();
                }
                else
                {
                    ppalTemp = palDestSurf.ppalGet();

                    if (flCreateFlag & XLATE_USE_SURFACE_PAL)
                    {
                        pxlate->flPrivate |= XLATE_USE_SURFACE_PAL;
                    }
                }

                XEPALOBJ palTemp(ppalTemp);

                while(cEntry--)
                {

                    pxlate->ai[cEntry] =
                               palTemp.ulGetNearestFromPalentry(
                                                 palSrc.palentryGet(cEntry));
                }

                if (bPalManaged && !(flCreateFlag & XLATE_USE_SURFACE_PAL))
                {
                    //
                    // Map from DC palette to surface palette.
                    //

                    pxlate->vMapNewXlate((palDestSurf.bValid()) ? ptransCurrent
                                                                : ptransFore);
                }
            }
        }
    }

    pxlate->vCheckForTrivial();
    return(pxlate);
}

/******************************Public*Routine******************************\
* pCreateXlate
*
* This allocates an xlate object with ulNumEntries.
*
* Returns: The pointer to the xlate object, NULL for failure.
*
* History:
*  17-May-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

PXLATE pCreateXlate(ULONG ulNumEntries)
{
    //
    // Allocate room for the XLATE.
    //

    PXLATE pxlate = (PXLATE) PALLOCNOZ((sizeof(XLATE) + sizeof(ULONG) * ulNumEntries),
                                       'tlxG');

    if (pxlate != NULL)
    {
        pxlate->iUniq = ulGetNewUniqueness(ulXlatePalUnique);
        if (ulNumEntries > 0)
        {
            pxlate->flXlate = XO_TABLE;
            pxlate->pulXlate = pxlate->ai;
            pxlate->flPrivate = 0;
        }
        else
        {
            pxlate->flXlate = 0;
            pxlate->pulXlate = NULL;
            pxlate->flPrivate = XLATE_RGB_SRC;
        }

        pxlate->iSrcType = pxlate->iDstType = 0;
        pxlate->cEntries = ulNumEntries;
        pxlate->lCacheIndex = XLATE_CACHE_INVALID;
        pxlate->ppalSrc = (PPALETTE) NULL;
        pxlate->ppalDst = (PPALETTE) NULL;
        pxlate->ppalDstDC = (PPALETTE) NULL;
    }
#if DBG
    else
    {
        WARNING("pCreateXlate failed memory allocation\n");
    }
#endif

    return(pxlate);
}


/******************************Public*Routine******************************\
* vCheckForTrivial
*
*   We need to check for trivial translation, it speeds up the inner-loop
*   for blting a great deal.
*
* History:
*
*  11-Oct-1995 -by- Tom Zakrajsek [tomzak]
* Fixed it to not mark xlates as trivial unless the Src and Dst were
* the same color depth.
*
*  17-Oct-1993 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

VOID XLATE::vCheckForTrivial()
{
    //
    // Check if we already recognized trivial during creation
    //

    if (flXlate & XO_TRIVIAL)
        return;

    //
    // Source Color is CMYK, XO_TRIVIAL will not be on.
    //

    if (flXlate & XO_FROM_CMYK)
        return;

    //
    // See if it's really just a trivial xlate.
    //

    if (cEntries)
    {
        //
        // Just make sure that each index translates to itself, and
        // that the color depths are the same for the source and
        // destination.
        //

        ULONG ulTemp;

        if ((ppalSrc != NULL) &&
            (ppalDst != NULL) &&
            (ppalSrc->cEntries != ppalDst->cEntries))
        {
            return;
        }

        for (ulTemp = 0; ulTemp < cEntries; ulTemp++)
        {
            if (pulXlate[ulTemp] != ulTemp)
                return;
        }
    }
    else
    {
        //
        // If the Src and Dst have the same masks it's identity.
        //

        XEPALOBJ palSrc(ppalSrc);
        XEPALOBJ palDst(ppalDst);

        if ((palSrc.bValid()) &&
            (palDst.bValid()) &&
            (!palDst.bIsIndexed()))
        {
            ASSERTGDI(!palSrc.bIsIndexed(), "ERROR Src not indexed?");

            FLONG flRedSrc, flGreSrc, flBluSrc;
            FLONG flRedDst, flGreDst, flBluDst;

            if (palSrc.bIsBitfields())
            {
                flRedSrc = palSrc.flRed();
                flGreSrc = palSrc.flGre();
                flBluSrc = palSrc.flBlu();
            }
            else
            {
                flGreSrc = 0x0000FF00;

                if (palSrc.bIsRGB())
                {
                    flRedSrc = 0x000000FF;
                    flBluSrc = 0x00FF0000;
                }
                else
                {
                    ASSERTGDI(palSrc.bIsBGR(), "What is it then?");
                    flRedSrc = 0x00FF0000;
                    flBluSrc = 0x000000FF;
                }
            }

            if (palDst.bIsBitfields())
            {
                flRedDst = palDst.flRed();
                flGreDst = palDst.flGre();
                flBluDst = palDst.flBlu();
            }
            else
            {
                flGreDst = 0x0000FF00;

                if (palDst.bIsRGB())
                {
                    flRedDst = 0x000000FF;
                    flBluDst = 0x00FF0000;
                }
                else
                {
                    ASSERTGDI(palDst.bIsBGR(), "What is it then?");
                    flRedDst = 0x00FF0000;
                    flBluDst = 0x000000FF;
                }
            }

            if ((flRedSrc != flRedDst) ||
                (flGreSrc != flGreDst) ||
                (flBluSrc != flBluDst))
            {
                return;
            }
        }
        else
            return;
    }

    //
    // We found it is really just trivial.
    //

    flXlate |= XO_TRIVIAL;
}


/******************************Public*Routine******************************\
* EXLATEOBJ::vAddToCache
*
*   Adds an xlate object to the cache.
*
* Arguments:
*
*   palSrc   -
*   palDst   -
*   palSrcDC -
*   palDstDC -
*
* History:
*  13-Jun-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

VOID
EXLATEOBJ::vAddToCache(
    XEPALOBJ palSrc,
    XEPALOBJ palDst,
    XEPALOBJ palSrcDC,
    XEPALOBJ palDstDC
    )
{
    ULONG   cEntries;
    SEMOBJ  semo(ghsemPalette);

    for (cEntries = 0; cEntries < XLATE_CACHE_SIZE; cEntries++)
    {
        ASSERTGDI(ulTableIndex < XLATE_CACHE_SIZE, "table index out of range");

        if (xlateTable[ulTableIndex].pxlate != (PXLATE) 0)
        {
            if (xlateTable[ulTableIndex].ulReference != 0)
            {
                ulTableIndex = (ulTableIndex + 1) & XLATE_MODULA;
                continue;
            }

            VFREEMEM(xlateTable[ulTableIndex].pxlate);
        }

        //
        // Now add ours into the cache.
        //

        xlateTable[ulTableIndex].ulReference = 1;
        xlateTable[ulTableIndex].pxlate = (PXLATE)pxlate;
        xlateTable[ulTableIndex].ulPalSrc = palSrc.ulTime();
        xlateTable[ulTableIndex].ulPalDst = palDst.ulTime();
        xlateTable[ulTableIndex].ulPalSrcDC = palSrcDC.ulTime();
        xlateTable[ulTableIndex].ulPalDstDC = palDstDC.ulTime();

        //
        // Mark it so the destructor doesn't free it
        //

        pxlate->lCacheIndex = (LONG) ulTableIndex;

        //
        // Put in palSrc the index where to be found most quickly
        //

        palSrc.iXlateIndex(ulTableIndex);

        //
        // Move the cache ahead so we don't get deleted right away
        //

        ulTableIndex = (ulTableIndex + 1) & XLATE_MODULA;
        return;
    }
}


/******************************Public*Routine******************************\
* EXLATEOBJ::bSearchCache
*
*   Searches cache for a previously created xlate.
*
* Arguments:
*
*   palSrc   - src surface palette
*   palDst   - dst surface palette
*   palSrcDC - src DC palette
*   palDstDC - dst DC palette
*   iForeDst - For Mono->Color this is what a 0 goes to
*   iBackDst - For Mono->Color this is what a 1 goes to
*   iBackSrc - For Color->Mono this is the color that goes to
*              1, all other colors go to 0.
*
* History:
*  13-Jun-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

BOOL
EXLATEOBJ::bSearchCache(
    XEPALOBJ palSrc,
    XEPALOBJ palDst,
    XEPALOBJ palSrcDC,
    XEPALOBJ palDstDC,
    ULONG    iForeDst,
    ULONG    iBackDst,
    ULONG    iBackSrc,
    ULONG    flCreate
    )
{
    //
    // Start ulIndex at the last place in the cache that an pxlate match was
    // found for palSrc.
    //

    ULONG ulIndex = palSrc.iXlateIndex();
    ULONG ulLoop;
    SEMOBJ  semo(ghsemPalette);

    for (ulLoop = 0; ulLoop < XLATE_CACHE_SIZE; ulLoop++)
    {
        //
        // If the destination is palette managed we need to make sure we are going
        // through the correct Destination DC and that it is up to date.
        //

        if ((xlateTable[ulIndex].ulPalSrc == palSrc.ulTime()) &&
            (xlateTable[ulIndex].ulPalDst == palDst.ulTime()) &&
            (xlateTable[ulIndex].ulPalDstDC == palDstDC.ulTime()))
        {
            //
            // Lock down all the cache objects.
            //

            pxlate = xlateTable[ulIndex].pxlate;
            ASSERTGDI(bValid(), "GDIERROR:invalid pxlate xlate cache entry\n");
            ASSERTGDI(pxlate->lCacheIndex == (LONG) ulIndex, "GDIERROR:bad xlate.lCacheIndex\n");
            ASSERTGDI(!(pxlate->flPrivate & XLATE_RGB_SRC), "GDIERROR:a RGB_SRC in the cache ???\n");
            ASSERTGDI(palSrc.cEntries() == pxlate->cEntries, "ERROR bSearchCache cEntries not matching palSrc");

            //
            // Now check if it's a to/from monochrome and if it is,
            // are the fore/back colors still valid.
            //

            if (
                 (
                   ((XLATE_USE_SURFACE_PAL|XLATE_USE_FOREGROUND) & pxlate->flPrivate) == flCreate
                 )
                    &&
                 (
                   (
                     !((XO_TO_MONO & pxlate->flXlate) || (XLATE_FROM_MONO & pxlate->flPrivate))
                   )
                     ||
                   (
                     (XO_TO_MONO & pxlate->flXlate) &&
                     (iBackSrc == pxlate->iBackSrc) &&
                     (xlateTable[ulIndex].ulPalSrcDC == palSrcDC.ulTime())
                   )
                     ||
                   (
                     (XLATE_FROM_MONO & pxlate->flPrivate) &&
                     (iForeDst == pxlate->iForeDst) &&
                     (iBackDst == pxlate->iBackDst)
                   )
                 )
               )
            {

                //
                // HOT DOG, valid cache entry.  It isn't deletable now
                // because we have it locked down.  Cache the index in palSrc.
                //

                InterlockedIncrement((LPLONG) &(xlateTable[ulIndex].ulReference));
                palSrc.iXlateIndex(ulIndex);
                return(TRUE);
            }
        }

        ulIndex = (ulIndex + 1) & XLATE_MODULA;
    }

    pxlate = (PXLATE) NULL;
    return(FALSE);
}

/******************************Public*Routine******************************\
* XLATEOBJ_iXlate
*
*   This translates an index of a src palette to the closest index in the
*   dst palette.
*
* Arguments:
*
*   pxlo   -
*   cIndex -
*
* History:
*  Mon 16-Mar-1992 -by- Patrick Haluptzok [patrickh]
* Fixed something.
*
*  Wed 24-Jul-1991 -by- Patrick Haluptzok [patrickh]
* put in support for RGB-->monochrome.
*
*  22-Jul-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

extern "C"
ULONG
XLATEOBJ_iXlate(
    XLATEOBJ *pxlo,
    ULONG     cIndex
    )
{
    if ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL))
    {
        return(cIndex);
    }

    if (pxlo->flXlate & XO_TABLE)
    {
        if (cIndex > pxlo->cEntries)
        {
            cIndex = cIndex % pxlo->cEntries;
        }

        return(((XLATE *) pxlo)->ai[cIndex]);
    }

    //
    // We are beyond source palette being PAL_INDEXED.
    //

    if (pxlo->flXlate & XO_TO_MONO)
    {
        if (cIndex == ((XLATE *) pxlo)->ai[0])
        {
            return(1);
        }
        else
        {
            return(0);
        }
    }

    ASSERTGDI(((XLATE *) pxlo)->ppalSrc != (PPALETTE) NULL, "ERROR ppalSrc is NULL");

    //
    // For 8bpp destinations, go through MarkE's optimized tablelookup code.  This is
    // not so much for performance reasons (XLATEOBJ_iXlate is already hopelessly slow),
    // but to make sure GDI always performs this translation in the same way (with some drivers,
    // StretchBlt to a device surface ends up in EngCopyBits which uses MarkE's table, but
    // on the same machines StretchBlt to a memory surface goes through ulTranslate)
    //
    
    PPALETTE ppal = (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED ? 
                     ((XLATE *) pxlo)->ppalDstDC : ((XLATE *) pxlo)->ppalDst);

    ASSERTGDI(ppal != (PPALETTE) NULL, "ERROR ppal is NULL");
    ASSERTGDI(((XLATE *) pxlo)->ppalDstDC != (PPALETTE) NULL, "ERROR ppalDstDC is NULL");

    if ((ppal->flPal & PAL_INDEXED) &&
        (((XLATE *) pxlo)->ppalDstDC->cEntries == 256))
    {
        PBYTE pxlate555 = ((XEPALOBJ) ppal).pGetRGBXlate();
        if (pxlate555 == NULL)
        {
            WARNING1("Error in XLATEOBJ_iXlate:  can't generate pxlate555");
            return 0;
        }
        cIndex = XLATEOBJ_ulIndexToPalSurf(pxlo, pxlate555, cIndex);
    }
    else
    {
        PAL_ULONG palul;

        palul.ul = ((XEPALOBJ) ((XLATE *) pxlo)->ppalSrc).ulIndexToRGB(cIndex);

        if (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED)
        {
            ASSERTGDI(((XLATE *) pxlo)->ppalDstDC != (PPALETTE) NULL, "ERROR ppalDstDC is NULL");
            
            cIndex = ((XEPALOBJ) (((XLATE *) pxlo)->ppalDstDC)).ulGetNearestFromPalentry(palul.pal, SE_DONT_SEARCH_EXACT_FIRST);


            if (((XLATE *) pxlo)->ppalDstDC == ppalDefault)
            {
                if (cIndex >= 10)
                {
                    cIndex += 236;
                }
            }
            else
            {
                ASSERTGDI(((XLATE *) pxlo)->ppalDstDC->flPal & PAL_DC, "ERROR in ulTranslate XLATEOBJ is hosed \n");
                ASSERTGDI(cIndex < ((XLATE *) pxlo)->ppalDstDC->cEntries, "ERROR in ultranslate3");

                if (((XLATE *) pxlo)->flPrivate & XLATE_USE_CURRENT)
                {
                    cIndex = (ULONG) ((XLATE *) pxlo)->ppalDstDC->ptransCurrent->ajVector[cIndex];
                }
                else
                {
                    cIndex = (ULONG) ((XLATE *) pxlo)->ppalDstDC->ptransFore->ajVector[cIndex];
                }
            }
        }
        else
        {
            ASSERTGDI(((XLATE *) pxlo)->ppalDst != (PPALETTE) NULL, "ERROR ppalDst is NULL");

            cIndex = ((XEPALOBJ) (((XLATE *) pxlo)->ppalDst)).ulGetNearestFromPalentry(palul.pal, ((XEPALOBJ) (((XLATE *) pxlo)->ppalDst)).cEntries() ? SE_DONT_SEARCH_EXACT_FIRST : SE_DO_SEARCH_EXACT_FIRST);
        }
    }

    return(cIndex);
}

/**************************************************************************\
* XLATEOBJ_pGetXlate555
*
*   Retrieve 555 rgb xlate vector from appropriate palette
*
* Arguments:
*
*   pxlo
*
* Return Value:
*
*   rgb555 xlate vector or NULL
*
* History:
*
*    2/27/1997 Mark Enstrom [marke]
*
\**************************************************************************/

PBYTE
XLATEOBJ_pGetXlate555(
    XLATEOBJ *pxlo
    )
{
    PBYTE pxlate555 = NULL;

    //
    // return hi color to palette 555 translate table if the xlateobj
    // is appropriate
    //

    if (
         (pxlo == NULL) ||
         (pxlo->flXlate & XO_TRIVIAL) ||
         (pxlo->flXlate & XO_TABLE)   ||
         (pxlo->flXlate & XO_TO_MONO)
       )
    {
        return(NULL);
    }

    //
    // determine palette type to map.
    //

    if (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED)
    {
        ASSERTGDI(((XLATE *) pxlo)->ppalDstDC != (PPALETTE) NULL, "ERROR ppalDstDC is NULL");

        pxlate555 = ((XEPALOBJ) (((XLATE *) pxlo)->ppalDstDC)).pGetRGBXlate();
    }
    else
    {
        if (((XEPALOBJ) (((XLATE *) pxlo)->ppalDst)).bValid())
        {
            pxlate555 = ((XEPALOBJ) (((XLATE *) pxlo)->ppalDst)).pGetRGBXlate();
        }
    }

    return(pxlate555);
}


/**************************************************************************\
* XLATEOBJ_ulIndexToPalSurf
*   - take generic pixel input, convert it to RGB, then use the rgb555
*     to palette index table to look up a palette index
*
* Arguments:
*
*   pxlo      - xlate object
*   pxlate555 - member of dest palette object, 555RGB to palette
*   ulRGB     - index input
*
*
* Return Value:
*
*   Destination 8 bit pixel
*
* History:
*
*    2/20/1997 Mark Enstrom [marke]
*
\**************************************************************************/

BYTE
XLATEOBJ_ulIndexToPalSurf(
    XLATEOBJ *pxlo,
    PBYTE     pxlate555,
    ULONG     ulRGB
    )
{
    ALPHAPIX apix;
    BYTE     palIndex = 0;

    ASSERTGDI(pxlate555 != NULL,"XLATEOBJ_ulIndexToPalSurf: NULL pxlate555\n");

    apix.ul = ulRGB;

    apix.ul = ((XEPALOBJ) ((XLATE *) pxlo)->ppalSrc).ulIndexToRGB(ulRGB);

    palIndex = pxlate555[((apix.pix.b & 0xf8) << 7) |
                         ((apix.pix.g & 0xf8) << 2) |
                         ((apix.pix.r & 0xf8) >> 3)];


    if (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED)
    {
        if (((XLATE *) pxlo)->ppalDstDC == ppalDefault)
        {
            if (palIndex >= 10)
            {
                palIndex += 236;
            }
        }
        else
        {
            if (((XLATE *) pxlo)->flPrivate & XLATE_USE_CURRENT)
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransCurrent->ajVector[palIndex];
            }
            else
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransFore->ajVector[palIndex];
            }
        }
    }

    return(palIndex);
}

/**************************************************************************\
* XLATEOBJ_RGB32ToPalSurf
*   - take 32 RGB pixel input, then use the rgb555
*     to palette index table to look up a palette index
*
* Arguments:
*
*   pxlo      - xlate object
*   pxlate555 - member of dest palette object, 555RGB to palette
*   ulRGB     - 32 bit RGB input pixel
*
*
* Return Value:
*
*   Destination 8 bit pixel
*
* History:
*
*    2/20/1997 Mark Enstrom [marke]
*
\**************************************************************************/

BYTE
XLATEOBJ_RGB32ToPalSurf(
    XLATEOBJ *pxlo,
    PBYTE     pxlate555,
    ULONG     ulRGB
    )
{
    ALPHAPIX apix;
    BYTE     palIndex = 0;

    ASSERTGDI(pxlate555 != NULL,"XLATEOBJ_RGB32ToPalSurf: NULL pxlate555\n");

    apix.ul = ulRGB;

    palIndex = pxlate555[((apix.pix.b & 0xf8) << 7) |
                         ((apix.pix.g & 0xf8) << 2) |
                         ((apix.pix.r & 0xf8) >> 3)];

    if (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED)
    {
        if (((XLATE *) pxlo)->ppalDstDC == ppalDefault)
        {
            if (palIndex >= 10)
            {
                palIndex += 236;
            }
        }
        else
        {
            if (((XLATE *) pxlo)->flPrivate & XLATE_USE_CURRENT)
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransCurrent->ajVector[palIndex];
            }
            else
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransFore->ajVector[palIndex];
            }
        }
    }

    return(palIndex);
}

/**************************************************************************\
* XLATEOBJ_RGB32ToPalSurf
*   - take 32 BGR pixel input, then use the rgb555
*     to palette index table to look up a palette index
*
* Arguments:
*
*   pxlo      - xlate object
*   pxlate555 - member of dest palette object, 555RGB to palette
*   ulRGB     - 32 bit BGR input pixel
*
*
* Return Value:
*
*   Destination 8 bit pixel
*
* History:
*
*    2/20/1997 Mark Enstrom [marke]
*
\**************************************************************************/

BYTE
XLATEOBJ_BGR32ToPalSurf(
    XLATEOBJ *pxlo,
    PBYTE     pxlate555,
    ULONG     ulRGB
    )
{
    BYTE     palIndex;
    ALPHAPIX apix;

    ASSERTGDI(pxlate555 != NULL,"XLATEOBJ_BGR32ToPalSurf: NULL pxlate555\n");

    apix.ul = ulRGB;

    palIndex = pxlate555[((apix.pix.r & 0xf8) << 7) |
                         ((apix.pix.g & 0xf8) << 2) |
                         ((apix.pix.b & 0xf8) >> 3)];

    if (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED)
    {
        if (((XLATE *) pxlo)->ppalDstDC == ppalDefault)
        {
            if (palIndex >= 10)
            {
                palIndex += 236;
            }
        }
        else
        {
            if (((XLATE *) pxlo)->flPrivate & XLATE_USE_CURRENT)
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransCurrent->ajVector[palIndex];
            }
            else
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransFore->ajVector[palIndex];
            }
        }
    }
    return(palIndex);
}

/**************************************************************************\
* XLATEOBJ_RGB16_565ToPalSurf
*   - take 16 bit 565 pixel input, then use the rgb555
*     to palette index table to look up a palette index
*
* Arguments:
*
*   pxlo      - xlate object
*   pxlate555 - member of dest palette object, 555RGB to palette
*   ulRGB     - 16 bit 565 input pixel
*
*
* Return Value:
*
*   Destination 8 bit pixel
*
* History:
*
*    2/20/1997 Mark Enstrom [marke]
*
\**************************************************************************/

BYTE
XLATEOBJ_RGB16_565ToPalSurf(
    XLATEOBJ *pxlo,
    PBYTE     pxlate555,
    ULONG     ulRGB
    )
{
    BYTE     palIndex = 0;

    ASSERTGDI(pxlate555 != NULL,"XLATEOBJ_BGR16_565ToPalSurf: NULL pxlate555\n");

    palIndex = pxlate555[((ulRGB & 0xf800) >> 1) |
                         ((ulRGB & 0x07c0) >> 1) |
                         ((ulRGB & 0x001f))];

    if (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED)
    {
        if (((XLATE *) pxlo)->ppalDstDC == ppalDefault)
        {
            if (palIndex >= 10)
            {
                palIndex += 236;
            }
        }
        else
        {
            if (((XLATE *) pxlo)->flPrivate & XLATE_USE_CURRENT)
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransCurrent->ajVector[palIndex];
            }
            else
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransFore->ajVector[palIndex];
            }
        }
    }
    return(palIndex);
}

/**************************************************************************\
* XLATEOBJ_RGB16_555ToPalSurf
*   - take 16 bit 555 pixel input, then use the rgb555
*     to palette index table to look up a palette index
*
* Arguments:
*
*   pxlo      - xlate object
*   pxlate555 - member of dest palette object, 555RGB to palette
*   ulRGB     - 16 bit 555 input pixel
*
*
* Return Value:
*
*   Destination 8 bit pixel
*
* History:
*
*    2/20/1997 Mark Enstrom [marke]
*
\**************************************************************************/

BYTE
XLATEOBJ_RGB16_555ToPalSurf(
    XLATEOBJ *pxlo,
    PBYTE     pxlate555,
    ULONG     ulRGB
    )
{
    BYTE     palIndex = 0;

    ASSERTGDI(pxlate555 != NULL,"XLATEOBJ_BGR16_555ToPalSurf: NULL pxlate555\n");

    palIndex = pxlate555[ulRGB & 0x7fff];

    if (((XLATE *) pxlo)->flPrivate & XLATE_PAL_MANAGED)
    {
        if (((XLATE *) pxlo)->ppalDstDC == ppalDefault)
        {
            if (palIndex >= 10)
            {
                palIndex += 236;
            }
        }
        else
        {
            if (((XLATE *) pxlo)->flPrivate & XLATE_USE_CURRENT)
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransCurrent->ajVector[palIndex];
            }
            else
            {
                palIndex = ((XLATE *) pxlo)->ppalDstDC->ptransFore->ajVector[palIndex];
            }
        }
    }
    return(palIndex);
}


/******************************Public*Routine******************************\
* XLATEMEMOBJ
*
* This is a special constructor for the UpdateColors case.  It deletes the
* old xlate when it is done.
*
* History:
*  12-Dec-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

XLATEMEMOBJ::XLATEMEMOBJ(
    XEPALOBJ palSurf,
    XEPALOBJ palDC
    )
{
    //
    // Assert some fundamental truths.
    //

    ASSERTGDI(palDC.bValid(), "invalid palDC in xlatememobj123\n");
    ASSERTGDI(palSurf.bIsPalManaged(), "invalid surface palette in UpdateColors xlate");
    ASSERTGDI(palSurf.bValid(), "invalid palSurf in xlatememobj123\n");
    ASSERTGDI(!(palDC.bIsRGB()), "RGB palDC in DC->surf xlate constructor123\n");
    ASSERTGDI(!(palSurf.bIsRGB()), "RGB palSurf in DC->surf xlate constructor123\n");
    ASSERTGDI(palDC.bIsPalDC(), "not a palDC for palDC in xlatememobj123\n");
    ASSERTGDI(palDC.ptransCurrent() != NULL, "ERROR ptrans Null UpdateColors");
    ASSERTGDI(palDC.ptransOld() != NULL, "ERROR ptransOld Null UpdateColors");

    ULONG ulTemp;
    PULONG pulTemp;

    pxlate = pCreateXlate(palSurf.cEntries());

    if (pxlate)
    {
        pxlate->ppalSrc = palSurf.ppalGet();
        pxlate->ppalDst = palSurf.ppalGet();
        pxlate->ppalDstDC = palDC.ppalGet();
        pulTemp = pxlate->ai;

        //
        // Set up identity vector for all entries
        //

        for (ulTemp = 0; ulTemp < pxlate->cEntries; ulTemp++)
        {
            pulTemp[ulTemp] = ulTemp;
        }

        //
        // Change the ones that are affected by the palette realizations.
        //

        PTRANSLATE ptransOld = palDC.ptransOld();
        PTRANSLATE ptransCurrent = palDC.ptransCurrent();

        for (ulTemp = 0; ulTemp < palDC.cEntries(); ulTemp++)
        {
            pulTemp[ptransOld->ajVector[ulTemp]] = (ULONG) ptransCurrent->ajVector[ulTemp];
        }

        pxlate->vCheckForTrivial();
    }
}


/******************************Public*Routine******************************\
* EXLATEOBJ::bCreateXlateFromTable
*
* This is used to initialize an xlate when the table is already computed.
* We need this to support journaling.
*
* History:
*  17-Mar-1992 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

BOOL
EXLATEOBJ::bCreateXlateFromTable(
    ULONG cEntry,
    PULONG pulVector,
    XEPALOBJ palDst
    )
{
    //
    // Assert good input data.
    //

    ASSERTGDI(palDst.bValid(), "ERROR bInitXlate bad palDst");
    ASSERTGDI(pulVector != (PULONG) NULL, "ERROR bInitXlate bad pulVector");
    ASSERTGDI(cEntry != 0, "ERROR bInitXlate cEntry is 0");

    //
    // Allocate room for the Xlate.
    //

    pxlate = pCreateXlate(cEntry);

    if (pxlate == NULL)
    {
        WARNING("ERROR bInitXlate failed call to pCreateXlate\n");
        return(FALSE);
    }

    pxlate->ppalDst = palDst.ppalGet();
    pxlate->iDstType = (USHORT) palDst.iPalMode();

    //
    // Ok, fill in the xlate vector.
    //

    RtlCopyMemory(pxlate->ai, pulVector, (UINT) (cEntry * sizeof(ULONG)));

    //
    // Init accelerators, out of here.
    //

    pxlate->vCheckForTrivial();
    return(TRUE);
}


/******************************Public*Routine******************************\
* XLATE::vCheckForICM()
*
* History:
*  26-Feb-1997 -by- Hideyuki Nagase [hideyukn]
* Wrote it.
\**************************************************************************/

VOID XLATE::vCheckForICM(HANDLE _hcmXform,ULONG _lIcmMode)
{
    //
    // First, initialize ICM mode as default (off)
    //
    lIcmMode = DC_ICM_OFF;
    hcmXform = NULL;

    //
    // Check is there any ICM is enabled ?
    //
    if (IS_ICM_ON(_lIcmMode))
    {
        if (IS_ICM_INSIDEDC(_lIcmMode))
        {
            //
            // Inside DC ICM is enabled with valid color transform.
            //

            //
            // Copy mode and color transform handle to XLATE.
            //
            lIcmMode = _lIcmMode;
            hcmXform = _hcmXform;

            if (IS_ICM_DEVICE(lIcmMode))
            {
                COLORTRANSFORMOBJ CXFormObj(_hcmXform);

                //
                // Check transform handle is valid or not.
                //
                if (CXFormObj.bValid())
                {
                    ICMMSG(("vCheckForICM():XO_DEVICE_ICM\n"));

                    //
                    // Device driver will do color matching.
                    //
                    flXlate |= XO_DEVICE_ICM;
                }
            }
            else if (IS_ICM_HOST(lIcmMode))
            {
                ICMMSG(("vCheckForICM():XO_HOST_ICM\n"));

                //
                // Inform the images is corrected by host CMM
                //
                flXlate |= XO_HOST_ICM;

                if (IS_CMYK_COLOR(lIcmMode) && (hcmXform != NULL))
                {
                    ICMMSG(("vCheckForICM():XO_FROM_CMYK\n"));

                    //
                    // The destination surface is 32bits CMYK color,
                    // This is only happen when ICM is done by host.
                    //
                    flXlate |= XO_FROM_CMYK;
                }
            }
        }
        else if (IS_ICM_OUTSIDEDC(_lIcmMode))
        {
            //
            // Outside DC is enabled, color correction is done by application.
            //

            //
            // Copy mode and color transform handle to XLATE.
            //
            lIcmMode = _lIcmMode;
            hcmXform = 0;

            //
            // Inform the images is corrected by Host (application) ICM
            //
            flXlate |= XO_HOST_ICM;
        }
    }
}

/******************************Public*Routine******************************\
* vXlateBitfieldsToBitfields
*
* Translates a color between to arbitrary bitfield (or RGB or BGR) formats.
\**************************************************************************/

ULONG FASTCALL iXlateBitfieldsToBitfields(XLATEOBJ* pxlo, ULONG ul)
{
    ul = ((XEPALOBJ) ((XLATE *) pxlo)->ppalSrc).ulIndexToRGB(ul);
    ul = ((XEPALOBJ) ((XLATE *) pxlo)->ppalDst).ulGetNearestFromPalentry(
                                        *((PALETTEENTRY*) &ul));

    return(ul);
}

ULONG FASTCALL iXlate565ToBGR(XLATEOBJ *pxlo, ULONG ul)
{
    return(((ul << 8) & 0xf80000)
         | ((ul << 3) & 0x070000)
         | ((ul << 5) & 0x00fc00)
         | ((ul >> 1) & 0x000300)
         | ((ul << 3) & 0x0000f8)
         | ((ul >> 2) & 0x000007));
}

ULONG FASTCALL iXlate555ToBGR(XLATEOBJ *pxlo, ULONG ul)
{
    return(((ul << 9) & 0xf80000)
         | ((ul << 4) & 0x070000)
         | ((ul << 6) & 0x00f800)
         | ((ul << 1) & 0x000700)
         | ((ul << 3) & 0x0000f8)
         | ((ul >> 2) & 0x000007));
}

ULONG FASTCALL iXlateBGRTo565(XLATEOBJ *pxlo, ULONG ul)
{
    return(((ul >> 8) & 0xf800)
         | ((ul >> 5) & 0x07e0)
         | ((ul >> 3) & 0x001f));
}

ULONG FASTCALL iXlateBGRTo555(XLATEOBJ *pxlo, ULONG ul)
{
    return(((ul >> 9) & 0x7c00)
         | ((ul >> 6) & 0x03e0)
         | ((ul >> 3) & 0x001f));
}

/******************************Public*Routine******************************\
* pfnXlateBetweenBitfields
*
* Returns a pointer to an optimized routine for converting between
* two bitfields (or RGB or BGR) colors.
\**************************************************************************/

PFN_pfnXlate XLATE::pfnXlateBetweenBitfields()
{
    PFN_pfnXlate pfnXlate;
    XEPALOBJ palSrc(ppalSrc);
    XEPALOBJ palDst(ppalDst);

    ASSERTGDI(palSrc.bIsBitfields() || palSrc.bIsRGB() || palSrc.bIsBGR(),
        "ERROR: Expected bitfields source");
    ASSERTGDI(palDst.bIsBitfields() || palDst.bIsRGB() || palDst.bIsBGR(),
        "ERROR: Expected bitfields destination");

    pfnXlate = iXlateBitfieldsToBitfields;

    if (palDst.bIsBGR())
    {
        if (palSrc.bIs565())
        {
            pfnXlate = iXlate565ToBGR;
        }
        else if (palSrc.bIs555())
        {
            pfnXlate = iXlate555ToBGR;
        }
    }
    else if (palSrc.bIsBGR())
    {
        if (palDst.bIs565())
        {
            pfnXlate = iXlateBGRTo565;
        }
        else if (palDst.bIs555())
        {
            pfnXlate = iXlateBGRTo555;
        }
    }

    return(pfnXlate);
}