#include "pch.h"

//////////////////////////////////////////////////////////////////////////////
//
// Surface
//
//////////////////////////////////////////////////////////////////////////////

class PrivateSurfaceImpl : public PrivateSurface {
public:
    //////////////////////////////////////////////////////////////////////////////
    //
    // Members
    //
    //////////////////////////////////////////////////////////////////////////////

    //
    // Debug Info
    //

    ZString                   m_strName;
    bool                      m_bDeviceFormat;
    bool                      m_bInContext;

    //
    // Surface Description
    //

    int                       m_id;
    WinPoint                  m_size;
    int                       m_pitch;
    TRef<PixelFormat>         m_ppf;
    BYTE*                     m_pbits;
    TRef<PrivatePalette>      m_ppalette;
    TRef<IObject>             m_pobjectMemory;

    bool                      m_bColorKey;
    Color                     m_colorKey;

    //
    // Device Format surfaces
    //

    TRef<PrivateEngine>       m_pengine;
    TRef<SurfaceSite>         m_psite;

    //
    // Video Surface
    //

    TRef<VideoSurface>        m_pvideoSurface;
    SurfaceType               m_stype;

    //
    // Format convertion
    //

    int                       m_idConverted;
    TRef<PixelFormat>         m_ppfConverted;
    TRef<PrivateSurfaceImpl>  m_psurfaceConverted;

    //
    // Drawing
    //

    TRef<PrivateContext>      m_pcontext;

    //
    // Transforms and clipping
    //

    WinPoint                  m_pointOffset;
    WinRect                   m_rectClip;
    WinPoint                  m_pointOffsetSave;
    WinRect                   m_rectClipSave;

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

    void Initialize()
    {
        m_pointOffset = WinPoint(0, 0);
        m_rectClip    = WinRect(WinPoint(0, 0), m_size);

        m_bColorKey   = false;
        m_bInContext  = false;

        m_colorKey    = Color(0, 0, 0);

        m_id          = 0;
        m_idConverted = -1;
    }

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

