#include "pch.h"

//////////////////////////////////////////////////////////////////////////////
//
// Fonts
//
//////////////////////////////////////////////////////////////////////////////

class FontImpl : public IEngineFont {
private:
    //////////////////////////////////////////////////////////////////////////////
    //
    // Types
    //
    //////////////////////////////////////////////////////////////////////////////

    class CharData {
    public:
        WinPoint m_size;
        int      m_offset;
    };

    //////////////////////////////////////////////////////////////////////////////
    //
    // Data members
    //
    //////////////////////////////////////////////////////////////////////////////

    int               m_height;
    int               m_width;
    CharData          m_data[256];
    BYTE*             m_pdata;
    int               m_dataSize;

    //////////////////////////////////////////////////////////////////////////////
    //
    // Private members
    //
    //////////////////////////////////////////////////////////////////////////////

    void FillFontData(HFONT hfont)
    {
        int length = 0;
        int index;

        //
        // figure out the total data size
        //

        for (index = 0; index < 256; index++) {
            int xsize = m_data[index].m_size.X();
            int ysize = m_data[index].m_size.Y();

            m_data[index].m_offset = length;

            length += ((xsize + 7) / 8) * ysize;
        }

        //
        // Allocate the data
        //

        m_dataSize = (length + 3) & (~3);
        m_pdata    = new BYTE[m_dataSize];

        //
        // Create a dib section to get the characters with
        //

        class MyHeader {
        public:
            BITMAPINFOHEADER bmih;
            RGBQUAD          colors[256];
        } bmih;

        bmih.bmih.biSize          = sizeof(BITMAPINFOHEADER);
        bmih.bmih.biWidth         = m_width; 
        bmih.bmih.biHeight        = -m_height; 
        bmih.bmih.biPlanes        = 1; 
        bmih.bmih.biBitCount      = 8; 
        bmih.bmih.biCompression   = BI_RGB; 
        bmih.bmih.biSizeImage     = 0; 
        bmih.bmih.biXPelsPerMeter = 0; 
        bmih.bmih.biYPelsPerMeter = 0; 
        bmih.bmih.biClrUsed       = 0; 
        bmih.bmih.biClrImportant  = 0; 

        for (index = 0; index < 256; index++) {
            bmih.colors[index].rgbBlue     = index;
            bmih.colors[index].rgbGreen    = index;
            bmih.colors[index].rgbRed      = index;
            bmih.colors[index].rgbReserved = 0;
        }

        BYTE*   pbits;
        HBITMAP hbitmap = 
            ::CreateDIBSection(
                NULL,
                (BITMAPINFO*)&bmih,
                DIB_RGB_COLORS,
                (void**)&pbits,
                NULL,
                0
            );
        ZAssert(hbitmap != NULL);

        HDC hdcBitmap = ::CreateCompatibleDC(NULL);
        ZAssert(hdcBitmap != NULL);
        HBITMAP hbitmapOld = (HBITMAP)::SelectObject(hdcBitmap, hbitmap);
        ZAssert(hbitmapOld != NULL);
        HFONT hfontOld = (HFONT)::SelectObject(hdcBitmap, hfont);
        ZVerify(hfontOld != NULL);
        ZVerify(::SetBkMode(hdcBitmap, TRANSPARENT));
        ZVerify(::SetTextColor(hdcBitmap, Color::White().MakeCOLORREF()) != CLR_INVALID);
        ZVerify(::SetBkColor  (hdcBitmap, Color::Black().MakeCOLORREF()) != CLR_INVALID);

        //
        // Get the character bitmaps
        //

        BYTE* pdata = m_pdata;
        int scanBytes = ((m_width + 3) / 4) * 4;

        for (index = 0; index < 256; index++) {
            int xsize = m_data[index].m_size.X();
            int ysize = m_data[index].m_size.Y();
            unsigned char ch = index;

            ZVerify(::FillRect(
                hdcBitmap, 
                &(RECT)WinRect(0, 0, m_width, m_height), 
                (HBRUSH)::GetStockObject(BLACK_BRUSH)
            ));
            ZVerify(::TextOut(hdcBitmap, 0, 0, (PCC)&ch, 1));

            //
            // pull out the data 
            //

            int xbytes = (xsize + 7) / 8;
            for (int yindex = 0; yindex < ysize; yindex++) {
                for (int xbyte = 0; xbyte < xbytes; xbyte++) {
                    WORD word = 0;
                    for (int xbit = 0; xbit < 8; xbit++) {
                        if (pbits[yindex * scanBytes + xbyte * 8 + xbit] == 0xff) {
                            word |= 0x100;
                        }
                        word >>= 1;
                    }    
                    *pdata = (BYTE)word;
                    pdata++;
                }
            }
        }
        ZAssert(pdata == m_pdata + length);

        //
        // Release the DC we created
        //

        ZVerify(::SelectObject(hdcBitmap, hfontOld));
        ZVerify(::SelectObject(hdcBitmap, hbitmapOld));
        ZVerify(::DeleteDC(hdcBitmap));
        ZVerify(::DeleteObject(hbitmap));
    }

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

