/******************************Module*Header**********************************\
*
*                           **************************
*                           * DirectDraw SAMPLE CODE *
*                           **************************
*
* Module Name: ddsurf.c
*
*  Content:    
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\*****************************************************************************/

#include "precomp.h"
#include "directx.h"
#include "dd.h"
#include "heap.h"
#include "d3dtxman.h"
#define ALLOC_TAG ALLOC_TAG_US2P
// Texture tables defined in the D3D side of the driver (d3d.c)
// TODO: move to dd.h or d3d.h
extern ULONG gD3DNumberOfTextureFormats;
extern DDSURFACEDESC gD3DTextureFormats[];


//---------------------------------------------------------------------------
// BOOL bComparePixelFormat
//
// Function used to compare 2 pixels formats for equality. This is a 
// helper function to bCheckTextureFormat. A return value of TRUE indicates 
// equality
//
//---------------------------------------------------------------------------


BOOL 
bComparePixelFormat(LPDDPIXELFORMAT lpddpf1, LPDDPIXELFORMAT lpddpf2)
{
    if (lpddpf1->dwFlags != lpddpf2->dwFlags)
    {
        return FALSE;
    }

    // same bitcount for non-YUV surfaces?
    if (!(lpddpf1->dwFlags & (DDPF_YUV | DDPF_FOURCC)))
    {
        if (lpddpf1->dwRGBBitCount != lpddpf2->dwRGBBitCount )
        {
            return FALSE;
        }
    }

    // same RGB properties?
    if (lpddpf1->dwFlags & DDPF_RGB)
    {
        if ((lpddpf1->dwRBitMask != lpddpf2->dwRBitMask) ||
            (lpddpf1->dwGBitMask != lpddpf2->dwGBitMask) ||
            (lpddpf1->dwBBitMask != lpddpf2->dwBBitMask) ||
            (lpddpf1->dwRGBAlphaBitMask != lpddpf2->dwRGBAlphaBitMask))
        { 
             return FALSE;
        }
    }
    
    // same YUV properties?
    if (lpddpf1->dwFlags & DDPF_YUV)	
    {
        if ((lpddpf1->dwFourCC != lpddpf2->dwFourCC) ||
            (lpddpf1->dwYUVBitCount != lpddpf2->dwYUVBitCount) ||
            (lpddpf1->dwYBitMask != lpddpf2->dwYBitMask) ||
            (lpddpf1->dwUBitMask != lpddpf2->dwUBitMask) ||
            (lpddpf1->dwVBitMask != lpddpf2->dwVBitMask) ||
            (lpddpf1->dwYUVAlphaBitMask != lpddpf2->dwYUVAlphaBitMask))
        {
             return FALSE;
        }
    }
    else if (lpddpf1->dwFlags & DDPF_FOURCC)
    {
        if (lpddpf1->dwFourCC != lpddpf2->dwFourCC)
        {
            return FALSE;
        }
    }

    // If Interleaved Z then check Z bit masks are the same
    if (lpddpf1->dwFlags & DDPF_ZPIXELS)
    {
        if (lpddpf1->dwRGBZBitMask != lpddpf2->dwRGBZBitMask)
        {
            return FALSE;
        }
    }

    return TRUE;
} // bComparePixelFormat

//---------------------------------------------------------------------------
//
// BOOL bCheckTextureFormat
//
// Function used to determine if a texture format is supported. It traverses 
// the deviceTextureFormats list. We use this in DdCanCreateSurface32. A
// return value of TRUE indicates that we do support the requested texture 
// format.
//
//---------------------------------------------------------------------------

BOOL 
bCheckTextureFormat(LPDDPIXELFORMAT lpddpf)
{
    DWORD i;

    // Run the list for a matching format
    for (i=0; i < gD3DNumberOfTextureFormats; i++)
    {
        if (bComparePixelFormat(lpddpf, 
                                &gD3DTextureFormats[i].ddpfPixelFormat))
        {
            return TRUE;
        }   
    }

    return FALSE;
} // bCheckTextureFormat


//-----------------------------------------------------------------------------
//
// DdCanCreateSurface32
//
// This entry point is called after parameter validation but before
// any object creation. You can decide here if it is possible for
// you to create this surface.  For example, if the person is trying
// to create an overlay, and you already have the maximum number of
// overlays created, this is the place to fail the call.
//
// You also need to check if the pixel format specified can be supported.
//
//-----------------------------------------------------------------------------

