/******************************Module*Header*******************************\
* Module Name: bitmap.c                                                    *
*                                                                          *
* Client side stubs that move bitmaps over the C/S interface.              *
*                                                                          *
* Created: 14-May-1991 11:04:49                                            *
* Author: Eric Kutter [erick]                                              *
*                                                                          *
* Copyright (c) 1991 Microsoft Corporation                                 *
\**************************************************************************/
#include "precomp.h"
#pragma hdrstop

ULONG cjBitmapBitsSize(CONST BITMAPINFO *pbmi)
{
// Check for PM-style DIB

    if (pbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
    {
        LPBITMAPCOREINFO pbmci;
        pbmci = (LPBITMAPCOREINFO)pbmi;
        return(CJSCAN(pbmci->bmciHeader.bcWidth,pbmci->bmciHeader.bcPlanes,
                      pbmci->bmciHeader.bcBitCount) *
                      pbmci->bmciHeader.bcHeight);
    }

// not a core header

    if ((pbmi->bmiHeader.biCompression == BI_RGB) ||
        (pbmi->bmiHeader.biCompression == BI_BITFIELDS))
    {
        return(CJSCAN(pbmi->bmiHeader.biWidth,pbmi->bmiHeader.biPlanes,
                      pbmi->bmiHeader.biBitCount) *
               ABS(pbmi->bmiHeader.biHeight));
    }
    else
    {
        return(pbmi->bmiHeader.biSizeImage);
    }
}


/**********************************************************************\
* pbmiConvertInfo
*
* Does two things:
*
* 1. takes BITMAPINFO, Converts BITMAPCOREHEADER
*    into BITMAPINFOHEADER and copies the the color table
*
* 2. also return the size of the size of INFO struct if bPackedDIB is FALSE
*    otherwise pass back the size of INFO plus cjBits
*
* 10-1-95 -by- Lingyun Wang [lingyunw]
\**********************************************************************/
LPBITMAPINFO pbmiConvertInfo(CONST BITMAPINFO *pbmi, ULONG iUsage, ULONG *count, BOOL bPackedDIB)
{
    LPBITMAPINFO pbmiNew;
    ULONG cjRGB;
    ULONG cColorsMax;
    ULONG cColors;
    UINT  uiBitCount;
    UINT  uiPalUsed;
    UINT  uiCompression;
    BOOL  bCoreHeader = FALSE;
    ULONG ulSize;
    ULONG cjBits = 0;
    PVOID pjBits, pjBitsNew;

    if (pbmi == (LPBITMAPINFO) NULL)
    {
        return(0);
    }

    //
    // Checking for different bitmap headers
    //
    ulSize = pbmi->bmiHeader.biSize;

    if (ulSize == sizeof(BITMAPCOREHEADER))
    {
        cjRGB = sizeof(RGBQUAD);
        uiBitCount = ((LPBITMAPCOREINFO)pbmi)->bmciHeader.bcBitCount;
        uiPalUsed = 0;
        uiCompression =  (UINT) BI_RGB;
        bCoreHeader = TRUE;
    }
    else if (ulSize >= sizeof(BITMAPINFOHEADER))
    {
        cjRGB    = sizeof(RGBQUAD);
        uiBitCount = pbmi->bmiHeader.biBitCount;
        uiPalUsed = pbmi->bmiHeader.biClrUsed;
        uiCompression = (UINT) pbmi->bmiHeader.biCompression;
    }
    else
    {
        WARNING("ConvertInfo failed - invalid header size\n");
        return(0);
    }

    //
    // figure out the size of the color table
    //
    if (uiCompression == BI_BITFIELDS)
    {
    // Handle 16 and 32 bit per pel bitmaps.

        if (iUsage == DIB_PAL_COLORS)
        {
            iUsage = DIB_RGB_COLORS;
        }

        switch (uiBitCount)
        {
        case 16:
        case 32:
            break;
        default:
            WARNING("ConvertInfo failed for BI_BITFIELDS\n");
            return(0);
        }

        uiPalUsed = cColorsMax = 3;
    }
    else if (uiCompression == BI_RGB)
    {
        switch (uiBitCount)
        {
        case 1:
            cColorsMax = 2;
            break;
        case 4:
            cColorsMax = 16;
            break;
        case 8:
            cColorsMax = 256;
            break;
        default:

            if (iUsage == DIB_PAL_COLORS)
            {
                iUsage = DIB_RGB_COLORS;
            }

            cColorsMax = 0;

            switch (uiBitCount)
            {
            case 16:
            case 24:
            case 32:
                break;
            default:
                WARNING("convertinfo failed invalid bitcount in bmi BI_RGB\n");
                return(0);
            }
        }
    }
    else if (uiCompression == BI_RLE4)
    {
        if (uiBitCount != 4)
        {
            // WARNING("cjBitmapSize invalid bitcount BI_RLE4\n");
            return(0);
        }

        cColorsMax = 16;
    }
    else if (uiCompression == BI_RLE8)
    {
        if (uiBitCount != 8)
        {
            // WARNING("cjBitmapSize invalid bitcount BI_RLE8\n");
            return(0);
        }

        cColorsMax = 256;
    }
    else
    {
        WARNING("convertinfo failed invalid Compression in header\n");
        return(0);
    }

    if (uiPalUsed != 0)
    {
        if (uiPalUsed <= cColorsMax)
            cColors = uiPalUsed;
        else
            cColors = cColorsMax;
    }
    else
        cColors = cColorsMax;

    if (iUsage == DIB_PAL_COLORS)
        cjRGB = sizeof(USHORT);
    else if (iUsage == DIB_PAL_INDICES)
        cjRGB = 0;

    if (bPackedDIB)
        cjBits = cjBitmapBitsSize(pbmi);

    //
    // if passed non COREHEADER, donot need to convert
    //
    if (!bCoreHeader)
    {
        pbmiNew = (LPBITMAPINFO)pbmi;
    }
    else
    {
        RGBTRIPLE *pTri;
        RGBQUAD *pQuad;

        //
        // allocate new header to hold the info
        //
        ulSize = sizeof(BITMAPINFOHEADER);

        pbmiNew = (PBITMAPINFO)LocalAlloc(LMEM_FIXED,ulSize +
                             cjRGB * cColors+cjBits);

        if (pbmiNew == NULL)
            return (0);

        pbmiNew->bmiHeader.biSize = ulSize;

        //
        // copy COREHEADER info over
        //

        pbmiNew->bmiHeader.biWidth = ((BITMAPCOREHEADER *)pbmi)->bcWidth;
        pbmiNew->bmiHeader.biHeight = ((BITMAPCOREHEADER *)pbmi)->bcHeight;
        pbmiNew->bmiHeader.biPlanes = ((BITMAPCOREHEADER *)pbmi)->bcPlanes;
        pbmiNew->bmiHeader.biBitCount = ((BITMAPCOREHEADER *)pbmi)->bcBitCount;
        pbmiNew->bmiHeader.biCompression = 0;
        pbmiNew->bmiHeader.biSizeImage = 0;
        pbmiNew->bmiHeader.biXPelsPerMeter = 0;
        pbmiNew->bmiHeader.biYPelsPerMeter = 0;
        pbmiNew->bmiHeader.biClrUsed = 0;
        pbmiNew->bmiHeader.biClrImportant = 0;

        //
        // copy the color table
        //

        pTri = (RGBTRIPLE *)((LPBYTE)pbmi + sizeof(BITMAPCOREHEADER));
        pQuad = (RGBQUAD *)((LPBYTE)pbmiNew + sizeof(BITMAPINFOHEADER));

        //
        // copy RGBTRIPLE to RGBQUAD
        //
        if (iUsage != DIB_PAL_COLORS)
        {
            INT cj = cColors;

            while (cj--)
            {
                pQuad->rgbRed = pTri->rgbtRed;
                pQuad->rgbGreen = pTri->rgbtGreen;
                pQuad->rgbBlue = pTri->rgbtBlue;
                pQuad->rgbReserved = 0;

                pQuad++;
                pTri++;
            }

            if (bPackedDIB)
                pjBits = (LPBYTE)pbmi + sizeof(BITMAPCOREHEADER) + cColors*sizeof(RGBTRIPLE);
        }
        else
        // DIB_PAL_COLORS
        {
            RtlCopyMemory((LPBYTE)pQuad,(LPBYTE)pTri,cColors * cjRGB);

            if (bPackedDIB)
                pjBits = (LPBYTE)pbmi + sizeof(BITMAPCOREHEADER) + cColors * cjRGB;
        }

        //
        // copy the packed bits
        //
        if (bPackedDIB)
        {
            pjBitsNew = (LPBYTE)pbmiNew + ulSize + cColors*cjRGB;

            RtlCopyMemory((LPBYTE)pjBitsNew,
                          (LPBYTE)pjBits,
                          cjBits);
        }
     }

    *count = ((ulSize + (cjRGB * cColors) + cjBits) + 3) & ~3;

    return((LPBITMAPINFO) pbmiNew);
}

ULONG cjBitmapScanSize(
    CONST BITMAPINFO *pbmi,
    int nScans
    )
{
// Check for PM-style DIB

    if (pbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
    {
        LPBITMAPCOREINFO pbmci;
        pbmci = (LPBITMAPCOREINFO)pbmi;

        return(CJSCAN(pbmci->bmciHeader.bcWidth,pbmci->bmciHeader.bcPlanes,
                      pbmci->bmciHeader.bcBitCount) * nScans);
    }

// not a core header

    if ((pbmi->bmiHeader.biCompression == BI_RGB) ||
        (pbmi->bmiHeader.biCompression == BI_BITFIELDS))
    {
        return(CJSCAN(pbmi->bmiHeader.biWidth,pbmi->bmiHeader.biPlanes,
                      pbmi->bmiHeader.biBitCount) * nScans);
    }
    //
    // rle: use image size, nScans is not used
    //
    //else if (nScans >= pbmi->bmiHeader.biHeight)
    //{
    else
    {
        return(pbmi->bmiHeader.biSizeImage);
    }
}

/******************************Public*Routine******************************\
* CopyCoreToInfoHeader
*
\**************************************************************************/

VOID CopyCoreToInfoHeader(LPBITMAPINFOHEADER pbmih, LPBITMAPCOREHEADER pbmch)
{
    pbmih->biSize = sizeof(BITMAPINFOHEADER);
    pbmih->biWidth = pbmch->bcWidth;
    pbmih->biHeight = pbmch->bcHeight;
    pbmih->biPlanes = pbmch->bcPlanes;
    pbmih->biBitCount = pbmch->bcBitCount;
    pbmih->biCompression = BI_RGB;
    pbmih->biSizeImage = 0;
    pbmih->biXPelsPerMeter = 0;
    pbmih->biYPelsPerMeter = 0;
    pbmih->biClrUsed = 0;
    pbmih->biClrImportant = 0;
}


/******************************Public*Routine******************************\
* DWORD SetDIBitsToDevice                                                  *
*                                                                          *
*   Can reduce it to 1 scan at a time.  If compressed mode, this could     *
*   gete very difficult.  There must be enough space for the header and    *
*   color table.  This will be needed for every batch.                     *
*                                                                          *
*   BITMAPINFO                                                             *
*       BITMAPINFOHEADER                                                   *
*       RGBQUAD[cEntries] | RGBTRIPLE[cEntries]                            *
*                                                                          *
*                                                                          *
*    1. compute header size (including color table)                        *
*    2. compute size of required bits                                      *
*    3. compute total size (header + bits + args)                          *
*    4. if (memory window is large enough for header + at least 1 scan     *
*                                                                          *
* History:                                                                 *
*  Tue 29-Oct-1991 -by- Patrick Haluptzok [patrickh]                       *
* Add shared memory action for large RLE's.                                *
*                                                                          *
*  Tue 19-Oct-1991 -by- Patrick Haluptzok [patrickh]                       *
* Add support for RLE's                                                    *
*                                                                          *
*  Thu 20-Jun-1991 01:41:45 -by- Charles Whitmer [chuckwh]                 *
* Added handle translation and metafiling.                                 *
*                                                                          *
*  14-May-1991 -by- Eric Kutter [erick]                                    *
* Wrote it.                                                                *
\**************************************************************************/

int SetDIBitsToDevice(
HDC          hdc,
int          xDest,
int          yDest,
DWORD        nWidth,
DWORD        nHeight,
int          xSrc,
int          ySrc,
UINT         nStartScan,
UINT         nNumScans,
CONST VOID * pBits,
CONST BITMAPINFO *pbmi,
UINT         iUsage)            // DIB_PAL_COLORS || DIB_RGB_COLORS
{
    LONG cScansCopied = 0;  // total # of scans copied
    LONG ySrcMax;           // maximum ySrc possible

// hold info about the header

    UINT uiWidth;
    UINT uiHeight;
    PULONG pulBits = NULL;
    INT cjHeader = 0;
    LPBITMAPINFO pbmiNew = NULL;
    ULONG cjBits;

    FIXUP_HANDLE(hdc);

// Let's validate the parameters so we don't gp-fault ourselves and
// to save checks later on.

    if ((nNumScans == 0)                   ||
        (pbmi      == (LPBITMAPINFO) NULL) ||
        (pBits     == (LPVOID) NULL)       ||
        ((iUsage   != DIB_RGB_COLORS) &&
         (iUsage   != DIB_PAL_COLORS) &&
         (iUsage   != DIB_PAL_INDICES)))
    {
        WARNING("You failed a param validation in SetDIBitsToDevice\n");
        return(0);
    }

    pbmiNew = pbmiConvertInfo(pbmi, iUsage, &cjHeader, FALSE);

    if (pbmiNew == NULL)
        return (0);

    uiWidth       = (UINT) pbmiNew->bmiHeader.biWidth;
    uiHeight      = (UINT) pbmiNew->bmiHeader.biHeight;

// Compute the minimum nNumScans to send across csr interface.
// It will also prevent faults as a result of overreading the source.

    ySrcMax = max(ySrc, ySrc + (int) nHeight);
    if (ySrcMax <= 0)
        return(0);
    ySrcMax = min(ySrcMax, (int) uiHeight);
    nNumScans = min(nNumScans, (UINT) ySrcMax - nStartScan);

// NEWFRAME support for backward compatibility.
// Ship the transform to the server side if needed.

    if (IS_ALTDC_TYPE(hdc))
    {
        PLDC pldc;

        if (IS_METADC16_TYPE(hdc))
        {
            cScansCopied = MF_AnyDIBits(
                   hdc,
                   xDest,yDest,0,0,
                   xSrc,ySrc,(int) nWidth,(int) nHeight,
                   nStartScan,nNumScans,
                   pBits,pbmi,
                   iUsage,
                   SRCCOPY,
                   META_SETDIBTODEV
                   );

            goto Exit;

        }

        DC_PLDC(hdc,pldc,0);

        if (pldc->iType == LO_METADC)
        {
            if (!MF_AnyDIBits(
                    hdc,
                    xDest,yDest,0,0,
                    xSrc,ySrc,(int) nWidth,(int) nHeight,
                    nStartScan,nNumScans,
                    pBits,pbmi,
                    iUsage,
                    SRCCOPY,
                    EMR_SETDIBITSTODEVICE
                    ))
            {
                cScansCopied = 0;
                goto Exit;
            }
        }

        if (pldc->fl & LDC_CALL_STARTPAGE)
            StartPage(hdc);

        if (pldc->fl & LDC_SAP_CALLBACK)
            vSAPCallback(pldc);

        //BUGBUG - for printing, we may want to band like in the old days
        //         so we can call vSAPCallback every few bands.

        if (pldc->fl & LDC_DOC_CANCELLED)
        {
            cScansCopied = 0;
            goto Exit;
        }
    }

// reset user's poll count so it counts this as output
// put it right next to BEGINMSG so that NtCurrentTeb() is optimized

    RESETUSERPOLLCOUNT();

    //
    // Calculate bitmap bits size based on BITMAPINFO and nNumScans
    //

    cjBits = cjBitmapScanSize(pbmi,nNumScans);

    // if the pBits are not dword aligned we need to allocate a buffer and copy them

    if ((ULONG)pBits & (sizeof(DWORD) - 1))
    {

        pulBits = LocalAlloc(LMEM_FIXED,cjBits);
        if (pulBits)
        {
            RtlCopyMemory(pulBits,pBits,cjBits);
            pBits = pulBits;
        }
    }

    cScansCopied = NtGdiSetDIBitsToDeviceInternal(
                        hdc,
                        xDest,
                        yDest,
                        nWidth,
                        nHeight,
                        xSrc,
                        ySrc,
                        nStartScan,
                        nNumScans,
                        (LPBYTE)pBits,
                        pbmiNew,
                        iUsage,
                        (UINT)cjBits,
                        (UINT)cjHeader,
                        TRUE);

    if (pulBits)
        LocalFree(pulBits);

Exit:
    if (pbmiNew && (pbmiNew != pbmi))
    {
        LocalFree (pbmiNew);
    }
    return (cScansCopied);
}

/******************************Public*Routine******************************\
* DWORD GetDIBits
*
*   Can reduce it to 1 scan at a time.  There must be enough space
*   for the header and color table.  This will be needed for every chunk
*
* History:
*  Wed 04-Dec-1991 -by- Patrick Haluptzok [patrickh]
* bug fix, only check for valid DC if DIB_PAL_COLORS.
*
*  Fri 22-Nov-1991 -by- Patrick Haluptzok [patrickh]
* bug fix, copy the header into memory window for NULL bits.
*
*  Tue 20-Aug-1991 -by- Patrick Haluptzok [patrickh]
* bug fix, make iStart and cNum be in valid range.
*
*  Thu 20-Jun-1991 01:44:41 -by- Charles Whitmer [chuckwh]
* Added handle translation.
*
*  14-May-1991 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

int GetDIBits(
HDC          hdc,
HBITMAP      hbm,
UINT         nStartScan,
UINT         nNumScans,
LPVOID       pBits,
LPBITMAPINFO pbmi,
UINT         iUsage)     // DIB_PAL_COLORS || DIB_RGB_COLORS
{
    PULONG pulBits = pBits;
    ULONG  cjBits;
    int    iRet;

    FIXUP_HANDLE(hdc);
    FIXUP_HANDLE(hbm);

    cjBits  = cjBitmapScanSize(pbmi,nNumScans);

    // if the pBits are not dword aligned we need to allocate a buffer and copy them

    if ((ULONG)pBits & (sizeof(DWORD) - 1))
    {
        pulBits = LocalAlloc(LMEM_FIXED,cjBits);

        if (pulBits == NULL)
            return(0);
    }

    iRet = NtGdiGetDIBitsInternal(
            hdc,
            hbm,
            nStartScan,
            nNumScans,
            (LPVOID)pulBits,
            pbmi,
            iUsage,
            cjBits,
            0);

    if (pulBits != pBits)
    {
        RtlCopyMemory(pBits,pulBits,cjBits);

        LocalFree(pulBits);
    }

    return(iRet);
}

/******************************Public*Routine******************************\
* CreateDIBitmap
*
* History:
*  Mon 25-Jan-1993 -by- Patrick Haluptzok [patrickh]
* Add CBM_CREATEDIB support.
*
*  Thu 20-Jun-1991 02:14:59 -by- Charles Whitmer [chuckwh]
* Added local handle support.
*
*  23-May-1991 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

HBITMAP CreateDIBitmap(
HDC                hdc,
CONST BITMAPINFOHEADER *pbmih,
DWORD              flInit,
CONST VOID        *pjBits,
CONST BITMAPINFO  *pbmi,
UINT               iUsage)
{

    LONG  cjBMI = 0;
    LONG  cjBits = 0;
    INT   cx = 0;
    INT   cy = 0;
    PULONG pulBits = NULL;
    HBITMAP hRet = (HBITMAP)-1;
    LPBITMAPINFO pbmiNew = NULL;

    FIXUP_HANDLEZ(hdc);

    if (iUsage & DIB_LOCALFLAGS)
    {
        hRet = 0;
    }
    else
    {
        //
        // covert pbmi
        //
        pbmiNew = pbmiConvertInfo(pbmi,iUsage,&cjBMI,FALSE);

        if (flInit & CBM_CREATEDIB)
        {
         // With CBM_CREATEDIB we ignore pbmih

            pbmih = (LPBITMAPINFOHEADER) pbmi;

            if (cjBMI == 0)
            {
                 hRet = 0;
            }
            else if (flInit & CBM_INIT)
            {
                if (pjBits == NULL)
                {
                    // doesn't make sence if they asked to initialize it but
                    // didn't pass the bits.

                    hRet = 0;
                }
                else
                {
                    cjBits = cjBitmapBitsSize(pbmiNew);
                }
            }
            else
            {
                pjBits = NULL;
            }
        }
        else
        {
            // compute the size of the optional init bits and BITMAPINFO

            if (flInit & CBM_INIT)
            {
                if (pjBits == NULL)
                {
                    // doesn't make sence if they asked to initialize it but
                    // didn't pass the bits.

                    flInit &= ~CBM_INIT;
                }
                else
                {
                    if (cjBMI == 0)
                    {
                        hRet = 0;
                    }
                    else
                    {
                        // compute the size of the bits

                        cjBits = cjBitmapBitsSize(pbmiNew);
                    }
                }
            }
        }

        //  if they passed us a zero height  or  zero  width
        //  bitmap then return a pointer to the stock bitmap

         if (pbmih)
        {
            if (pbmih->biSize >= sizeof(BITMAPINFOHEADER))
            {
               cx = pbmih->biWidth;
               cy = pbmih->biHeight;
            }
            else
            {
               cx = ((LPBITMAPCOREHEADER) pbmih)->bcWidth;
               cy = ((LPBITMAPCOREHEADER) pbmih)->bcHeight;
            }

            if ((cx == 0) || (cy == 0))
            {
               hRet = GetStockObject(PRIV_STOCK_BITMAP);
            }
        }

        // if hRet is still -1, then all is OK and we need to try to the bitmap

        if (hRet == (HBITMAP)-1)
        {
            // if the pJBits are not dword aligned we need to allocate a buffer and copy them

            if ((ULONG)pjBits & (sizeof(DWORD) - 1))
            {
                pulBits = LocalAlloc(LMEM_FIXED,cjBits);
                if (pulBits)
                {
                    RtlCopyMemory(pulBits,pjBits,cjBits);
                    pjBits = pulBits;
                }
            }

            hRet = NtGdiCreateDIBitmapInternal(hdc,
                                               cx,
                                               cy,
                                               flInit,
                                               (LPBYTE) pjBits,
                                               (LPBITMAPINFO) pbmiNew,
                                               iUsage,
                                               cjBMI,
                                               cjBits,
                                               0);

            if (pulBits)
                LocalFree(pulBits);
        }

        if (pbmiNew && (pbmiNew != pbmi))
            LocalFree(pbmiNew);
    }
    return(hRet);
}


/******************************Public*Routine******************************\
* Set/GetBitmapBits                                                        *
*                                                                          *
* History:                                                                 *
*  05-Jun-1991 -by- Eric Kutter [erick]                                    *
* Wrote it.                                                                *
\**************************************************************************/

LONG WINAPI SetBitmapBits(
HBITMAP      hbm,
DWORD        c,
CONST VOID *pv)
{
    LONG   lRet;

    FIXUP_HANDLE(hbm);

    lRet = (LONG)NtGdiSetBitmapBits(hbm,c,(PBYTE)pv);

    return(lRet);
}

LONG WINAPI GetBitmapBits(
HBITMAP hbm,
LONG    c,
LPVOID  pv)
{
    LONG   lRet;

    FIXUP_HANDLE(hbm);

    lRet = (LONG)NtGdiGetBitmapBits(hbm,c,(PBYTE)pv);

    return(lRet);
}

/******************************Public*Routine******************************\
* GdiGetPaletteFromDC
*
* Returns the palette for the DC, 0 for error.
*
* History:
*  04-Oct-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

HANDLE GdiGetPaletteFromDC(HDC h)
{
    return((HANDLE)GetDCObject(h,LO_PALETTE_TYPE));
}

/******************************Public*Routine******************************\
* GdiGetDCforBitmap
*
* Returns the DC a bitmap is selected into, 0 if none or if error occurs.
*
* History:
*  22-Sep-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

HDC GdiGetDCforBitmap(HBITMAP hbm)
{
    FIXUP_HANDLE(hbm);

    return (NtGdiGetDCforBitmap(hbm));
}

/******************************Public*Routine******************************\
* SetDIBits
*
* API to initialize bitmap with DIB
*
* History:
*  Sun 22-Sep-1991 -by- Patrick Haluptzok [patrickh]
* Make it work even if it is selected into a DC, Win3.0 compatibility.
*
*  06-Jun-1991 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/

int WINAPI SetDIBits(
HDC          hdc,
HBITMAP      hbm,
UINT         iStartScans,
UINT         cNumScans,
CONST VOID  *pInitBits,
CONST BITMAPINFO *pInitInfo,
UINT         iUsage)
{
    HDC hdcTemp;
    HBITMAP hbmTemp;
    int iReturn = 0;
    BOOL bMakeDC = FALSE;
    HPALETTE hpalTemp;
    DWORD cWidth;
    DWORD cHeight;

    FIXUP_HANDLE(hdc);
    FIXUP_HANDLE(hbm);

    // if no bits or hbm is not a bitmap, fail

    if ((pInitBits == (PVOID) NULL) ||
        (GRE_TYPE(hbm) != SURF_TYPE))
    {
        return(0);
    }

    // First we need a DC to select this bitmap into.  If he is already in a
    // DC we just use that DC temporarily to blt to (we still have to select
    // it in and out because someone might do a SaveDC and select another
    // bitmap in).  If he hasn't been stuck in a DC anywhere we just create
    // one temporarily.

    hdcTemp = GdiGetDCforBitmap(hbm);

    if (hdcTemp == (HDC) 0)
    {
        hdcTemp = CreateCompatibleDC(hdc);
        bMakeDC = TRUE;

        if (hdcTemp == (HDC) NULL)
        {
            WARNING("SetDIBits failed CreateCompatibleDC, is hdc valid?\n");
            return(0);
        }
    }
    else
    {
        if (SaveDC(hdcTemp) == 0)
            return(0);
    }

    hbmTemp = SelectObject(hdcTemp, hbm);

    if (hbmTemp == (HBITMAP) 0)
    {
        //WARNING("ERROR SetDIBits failed to Select, is bitmap valid?\n");
        goto Error_SetDIBits;
    }

    if (hdc != (HDC) 0)
    {
        hpalTemp = SelectPalette(hdcTemp, GdiGetPaletteFromDC(hdc), 0);
    }

    if (pInitInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
    {
        cWidth  = pInitInfo->bmiHeader.biWidth;
        cHeight = ABS(pInitInfo->bmiHeader.biHeight);
    }
    else
    {
        cWidth  = ((LPBITMAPCOREHEADER)pInitInfo)->bcWidth;
        cHeight = ((LPBITMAPCOREHEADER)pInitInfo)->bcHeight;
    }

    iReturn = SetDIBitsToDevice(hdcTemp,
                                0,
                                0,
                                cWidth,
                                cHeight,
                                0, 0,
                                iStartScans,
                                cNumScans,
                                (VOID *) pInitBits,
                                pInitInfo,
                                iUsage);

    if (hdc != (HDC) 0)
    {
        SelectPalette(hdcTemp, hpalTemp, 0);
    }

    SelectObject(hdcTemp, hbmTemp);

Error_SetDIBits:

    if (bMakeDC)
    {
        DeleteDC(hdcTemp);
    }
    else
    {
        RestoreDC(hdcTemp, -1);
    }

    return(iReturn);
}

/******************************Public*Routine******************************\
* StretchDIBits()
*
*
* Effects:
*
* Warnings:
*
* History:
*  22-Jul-1991 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

int WINAPI StretchDIBits(
HDC           hdc,
int           xDest,
int           yDest,
int           nDestWidth,
int           nDestHeight,
int           xSrc,
int           ySrc,
int           nSrcWidth,
int           nSrcHeight,
CONST VOID   *pj,
CONST BITMAPINFO  *pbmi,
UINT          iUsage,
DWORD         lRop)
{
    LONG cPoints = 0;
    LONG cjHeader;
    LONG cjBits;
    ULONG ulResult = 0;
    PULONG pulBits = NULL;
    int   iRet;
    BITMAPINFO * pbmiNew = NULL;

    FIXUP_HANDLE(hdc);

// NEWFRAME support for backward compatibility.
// Ship the transform to the server side if needed.

    if (IS_ALTDC_TYPE(hdc))
    {
        PLDC pldc;

        if (IS_METADC16_TYPE(hdc))
        {
            return(MF_AnyDIBits(
                   hdc,
                   xDest,
                   yDest,
                   nDestWidth,
                   nDestHeight,
                   xSrc,
                   ySrc,
                   nSrcWidth,
                   nSrcHeight,
                   0,
                   0,
                   (BYTE *) pj,
                   pbmi,
                   iUsage,
                   lRop,
                   META_STRETCHDIB
                   ));
        }

        DC_PLDC(hdc,pldc,ulResult);

        if (pldc->iType == LO_METADC)
        {
            if (!MF_AnyDIBits(hdc,
                              xDest,
                              yDest,
                              nDestWidth,
                              nDestHeight,
                              xSrc,
                              ySrc,
                              nSrcWidth,
                              nSrcHeight,
                              0,
                              0,
                              (BYTE *) pj,
                              pbmi,
                              iUsage,
                              lRop,
                              EMR_STRETCHDIBITS
                              ))
            {
                return(0);
            }
        }

        if (pldc->fl & LDC_SAP_CALLBACK)
            vSAPCallback(pldc);

        if (pldc->fl & LDC_DOC_CANCELLED)
            return(0);

        if (pldc->fl & LDC_CALL_STARTPAGE)
            StartPage(hdc);
    }

// compute the size

    if (pbmi != NULL)
    {
        pbmiNew = pbmiConvertInfo (pbmi, iUsage, &cjHeader,FALSE);

        if (pbmiNew == NULL)
            return (0);

        cjBits   = cjBitmapBitsSize(pbmiNew);
    }
    else
    {
        cjHeader = 0;
        cjBits   = 0;
    }

// reset user's poll count so it counts this as output
// put it right next to BEGINMSG so that NtCurrentTeb() is optimized

    RESETUSERPOLLCOUNT();

    // if the pj are not dword aligned we need to allocate a buffer and copy them

    if ((ULONG)pj & (sizeof(DWORD) - 1))
    {
        pulBits = LocalAlloc(LMEM_FIXED,cjBits);
        if (pulBits)
        {
            RtlCopyMemory(pulBits,pj,cjBits);
            pj = pulBits;
        }
    }

    iRet = NtGdiStretchDIBitsInternal(
                                        hdc,
                                        xDest,
                                        yDest,
                                        nDestWidth,
                                        nDestHeight,
                                        xSrc,
                                        ySrc,
                                        nSrcWidth,
                                        nSrcHeight,
                                        (LPBYTE) pj,
                                        (LPBITMAPINFO) pbmiNew,
                                        iUsage,
                                        lRop,
                                        cjHeader,
                                        cjBits);


    if (pulBits)
        LocalFree(pulBits);

    if (pbmiNew && (pbmiNew != pbmi))
        LocalFree(pbmiNew);

    return(iRet);
}

/******************************Public*Routine******************************\
*
* History:
*  28-May-1991 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

HBITMAP CreateBitmap(
int         nWidth,
int         nHeight,
UINT        nPlanes,
UINT        nBitCount,
CONST VOID *lpBits)
{
    LONG    cj;
    HBITMAP hbm = (HBITMAP)0;
    INT     ii;

// check if it is an empty bitmap

    if ((nWidth == 0) || (nHeight == 0))
    {
        return(GetStockObject(PRIV_STOCK_BITMAP));
    }

// Pass call to the server

    if (lpBits == (VOID *) NULL)
        cj = 0;
    else
    {
        cj = (((nWidth*nPlanes*nBitCount + 15) >> 4) << 1) * nHeight;

        if (cj < 0)
        {
            GdiSetLastError(ERROR_INVALID_PARAMETER);
            return((HBITMAP)0);
        }
    }

    hbm = NtGdiCreateBitmap(nWidth,
                            nHeight,
                            nPlanes,
                            nBitCount,
                            (LPBYTE) lpBits);

    return(hbm);
}

/******************************Public*Routine******************************\
* HBITMAP CreateBitmapIndirect(CONST BITMAP * pbm)
*
* NOTE: if the bmWidthBytes is larger than it needs to be, GetBitmapBits
* will return different info than the set.
*
* History:
*  Tue 18-Jan-1994 -by- Bodin Dresevic [BodinD]
* update: added bmWidthBytes support
*  28-May-1991 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

HBITMAP CreateBitmapIndirect(CONST BITMAP * pbm)
{
    HBITMAP hbm    = (HBITMAP)0;
    LPBYTE  lpBits = (LPBYTE)NULL; // important to zero init
    BOOL    bAlloc = FALSE;        // indicates that tmp bitmap was allocated

// compute minimal word aligned scan width in bytes given the number of
// pixels in x. The width refers to one plane only. Our multi - planar
// support is broken anyway. I believe that we should take an early
// exit if bmPlanes != 1. [bodind].

    LONG cjWidthWordAligned = ((pbm->bmWidth * pbm->bmBitsPixel + 15) >> 4) << 1;

// Win 31 requires at least WORD alinged scans, have to reject inconsistent
// input, this is what win31 does

    if
    (
     (pbm->bmWidthBytes & 1)           ||
     (pbm->bmWidthBytes == 0)          ||
     (pbm->bmWidthBytes < cjWidthWordAligned)
    )
    {
        GdiSetLastError(ERROR_INVALID_PARAMETER);
        return (HBITMAP)0;
    }

// take an early exit if this is not the case we know how to handle:

    if (pbm->bmPlanes != 1)
    {
        WARNING("gdi32: can not handle bmPlanes != 1\n");
        GdiSetLastError(ERROR_INVALID_PARAMETER);
        return (HBITMAP)0;
    }

// if bmBits is nonzero and bmWidthBytes is bigger than the minimal required
// word aligned width we will first convert the bitmap to one that
// has the rows that are minimally word aligned:

    if (pbm->bmBits)
    {
        if (pbm->bmWidthBytes > cjWidthWordAligned)
        {
            PBYTE pjSrc, pjDst, pjDstEnd;
            ULONGLONG lrg;

            lrg = UInt32x32To64(
                       (ULONG)cjWidthWordAligned,
                       (ULONG)pbm->bmHeight
                       );

            if (lrg > ULONG_MAX  ||
                !(lpBits = (LPBYTE)LOCALALLOC((size_t) lrg)))
            {
            // the result does not fit in 32 bits, alloc memory will fail
            // this is too big to digest

                GdiSetLastError(ERROR_NOT_ENOUGH_MEMORY);
                return (HBITMAP)0;
            }

        // flag that we have allocated memory so that we can free it later

            bAlloc = TRUE;

        // convert bitmap to minimally word aligned format

            pjSrc = (LPBYTE)pbm->bmBits;
            pjDst = lpBits;
            pjDstEnd = lpBits + (size_t) lrg;

            while (pjDst < pjDstEnd)
            {
                RtlCopyMemory(pjDst,pjSrc, cjWidthWordAligned);
                pjDst += cjWidthWordAligned, pjSrc += pbm->bmWidthBytes;
            }
        }
        else
        {
        // bits already in minimally aligned format, do nothing

            ASSERTGDI(
                pbm->bmWidthBytes == cjWidthWordAligned,
                "pbm->bmWidthBytes != cjWidthWordAligned\n"
                );
            lpBits = (LPBYTE)pbm->bmBits;
        }
    }

    hbm = CreateBitmap(
                pbm->bmWidth,
                pbm->bmHeight,
                (UINT) pbm->bmPlanes,
                (UINT) pbm->bmBitsPixel,
                lpBits);

    if (bAlloc)
        LOCALFREE(lpBits);

    return(hbm);
}

/******************************Public*Routine******************************\
* CreateDIBSection
*
* Allocate a file mapping object for a DIB.  Return the pointer to it
* and the handle of the bitmap.
*
* History:
*
*  25-Aug-1993 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

HBITMAP
WINAPI
CreateDIBSection(
    HDC hdc,
    CONST BITMAPINFO *pbmi,
    UINT iUsage,
    VOID **ppvBits,
    HANDLE hSectionApp,
    DWORD dwOffset)
{
    HBITMAP hbm = NULL;
    PVOID   pjBits = NULL;
    BITMAPINFO * pbmiNew = NULL;
    INT     cjHdr;

    FIXUP_HANDLE(hdc);

    pbmiNew = pbmiConvertInfo(pbmi, iUsage, &cjHdr, FALSE);

    //
    // dwOffset has to be a multiple of 4 (sizeof(DWORD))
    // if there is a section.  If the section is NULL we do
    // not care
    //

    if ( (hSectionApp == NULL) ||
         ((dwOffset & 3) == 0) )
    {
        hbm = NtGdiCreateDIBSection(
                                hdc,
                                hSectionApp,
                                dwOffset,
                                (LPBITMAPINFO) pbmiNew,
                                iUsage,
                                cjHdr,
                                0,
                                (PVOID *)&pjBits);

        if ((hbm == NULL) || (pjBits == NULL))
        {
            hbm = 0;
            pjBits = NULL;
        }
    }

    //
    // Assign the appropriate value to the caller's pointer
    //

    if (ppvBits != NULL)
    {
        *ppvBits = pjBits;
    }

    if (pbmiNew && (pbmiNew != pbmi))
        LocalFree(pbmiNew);

    return(hbm);

}