/******************************Module*Header**********************************\
*
*                           *********************
*                           * DDraw SAMPLE CODE *
*                           *********************
*
* Module Name: ddldblt.c
*
* Content:     DirectDraw System to Videomemory download routines
*
* 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"

typedef struct tagSHORTDWORD 
{
    BYTE Red;
    BYTE Green;
    BYTE Blue;
} SHORTDWORD, *LPSHORTDWORD;

//-----------------------------------------------------------------------------
//
// PermediaPatchedTextureDownload
//
// Do a texture download to the linear region of memory. Access to textures
// is faster if they are stored as "patched". This function downloads a texture
// from system to videomemory and rearranges the data in the patched format.
//
// ppdev---------the PPDev
// pPrivateDest--DDraw private surface data for the dest. surface
// fpSrcVidMem---linear pointer to source systemmemory surface
// lSrcPitch-----pitch of source surface
// rSrc----------source rectangle
// fpDstVidMem---offset in videomemory of dest surface
// lDstPitch-----pitch of dest. surface
// rDest---------destination rectangle
//
//-----------------------------------------------------------------------------

VOID 
PermediaPatchedTextureDownload (PPDev ppdev, 
                                PermediaSurfaceData* pPrivateDest, 
                                FLATPTR     fpSrcVidMem,
                                LONG        lSrcPitch,
                                RECTL*      rSrc, 
                                FLATPTR     fpDstVidMem,
                                LONG        lDstPitch,
                                RECTL*      rDest)
{
    PERMEDIA_DEFS(ppdev);

    ULONG ulTextureBase = (ULONG)(fpDstVidMem);
    LONG  lWidth = rDest->right - rDest->left;
    LONG  lLines = rDest->bottom - rDest->top;

    DBG_DD((5,"DDraw:PermediaPatchedTextureDownload:, PrivateDest: 0x%x",
                pPrivateDest));

    if (NULL == fpSrcVidMem)
    {
        DBG_DD(( 0, "DDraw:PermediaPatchedTextureDownload"
            " unexpected NULL = fpSrcVidMem"));
        return;
    }
    ASSERTDD(CHECK_P2_SURFACEDATA_VALIDITY(pPrivateDest),
             "Blt32: Destination Private Data not valid!");

    DBG_DD((6,"  Texture Base: 0x%x DstPitch=0x%x", 
                ulTextureBase, lDstPitch));
    DBG_DD((6,"  Source Base: 0x%x SourcePitch: 0x%x", 
                fpSrcVidMem,lSrcPitch));
    DBG_DD((6,"  rSource->left: 0x%x, rSource->right: 0x%x", 
                rSrc->left,rSrc->right));
    DBG_DD((6,"  rSource->top: 0x%x, rSource->bottom: 0x%x\n", 
                rSrc->top, rSrc->bottom));
    DBG_DD((6,"  rDest->left: 0x%x, rDest->right: 0x%x", 
                rDest->left,rDest->right));
    DBG_DD((6,"  rDest->top: 0x%x, rDest->bottom: 0x%x\n", 
                rDest->top, rDest->bottom));

    //
    //  define some handy variables
    //
    
    LONG lPixelSize=pPrivateDest->SurfaceFormat.PixelSize;

    RESERVEDMAPTR(18);

    SEND_PERMEDIA_DATA( ColorDDAMode,        __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA( AlphaBlendMode,      __PERMEDIA_DISABLE);                
    SEND_PERMEDIA_DATA( Window, PM_WINDOW_DISABLELBUPDATE(__PERMEDIA_ENABLE));
    SEND_PERMEDIA_DATA( dXDom, 0x0);
    SEND_PERMEDIA_DATA( dXSub, 0x0);
    SEND_PERMEDIA_DATA( FBReadPixel, pPrivateDest->SurfaceFormat.FBReadPixel);
 
    switch (lPixelSize)
    {
    case __PERMEDIA_4BITPIXEL:
        // There are half as many 8-bit, 4-bit texels
        DBG_DD((6,"  Texture is 4-Bit indexed"));
        lWidth >>= 1;
        SEND_PERMEDIA_DATA(DitherMode, 0);
        break;
    case __PERMEDIA_8BITPIXEL: 
        DBG_DD((6,"  Texture is 8-Bit indexed"));
        SEND_PERMEDIA_DATA(DitherMode, 0);
        break;
    default:
        if (lPixelSize != __PERMEDIA_24BITPIXEL) {
            DBG_DD((6,"  Texture is BGR"));
            ulTextureBase >>= lPixelSize;
        } else {
            DBG_DD((6,"  Texture is 24-Bit BGR"));
            ulTextureBase /= 3;
        }
        // Setup the Dither unit
        SEND_PERMEDIA_DATA(DitherMode,( 
                (INV_COLOR_MODE << PM_DITHERMODE_COLORORDER)|
                (1 << PM_DITHERMODE_ENABLE)                 |
                (pPrivateDest->SurfaceFormat.Format << 
                    PM_DITHERMODE_COLORFORMAT) |
                (pPrivateDest->SurfaceFormat.FormatExtension << 
                    PM_DITHERMODE_COLORFORMATEXTENSION) ));
        break;
    }

    DBG_DD((6,"  Partial Products: 0x%x", pPrivateDest->ulPackedPP));
    DBG_DD((6,"  Texture Width: 0x%x, Downloaded as: 0x%x", 
                (rDest->right - rDest->left),lWidth));
    DBG_DD((6,"  Texture Height: 0x%x", rDest->bottom - rDest->top));
    DBG_DD((6,"  PixelSize: 0x%x", pPrivateDest->SurfaceFormat.PixelSize));
    DBG_DD((6,"  Format: 0x%x", pPrivateDest->SurfaceFormat.Format));
    DBG_DD((6,"  Format Extension: 0x%x", 
                 pPrivateDest->SurfaceFormat.FormatExtension));

    // Downloading a texture, disable texture colour mode.
    SEND_PERMEDIA_DATA(TextureColorMode,    (0 << PM_TEXCOLORMODE_ENABLE));
    SEND_PERMEDIA_DATA(LogicalOpMode, 0);

    //
    //  all textures get by default marked as P2_CANPATCH,
    //  except 4 bit paletted textures
    //
    if (pPrivateDest->dwFlags & P2_CANPATCH) {

        // Mark the texture as being patched.
        pPrivateDest->dwFlags |= P2_ISPATCHED;

        // set up partial product and patch
        SEND_PERMEDIA_DATA(FBReadMode,  
                PM_FBREADMODE_PARTIAL(pPrivateDest->ulPackedPP) |
                PM_FBREADMODE_PATCHENABLE(__PERMEDIA_ENABLE) |
                PM_FBREADMODE_PATCHMODE(__PERMEDIA_SUBPATCH) );

    } else {

        // This texture isn't patched
        pPrivateDest->dwFlags &= ~P2_ISPATCHED;

        // Load up the partial products of the texture, don't use patching
        SEND_PERMEDIA_DATA(FBReadMode, 
                PM_FBREADMODE_PARTIAL(pPrivateDest->ulPackedPP));
    }

    SEND_PERMEDIA_DATA(FBPixelOffset, 0);
    SEND_PERMEDIA_DATA(FBWindowBase, ulTextureBase);

    // Use left to right and top to bottom
    if (lWidth == 2048)
    {
        // special case for 2048-wide textures because of the precision
        // of the StartXSub register
        SEND_PERMEDIA_DATA(StartXDom, INTtoFIXED(-1));
        SEND_PERMEDIA_DATA(StartXSub, INTtoFIXED(lWidth-1));
    }
    else
    {
        SEND_PERMEDIA_DATA(StartXDom, INTtoFIXED(0));
        SEND_PERMEDIA_DATA(StartXSub, INTtoFIXED(lWidth));
    }
    SEND_PERMEDIA_DATA(StartY,    INTtoFIXED(0));
    SEND_PERMEDIA_DATA(dY,        INTtoFIXED(1));
    SEND_PERMEDIA_DATA(Count,     (lLines));
    SEND_PERMEDIA_DATA(Render,    __RENDER_TRAPEZOID_PRIMITIVE |
                                  __RENDER_SYNC_ON_HOST_DATA);
    COMMITDMAPTR();

    switch (lPixelSize) {

    case __PERMEDIA_4BITPIXEL:
    case __PERMEDIA_8BITPIXEL:
    {
        BYTE* pTextureData = (BYTE*)fpSrcVidMem;

        //
        //  download texture data line by line
        //
        while(lLines-- > 0)
        {
            LONG lWords=lWidth;
            BYTE *pData=pTextureData;

            RESERVEDMAWORDS(lWords+1);

            LD_INPUT_FIFO_DATA( __Permedia2TagColor | 
                                ((lWords-1) << 16));

            while (lWords--)
            {
                LD_INPUT_FIFO_DATA(*pData++);
            }

            COMMITDMAPTR();

            //
            //  force flush only every couple of lines
            //
            if ((lLines & 3)==0)
            {
                FLUSHDMA();
            }

            pTextureData += lSrcPitch;
        }
    }
    break;

    case __PERMEDIA_16BITPIXEL:
    {
        BYTE* pTextureData  = (BYTE*)fpSrcVidMem;

        if (pPrivateDest->SurfaceFormat.RedMask == 0x7c00)
        {
            DBG_DD((6,"  Texture is BGR, 16 bit 5:5:5:1"));

            //
            //  download texture data line by line
            //
            while(lLines-- > 0)
            {
                LONG lWords=lWidth;
                WORD *pData=(WORD*)pTextureData;

                RESERVEDMAWORDS(lWords+1);

                LD_INPUT_FIFO_DATA( __Permedia2TagColor | 
                                        ((lWords-1) << 16));

                while (lWords--)
                {
                    LD_INPUT_FIFO_DATA(FORMAT_5551_32BIT((DWORD)*pData));
                    pData++;
                }

                COMMITDMAPTR();

                //
                //  force flush only every couple of lines
                //
                if ((lLines & 3)==0)
                {
                    FLUSHDMA();
                }

                pTextureData += lSrcPitch;
            }
        }
        else if(pPrivateDest->SurfaceFormat.RedMask == 0xF00)
        {
            DBG_DD((6,"  Texture is BGR, 16 bit 4:4:4:4"));
            //
            //  download texture data line by line
            //
            while(lLines-- > 0)
            {
                LONG lWords=lWidth;
                WORD *pData=(WORD*)pTextureData;

                RESERVEDMAWORDS(lWords+1);

                LD_INPUT_FIFO_DATA( __Permedia2TagColor | 
                                    ((lWords-1) << 16));

                while (lWords--)
                {
                    LD_INPUT_FIFO_DATA(FORMAT_4444_32BIT((DWORD)*pData));
                    pData++;
                }

                COMMITDMAPTR();

                //
                //  force flush only every couple of lines
                //
                if ((lLines & 3)==0)
                {
                    FLUSHDMA();
                }

                pTextureData += lSrcPitch;
            }
        }
        else
        {
            DBG_DD((6,"  Texture is BGR, 16 bit 5:6:5"));
            //
            //  download texture data line by line
            //
            while(lLines-- > 0)
            {
                LONG lWords=lWidth;
                WORD *pData=(WORD*)pTextureData;

                RESERVEDMAWORDS(lWords+1);

                LD_INPUT_FIFO_DATA( __Permedia2TagColor | 
                                    ((lWords-1) << 16));

                while (lWords--)
                {
                    LD_INPUT_FIFO_DATA(FORMAT_565_32BIT((DWORD)*pData));
                    pData++;
                }

                COMMITDMAPTR();

                //
                //  force flush only every couple of lines
                //
                if ((lLines & 3)==0)
                {
                    FLUSHDMA();
                }

                pTextureData += lSrcPitch;
            }
        }
    }
    break;

    case __PERMEDIA_24BITPIXEL:
    case __PERMEDIA_32BITPIXEL:
    {
        BYTE* pTextureData  = (BYTE*)fpSrcVidMem;
        //
        //  download texture data line by line
        //
        while(lLines-- > 0)
        {
            LONG lWords=lWidth;
            ULONG *pData=(ULONG*)pTextureData;

            RESERVEDMAWORDS(lWords+1);

            LD_INPUT_FIFO_DATA( __Permedia2TagColor | 
                                ((lWords-1) << 16));

            while (lWords--)
            {
                LD_INPUT_FIFO_DATA(*pData++);
            }

            COMMITDMAPTR();

            //
            //  force flush only every couple of lines
            //
            if ((lLines & 3)==0)
            {
                FLUSHDMA();
            }

            pTextureData += lSrcPitch;
        }
    }
    break;

    }

    RESERVEDMAPTR(2);
    SEND_PERMEDIA_DATA(DitherMode, 0);
    SEND_PERMEDIA_DATA(WaitForCompletion, 0);
    COMMITDMAPTR();

} // PermediaPatchedTextureDownload 


//-----------------------------------------------------------------------------
//
// PermediaPackedDownload
//
// Function to do a system to video memory blt.
// Uses the packed bit on Permedia to do the packing for us. Needs
// to setup the offset bit for alignment and doesn't need to adjust
// the partial products. The calling function guarantees that the
// source and destination rects have the same size.
// 
//
// ppdev----------the PPDev 
// pPrivateDst----Permedia Surface data for destination
// lpSourceSurf---DDraw LCL for source surface 
// rSrc-----------source rect
// lpDestSurf-----DDraw LCL for destination surface
// rDest----------dest rect
//
//-----------------------------------------------------------------------------

VOID
PermediaPackedDownload(PPDev ppdev, 
                       PermediaSurfaceData* pPrivateDst, 
                       LPDDRAWI_DDRAWSURFACE_LCL lpSourceSurf, 
                       RECTL* rSrc, 
                       LPDDRAWI_DDRAWSURFACE_LCL lpDestSurf, 
                       RECTL* rDst)
{
    PERMEDIA_DEFS(ppdev);

    LONG  lDstOffset;           // dest offset in packed coordinates
    LONG  lSrcOffset;           // source offset in buffer in bytes
    LONG  lDstLeft, lDstRight;  // left and right dst in packed coordiantes
    LONG  lSrcLeft, lSrcRight;  // left and right src in packed coordiantes
    LONG  lPackedWidth;         // packed width to download
    LONG  lPixelMask;           // mask for pixels per packed DWORD
    LONG  lOffset;              // relative offset between src and dest 
    LONG  lPixelShift;          // handy helper var which contains pixel 
                                // shift from packed to surface format
    LONG  lPixelSize;           // just a helper
    LONG  lExtraDword;          // chip needs extra dummy 
                                // DWORD passed at end of line

    DBG_DD((5,"DDraw:PermediaPackedDownload, PrivateDst: 0x%x",
                pPrivateDst));

    ASSERTDD(CHECK_P2_SURFACEDATA_VALIDITY(pPrivateDst), 
                "Blt: Destination Private Data not valid!");
    ASSERTDD((rSrc->right-rSrc->left)==(rDst->right-rDst->left),
                "PermediaPackedDownload: src and dest rect width not equal");
    ASSERTDD((rSrc->bottom-rSrc->top)==(rDst->bottom-rDst->top),
                "PermediaPackedDownload: src and dest rect height not equal");

    // get a handy variable for pixel shifts, masks and size
    lPixelSize=pPrivateDst->SurfaceFormat.PixelSize;
    lPixelMask=pPrivateDst->SurfaceFormat.PixelMask;
    lPixelShift=pPrivateDst->SurfaceFormat.PixelShift;

    // offset in dst buffer adjusted to packed format
    lDstOffset =(LONG)((UINT_PTR)(lpDestSurf->lpGbl->fpVidMem) >> lPixelShift);

    // calculate offset in source buffer adjusted to packed format
    lSrcOffset = ((rSrc->left & ~lPixelMask) << lPixelShift) + 
                  (rSrc->top * lpSourceSurf->lpGbl->lPitch);

    // Calculate the relative offset within the dword packed dimensions
    lOffset = ((rDst->left & lPixelMask) - 
               (rSrc->left & lPixelMask)) & 0x7;

    // set up the left and right end of the unpacked source data
    lDstLeft  = rDst->left;
    lDstRight = rDst->right;

    // precalc packed width for 32 bit case
    lPackedWidth = lDstRight-lDstLeft;
    lExtraDword=0;

    if (lPixelSize != __PERMEDIA_32BITPIXEL) 
    {
        // we need to check both source and dest
        // if they have different alignments
        LONG lSrcLeft  = rSrc->left;
        LONG lSrcRight = rSrc->right;

        // Set up the relative offset to allow us to download packed word
        // and byte aligned data.
        if (lPixelSize == __PERMEDIA_4BITPIXEL) 
        {
            lDstLeft >>= 3;
            lSrcLeft >>= 3;
            lDstRight = (lDstRight + 7) >> 3;
            lSrcRight = (lSrcRight + 7) >> 3;
        }
        else 
        if (lPixelSize == __PERMEDIA_8BITPIXEL) 
        {
            lDstLeft >>= 2;
            lSrcLeft >>= 2;
            lDstRight = (lDstRight + 3) >> 2;
            lSrcRight = (lSrcRight + 3) >> 2;
        }
        else 
        {
            lDstLeft >>= 1;
            lSrcLeft >>= 1;
            lDstRight = (lDstRight + 1) >> 1;
            lSrcRight = (lSrcRight + 1) >> 1;
        }

        if ((lSrcRight-lSrcLeft) < (lDstRight-lDstLeft))
        {
            lExtraDword=1;
            lPackedWidth = lDstRight-lDstLeft;
        } else
        {
            lPackedWidth = lSrcRight-lSrcLeft;
        }
    } 

    RESERVEDMAPTR(12);
    SEND_PERMEDIA_DATA(FBReadPixel, pPrivateDst->SurfaceFormat.FBReadPixel);

    // No logical ops in SYS->VIDMEM Blits
    SEND_PERMEDIA_DATA(LogicalOpMode, 0);

    // Load up the partial products of image
    SEND_PERMEDIA_DATA(FBReadMode, (pPrivateDst->ulPackedPP) |
                                   PM_FBREADMODE_PACKEDDATA(__PERMEDIA_ENABLE)|
                                   PM_FBREADMODE_RELATIVEOFFSET(lOffset) );

    SEND_PERMEDIA_DATA(FBPixelOffset, 0);
    SEND_PERMEDIA_DATA(FBWindowBase,  lDstOffset);

    // Use left to right and top to bottom
    SEND_PERMEDIA_DATA(StartXDom,       INTtoFIXED(lDstLeft));
    SEND_PERMEDIA_DATA(StartXSub,       INTtoFIXED(lDstLeft+lPackedWidth));
    SEND_PERMEDIA_DATA(PackedDataLimits,PM_PACKEDDATALIMITS_OFFSET(lOffset) | 
                                        (rDst->left << 16) | 
                                         rDst->right);
    SEND_PERMEDIA_DATA(StartY,          INTtoFIXED(rDst->top));
    SEND_PERMEDIA_DATA(dY,              INTtoFIXED(1));
    SEND_PERMEDIA_DATA(Count,           (rDst->bottom - rDst->top));
    SEND_PERMEDIA_DATA(Render,          __RENDER_TRAPEZOID_PRIMITIVE | 
                                        __RENDER_SYNC_ON_HOST_DATA);
    COMMITDMAPTR();

    //
    // introduce some more handy pointers and LONGs
    //
    BYTE *pSurfaceData = (BYTE *)lpSourceSurf->lpGbl->fpVidMem + lSrcOffset;
    LONG lPitch =lpSourceSurf->lpGbl->lPitch;
    LONG lHeight=rDst->bottom - rDst->top;

    //
    // pump the whole thing in one huge block 
    // if the pitch and linewidth are the same and no extra treatment
    // for the buffer end is necessary
    //
    if ((lExtraDword==0) &&
        (lPackedWidth*(LONG)sizeof(ULONG))==lPitch)
    {
        vBlockLoadInputFifo( pP2dma, 
                             __Permedia2TagColor, 
                             (ULONG*)pSurfaceData, 
                             lPackedWidth*lHeight);
    } else
    {
        //
        //  lExtraDword is zero or 1, depends if we have to do a special
        //  treatment after this while block
        //
        while (lHeight>lExtraDword)
        {
            LONG lWords=lPackedWidth;
            ULONG *pImage=(ULONG*)pSurfaceData;

            RESERVEDMAWORDS(lWords+1);

            LD_INPUT_FIFO_DATA( __Permedia2TagColor | 
                                ((lWords-1) << 16));

            while (lWords--)
            {
                LD_INPUT_FIFO_DATA(*pImage++);
            }

            COMMITDMAPTR();

            //
            //  force flush only every couple of lines
            //
            if ((lHeight & 3)==0)
            {
                FLUSHDMA();
            }

            pSurfaceData += lPitch;
            lHeight--;
        }

        //
        // treat last line separately, because we could read over the
        // end of buffer here if the source and dest rects are aligned
        // differently. lHeight will only be one here if lExtraDword==1
        //
        if (lHeight==1)
        {
            LONG lWords=lPackedWidth-1;
            ULONG *pImage=(ULONG*)pSurfaceData;

            RESERVEDMAWORDS(lWords+1);

            LD_INPUT_FIFO_DATA( __Permedia2TagColor | 
                                ((lWords-1) << 16));

            while (lWords--)
            {
                LD_INPUT_FIFO_DATA(*pImage++);
            }

            COMMITDMAPTR();

            //
            // send extra dummy DWORD
            //
            RESERVEDMAPTR(1);
            SEND_PERMEDIA_DATA( Color, 0);
            COMMITDMAPTR();

        }

        FLUSHDMA();
    }
} // PermediaPackedDownload 