DWORD CALLBACK 
DdCanCreateSurface(LPDDHAL_CANCREATESURFACEDATA pccsd)
{    
    PPDev ppdev=(PPDev)pccsd->lpDD->dhpdev;
    PERMEDIA_DEFS(ppdev);

    PDD_SURFACEDESC lpDDS=pccsd->lpDDSurfaceDesc;
    
    DBG_DD((2,"DDraw:DdCanCreateSurface"));
    
    if(lpDDS->dwLinearSize == 0)
    {
        // rectangular surface
        // Reject all widths larger than we can create partial products for.
        DUMPSURFACE(10, NULL, lpDDS);
        
        if (lpDDS->dwWidth > (ULONG)(2 << MAX_PARTIAL_PRODUCT_P2)) 
        {
            DBG_DD((1,"DDraw:DdCanCreateSurface: Surface rejected:"));
            DBG_DD((1,"  Width Requested: %ld (max. %ld)", 
                lpDDS->dwWidth,(2 << MAX_PARTIAL_PRODUCT_P2)));

            pccsd->ddRVal = DDERR_INVALIDPIXELFORMAT;
            return DDHAL_DRIVER_HANDLED;
        }
    }
    
    // We only support 16bits & 15bits (for stencils) Z-Buffer on PERMEDIA
    if ((lpDDS->ddsCaps.dwCaps & DDSCAPS_ZBUFFER) &&
        (lpDDS->ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY))
    {
        DWORD dwBitDepth;
        
        // verify where the right z buffer bit depth is
        if (DDSD_ZBUFFERBITDEPTH & lpDDS->dwFlags)
            dwBitDepth = lpDDS->dwZBufferBitDepth;
        else
            dwBitDepth = lpDDS->ddpfPixelFormat.dwZBufferBitDepth;
        
        // Notice we have to check for a BitDepth of 16 even if a stencil 
        // buffer is present. dwZBufferBitDepth in this case will be the 
        // sum of the z buffer and the stencil buffer bit depth.
        if (dwBitDepth == 16)
        {
            pccsd->ddRVal = DD_OK;
        }
        else
        {
            DBG_DD((2,"DDraw:DdCanCreateSurface: ERROR: "
                       "Depth buffer not 16Bits! - %d", dwBitDepth));
            
            pccsd->ddRVal = DDERR_INVALIDPIXELFORMAT;
            
        }
        return DDHAL_DRIVER_HANDLED;
    }

    // pccsd->bIsDifferentPixelFormat tells us if the pixel format of the
    // surface being created matches that of the primary surface.  It can be
    // true for Z buffer and alpha buffers, so don't just reject it out of
    // hand...
 
    if (pccsd->bIsDifferentPixelFormat)
    {
        DBG_DD((3,"  Pixel Format is different to primary"));

        if(lpDDS->ddpfPixelFormat.dwFlags & DDPF_FOURCC)
        {
            DBG_DD((3, "  FourCC requested (%4.4hs, 0x%08lx)", (LPSTR) 
                        &lpDDS->ddpfPixelFormat.dwFourCC,
                        lpDDS->ddpfPixelFormat.dwFourCC ));

            switch (lpDDS->ddpfPixelFormat.dwFourCC)
            {
            case FOURCC_YUV422:
                DBG_DD((3,"  Surface requested is YUV422"));
                if (ppdev->iBitmapFormat == BMF_8BPP)
                {
                    pccsd->ddRVal = DDERR_INVALIDPIXELFORMAT;
                } else
                {
                    lpDDS->ddpfPixelFormat.dwYUVBitCount = 16;
                    pccsd->ddRVal = DD_OK;
                }
                return DDHAL_DRIVER_HANDLED;

            default:
                DBG_DD((3,"  ERROR: Invalid FOURCC requested, refusing"));
                pccsd->ddRVal = DDERR_INVALIDPIXELFORMAT;
                return DDHAL_DRIVER_HANDLED;
            }
        }
        else if((lpDDS->ddsCaps.dwCaps & DDSCAPS_TEXTURE))
        {

            if (bCheckTextureFormat(&pccsd->lpDDSurfaceDesc->ddpfPixelFormat))
            {
                // texture surface is in one or our supported texture formats
                DBG_DD((3, "  Texture Surface - OK" ));
                pccsd->ddRVal = DD_OK;
                return DDHAL_DRIVER_HANDLED;
            }
            else
            {
                // we don't support this kind of texture format
                DBG_DD((3, "  ERROR: Texture Surface - NOT OK" ));
                pccsd->ddRVal = DDERR_INVALIDPIXELFORMAT;
                return DDHAL_DRIVER_HANDLED;
            }
        }
        pccsd->ddRVal = DDERR_INVALIDPIXELFORMAT;
        return DDHAL_DRIVER_HANDLED;
    }
    
    pccsd->ddRVal = DD_OK;
    return DDHAL_DRIVER_HANDLED;
    
} // DdCanCreateSurface32 

//-----------------------------------------------------------------------------
//
//  DdCreateSurface
//
//  This function is called by DirectDraw if a new surface is created. If the 
//  driver has its own memory manager, here is the place to allocate the 
//  videomemory or to fail the call. Note that we return 
//  DDHAL_DRIVER_NOTHANDLED here to indicate that we do not manage the heap.
//  fpVidMem is set to DDHAL_PLEASEALLOC_BLOCKSIZE, and the DDraw memory
//  manager wll allocate the memory for us.
//  
//  Note that the Permedia chip requires a partial product
//  to be setup for each surface.  They also limit the widths to a multiple
//  of 32 for the Partial Products to work.  The below code adjusts the
//  surfaces to meet this requirement.  Note that if we are using a
//  rectangular allocation scheme, the surface is already OK as the desktop
//  is a good width anyway.  This code also handles YUV 16 Bit colour space
//  compressed format (FOURCC_YUV422) which will always be 16 bits, regardless
//  of the desktop resolution/requested depth.
//
//-----------------------------------------------------------------------------