    void GetSizes(HFONT hfont)
	{
        HDC hdc = ::GetDC(NULL);
        ZVerify(hdc != NULL);

        HFONT hfontOld = (HFONT)::SelectObject(hdc, hfont);
        ZVerify(hfontOld != NULL);

        //
        // Get the character widths, offsets
        //

        m_height = 0;
        m_width  = 0;
        int index;

        for (index = 0; index < 256; index++) {
            SIZE size;
            unsigned char ch = index;

            ZVerify(::GetTextExtentPoint(
                hdc,
                (PCC)&ch,
                1,
                &size
            ));

            m_data[index].m_size   = WinPoint(size.cx, size.cy);

            m_height = max(m_height, size.cy);
            m_width  = max(m_width,  size.cx);
        }

        //
        // release the hdc
        //

        ZVerify(::SelectObject(hdc, hfontOld));
        ZVerify(::ReleaseDC(NULL, hdc));
    
        //
        // Make that all of the value GDI returned to us are good
        //

        for (index = 0; index < 256; index++) {
            ZAssert(m_data[index].m_size.X() >  0           );
            ZAssert(m_data[index].m_size.X() <  m_height * 4);
            ZAssert(m_data[index].m_size.Y() == m_height    );
        }
    }

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

public:
    FontImpl(HFONT hfont) :
        m_pdata(NULL)
    {
        GetSizes(hfont);
        FillFontData(hfont);
        ZVerify(::DeleteObject(hfont));
    }

    FontImpl(IBinaryReaderSite* psite)
    {
        m_height = psite->GetDWORD();
        m_width  = psite->GetDWORD();
        m_dataSize = psite->GetDWORD();

        memcpy(m_data, psite->GetPointer(), sizeof(m_data));
        psite->MovePointer(sizeof(m_data));

        m_pdata = new BYTE[m_dataSize]; 
        memcpy(m_pdata, psite->GetPointer(), m_dataSize);
        psite->MovePointer(m_dataSize);
    }