    void AllocateSurface()
    {
        if (m_stype.Test(SurfaceTypeVideo())) {
            m_pvideoSurface =
                m_pengine->CreateVideoSurface(
                    m_stype,
                    m_ppf,
                    m_ppalette,
                    m_size,
                    m_pitch,
                    m_pbits
                );
        } else {
            m_pitch = (m_size.X() * m_ppf->PixelBytes() + 3) & (~0x3);
            m_pbits = new BYTE[m_pitch * m_size.Y()];
        }
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // A device format surface
    //
    //////////////////////////////////////////////////////////////////////////////

    PrivateSurfaceImpl(
        PrivateEngine*  pengine,
        PixelFormat*    ppf,
        PrivatePalette* ppalette,
        const WinPoint& size,
        SurfaceType     stype,
        SurfaceSite*    psite
    ) :
        m_pengine(pengine),
        m_ppf(ppf),
        m_ppalette(ppalette),
        m_size(size),
        m_bDeviceFormat(true),
        m_stype(stype),
        m_psite(psite),
        m_pbits(NULL)
    {
        Initialize();
        AllocateSurface();

        if (m_psite)
            m_psite->UpdateSurface(this);
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Wrap a previously created VideoSurface
    //
    //////////////////////////////////////////////////////////////////////////////

    PrivateSurfaceImpl(
        PrivateEngine* pengine,
        VideoSurface*  pvideoSurface,
        SurfaceSite*   psite
    ) :
        m_pengine(pengine),
        m_bDeviceFormat(false),
        m_stype(pvideoSurface->GetSurfaceType()),
        m_pvideoSurface(pvideoSurface),
        m_ppf(pvideoSurface->GetPixelFormat()),
        m_pbits(NULL),
        m_size(pvideoSurface->GetSize()),
        m_pitch(pvideoSurface->GetPitch()),
        m_psite(psite)
    {
        Initialize();
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Create a surface from a binary representation
    //
    //////////////////////////////////////////////////////////////////////////////

    PrivateSurfaceImpl(
              PrivateEngine*  pengine,
              PixelFormat*    ppf,
              PrivatePalette* ppalette,
        const WinPoint&       size,
              int             pitch,
              BYTE*           pdata,
              IObject*        pobjectMemory
    ) :
        m_pengine(pengine),
        m_bDeviceFormat(false),
        m_stype(SurfaceType2D()),
        m_size(size),
        m_pobjectMemory(pobjectMemory),
        m_pitch(pitch),
        m_ppf(ppf),
        m_ppalette(ppalette),
        m_pbits(pdata)
    {
        Initialize();
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Write a surface to a binary MDL file
    //
    //////////////////////////////////////////////////////////////////////////////
    
    void Write(ZFile* pfile)
    {
        BinarySurfaceInfo bsi;

        bsi.m_size      = m_size;
        bsi.m_pitch     = m_pitch;
        bsi.m_bitCount  = m_ppf->PixelBits();
        bsi.m_redMask   = m_ppf->RedMask();
        bsi.m_greenMask = m_ppf->GreenMask();
        bsi.m_blueMask  = m_ppf->BlueMask();
        bsi.m_alphaMask = m_ppf->AlphaMask();
        bsi.m_bColorKey = m_bColorKey;

        pfile->Write((void*)&bsi, sizeof(bsi));
        pfile->Write((void*)m_pbits, m_pitch * m_size.Y());
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // More constructors
    //
    //////////////////////////////////////////////////////////////////////////////

    TRef<Surface> CreateCompatibleSurface(const WinPoint& size, SurfaceType stype, SurfaceSite* psite)
    {
        return m_pengine->CreateCompatibleSurface(this, size, stype, psite);
    }

    TRef<Surface> Copy()
    {
        TRef<Surface> psurface = m_pengine->CreateCompatibleSurface(this, m_size, GetSurfaceType(), m_psite);

        //  : turn off color key while blting

        psurface->BitBlt(WinPoint(0, 0), this);
        return psurface;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Destructor
    //
    //////////////////////////////////////////////////////////////////////////////

    ~PrivateSurfaceImpl()
    {
        m_pengine->RemovePrivateSurface(this);
        if (m_pobjectMemory == NULL && m_pbits != NULL) {
            delete m_pbits;
        }
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Site
    //
    //////////////////////////////////////////////////////////////////////////////

    void SetSite(SurfaceSite* psite)
    {
        m_psite = psite;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Device Specific Surface
    //
    //////////////////////////////////////////////////////////////////////////////

    void ClearDevice()
    {
        m_pvideoSurface = NULL;
    }

    void SetPixelFormat(PixelFormat* ppf)
    {
        ZAssert(m_pobjectMemory == NULL);
        ZAssert(m_ppalette      == NULL);

        m_pcontext      = NULL;
        m_pvideoSurface = NULL;

        if (m_pbits) {
            delete m_pbits;
            m_pbits = NULL;
        }

        m_ppf = ppf;
        AllocateSurface();

        if (m_psite) {
            m_psite->UpdateSurface(this);
        }
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Surface updates
    //
    //////////////////////////////////////////////////////////////////////////////

    void SurfaceChanged()
    {
        m_id++;
        if (m_id < 0) m_id = 0;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Attributes
    //
    //////////////////////////////////////////////////////////////////////////////

    VideoSurface* GetVideoSurfaceNoAlloc()
    {
        return m_pvideoSurface;
    }

    VideoSurface* GetVideoSurface()
    {
        if (m_pvideoSurface == NULL) {
            ZAssert(m_pbits != NULL);

            m_pvideoSurface =
                m_pengine->CreateVideoSurface(
                    m_stype,
                    m_ppf,
                    m_ppalette,
                    m_size,
                    m_pitch,
                    m_pbits
                );

            //
            // If this surface has a color set the color key on all of the DD Surfaces
            //

            if (HasColorKey()) {
                SetColorKey(GetColorKey());
            }
        }

        return m_pvideoSurface;
    }

    PixelFormat* GetPixelFormat()
    {
        return m_ppf;
    }

    Palette* GetPalette()
    {
        return m_ppalette;
    }

    void SetName(const ZString& str)
    {
        m_strName = str;
    }

    const ZString& GetName()
    {
        return m_strName;
    }

    Engine* GetEngine()
    { 
        return m_pengine;               
    }

    const WinPoint& GetSize()
    { 
        return m_size;                  
    }

    WinRect GetRect()
    {
        return 
            WinRect(
                WinPoint(0, 0),
                m_size
            );
    }

    SurfaceType GetSurfaceType() 
    { 
        return m_stype;           
    }

    bool HasColorKey()
    {
        return m_bColorKey;
    }

    const Color& GetColorKey()
    {
        ZAssert(m_bColorKey);
        return m_colorKey;
    }

    void SetColorKey(const Color& color)
    {
        m_bColorKey = true;
        m_colorKey = color;

        if (m_pvideoSurface) {
            m_pvideoSurface->SetColorKey(color);
        }

        if (m_psurfaceConverted) {
            m_psurfaceConverted->SetColorKey(color);
        }
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Drawing context
    //
    //////////////////////////////////////////////////////////////////////////////

    Context* GetContext()
    {
		ZAssert(!m_bInContext);

		m_bInContext = true;

        if (m_pcontext == NULL) {
            m_pcontext = CreateContextImpl(this);
        }

        if (m_pcontext->IsValid()) {
            //
            // Remove any offset or clipping
            //

            m_rectClipSave    = m_rectClip;
            m_rectClip        = WinRect(WinPoint(0, 0), m_size);
            m_pointOffsetSave = m_pointOffset;
            m_pointOffset     = WinPoint(0, 0);

            m_pcontext->BeginRendering();

            return m_pcontext;
        }

        m_pcontext   = NULL;
        m_bInContext = false;
        return NULL;
    }

    void ReleaseContext(Context* pcontext)
    {
		ZAssert(m_bInContext);
        ZAssert(m_pcontext == pcontext);

		m_bInContext = false;

        m_pcontext->EndRendering();

        m_pointOffset = m_pointOffsetSave;
        m_rectClip    = m_rectClipSave;
    }

    // KGJV 32B - we make this public
    Surface* GetConvertedSurface(PixelFormat* ppf)
    {
        return (Surface *)GetConvertedPrivateSurface(ppf); 
    }
    // KGJV 32B - GetConvertedSurface -> GetConvertedPrivateSurface
    PrivateSurface* GetConvertedPrivateSurface(PixelFormat* ppf)
    {
        if (ppf == m_ppf) {
            return this;
        } else if (ppf != m_ppfConverted || m_idConverted != m_id) {
            if (ppf != m_ppfConverted) {
                //
                // Create a surface with the requested pixel format
                //
                m_ppfConverted = ppf;

                m_psurfaceConverted =
                    new PrivateSurfaceImpl(
                        m_pengine,
                        ppf,
                        NULL,
                        m_size,
                        m_stype,
                        NULL
                
                    );
                }

                if (HasColorKey()) {
                    m_psurfaceConverted->SetColorKey(GetColorKey());
                }

                m_idConverted = m_id - 1;
            }

            //
            // Do the bits need to be updated?
            //

            if (m_idConverted != m_id) {
                //
                // Do a conversion blt
                //

                m_psurfaceConverted->BltConvert(
                    WinPoint(0, 0),
                    this,
                    WinRect(WinPoint(0, 0), m_size)
                );

                //debugf("GetConvertedSurface : %s : Convert %d to %d\n",(const char *)GetName(),m_ppf->PixelBits(),ppf->PixelBits());

                m_idConverted = m_id;
        }

        return m_psurfaceConverted;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Surface Access calls
    //
    //////////////////////////////////////////////////////////////////////////////

    int GetPitch()
    {
        if (m_pvideoSurface) {
            return m_pvideoSurface->GetPitch();
        } else {
            return m_pitch;
        }
    }

    const BYTE* GetPointer()
    {
        if (m_pvideoSurface && (!m_pvideoSurface->IsMemoryShared())) {
            return m_pvideoSurface->GetPointer();
        } else {
            return m_pbits;
        }
    }

    const BYTE* GetPointer(const WinPoint& point)
    {
        return 
              GetPointer()
            + point.Y() * GetPitch()
            + point.X() * m_ppf->PixelBytes();
    }

    BYTE* GetWritablePointer()
    {
        SurfaceChanged();

        if (m_pvideoSurface && (!m_pvideoSurface->IsMemoryShared())) {
            return m_pvideoSurface->GetPointer();
        } else {
            return m_pbits;
        }
    }

    BYTE* GetWritablePointer(const WinPoint& point)
    {
        return 
              GetWritablePointer()
            + point.Y() * GetPitch()
            + point.X() * m_ppf->PixelBytes();
    }

    void ReleasePointer()
    {
        if (m_pbits == NULL) {
            m_pvideoSurface->ReleasePointer();
        }
    }

    Pixel GetPixel(const WinPoint& point)
    {
        return m_ppf->GetPixel(GetPointer(point));
    }

    Color GetColor(const WinPoint& point)
    {
        if (m_ppalette) {
            return m_ppalette->GetColor(GetPixel(point).Value());
        } else {
            return m_ppf->MakeColor(GetPixel(point));
        }
    }

    void SetPixel(const WinPoint& point, Pixel pixel)
    {
        m_ppf->SetPixel(GetWritablePointer(point), pixel);
    }

    void SetColor(const WinPoint& point, const Color& color)
    {
        ZAssert(m_ppalette == NULL);
        SetPixel(point, m_ppf->MakePixel(color));
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Clipping and transforms
    //
    //////////////////////////////////////////////////////////////////////////////

    void Offset(const WinPoint& pointOffset)
    {
        m_pointOffset += pointOffset;
    }

    const WinPoint& GetOffset()
    {
        return m_pointOffset;
    }

    WinRect GetClipRect()
    {
        WinRect rect = m_rectClip;
        rect.Offset(-m_pointOffset);
        return rect;
    }

    void SetClipRect(const WinRect& rectLocal)
    {
        WinRect rectClip(rectLocal);
        rectClip.Offset(m_pointOffset);
        m_rectClip.Intersect(rectClip);
    }

    void RestoreClipRect(const WinRect& rectLocal)
    {
        m_rectClip = rectLocal;
        m_rectClip.Offset(m_pointOffset);
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // GDI calls
    //
    //////////////////////////////////////////////////////////////////////////////

    void DrawString(
        IEngineFont* pfont, 
        const Color& color, 
        const WinPoint& point, 
        const ZString& str
    ) {
        pfont->DrawString(
            this, 
            point + m_pointOffset, 
            m_rectClip,
            str, 
            color
        );
    }

    void DrawStringWithShadow(
        IEngineFont* pfont, 
        const Color& color, 
        const Color& colorShadow,
        const WinPoint& point, 
        const ZString& str
    ) {
        DrawString(pfont, colorShadow, point + WinPoint(1, 1), str);
        DrawString(pfont, color      , point                 , str);
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Format converting blt
    //
    //////////////////////////////////////////////////////////////////////////////

    void BltConvert(
        const WinPoint&           point, 
              PrivateSurfaceImpl* psurfaceSource, 
        const WinRect&            rectSource
    ) {
        //
        // Source info
        //

        PixelFormat* ppfSource   = psurfaceSource->GetPixelFormat();
        const BYTE*  psource     = psurfaceSource->GetPointer(rectSource.Min());
        int          pitchSource = psurfaceSource->GetPitch();
        int          bytesSource = ppfSource->PixelBytes();

        //
        // Dest info
        //

        BYTE* pdest     = GetWritablePointer(point);
        int   bytesDest = m_ppf->PixelBytes();
        int   pitchDest = GetPitch();

        //
        // Do the appropriate blt
        //

        if (
               bytesSource == 2 
            && bytesDest   == 2
        ) {
            ZAssert(ppfSource->RedSize()  == 0x1f);
            ZAssert(ppfSource->BlueSize() == 0x1f);

            ZAssert(m_ppf->RedSize()  == 0x1f);
            ZAssert(m_ppf->BlueSize() == 0x1f);

            if (ppfSource->GreenSize() == 0x1f) {
                //
                // Convert 555 to 565
                //

                ZAssert(m_ppf->GreenSize() == 0x3f);

                for (int y = rectSource.YSize(); y > 0; y--) {
                    for (int x = 0; x < rectSource.XSize(); x++) {
                        WORD wSource = ((WORD*)psource)[x];
                        WORD wDest   = 
                              ((wSource & 0xffe0) << 1)
                            | (wSource & 0x1f);

                        ((WORD*)pdest)[x] = wDest;
                    }
                    psource += pitchSource;
                    pdest   += pitchDest;
                }
            } else {
                //
                // Convert 565 to 555
                //

                ZAssert(ppfSource->GreenSize() == 0x3f);
                ZAssert(m_ppf    ->GreenSize() == 0x1f);

                for (int y = rectSource.YSize(); y > 0; y--) {
                    for (int x = 0; x < rectSource.XSize(); x++) {
                        WORD wSource = ((WORD*)psource)[x];
                        WORD wDest   = 
                              ((wSource >> 1) & 0xffe0)
                            | (wSource & 0x1f);

                        ((WORD*)pdest)[x] = wDest;
                    }
                    psource += pitchSource;
                    pdest   += pitchDest;
                }
            }
        } else {
            //
            // Not going to or from an important format so go through the slow code
            //

            for (int y = rectSource.YSize(); y > 0; y--) {
                for (int x = 0; x < rectSource.XSize(); x++) {
                    m_ppf->SetColor(
                        pdest + x * bytesDest,
                        ppfSource->GetColor(psource + x * bytesSource)
                    );
                }
                psource += pitchSource;
                pdest   += pitchDest;
            }
        }

        psurfaceSource->ReleasePointer();
        ReleasePointer();
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Blts
    //
    //////////////////////////////////////////////////////////////////////////////

    void UnclippedBltHandled(PrivateSurface* psurfaceSource, const WinRect& rect, const WinPoint& pointSource, BYTE* pdest)
    {
        const BYTE* psource     = psurfaceSource->GetPointer(pointSource);
        int         pitchSource = psurfaceSource->GetPitch();
        int         pitchDest   = GetPitch();
        int         scanSize    = rect.XSize() * m_ppf->PixelBytes();
        int         ysize       = rect.YSize();

        if (psurfaceSource->HasColorKey()) {
            //ZAssert(m_ppf->PixelBytes() == 2); // KGJV 32B 
            // debugf("UnclippedBltHandled : m_ppf->PixelBytes() = %d\n",m_ppf->PixelBytes());
            if (m_ppf->PixelBytes() == 2)
            {
                int xdqwords = scanSize / 16;
                int xwords   = (scanSize - 16 * xdqwords) / 2;

                __asm {
                  yloop:
                    mov     esi, [psource]
                    mov     edi, [pdest]

                    mov     ecx, xdqwords
                    or      ecx, ecx
                    je      xwordsTop
                  xdqwordloop:
                    mov     dx, [esi + 0]
                    or      dx, dx
                    je      skip1
                    mov     [edi + 0], dx
                  skip1:
                    mov     dx, [esi + 2]
                    or      dx, dx
                    je      skip2
                    mov     [edi + 2], dx
                  skip2:
                    mov     dx, [esi + 4]
                    or      dx, dx
                    je      skip3
                    mov     [edi + 4], dx
                  skip3:
                    mov     dx, [esi + 6]
                    or      dx, dx
                    je      skip4
                    mov     [edi + 6], dx
                  skip4:
                    mov     dx, [esi + 8]
                    or      dx, dx
                    je      skip5
                    mov     [edi + 8], dx
                  skip5:
                    mov     dx, [esi + 10]
                    or      dx, dx
                    je      skip6
                    mov     [edi + 10], dx
                  skip6:
                    mov     dx, [esi + 12]
                    or      dx, dx
                    je      skip7
                    mov     [edi + 12], dx
                  skip7:
                    mov     dx, [esi + 14]
                    or      dx, dx
                    je      skip8
                    mov     [edi + 14], dx
                  skip8:
                    add     esi, 16
                    add     edi, 16
                    dec     ecx
                    jne     xdqwordloop
              
                  xwordsTop:  
                    mov     ecx, xwords
                    or      ecx, ecx
                    je      nowords
                  xwordloop:
                    mov     dx, [esi]
                    or      dx, dx
                    je      skipword
                    mov     [edi], dx
                  skipword:
                    add     esi, 2
                    add     edi, 2
                    dec     ecx
                    jne     xwordloop

                  nowords:
                    mov     eax, pitchSource
                    mov     ebx, pitchDest
                    add     [psource], eax
                    add     [pdest], ebx
                    dec     ysize
                    jne     yloop
                }
            }
            else // KGJV 32B : assert (m_ppf->PixelBytes() == 4)
            {
                int xdqwords = scanSize / 32;
                int xwords   = (scanSize - 32 * xdqwords) / 4;

                __asm {
                  b32_yloop:
                    mov     esi, [psource]
                    mov     edi, [pdest]

                    mov     ecx, xdqwords
                    or      ecx, ecx
                    je      b32_xwordsTop
                  b32_xdqwordloop:
                    mov     edx, [esi + 0]
                    or      edx, edx
                    je      b32_skip1
                    mov     [edi + 0], edx
                  b32_skip1:
                    mov     edx, [esi + 4]
                    or      edx, edx
                    je      b32_skip2
                    mov     [edi + 4], edx
                  b32_skip2:
                    mov     edx, [esi + 8]
                    or      edx, edx
                    je      b32_skip3
                    mov     [edi + 8], edx
                  b32_skip3:
                    mov     edx, [esi + 12]
                    or      edx, edx
                    je      b32_skip4
                    mov     [edi + 12], edx
                  b32_skip4:
                    mov     edx, [esi + 16]
                    or      edx, edx
                    je      b32_skip5
                    mov     [edi + 16], edx
                  b32_skip5:
                    mov     edx, [esi + 20]
                    or      edx, edx
                    je      b32_skip6
                    mov     [edi + 20], edx
                  b32_skip6:
                    mov     edx, [esi + 24]
                    or      edx, edx
                    je      b32_skip7
                    mov     [edi + 24], edx
                  b32_skip7:
                    mov     edx, [esi + 28]
                    or      edx, edx
                    je      b32_skip8
                    mov     [edi + 28], edx
                  b32_skip8:
                    add     esi, 32
                    add     edi, 32
                    dec     ecx
                    jne     b32_xdqwordloop
              
                  b32_xwordsTop:  
                    mov     ecx, xwords
                    or      ecx, ecx
                    je      b32_nowords
                  b32_xwordloop:
                    mov     edx, [esi]
                    or      edx, edx
                    je      b32_skipword
                    mov     [edi], edx
                  b32_skipword:
                    add     esi, 4
                    add     edi, 4
                    dec     ecx
                    jne     b32_xwordloop

                  b32_nowords:
                    mov     eax, pitchSource
                    mov     ebx, pitchDest
                    add     [psource], eax
                    add     [pdest], ebx
                    dec     ysize
                    jne     b32_yloop
                }
            }
        } else {
            int xdwords = scanSize / 4;
            int xwords  = (scanSize - 4 * xdwords) / 2;

             __asm {
                mov     edx, psource
                mov     ebx, pdest

              nokeyyloop:
                mov     esi, edx
                mov     edi, ebx
                mov     ecx, xdwords
                rep     movsd    

                test    [xwords], 1
                je      nokeynowords

                mov     ax, [esi]
                mov     [edi], ax

              nokeynowords:
                add     edx, pitchSource
                add     ebx, pitchDest
                dec     ysize
                jne     nokeyyloop
             }
        }
    }

    void UnclippedBlt(
        const WinRect& rect, 
        PrivateSurfaceImpl* psurfaceSourceOriginal, 
        const WinPoint& pointSource
    ) {
        ZAssert(rect.XMin() >= 0                                                       );
        ZAssert(rect.XMax() <= m_size.X()                                              );
        ZAssert(rect.YMin() >= 0                                                       );
        ZAssert(rect.YMax() <= m_size.Y()                                              );
        // ZAssert(rect.XSize() + pointSource.X() <= psurfaceSourceOriginal->GetSize().X()); mmf took these two out as they assert too often in debug build
        // ZAssert(rect.YSize() + pointSource.Y() <= psurfaceSourceOriginal->GetSize().Y()); mmf return and log these and perhaps fix them ? 

        if (rect.XSize() == 0 || rect.YSize() == 0) {
            return;            
        }

        PrivateSurface* psurfaceSource = psurfaceSourceOriginal->GetConvertedPrivateSurface(m_ppf);
        ZAssert(psurfaceSource->GetPixelFormat() == m_ppf);

        /* Uncomment this code to use DDraw for blts
		   KGJV 32B: seems some ATI have issues with DDraw & colorkey
		   so we keep cpu code for now.
        if (m_pbits == NULL) {
            m_pvideoSurface->UnclippedBlt(
                rect, 
                psurfaceSource->GetVideoSurface(), 
                pointSource
            );
        } else*/
        {
            BYTE* pdest = GetWritablePointer(rect.Min());

            __try {
                UnclippedBltHandled(psurfaceSource, rect, pointSource, pdest);
            } __except (true) {
                //
                // DDraw took away our access to some video memory.  Ignore the exception
                // and continue.
                //
            }

            psurfaceSource->ReleasePointer();
            ReleasePointer();
        }
    }

    void BitBlt(const WinPoint& point, Surface* psurfaceSourceArg, const WinRect& rectSourceArg)
    {
        PrivateSurfaceImpl* psurfaceSource = (PrivateSurfaceImpl*)psurfaceSourceArg;

        //
        // Calculate the target rectangle
        //

        WinPoint origin = point + m_pointOffset;
        WinRect rectTarget(origin, origin + rectSourceArg.Size());

        //
        // Clip it to the surface
        //

        rectTarget.Intersect(m_rectClip);

        //
        // Only blt if the target isn't empty
        //

        if (!rectTarget.IsEmpty()) {
            //
            // adjust the source rect to match the target rect
            //

            WinPoint SourceOrigin = rectSourceArg.Min() + (rectTarget.Min() - origin);
            WinRect rectSource(SourceOrigin, SourceOrigin + rectTarget.Size());

            //
            // do the blt
            //

            UnclippedBlt(rectTarget, psurfaceSource, rectSource.Min());
        }

        SurfaceChanged();
    }

    void BitBlt(const WinPoint& point, Surface* psurfaceSource)
    {
        BitBlt(
            point, 
            psurfaceSource, 
            WinRect(
                WinPoint(0, 0), 
                psurfaceSource->GetSize()
            )
        );
    }

    void BitBltFromCenter(const WinPoint& point, Surface* psurfaceSource)
    {
        BitBlt(
            point - psurfaceSource->GetSize() / 2, 
            psurfaceSource
        );
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // StretchBlts
    //
    //////////////////////////////////////////////////////////////////////////////

    void BitBlt(const WinRect& rectTargetArg, Surface* psurfaceSourceArg, const WinRect& rectSource)
    {
        PrivateSurfaceImpl* psurfaceSource = (PrivateSurfaceImpl*)psurfaceSourceArg;

        //
        // Calculate the target rectangle
        //

        WinRect rectTarget = rectTargetArg;
        rectTarget.Offset(m_pointOffset);

        // !!! this function is unclipped
        // !!! rectTarget.Intersect(m_rectClip);

        //
        // Only blt if the target isn't empty
        //

        if (!rectTarget.IsEmpty()) {
            //
            // do the blt
            //

            GetVideoSurface()->UnclippedBlt(
                rectTarget,
                psurfaceSource->GetVideoSurface(),
                rectSource,
                HasColorKey()
            );
        }

        SurfaceChanged();
    }

    void BitBlt(const WinRect& rectTarget, Surface* psurfaceSource)
    {
        BitBlt(
            rectTarget,
            psurfaceSource, 
            WinRect(
                WinPoint(0, 0), 
                psurfaceSource->GetSize()
            )
        );
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Direct Draw Fills
    //
    //////////////////////////////////////////////////////////////////////////////

    void UnclippedFill(const WinRect& rect, Pixel pixel)
    {
        ZAssert(rect.XMin() >= 0         );
        ZAssert(rect.XMax() <= m_size.X());
        ZAssert(rect.YMin() >= 0         );
        ZAssert(rect.YMax() <= m_size.Y());

        // KGJV 32B 
        if (m_pbits == NULL) {
            m_pvideoSurface->UnclippedFill(rect, pixel);
        } else {
            // KGJV 32B 
            // ZAssert(m_ppf->PixelBytes() == 2);
            if (m_ppf->PixelBytes() == 4) // KGJV 32B
            {
                BYTE* pdest    = GetWritablePointer(rect.Min());
                //int   scanSize = rect.XSize() * 4; // unused ?
                DWORD  wPixel   = (DWORD)pixel.Value();
                int   pitch    = GetPitch();

                for (int y = rect.YSize(); y > 0; y--) {
                    for (int x = 0; x < rect.XSize(); x++) {
                        ((DWORD*)pdest)[x] = wPixel;
                    }
                    pdest += pitch;
                }            }
            else // defaut old code
            {
                BYTE* pdest    = GetWritablePointer(rect.Min());
                int   scanSize = rect.XSize() * 2;
                WORD  wPixel   = (WORD)pixel.Value();
                int   pitch    = GetPitch();

                for (int y = rect.YSize(); y > 0; y--) {
                    for (int x = 0; x < rect.XSize(); x++) {
                        ((WORD*)pdest)[x] = wPixel;
                    }
                    pdest += pitch;
                }
            }
            ReleasePointer();
        }
    }

    void FillRect(const WinRect& rectArg, Pixel pixel)
    {
        //
        // Offset the rect to the origin of the surface
        //

        WinRect rect = rectArg;
        rect.Offset(m_pointOffset);

        //
        // Clip it to the surface
        //

        rect.Intersect(m_rectClip);

        //
        // Only blt if the target isn't empty
        //

        if (!rect.IsEmpty()) {
            UnclippedFill(rect, pixel);
        }

        SurfaceChanged();
    }

    void FillRect(const WinRect& rect, const Color& color)
    {
        FillRect(rect, m_ppf->MakePixel(color));
    }

    void FillSurface(Pixel pixel)
    {
        FillRect(WinRect(-m_pointOffset, m_size - m_pointOffset), pixel);
    }

    void FillSurface(const Color& color)
    {
        FillSurface(m_ppf->MakePixel(color));
    }

    void FillSurfaceWithColorKey()
    {
        FillSurface(m_colorKey);
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Load a surface from a bitmap
    //
    //////////////////////////////////////////////////////////////////////////////

    void BitBltFromDC(HDC hdc) 
    {
        GetVideoSurface()->BitBltFromDC(hdc);
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Save the surface to a file
    //
    //////////////////////////////////////////////////////////////////////////////

    void Save(ZFile* pfile)
    {
        ZAssert(m_ppf->PixelBits() == 16);

        BITMAPFILEHEADER bmfh;
        BITMAPINFOHEADER bmih;

        int sizeHeader = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 12;
        int sizeBits   = m_pitch * m_size.Y();

        bmfh.bfType          = 0x4d42;
        bmfh.bfSize          = sizeHeader + sizeBits;
        bmfh.bfReserved1     = 0;
        bmfh.bfReserved2     = 0;
        bmfh.bfOffBits       = sizeHeader;

        bmih.biSize          = sizeof(BITMAPINFOHEADER);
        bmih.biWidth         = m_size.X();
        bmih.biHeight        = m_size.Y();
        bmih.biPlanes        = 1;
        bmih.biBitCount      = (WORD)m_ppf->PixelBits();
        bmih.biCompression   = BI_BITFIELDS;
        bmih.biSizeImage     = 0;
        bmih.biXPelsPerMeter = 0;
        bmih.biYPelsPerMeter = 0;
        bmih.biClrUsed       = 0;
        bmih.biClrImportant  = 0;

        DWORD masks[3] =
            {
                m_ppf->RedMask(),
                m_ppf->GreenMask(),
                m_ppf->BlueMask()
            };

        //
        // Write out the header
        //

        pfile->Write(&bmfh, sizeof(BITMAPFILEHEADER));
        pfile->Write(&bmih, sizeof(BITMAPINFOHEADER));
        pfile->Write(masks, 12);

        //
        // Write out the bits
        //

        for (int y = m_size.Y() - 1 ; y >= 0; y--) { 
            pfile->Write(m_pbits + m_pitch * y, m_pitch);
        }
    }
};

//////////////////////////////////////////////////////////////////////////////
//
// Used to create a surface with a certain pixel format
//
//////////////////////////////////////////////////////////////////////////////

TRef<PrivateSurface> CreatePrivateSurface(
    PrivateEngine*  pengine,
    PixelFormat*    ppf,
    PrivatePalette* ppalette,
    const WinPoint& size,
    SurfaceType     stype,
    SurfaceSite*    psite
) {
    return new PrivateSurfaceImpl(pengine, ppf, ppalette, size, stype, psite);
}

//////////////////////////////////////////////////////////////////////////////
//
// Used to create a surface from a memory mapped file
//
//////////////////////////////////////////////////////////////////////////////

TRef<PrivateSurface> CreatePrivateSurface(
          PrivateEngine*  pengine,
          PixelFormat*    ppf,
          PrivatePalette* ppalette,
    const WinPoint&       size,
          int             pitch,
          BYTE*           pdata,
          IObject*        pobjectMemory
) {
    return new PrivateSurfaceImpl(pengine, ppf, ppalette, size, pitch, pdata, pobjectMemory);
}

//////////////////////////////////////////////////////////////////////////////
//
// Create a Private surface from an existing VideoSurface.  This is used to
//
// - wrap the primary surface
// - wrap offscreen video memory surfaces
//
//////////////////////////////////////////////////////////////////////////////

TRef<PrivateSurface> CreatePrivateSurface(
    PrivateEngine* pengine,
    VideoSurface*  pvideoSurface,
    SurfaceSite*   psite
) {
    return new PrivateSurfaceImpl(pengine, pvideoSurface, psite);
}