DWORD CALLBACK 
DdCreateSurface(PDD_CREATESURFACEDATA lpCreateSurface)
{
    PPDev ppdev=        (PPDev)lpCreateSurface->lpDD->dhpdev;
    PERMEDIA_DEFS(ppdev);

    DWORD               dwBitDepth;
    DD_SURFACE_LOCAL*   lpSurfaceLocal;
    DD_SURFACE_GLOBAL*  lpSurfaceGlobal;
    LPDDSURFACEDESC     lpSurfaceDesc;
    BOOL                bYUV = FALSE;
    BOOL                bResize = FALSE;
    DWORD               dwExtraBytes;
    PermediaSurfaceData*pPrivateData = NULL;

    DBG_DD((2, "DdCreateSurface called"));

    //
    // See if any of these surfaces are Z buffers. If they are, ensure that the
    // pitch is a valid LB width. The minimum partial product is 32 words or
    // 32 pixels on Permedia 2
    //
    // On Windows NT, dwSCnt will always be 1, so there will only ever
    // be one entry in the 'lplpSList' array:
    //
    ASSERTDD(lpCreateSurface->dwSCnt == 1,
             "DdCreateSurface: Unexpected dwSCnt value not equal to one");

    lpSurfaceLocal = lpCreateSurface->lplpSList[0];
    lpSurfaceGlobal = lpSurfaceLocal->lpGbl;    
    lpSurfaceDesc   = lpCreateSurface->lpDDSurfaceDesc;

    //
    // We repeat the same checks we did in 'DdCanCreateSurface' because
    // it's possible that an application doesn't call 'DdCanCreateSurface'
    // before calling 'DdCreateSurface'.
    //
    ASSERTDD(lpSurfaceGlobal->ddpfSurface.dwSize == sizeof(DDPIXELFORMAT),
        "NT is supposed to guarantee that ddpfSurface.dwSize is valid");

    //
    // If the surface has already been allocated, don't reallocate it, just
    // reset it. This will happen if the surface is the primary surface.
    //

    if ( lpSurfaceGlobal->dwReserved1 != 0 )
    {
        pPrivateData = (PermediaSurfaceData*)lpSurfaceGlobal->dwReserved1;
        if ( CHECK_P2_SURFACEDATA_VALIDITY(pPrivateData) )
        {
            DBG_DD((0, "  Already allocated Private Surface data 0x%x",
                     pPrivateData));
            memset(pPrivateData, 0, sizeof(PermediaSurfaceData));
        }
        else
        {
            pPrivateData = NULL;
        }
    }

    //
    // If the data isn't valid allocate it.
    //
    if ( pPrivateData == NULL )
    {
        pPrivateData = (PermediaSurfaceData *)
            ENGALLOCMEM(FL_ZERO_MEMORY, 
                        sizeof(PermediaSurfaceData), 
                        ALLOC_TAG);

        if ( pPrivateData == NULL )
        {
            DBG_DD((0, "DDraw:DdCreateSurface: "
                        "Not enough memory for private surface data!"));
            lpCreateSurface->ddRVal = DDERR_OUTOFMEMORY;
            
            return DDHAL_DRIVER_HANDLED;
        }
    }

    //
    // Store the pointer to the new data
    //
    lpSurfaceGlobal->dwReserved1 = (UINT_PTR)pPrivateData;
    DBG_DD((3,"DDraw:DdCreateSurface privatedata=0x%x lpGbl=0x%x lpLcl=0x%x "
        "dwFlags=%08lx &dwReserved1=0x%x", pPrivateData, lpSurfaceGlobal,
        lpSurfaceLocal, lpSurfaceLocal->dwFlags, 
        &lpSurfaceGlobal->dwReserved1));
    //
    // Set the magic number
    //
    pPrivateData->MagicNo = SURF_MAGIC_NO;
    
    //
    // Store away the important information
    //

    SetupPrivateSurfaceData(ppdev, pPrivateData, lpSurfaceLocal);

    if ( pPrivateData->SurfaceFormat.PixelSize != __PERMEDIA_24BITPIXEL )
    {
        dwBitDepth = (8 << pPrivateData->SurfaceFormat.PixelSize);
    }
    else
    {
        dwBitDepth = 24;
    }

    //
    // If the surface is a Z Buffer, then we always need to check the
    // pitch/partial products, and we need to get the depth from the
    // dwZBufferBitDepth field.
    //
    bYUV = FALSE;
    bResize = FALSE;
    dwExtraBytes = 0;

    //
    // get correct bit depth for Z buffers
    //
    if ( (lpSurfaceLocal->dwFlags & DDRAWISURF_HASPIXELFORMAT)
       &&(lpSurfaceGlobal->ddpfSurface.dwFlags & DDPF_ZBUFFER) )
    {
        DBG_DD((5,"  Surface is Z Buffer - Adjusting"));
        dwBitDepth = lpSurfaceGlobal->ddpfSurface.dwZBufferBitDepth;
    }

    if ( lpSurfaceGlobal->ddpfSurface.dwFlags & DDPF_FOURCC )
    {
        //
        // The surface is a YUV format surface or we fail 
        //

        switch ( lpSurfaceGlobal->ddpfSurface.dwFourCC )
        {
            case FOURCC_YUV422:
                DBG_DD((3,"  Surface is YUV422 - Adjusting"));
                lpSurfaceGlobal->ddpfSurface.dwYUVBitCount = 16;
                dwBitDepth = 16;
                bYUV = TRUE;
                break;

            default:
                //
                // We should never get here, as CanCreateSurface will
                // validate the YUV format for us.
                //
                ASSERTDD(0, "Trying to create an invalid YUV surface!");
                break;
        }
    }

    //
    // If the surface is a p2 texture and it is using a LUT, then we need to
    // allocate extra local buffer memory for the LUT entries (only on p2).
    //
    if ( (lpSurfaceLocal->ddsCaps.dwCaps & DDSCAPS_TEXTURE)
       &&(pPrivateData->SurfaceFormat.Format == PERMEDIA_8BIT_PALETTEINDEX) )
    {
        DBG_DD((7,"  Texture is a P2 8Bit LUT"));
        bResize = TRUE;
        dwExtraBytes = (256 * sizeof(DWORD));
    }

    DBG_DD((5,"  Surface Pitch is: 0x%x",  lpSurfaceGlobal->lPitch));

    //
    // Width is in pixels/texels
    //
    LONG lPitch;
    lPitch = lpSurfaceGlobal->wWidth;

    DBG_DD((4,"  Source Surface is %d texels/depth values across",
               lpSurfaceGlobal->wWidth));

    // align before hand to a DWPORD boundary
    if (pPrivateData->SurfaceFormat.PixelSize == __PERMEDIA_4BITPIXEL)
    {
        lPitch = ((lPitch >> 1) + 31) & ~31;
    }
    else
    {
        lPitch = (lPitch + 31) & ~31;
    }

    ULONG ulPackedPP;
    vCalcPackedPP( lPitch, &lPitch, &ulPackedPP);

    DBG_DD((7,"  Surface is 0x%x bits deep", dwBitDepth));

    if ( pPrivateData->SurfaceFormat.PixelSize != __PERMEDIA_4BITPIXEL )
    {
        //
        // Convert back to BYTES
        //
        if ( dwBitDepth != 24 )
        {
            lPitch <<= ((int)dwBitDepth) >> 4;
        }
        else
        {
            lPitch *= 3;
        }
    }

    pPrivateData->dwFlags |= P2_PPVALID;

    DWORD dwExtraLines = 0;

    if ( !bYUV )
    {
        //
        // PM Textures must be at least 32 high
        //
        if ( lpSurfaceGlobal->wHeight < 32 )
        {
            dwExtraLines = 32 - lpSurfaceGlobal->wHeight;
        }
    }

    lpSurfaceGlobal->dwBlockSizeX = 
        lPitch * (DWORD)(lpSurfaceGlobal->wHeight + dwExtraLines);
    lpSurfaceGlobal->dwBlockSizeY = 1;
    lpSurfaceGlobal->lPitch = lPitch;

    //
    // Store the partial productes in the structure
    //
    pPrivateData->ulPackedPP = ulPackedPP;

    DBG_DD((4, "  New Width of surface in Bytes: %d", lPitch));

    //
    // This flag is set if the surface needs resizing. This is currently only
    // used for the P2 LUT based textures.
    //
    if ( bResize )
    {
        DWORD dwExtraScanlines = 0;
        LONG  lExtraRemaining = (LONG)dwExtraBytes;

        //
        // ExtraScanlines is the count x, which * pitch is what we need to get
        // enough memory to hold the LUT.  This algorithm will ensure that even
        // requests for sizes less than a pitch length will get allocated.
        //
        do
        {
            dwExtraScanlines++;
            lExtraRemaining -= (LONG)lpSurfaceGlobal->lPitch;
        } while ( lExtraRemaining > 0 );

        DBG_DD((4, "Calculated extra Pitch lines = %d", dwExtraScanlines));

        //
        // Stretch the surface a little more in multiples of pitch.
        //
        lpSurfaceGlobal->dwBlockSizeX +=dwExtraScanlines * 
                                        lpSurfaceGlobal->lPitch;
    }// if ( bResize )

    
    //
    // Modify surface descriptions as appropriate and let Direct
    // Draw perform the allocation if the surface was not the primary
    //
    if (lpSurfaceLocal->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
    {
        lpSurfaceGlobal->fpVidMem = NULL;
    }
    else
    {
        lpSurfaceGlobal->fpVidMem = DDHAL_PLEASEALLOC_BLOCKSIZE;
    }
    
    lpSurfaceDesc->lPitch   = lpSurfaceGlobal->lPitch;
    lpSurfaceDesc->dwFlags |= DDSD_PITCH;

    if (lpSurfaceLocal->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
    {
        if (lpSurfaceDesc->dwFlags & DDSD_PIXELFORMAT)
        {
            lPitch =
            lpSurfaceDesc->lPitch   = 
            lpSurfaceGlobal->lPitch =
                ((lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount*
                lpSurfaceGlobal->wWidth+31)/32)*4;  //make it DWORD aligned
        }

        lpSurfaceGlobal->dwUserMemSize = lPitch * 
                                        (DWORD)(lpSurfaceGlobal->wHeight);
        lpSurfaceGlobal->fpVidMem = DDHAL_PLEASEALLOC_USERMEM;
    }

    lpCreateSurface->ddRVal = DD_OK;

    return DDHAL_DRIVER_NOTHANDLED;

}// DdCreateSurface()

//-----------------------------------------------------------------------------
//
// DdDestroySurface
//
// Frees up the private memory allocated with this surface.  Note that
// we return DDHAL_DRIVER_NOTHANDLED indicating that we didn't actually
// free the surface, since the heap is managed by DDraw.
//
//-----------------------------------------------------------------------------
extern TextureCacheManager P2TextureManager;

DWORD CALLBACK 
DdDestroySurface( LPDDHAL_DESTROYSURFACEDATA psdd )
{
    PermediaSurfaceData *pPrivateData= 
        (PermediaSurfaceData*)psdd->lpDDSurface->lpGbl->dwReserved1;

    
    DBG_DD((3,"DDraw:DdDestroySurface pPrivateData=0x%x "
        "lpGbl=0x%x lpLcl=0x%x dwFlags=%08lx &dwReserved1=0x%x",
        pPrivateData, psdd->lpDDSurface->lpGbl, psdd->lpDDSurface,
        psdd->lpDDSurface->dwFlags, &psdd->lpDDSurface->lpGbl->dwReserved1));

    if (CHECK_P2_SURFACEDATA_VALIDITY(pPrivateData))
    {
        PPERMEDIA_D3DTEXTURE pTexture=NULL;
        if ((psdd->lpDDSurface->lpSurfMore->dwSurfaceHandle) &&
            (HandleList[psdd->lpDDSurface->dwReserved1].dwSurfaceList) &&
            (psdd->lpDDSurface->lpSurfMore->dwSurfaceHandle <
                PtrToUlong(HandleList[psdd->lpDDSurface->dwReserved1].
                    dwSurfaceList[0])))
            pTexture=HandleList[psdd->lpDDSurface->dwReserved1].dwSurfaceList
                [psdd->lpDDSurface->lpSurfMore->dwSurfaceHandle];

        DBG_DD((3,"psdd->lpDDSurface->lpSurfMore->ddsCapsEx.dwCaps2=%08lx",
            psdd->lpDDSurface->lpSurfMore->ddsCapsEx.dwCaps2));
        
        if (psdd->lpDDSurface->lpSurfMore->ddsCapsEx.dwCaps2 & 
            DDSCAPS2_TEXTUREMANAGE)
        {
            DBG_D3D((3, "free fpVidMem=%08lx fpVidMem=%08lx",
                pPrivateData->fpVidMem,psdd->lpDDSurface->lpGbl->fpVidMem));
            if ((pPrivateData->fpVidMem) && pTexture)
            {
                ASSERTDD(CHECK_D3DSURFACE_VALIDITY(pTexture),
                    "Invalid pTexture in DdDestroySurface");
                TextureCacheManagerRemove(&P2TextureManager,pTexture);
            }
            if (DDRAWISURF_INVALID & psdd->lpDDSurface->dwFlags)
            {
                // indicate that driver takes care of the lost surface already
                psdd->ddRVal = DD_OK;
                return DDHAL_DRIVER_HANDLED;
            }
        }
        if (pTexture)
        {
            ENGFREEMEM(pTexture);
            HandleList[psdd->lpDDSurface->dwReserved1].dwSurfaceList
                [psdd->lpDDSurface->lpSurfMore->dwSurfaceHandle]=0;
        }
        pPrivateData->MagicNo = NULL;        
        ENGFREEMEM(pPrivateData);
        psdd->lpDDSurface->lpGbl->dwReserved1 = 0;    
    }
#if DBG
    else
    {
        if (pPrivateData) {
            ASSERTDD(0, "DDraw:DdDestroySurface:ERROR:"
                        "Private Surface data not valid??");
        }
        DBG_DD((0, "DDraw:DdDestroySurface:WARNING:"
                    "No Private data in destroyed surface"));
    }
#endif    
    return DDHAL_DRIVER_NOTHANDLED;

} // DdDestroySurface 

//-----------------------------------------------------------------------------
//
// SetupPrivateSurfaceData
//
// Function to get info about DDRAW surface and store it away in the
// private structure.  Useful for partial products, pixel depths,
// texture setup (patching/formats), etc. 
//
//-----------------------------------------------------------------------------

VOID
SetupPrivateSurfaceData( PPDev ppdev, 
                         PermediaSurfaceData* pPrivateData, 
                         LPDDRAWI_DDRAWSURFACE_LCL pSurface)
{
    DDPIXELFORMAT* pPixFormat = NULL;
    
    ASSERTDD(CHECK_P2_SURFACEDATA_VALIDITY(pPrivateData), 
        "SetupPrivateSurfaceData: Private Surface data pointer invalid!!");
    ASSERTDD(pSurface, "SetupPrivateSurfaceData: Surface pointer invalid");
    
    DBG_DD((5,"DDraw:SetupPrivateSurfaceData"));
    DBG_DD((6,"  Width: %d, Height: %d", 
        pSurface->lpGbl->wWidth, pSurface->lpGbl->wHeight));

    // Surface is the primary surface
    if (pSurface->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
    {
        DBG_DD((6,"  Surface is Primary"));
        pPrivateData->dwFlags |= P2_SURFACE_PRIMARY;
        pPixFormat = &ppdev->ddpfDisplay;
    } // Either the surface is a texture or it has a valid pixel format.
    else
    {
        DUMPSURFACE(6, pSurface, NULL);
        pPixFormat = &pSurface->lpGbl->ddpfSurface;
    }
    
    // At surface creation the surface has not been patched.
    pPrivateData->dwFlags &= ~P2_ISPATCHED;
    
    if (pSurface->ddsCaps.dwCaps & DDSCAPS_TEXTURE)
    {
        // If the user has chosen the normal mechanism, then patch the surface.
        if (pSurface->ddsCaps.dwCaps & DDSCAPS_ALLOCONLOAD) 
        {
            DBG_DD((6,"  Remembering to patch this surface"));
            pPrivateData->dwFlags |= P2_CANPATCH;
        }
        else
        {
            pPrivateData->dwFlags &= ~P2_CANPATCH;
        }
    }
     
    // Initially assume no Alpha
    pPrivateData->SurfaceFormat.bAlpha = FALSE;

    if (pPixFormat != NULL)
    {
        if (pPixFormat->dwFlags & DDPF_FOURCC)
        {
            pPrivateData->dwFlags |= P2_SURFACE_FORMAT_VALID;
            switch( pPixFormat->dwFourCC )
            {
            case FOURCC_YUV422:
                DBG_DD((6,"  Surface is 4:2:2 YUV"));
                pPrivateData->SurfaceFormat.Format = PERMEDIA_YUV422;
                pPrivateData->SurfaceFormat.FormatExtension = 
                    PERMEDIA_YUV422_EXTENSION;
                pPrivateData->SurfaceFormat.PixelSize = __PERMEDIA_16BITPIXEL;
                pPrivateData->SurfaceFormat.FBReadPixel= __PERMEDIA_16BITPIXEL;
                pPrivateData->SurfaceFormat.PixelMask = 1;
                pPrivateData->SurfaceFormat.PixelShift= 1;
                pPrivateData->SurfaceFormat.ColorComponents = 2;
                pPrivateData->SurfaceFormat.logPixelSize = log2(16);
                pPrivateData->SurfaceFormat.ColorOrder = 0;
                break;
            }
        }
        else if (pPixFormat->dwFlags & DDPF_PALETTEINDEXED4)
        {
            DBG_DD((6,"  Surface is 4-Bit Palette"));
            pPrivateData->dwFlags |= P2_SURFACE_FORMAT_VALID;
            pPrivateData->dwFlags |= P2_SURFACE_FORMAT_PALETTE;
            pPrivateData->SurfaceFormat.Format = PERMEDIA_4BIT_PALETTEINDEX;
            pPrivateData->SurfaceFormat.FormatExtension = 0;
            pPrivateData->SurfaceFormat.PixelSize = __PERMEDIA_4BITPIXEL;
            pPrivateData->SurfaceFormat.FBReadPixel= __PERMEDIA_8BITPIXEL;
            pPrivateData->SurfaceFormat.PixelMask = 7;
            pPrivateData->SurfaceFormat.PixelShift= 0;
            pPrivateData->SurfaceFormat.ColorComponents = 3;
            pPrivateData->SurfaceFormat.logPixelSize = log2(4);
            pPrivateData->SurfaceFormat.ColorOrder = 0;
            pPrivateData->dwFlags &= ~P2_CANPATCH;
        }
        else if (pPixFormat->dwFlags & DDPF_PALETTEINDEXED8)
        {
            DBG_DD((6,"  Surface is 8-Bit Palette"));
            pPrivateData->dwFlags |= P2_SURFACE_FORMAT_VALID;
            pPrivateData->dwFlags |= P2_SURFACE_FORMAT_PALETTE;
            pPrivateData->SurfaceFormat.Format = PERMEDIA_8BIT_PALETTEINDEX;
            pPrivateData->SurfaceFormat.FormatExtension = 0;
            pPrivateData->SurfaceFormat.PixelSize = __PERMEDIA_8BITPIXEL;
            pPrivateData->SurfaceFormat.FBReadPixel= __PERMEDIA_8BITPIXEL;
            pPrivateData->SurfaceFormat.PixelMask = 3;
            pPrivateData->SurfaceFormat.PixelShift= 0;
            pPrivateData->SurfaceFormat.ColorComponents = 3;
            pPrivateData->SurfaceFormat.Texture16BitMode = 1;
            pPrivateData->SurfaceFormat.logPixelSize = log2(8);
            pPrivateData->SurfaceFormat.ColorOrder = 0;
        }
        else
        {
            if (SetRGBAlphaSurfaceFormat( pPixFormat, 
                                         &pPrivateData->SurfaceFormat))
            {
                pPrivateData->dwFlags |= P2_SURFACE_FORMAT_VALID;
            } else
            {
                pPrivateData->dwFlags &= ~P2_SURFACE_FORMAT_VALID;
            }
        }
    }
    else
    {
        ASSERTDD(0, "SetupPrivateSurfaceData:"
                    "Can't get valid surface format pointer");
    }
    

} // SetupPrivateSurfaceData 

//-----------------------------------------------------------------------------
//
// list all valid pixel formats for Permedia 2
// The P2 supports BGR for all formats, so these formats are not
// explicitely listed here, also formats having no alpha channel are permitted
//  
//-----------------------------------------------------------------------------

DDPIXELFORMAT Permedia2PixelFormats[] = {
    // 32 bit RGBa
    {PERMEDIA_8888_RGB,0,0,32,0x000000ff,0x0000ff00,0x00ff0000,0xff000000},     
    // 16 bit 5:6:5, RGB
    {PERMEDIA_565_RGB ,0,0,16,0x0000001f,0x000007e0,0x0000f800,0x00000000},     
    // 16 bit 4:4:4:4RGBa
    {PERMEDIA_444_RGB ,0,0,16,0x0000000f,0x000000f0,0x00000f00,0x0000f000},     
    // 15 bit 5:5:5, RGBa
    {PERMEDIA_5551_RGB,0,0,16,0x0000001f,0x000003e0,0x00007c00,0x00008000},     
    //  8 bit 3:3:2  RGB
    // 332 format is not symmetric. Its listed twice for BGR/RGB case
    {PERMEDIA_332_RGB ,1,0, 8,0x00000007,0x00000038,0x000000c0,0x00000000},     
    {PERMEDIA_332_RGB ,0,0, 8,0x00000003,0x0000001c,0x000000e0,0x00000000},
    // 24 bit RGB
    {PERMEDIA_888_RGB ,0,0,24,0x000000ff,0x0000ff00,0x00ff0000,0x00000000}      
};
#define N_PERMEDIA2PIXELFORMATS \
    (sizeof(Permedia2PixelFormats)/sizeof(DDPIXELFORMAT))

BOOL
ValidRGBAlphaSurfaceformat( DDPIXELFORMAT *pPixFormat, INT *pIndex)
{
    INT i;

    if (pPixFormat==NULL) 
        return FALSE;

    if (pPixFormat->dwSize < sizeof(DDPIXELFORMAT))
        return FALSE;

    // The Z-Buffer is a special case. Its basically a 16 bit surface
    if (pPixFormat->dwFlags & DDPF_ZBUFFER)
    {
        if (pIndex!=0) *pIndex=1;
        return TRUE;
    }

    if ((pPixFormat->dwFlags & DDPF_RGB)==0)
        return FALSE;

    for ( i=0; i<N_PERMEDIA2PIXELFORMATS; i++)
    {
        // check if the RGB and alpha masks fit.
        // on Permedia we can swap R and B, so allow also BGR formats
        if ((((pPixFormat->dwRBitMask == 
                    Permedia2PixelFormats[i].dwRBitMask) &&
              (pPixFormat->dwBBitMask == 
                    Permedia2PixelFormats[i].dwBBitMask)) ||
             ((pPixFormat->dwRBitMask == 
                    Permedia2PixelFormats[i].dwBBitMask) &&
              (pPixFormat->dwBBitMask == 
                    Permedia2PixelFormats[i].dwRBitMask))) &&
            (pPixFormat->dwGBitMask == 
                    Permedia2PixelFormats[i].dwGBitMask) &&
            ((pPixFormat->dwRGBAlphaBitMask == 
                    Permedia2PixelFormats[i].dwRGBAlphaBitMask) ||
             (pPixFormat->dwRGBAlphaBitMask == 0) ||
             ((pPixFormat->dwFlags&DDPF_ALPHAPIXELS)==0)) &&
              (pPixFormat->dwRGBBitCount==
                    Permedia2PixelFormats[i].dwRGBBitCount)
            )
        {
            if (pIndex!=NULL)
            {
                *pIndex = i;
            }
            return TRUE;
        }
    }
     
    // no pixel format matched...

    return FALSE;

} // ValidRGBAlphaSurfaceformat


//-----------------------------------------------------------------------------
//
//  SetRGBAlphaSurfaceFormat
//
//  Store away pixel format information of a surface in the Permedia native 
//  format
//
//-----------------------------------------------------------------------------

BOOL
SetRGBAlphaSurfaceFormat(DDPIXELFORMAT *pPixFormat, 
                         PERMEDIA_SURFACE *pSurfaceFormat)
{
    INT iFormatIndex;

    if (!ValidRGBAlphaSurfaceformat( pPixFormat, &iFormatIndex))
    {
        DBG_DD((1,"couldn't set SurfaceFormat"));
        return FALSE;        
    }

    DBG_DD((6,"  Surface RGB Data Valid"));

    pSurfaceFormat->RedMask = pPixFormat->dwRBitMask;
    pSurfaceFormat->GreenMask = pPixFormat->dwGBitMask;
    pSurfaceFormat->BlueMask = pPixFormat->dwBBitMask;
    pSurfaceFormat->bPreMult = FALSE;

    if (pPixFormat->dwFlags & DDPF_ALPHAPIXELS)
    {
        pSurfaceFormat->AlphaMask = pPixFormat->dwRGBAlphaBitMask;
    
        if (pSurfaceFormat->AlphaMask!=0)
        {
            pSurfaceFormat->bAlpha = TRUE;
        } 

        if (pPixFormat->dwFlags & DDPF_ALPHAPREMULT)
        {
            pSurfaceFormat->bPreMult = TRUE;
        }
    }

    pSurfaceFormat->ColorOrder = Permedia2PixelFormats[iFormatIndex].dwFlags;

    // check for the BGR case
    if (pPixFormat->dwRBitMask == 
        Permedia2PixelFormats[iFormatIndex].dwRBitMask)
        pSurfaceFormat->ColorOrder = !pSurfaceFormat->ColorOrder;
            
    switch (pPixFormat->dwRGBBitCount)
    {
    case 24:
        DBG_DD((6,"  Surface is 8:8:8 Packed 24 Bit"));
        pSurfaceFormat->Format = PERMEDIA_888_RGB;
        pSurfaceFormat->FormatExtension = PERMEDIA_888_RGB_EXTENSION;
        pSurfaceFormat->PixelSize = __PERMEDIA_24BITPIXEL;
        pSurfaceFormat->FBReadPixel= __PERMEDIA_24BITPIXEL;
        pSurfaceFormat->PixelMask = 0;  // not valid for 24 bit
        pSurfaceFormat->PixelShift= 0;
        pSurfaceFormat->logPixelSize = 0;
        pSurfaceFormat->ColorComponents = 3;
        break;

    case 32:
        DBG_DD((6,"  Surface is 8:8:8:8"));
        pSurfaceFormat->Format = PERMEDIA_8888_RGB;
        pSurfaceFormat->FormatExtension = PERMEDIA_8888_RGB_EXTENSION;
        pSurfaceFormat->PixelSize = __PERMEDIA_32BITPIXEL;
        pSurfaceFormat->FBReadPixel= __PERMEDIA_32BITPIXEL;
        pSurfaceFormat->PixelMask = 0;
        pSurfaceFormat->PixelShift= 2;
        pSurfaceFormat->logPixelSize = log2(32);
        pSurfaceFormat->ColorComponents = 3;
        break;

    case 16:
        pSurfaceFormat->logPixelSize = log2(16);
        pSurfaceFormat->PixelSize = __PERMEDIA_16BITPIXEL;
        pSurfaceFormat->FBReadPixel= __PERMEDIA_16BITPIXEL;
        pSurfaceFormat->PixelMask = 1;  // not valid for 24 bit
        pSurfaceFormat->PixelShift= 1;
        switch (Permedia2PixelFormats[iFormatIndex].dwSize)
        {
        case PERMEDIA_565_RGB:
            pSurfaceFormat->Texture16BitMode = 0;
            pSurfaceFormat->Format = PERMEDIA_565_RGB;
            pSurfaceFormat->FormatExtension = PERMEDIA_565_RGB_EXTENSION;
            pSurfaceFormat->ColorComponents = 2;
            DBG_DD((6,"  Surface is 5:6:5"));
            break;

        case PERMEDIA_444_RGB:
            pSurfaceFormat->Format = PERMEDIA_444_RGB;
            pSurfaceFormat->FormatExtension = 0;
            pSurfaceFormat->ColorComponents = 3;
            if (pPixFormat->dwRGBAlphaBitMask != 0)
            {
                DBG_DD((6,"  Surface is 4:4:4:4"));
            } else
            {
                DBG_DD((6,"  Surface is 4:4:4:0"));
            }
            break;

        case PERMEDIA_5551_RGB:
            pSurfaceFormat->Texture16BitMode = 1;
            pSurfaceFormat->Format = PERMEDIA_5551_RGB;
            pSurfaceFormat->FormatExtension = PERMEDIA_5551_RGB_EXTENSION;
            if (pPixFormat->dwRGBAlphaBitMask != 0)
            {
                DBG_DD((6,"  Surface is 5:5:5:1"));
                pSurfaceFormat->ColorComponents = 3;
            }
            else
            {
                DBG_DD((6,"  Surface is 5:5:5"));
                pSurfaceFormat->ColorComponents = 2;
            }
            break;
        default: 
            ASSERTDD( FALSE, "  16 bit Surface has unknown format");
            break;
        }
        break;

    case 8:
        pSurfaceFormat->PixelSize = __PERMEDIA_8BITPIXEL;
        pSurfaceFormat->FBReadPixel= __PERMEDIA_8BITPIXEL;
        pSurfaceFormat->PixelMask = 3;
        pSurfaceFormat->PixelShift= 0;
        pSurfaceFormat->logPixelSize = log2(8);
        if (Permedia2PixelFormats[iFormatIndex].dwSize==PERMEDIA_2321_RGB)
        {
            pSurfaceFormat->Format = PERMEDIA_2321_RGB;
            pSurfaceFormat->FormatExtension = PERMEDIA_2321_RGB_EXTENSION;
            pSurfaceFormat->ColorComponents = 3;
        }
        else if (Permedia2PixelFormats[iFormatIndex].dwSize==PERMEDIA_332_RGB)
        {
            pSurfaceFormat->Format = PERMEDIA_332_RGB;
            pSurfaceFormat->FormatExtension = PERMEDIA_332_RGB_EXTENSION;
            pSurfaceFormat->ColorComponents = 2;
        } else
        {
            ASSERTDD( FALSE, "  Surface (8bit) has unknown format");
        }
        break;

    case 0:
        DBG_DD((6,"  Surface is palleted"));
        pSurfaceFormat->Format = PERMEDIA_8BIT_PALETTEINDEX;
        pSurfaceFormat->FormatExtension = PERMEDIA_8BIT_PALETTEINDEX_EXTENSION;
        pSurfaceFormat->PixelSize = __PERMEDIA_8BITPIXEL;
        pSurfaceFormat->FBReadPixel= __PERMEDIA_8BITPIXEL;
        pSurfaceFormat->PixelMask = 3;  // not valid for 24 bit
        pSurfaceFormat->PixelShift= 0;
        pSurfaceFormat->logPixelSize = log2(8);
        pSurfaceFormat->ColorComponents = 0;
        break;

    default:
        ASSERTDD( FALSE, "  Surface has unknown pixelformat");
        return FALSE;
    }

    return TRUE;

}  // SetRGBAlphaSurfaceFormat