    ~FontImpl()
    {
        if (m_pdata) {
            delete m_pdata;
        }
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // members
    //
    //////////////////////////////////////////////////////////////////////////////

    void Write(IMDLBinaryFile* pmdlFile)
    {
        pmdlFile->WriteReference("ImportFont");
        TRef<ZFile> pfile = pmdlFile->WriteBinary();

        pfile->Write(m_height);
        pfile->Write(m_width);
        pfile->Write(m_dataSize);
        pfile->Write(m_data, sizeof(m_data));
        pfile->Write(m_pdata, m_dataSize);
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // IEngineFont members
    //
    //////////////////////////////////////////////////////////////////////////////

    int GetHeight()
    {
        return m_height;
    }

    WinPoint GetTextExtent(const ZString& str)
    {
        int count = str.GetLength();
        int width = 0;

        for (int index = 0; index < count; index++) {
            if (str[index] == START_COLOR_CODE)
                index += 10 - 1;
            else if (str[index] == END_COLOR_CODE)
                index += 2 - 1;
            else
                width += m_data[(BYTE)str[index]].m_size.X();
        }

        return WinPoint(width, m_height);
    }

    WinPoint GetTextExtent(const char* sz)
    {
        int count = strlen(sz);
        int width = 0;

        for (int index = 0; index < count; index++) {
            if (sz[index] == START_COLOR_CODE)
                index += 10 - 1;
            else if (sz[index] == END_COLOR_CODE)
                index += 2 - 1;
            else
                width += m_data[(BYTE)sz[index]].m_size.X();
        }

        return WinPoint(width, m_height);
    }

    int GetMaxTextLength(const ZString& str, int maxWidth, bool bLeft)
    {
        int count = str.GetLength();
        int width = 0;

        for (int index = 0; index < count; index++) {
            int ichar = bLeft ? index : count - (index + 1);
            // note that this method is not adjusted to account for colored text, since it
            // may work backwards across the string, resulting in a difficult case that I
            // don't think is causing problems right now, ergo no need to fix it.
            width += m_data[(BYTE)str[ichar]].m_size.X();
            if (width > maxWidth)
                return index;
        }

        return count;
    }

    void DrawClippedChar(
        const WinRect& rectClip, 
        byte ch, 
        int ytop, 
        int ysize, 
        int x, BYTE* 
        pdestTop, 
        DWORD pixel, // KGJV 32B
        int pitchDest,
		int pixelBytes // KGJV 32B - bbp
    ) {
        const CharData& charData  = m_data[ch];
        int             xsizeChar = charData.m_size.X();

        //
        // figure out the left and right clipping
        //

        int xleft;
        
        if (x < rectClip.XMin()) {
            xleft = rectClip.XMin() - x;
        } else {
            xleft = 0;
        }

        int xright;

        if (x + xsizeChar > rectClip.XMax()) {
            xright = rectClip.XMax() - x;
        } else {
            xright = xsizeChar;
        }

        //
        // pointers
        //

        int   bytesSource         = (xsizeChar + 7) / 8;
        BYTE* psourceStart        = 
              m_pdata 
            + charData.m_offset 
            + bytesSource * ytop;
        BYTE* pdestStart          = pdestTop + (x + xleft) * pixelBytes; // KGJV 32B 2 -> pixelBytes

        //
        // copy bits between xleft and xright
        //
        
        for (int scans = ysize; scans > 0; scans--) {
            BYTE* psource = psourceStart;
            BYTE* pdest   = pdestStart;
            int  ibit     = 0;
            BYTE byte     = *psource;
            psource++;

            //
            // skip clipped left bits
            //

            while (ibit < xleft) {
                byte = byte >> 1;
                ibit++;
                if (ibit & 0x7 == 0) {
                    byte = *psource;
                    psource++;
                }
            }

            //
            // render bits between xleft and xright
            //

            while (ibit < xright) {
                if (byte & 1) {
					if (pixelBytes==4) // KGJV 32B
						*(DWORD*)pdest = pixel;
					else // assumes 16bpp
						*(WORD*)pdest = (WORD)pixel; 
                }

                pdest += pixelBytes; // KGJV 32B
                byte   = byte >> 1;
                ibit  += 1;
                if (ibit & 0x7 == 0) {
                    byte = *psource;
                    psource++;
                }
            }

            psourceStart += bytesSource;
            pdestStart   += pitchDest;
        }
    }

    void DrawString(
              Surface*  psurface, 
        const WinPoint& point, 
        const WinRect&  rectClip,
        const ZString&  str, 
        const Color&    color
    ) {
        if (str.GetLength() == 0) {
            return;
        }

        //
        // are we clipped in the y direction?
        //

        int ytop    = 0;
        int ybottom = m_height;

        //
        // is the top clipped?
        //

        if (point.Y() < rectClip.YMin()) {
            ytop = rectClip.YMin() - point.Y();
        }

        //
        // is the bottom clipped?
        //

        if (point.Y() + m_height > rectClip.YMax()) {
            ybottom = rectClip.YMax() - point.Y();
        } 

        //
        // figure out the total number of scan lines to draw
        //

        int ysize = ybottom - ytop;

        if (ysize <= 0) {
            //
            // clipped in y, nothing to draw
            //

            return;
        }

        //
        // Get a pointer to the destination memory
        //

        BYTE* pdestTop = 
              psurface->GetWritablePointer() 
            + (point.Y() + ytop) * psurface->GetPitch();

        //
        // All access to the surface needs to be inside an exception handler
        //

        //KGJV 32B
        //debugf("drawstring: @%d,%d = %s\n",point.X(), point.Y(), (const char *)str);
        __try {
            int ichar = 0;
            int x = point.X ();
            DrawStringHandled(
                psurface, 
                x, 
                rectClip,
                str, 
                color,
                pdestTop,
                ytop,
                ybottom,
                ysize,
                ichar
            );
        } __except (true) {
            //
            // DDraw took away our access to some video memory.  Ignore the exception
            // and continue.
            //
        }

        psurface->ReleasePointer();
    }

    int  HexDigitToInt (int hexDigit)
    {
        if (isdigit (hexDigit))
            return hexDigit - '0';
        else if (isxdigit (hexDigit))
            return (tolower (hexDigit) - 'a') + 10;
        else
            return -1;
    }

    int ReadHexPair (const ZString& str, int& ichar)
    {
        int highNibble = HexDigitToInt (str[ichar++]);
        int lowNibble = HexDigitToInt (str[ichar++]);
        assert ((highNibble >= 0) && (lowNibble >= 0));
        return ((highNibble << 4) | lowNibble);
    }

    Color   ReadColor (const ZString& str, int& ichar)
    {
        float   red = ReadHexPair (str, ichar) / 255.0f;
        float   green = ReadHexPair (str, ichar) / 255.0f;
        float   blue = ReadHexPair (str, ichar) / 255.0f;
        float   alpha = ReadHexPair (str, ichar) / 255.0f;
        return Color (red, green, blue, alpha);
    }

    void DrawColorCodedString(
              Surface*  psurface, 
              int&      x, 
        const WinRect&  rectClip,
        const ZString&  str, 
              BYTE*     pdestTop,
              int       ytop,
              int       ybottom,
              int       ysize,
              int&      ichar
    ) 
    {
        // we started here because we encountered a color code,
        // so the first thing is to skip over the color code and
        // the required following character
        ichar += 2;

        // next we need to read the color, in the form rrggbbaa,
        // where each pair of digits is 
        Color   nextColor = ReadColor (str, ichar);
        DrawStringHandled (psurface, x, rectClip, str, nextColor, pdestTop, ytop, ybottom, ysize, ichar);
    }


    void DrawStringHandled(
              Surface*  psurface, 
              int&      x, 
        const WinRect&  rectClip,
        const ZString&  str, 
        const Color&    color,
              BYTE*     pdestTop,
              int       ytop,
              int       ybottom,
              int       ysize,
              int&      ichar
    ) {
        int length = str.GetLength();

        //
        // get all the surface info
        //

        PixelFormat* ppf        = psurface->GetPixelFormat();
        int          pixelBytes = ppf->PixelBytes();
        WinPoint     sizeDest   = psurface->GetSize();
        int          pitchDest  = psurface->GetPitch();
        WORD         pixel      = (WORD)(ppf->MakePixel(color).Value());

        //ZAssert(pixelBytes == 2); // KGJV 32B
        DWORD        pixel32    = ppf->MakePixel(color).Value();
        //
        // loop through characters that are clipped on the left
        //

        bool    loop = true;
        while (loop) 
        {
            BYTE    ch = str[ichar];
            switch (ch)
            {
                case START_COLOR_CODE:
                    DrawColorCodedString (psurface, x, rectClip, str, pdestTop, ytop, ybottom, ysize, ichar);
                    if (ichar >= length)
                        return;
                    break;
                case END_COLOR_CODE:
                    ichar += 2;
                    return;
                default:
                    {
                        const CharData& charData  = m_data[ch];
                        int             xsizeChar = charData.m_size.X();
                        if ((x + xsizeChar) > rectClip.XMin())
                        {
                            loop = false;
                            break;
                        }
                        if (++ichar >= length)
                            return;
                        x += xsizeChar;
                    }
                    break;
            }
        }

        //
        // there can be one character clipped on the left
        //

        if (x < rectClip.XMin()) 
        {
            DrawClippedChar(rectClip, str[ichar], ytop, ysize, x, pdestTop, pixel, pitchDest, pixelBytes);
            if (++ichar >= length)
                return;
            x     += m_data[(BYTE)str[ichar]].m_size.X();
        }

        //
        // loop through unclipped characters
        //
        loop = true;
        while (loop) 
        {
            BYTE            ch        = str[ichar];
            switch (ch)
            {
                case START_COLOR_CODE:
                    DrawColorCodedString (psurface, x, rectClip, str, pdestTop, ytop, ybottom, ysize, ichar);
                    if (ichar >= length)
                        return;
                    break;
                case END_COLOR_CODE:
                    ichar += 2;
                    return;
                default:
                    {
                        const CharData& charData  = m_data[ch];
                        int             xsizeChar = charData.m_size.X();
                        if ((x + xsizeChar) > rectClip.XMax())
                        {
                            loop = false;
                            break;
                        }

                        int   scans               = ysize;
                        int   bytesSource         = (xsizeChar + 7) / 8;
                        BYTE* psource             = m_pdata + charData.m_offset + (bytesSource * ytop);
                        BYTE* pdest               = pdestTop + x * pixelBytes; // KGJV 32B (was 2)

                        //
                        // transparent blt 1bpp -> 16bpp
                        //

                        // KGJV 32B - 16 bpp mode
                        if (pixelBytes == 2)
                        {
                        _asm {
                            mov     esi, psource
                            mov     ax,  pixel
                            mov     ecx, pdest

                          yloop:
                            mov     edi, ecx
                            mov     ebx, [bytesSource]

                          byteLoop:
                            mov     dl, [esi]
                            inc     esi
                            shr     dl, 1
                            jnc     skipbit1
                            mov     [edi + 0], ax
                          skipbit1:
                            shr     dl, 1
                            jnc     skipbit2
                            mov     [edi + 2], ax
                          skipbit2:
                            shr     dl, 1
                            jnc     skipbit3
                            mov     [edi + 4], ax
                          skipbit3:
                            shr     dl, 1
                            jnc     skipbit4
                            mov     [edi + 6], ax
                          skipbit4:
                            shr     dl, 1
                            jnc     skipbit5
                            mov     [edi + 8], ax
                          skipbit5:
                            shr     dl, 1
                            jnc     skipbit6
                            mov     [edi + 10], ax
                          skipbit6:
                            shr     dl, 1
                            jnc     skipbit7     
                            mov     [edi + 12], ax
                          skipbit7:
                            shr     dl, 1
                            jnc     skipbit8
                            mov     [edi + 14], ax
                          skipbit8:
                            add     edi, 16
                            dec     ebx
                            jne     byteLoop

                            add     ecx, [pitchDest]
                            dec     [scans]
                            jne     yloop
                        }
                        }

                        else  // KGJV 32B - (assume) 32 bpp mode
                        {
                        _asm {
                            mov     esi, psource
                            mov     eax, pixel32
                            mov     ecx, pdest

                          b32_yloop:
                            mov     edi, ecx
                            mov     ebx, [bytesSource]

                          b32_byteLoop:
                            mov     dl, [esi]
                            inc     esi
                            shr     dl, 1
                            jnc     b32_skipbit1
                            mov     [edi + 0], eax
                          b32_skipbit1:
                            shr     dl, 1
                            jnc     b32_skipbit2
                            mov     [edi + 4], eax
                          b32_skipbit2:
                            shr     dl, 1
                            jnc     b32_skipbit3
                            mov     [edi + 8], eax
                          b32_skipbit3:
                            shr     dl, 1
                            jnc     b32_skipbit4
                            mov     [edi + 12], eax
                          b32_skipbit4:
                            shr     dl, 1
                            jnc     b32_skipbit5
                            mov     [edi + 16], eax
                          b32_skipbit5:
                            shr     dl, 1
                            jnc     b32_skipbit6
                            mov     [edi + 20], eax
                          b32_skipbit6:
                            shr     dl, 1
                            jnc     b32_skipbit7     
                            mov     [edi + 24], eax
                          b32_skipbit7:
                            shr     dl, 1
                            jnc     b32_skipbit8
                            mov     [edi + 28], eax

                          b32_skipbit8:
                            add     edi, 32
                            dec     ebx
                            jne     b32_byteLoop

                            add     ecx, [pitchDest]
                            dec     [scans]
                            jne     b32_yloop
                        }
                        }
                        if (++ichar >= length)
                            return;
                        x += xsizeChar;
                    }
                    break;
            }

        }

        //
        // one character can be clipped on the right
        //

        if (x < rectClip.XMax())
		{
           DrawClippedChar(rectClip, str[ichar], ytop, ysize, x, pdestTop, pixel32, pitchDest, pixelBytes);
		}
    }
};

TRef<IEngineFont> CreateEngineFont(HFONT hfont)
{
    return new FontImpl(hfont);
}

TRef<IEngineFont> CreateEngineFont(IBinaryReaderSite* psite)
{
    return new FontImpl(psite);
}

//////////////////////////////////////////////////////////////////////////////
//
// Color code utility function
//
//////////////////////////////////////////////////////////////////////////////

ZString ConvertColorToString (const Color& color)
{
    char    buffer[9] = {0};
    sprintf (buffer, "%02x%02x%02x%02x",
        int (color.R () * 255.0f),
        int (color.G () * 255.0f),
        int (color.B () * 255.0f),
        int (color.A () * 255.0f)
    );
    return ZString (buffer);
}

//////////////////////////////////////////////////////////////////////////////
//
// Fonts
//
//////////////////////////////////////////////////////////////////////////////

ZString GetString(int indent, TRef<IEngineFont> pfont)
{
    ZUnimplemented();

    return "font";
}

void Write(IMDLBinaryFile* pmdlFile, TRef<IEngineFont> pfont)
{
    FontImpl* pimpl; CastTo(pimpl, pfont);

    pimpl->Write(pmdlFile);
}

ZString GetFunctionName(const TRef<IEngineFont>& value)
{
    return "ImportFont";
}

//////////////////////////////////////////////////////////////////////////////
//
// Alternate blting implementations
//
//////////////////////////////////////////////////////////////////////////////

            /* version 1
            _asm {
                mov     edi, pdest
                mov     esi, psource
                mov     ax,  pixel

              yloop:
                mov     ebx, bytesSource
                and     ebx, ebx
                je      extraLoop

              byteLoop:
                mov     dl, [esi]
                inc     esi
                mov     ecx, 8

              bitloop1:
                shr     dl, 1
                jnc     bitloop1Skip
                mov     [edi], ax
              bitloop1Skip:
                add     edi, 2
                dec     ecx
                jne     bitloop1

                dec     ebx
                jne     byteLoop

              extraLoop:
                mov     ecx, extraSource
                and     ecx, ecx
                je      end

                mov     dl, [esi]
                inc     esi

              bitloop2:
                shr     dl, 1
                jnc     bitloop2Skip
                mov     [edi], ax
              bitloop2Skip:
                add     edi, 2
                dec     ecx
                jne     bitloop2

              end:
                add     edi, [scanDeltaDest]
                dec     [ysizeSource]
                jne     yloop
            }
            */

            /* this doesn't work yet.  Try to use a jump table dependant on the number of bits
            static void* bitJumps[8] = {
                bits8,
                bits1,
                bits2,
                bits3,
                bits4,
                bits5,
                bits6,
                bits7
            };

            _asm {
                mov     esi, psource
                mov     ax,  pixel
                mov     ecx, pdest

              ayloop:
                mov     edi, ecx

                //
                // eax  pixel
                // ebx  temporary
                // ecx  pdestScanLine
                // dl   bits
                // edi  destination
                // esi  source
                // ebp
                //

              byteLoop:
                mov     dl, [esi]
                inc     esi

                mov     ebx, [xsize]
                cmp     ebx, 8
                jg      bits8
                and     ebx, 0x7 
                jmp     [bitJumps + ecx]

              bits8:
                shl     dl, 1
                jnc     bits7
                mov     [edi + 14], ax
              bits7:
                shl     dl, 1
                jnc     bits6
                mov     [edi + 12], ax
              bits6:
                shl     dl, 1
                jnc     bits5
                mov     [edi + 10], ax
              bits5:
                shl     dl, 1
                jnc     bits4
                mov     [edi +  8], ax
              bits4:
                shl     dl, 1
                jnc     bits3
                mov     [edi +  6], ax
              bits3:
                shl     dl, 1
                jnc     bits2    
                mov     [edi +  4], ax
              bits2:
                shl     dl, 1
                jnc     bits1
                mov     [edi +  2], ax
              bits1:
                shl     dl, 1
                jnc     bits1
                mov     [edi +  0], ax

                add     edi, 16
                dec     [xsize]
                jne     byteLoop

                add     ecx, [pitchDest]
                dec     [ysizeSource]
                jne     ayloop
            }
            */

            /*
            do {
                //
                // Copy the byte sized chunks
                //

                {
                    
                    for (int iBytes = bytesSource; iBytes > 0; iBytes--) {
                        BYTE byte = *psource;
                        psource++;

                        for (int index = 8; index > 0; index--) {
                            if (byte & 1) {
                                *(WORD*)pdest = pixel;
                            }

                            byte >>= 1;
                            pdest += 2;
                        }
                    }
                }

                //
                // Copy the last few bits
                //

                if (extraSource != 0) {
                    BYTE byte = *psource;
                    psource++;
                    for (int index = extraSource; index > 0; index--) {
                        if (byte & 1) {
                            *(WORD*)pdest = pixel;
                        }

                        byte >>= 1;
                        pdest += 2;
                    }
                }

                //
                // Next scan line
                //

                pdest       += scanDeltaDest;
                psource     += scanDeltaSource;
                ysizeSource -= 1;
            } while (ysizeSource > 0);
            */