/******************************Module*Header*******************************\
* Module Name: dibmp.c
*
* Contains functions that manipulate the DIB color
*
* Created: 30-Jan-1992 21:21:05
* Author: Petrus Wong
*
* Copyright (c) 1990 Microsoft Corporation
*
* These functions will be called from the ChildWndProc() as it processes
* the corresponding WM_COMMAND message for changing the color of the bitmap.
*
* iCreatePenFrPal() is called by InitializeApp() for creating pens based on
* either system or custom palette.
*
* Dependencies:
*       none
*
\**************************************************************************/
#include <windows.h>
#include <stdio.h>
#include "jtypes.h"
#include "dibmp.h"

#define CYCLETHRD

//#define DEBUG

#ifndef DEBUG
   #undef OutputDebugString
   #define OutputDebugString(LPCSTR)
#endif

extern HPALETTE        ghPal, ghPalOld;
extern HWND ghwndMain;
extern HWND ghwndClient;
extern HANDLE hQuitEvent;
extern char   gtext[256];
extern HANDLE ghAccel;
BOOL bValidMode(INT);
BOOL bColor2BW(PBITMAPINFO);
BOOL bChangeDIBColor(HDC, PINFO, INT);
BOOL bColorShift(PBITMAPINFO);
BOOL bColorReplace(PBITMAPINFO);
BOOL bColorCycle(HDC, RECT, PBYTE, PBITMAPINFO, PINFO);
BOOL bCycle(HWND);
BOOL bColor2Mono(HDC, PINFO, PBITMAPINFO);
INT  iCreatePenFrPal(HDC, PVOID *, INT, HPALETTE *);
HPALETTE CopyPalette(HPALETTE);
extern PINFO pGetInfoData(HWND);
extern BOOL bReleaseInfoData(HWND);

/******************************Public*Routine******************************\
*
* bChangeDIBColor
*
* Effects: Main entry point for the color changing operations.  It
*          1. validates the operation
*          2. retrieves the bitmap info from the graphics engine
*          3. dispatches the call to the right function
*          4. saves the resulting bitmap
*          5. updates the screen
*
* Warnings:
*
* History:
*  30-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL bChangeDIBColor(HDC hDC, PINFO pInfo, INT iMode)
{
    BOOL                 bSuccess;
    HBITMAP              hTmpBmp, hBmpOld;
    PBITMAPINFO          pbmi;
    PBYTE                pBits;
    BITMAPINFO           bmi;
    PBYTE                pjTmp, pjTmpBmi;
    ULONG                sizBMI;
    RECT                 rc;
    BOOL                 bCycle;


    bSuccess = TRUE;
    if (!pInfo->hBmpSaved) {
        MessageBox(ghwndMain, "There's no Bitmap to change!", "Error", MB_OK);
        return FALSE;
    }

    if (!bValidMode(iMode)) {
        MessageBox(ghwndMain, "bChangeDIBColor: invalid mode!", "Error", MB_OK);
        return FALSE;
    }

    if (pInfo->hPal) {
        HPALETTE hPalOld;
        int     ii;

        hPalOld = SelectPalette(hDC, pInfo->hPal, FALSE);
        ii = RealizePalette(hDC);

        if (ii){
            UpdateColors (hDC);
        }

    }

    //
    // Let the graphics engine to retrieve the dimension of the bitmap for us
    // GetDIBits uses the size to determine if it's BITMAPCOREINFO or BITMAPINFO
    // if BitCount != 0, color table will be retrieved
    //
    bmi.bmiHeader.biSize = 0x28;              // GDI need this to work
    bmi.bmiHeader.biBitCount = 0;             // don't get the color table
    if ((GetDIBits(hDC, pInfo->hBmpSaved, 0, 0, (LPSTR)NULL, &bmi, DIB_RGB_COLORS)) == 0) {
        MessageBox(ghwndMain, "GetDIBits failed!", "Error", MB_OK);
        return FALSE;
    }

    //
    // Now that we know the size of the image, alloc enough memory to retrieve
    // the actual bits
    //
    if ((pBits = (PBYTE)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                bmi.bmiHeader.biSizeImage)) == NULL) {
        MessageBox(ghwndMain, "Failed in Memory Allocation for pBits!", "Error", MB_OK);
        return FALSE;
    }

    //
    // Note: 24 bits per pixel has no color table.  So, we don't have to
    // allocate memory for retrieving that.  Otherwise, we do.
    //
    pbmi = &bmi;                                      // assume no color table
    if (bmi.bmiHeader.biBitCount != 24) {             // has color table
        sizBMI = sizeof(BITMAPINFO)+sizeof(RGBQUAD)*(1<<bmi.bmiHeader.biBitCount);
        //
        // I need more memory for the color table
        //
        if ((pbmi = (PBITMAPINFO)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizBMI )) == NULL) {
            MessageBox(ghwndMain, "Failed in Memory Allocation for pbmi!", "Error", MB_OK);
            bSuccess = FALSE;
            goto ErrExit1;
        }

        //
        // Now that we've a bigger chunk of memory, let's copy the Bitmap
        // info header data over
        //
        pjTmp = (PBYTE)pbmi;
        pjTmpBmi = (PBYTE)&bmi;
        sizBMI = sizeof(BITMAPINFOHEADER);

        while(sizBMI--)
        {
            *(((PBYTE)pjTmp)++) = *((pjTmpBmi)++);
        }

    }

    //
    // Bitmap can't be selected into a DC when calling GetDIBits
    // Assume that the hDC is the DC where the bitmap would have been selected
    // if indeed it has been selected
    //
    if (hTmpBmp = CreateCompatibleBitmap(hDC, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight)) {
        hBmpOld = SelectObject(hDC, hTmpBmp);
        if ((GetDIBits(hDC, pInfo->hBmpSaved, 0, pbmi->bmiHeader.biHeight, (LPSTR)pBits, pbmi, DIB_RGB_COLORS))==0){
            MessageBox(ghwndMain, "Failed in GetDIBits!", "Error", MB_OK);
            bSuccess = FALSE;
            goto ErrExit4;
        }
    } else {
        MessageBox(ghwndMain, "Failed in creating bitmap!", "Error", MB_OK);
        bSuccess = FALSE;
        goto ErrExit3;
    }

    bCycle = FALSE;
    switch (iMode) {
        case MM_BW:
            if ((!bColor2Mono(hDC, pInfo, pbmi)) ||
                (pInfo->hBmpMono == NULL)) {
                MessageBox(ghwndMain, "Failed in Color2Mono!", "Error", MB_OK);
                bSuccess = FALSE;
                goto ErrExit4;
            }
            pInfo->bUseMono = TRUE;
            break;
        case MM_SHIFT:

            if (!bColorShift(pbmi)) {
                MessageBox(ghwndMain, "Failed in ColorShift!", "Error", MB_OK);
                bSuccess = FALSE;
                goto ErrExit4;
            }
            break;
        case MM_CUSTOM:
            bColorReplace(pbmi);
            break;

        case MM_CYCLE:
            bCycle = TRUE;
            break;

        default:
            bSuccess = FALSE;
            break;
    }

    if (SetDIBits(hDC, pInfo->hBmpSaved, 0, pbmi->bmiHeader.biHeight, (LPSTR)pBits,
            pbmi, DIB_RGB_COLORS) == 0) {
        MessageBox(ghwndMain, "Failed in SetDIBits!", "Error", MB_OK);
        bSuccess = FALSE;
        goto ErrExit4;
    }

    if (bCycle)
        pInfo->bSetDIBsToDevice = TRUE;

#if 0
    if (pInfo->bSetDIBsToDevice && !pInfo->bUseMono) {
        //GetWindowRect(pInfo->hwnd, &rc);
        GetClientRect(pInfo->hwnd, &rc);
        //SetMapMode(hDC, MM_TEXT);

        //if (SetDIBitsToDevice(hDC, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
        //     0, 0, 0, pbmi->bmiHeader.biHeight, (LPSTR)pBits, pbmi, DIB_RGB_COLORS) == 0) {
        //    MessageBox(ghwndMain, "Failed in SetDIBitsToDevice!", "Error", MB_OK);
        //    bSuccess = FALSE;
        //    goto ErrExit4;
        //}
    } else {
#endif
        InvalidateRect(pInfo->hwnd, NULL, FALSE);
//    }

    if (bCycle) {
        //
        // This is commented out because I don't want the system colors
        // be affected by color cycling
        //
        //SetSystemPaletteUse(hDC, SYSPAL_NOSTATIC);
        bColorCycle(hDC, rc, pBits, pbmi, pInfo);
        //SetSystemPaletteUse(hDC, SYSPAL_STATIC);
    }

ErrExit4:
    SelectObject(hDC, hBmpOld);
    DeleteObject(hTmpBmp);
ErrExit3:
    GlobalFree(pbmi);
ErrExit1:
    GlobalFree(pBits);
    return bSuccess;
}


/******************************Public*Routine******************************\
*
* bValidMode
*
* Effects: validates the change mode for ChangeDIBColor function
*          returns TRUE if mode is valid.  Otherwise, FALSE
*
* Warnings:
*
* History:
*  30-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL bValidMode(INT iMode)
{
    if ((iMode == MM_BW)     || (iMode == MM_SHIFT) ||
        (iMode == MM_CUSTOM) || (iMode == MM_CYCLE))
        return TRUE;
    else
        return FALSE;
}

/******************************Public*Routine******************************\
*
* bColor2BW
*
* Effects: changes the color table in the bitmapinfo to black and white
*
* Warnings: doesn't check if pbmi is valid or not
*
* History:
*  30-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL bColor2BW(PBITMAPINFO pbmi)
{
    INT iEntry;

    //
    // CR! What's the right behavior?  Maybe I should convert this to a
    // monochrome bitmap.  This will be very useful in MaskBlt().
    //
    iEntry = 1<<pbmi->bmiHeader.biBitCount;
    while (--iEntry >= 0) {
        if (iEntry <= iEntry / 2) {
                pbmi->bmiColors[iEntry].rgbBlue     = 0x00;
                pbmi->bmiColors[iEntry].rgbGreen    = 0x00;
                pbmi->bmiColors[iEntry].rgbRed      = 0x00;
                pbmi->bmiColors[iEntry].rgbReserved = 0x00;
         } else {
                pbmi->bmiColors[iEntry].rgbBlue     = 0xFF;
                pbmi->bmiColors[iEntry].rgbGreen    = 0xFF;
                pbmi->bmiColors[iEntry].rgbRed      = 0xFF;
                pbmi->bmiColors[iEntry].rgbReserved = 0x00;
        }
    }
    return TRUE;
}


/******************************Public*Routine******************************\
*
* bColorShift
*
* Effects: Shifting entries of the color table
*
* Warnings:
*
* History:
*  30-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bColorShift(PBITMAPINFO pbmi)
{
    INT iEntry;
    RGBQUAD     Tmp;

    iEntry = 1<<pbmi->bmiHeader.biBitCount;

    Tmp.rgbBlue     = pbmi->bmiColors[iEntry-1].rgbBlue;
    Tmp.rgbGreen    = pbmi->bmiColors[iEntry-1].rgbGreen;
    Tmp.rgbRed      = pbmi->bmiColors[iEntry-1].rgbRed;
    Tmp.rgbReserved = pbmi->bmiColors[iEntry-1].rgbReserved;

    while (--iEntry > 0) {
         pbmi->bmiColors[iEntry].rgbBlue     =
            pbmi->bmiColors[iEntry-1].rgbBlue;
         pbmi->bmiColors[iEntry].rgbGreen    =
            pbmi->bmiColors[iEntry-1].rgbGreen;
         pbmi->bmiColors[iEntry].rgbRed      =
            pbmi->bmiColors[iEntry-1].rgbRed;
         pbmi->bmiColors[iEntry].rgbReserved =
            pbmi->bmiColors[iEntry-1].rgbReserved;
    }
    pbmi->bmiColors[0].rgbBlue     = Tmp.rgbBlue;
    pbmi->bmiColors[0].rgbGreen    = Tmp.rgbGreen;
    pbmi->bmiColors[0].rgbRed      = Tmp.rgbRed;
    pbmi->bmiColors[0].rgbReserved = Tmp.rgbReserved;
    return TRUE;

}

BOOL bColorReplace(PBITMAPINFO pbmi)
{
    return FALSE;
    UNREFERENCED_PARAMETER(pbmi);
}

/******************************Public*Routine******************************\
*
* bColorCycle
*
* Effects:  Create a logical palette with a good spread of color
*           Animate the palette, shift the palette entries and animate
*           again for 256 times altogether.
*           This creates the color cycling effect.
*
* Warnings: Only works in device that support palette.
*
* History:
*  31-May-1992      Petrus Wong     Check if drawing window is gone or not
*  24-Apr-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bColorCycle(HDC hDC, RECT rc, PBYTE pBits, PBITMAPINFO pbmi, PINFO pInfo)
{
    INT             iEntry, i, iNumClr;
    PLOGPALETTE     plogPat;
    ULONG           ulSize;
    BOOL            bSuccess;
    HPALETTE        hPal, hPalOld;
    PALETTEENTRY    peTemp;

#ifdef CYCLETHRD
    DWORD           dwWait;
#else
    MSG             msg;
#endif
    BOOL            bQuit;

//UINT uRC;

    bSuccess = TRUE;
    iEntry = 1<<pbmi->bmiHeader.biBitCount;

    iNumClr = 236;          // 256 minus 20 reserved colors
    ulSize = sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*256;

    if ((plogPat = (PLOGPALETTE) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, ulSize)) == NULL) {
        MessageBox(ghwndMain, "Failed in Memory Allocation for plogPat!", "Error", MB_OK);
        bSuccess = FALSE;
        goto ErrExit1;
    }

    plogPat->palVersion = 0x300;
    plogPat->palNumEntries = (WORD) 256;

    {
    int     iFirst, iSecond, iInc;

        iFirst = iNumClr/3;
        iSecond = iFirst * 2;
        iInc = (iNumClr <= 256) ? 3 : 1;

        for (i = 0; i < iNumClr; i++) {
            if (i < iFirst) {
                plogPat->palPalEntry[i+10].peBlue   = 255-(i+1)*iInc;
                plogPat->palPalEntry[i+10].peGreen  = 0;
                plogPat->palPalEntry[i+10].peRed    = (i+1)*iInc;
                plogPat->palPalEntry[i+10].peFlags  = PC_RESERVED;
                continue;
            }
            if (i < iSecond) {
                plogPat->palPalEntry[i+10].peBlue   = 0;
                plogPat->palPalEntry[i+10].peGreen  = (i+1-iFirst)*iInc;
                plogPat->palPalEntry[i+10].peRed    = 255-(i+1-iFirst)*iInc;
                plogPat->palPalEntry[i+10].peFlags  = PC_RESERVED;
                continue;
            }
            plogPat->palPalEntry[i+10].peBlue   = (i+1-iSecond)*iInc;
            plogPat->palPalEntry[i+10].peGreen  = 255-(i+1-iSecond)*iInc;
            plogPat->palPalEntry[i+10].peRed    = 0;
            plogPat->palPalEntry[i+10].peFlags  = PC_RESERVED;
        }
    }

    if ((hPal = CreatePalette(plogPat)) == (HPALETTE) NULL) {
        MessageBox(ghwndMain, "Failed in creating palette!", "Error", MB_OK);
        bSuccess = FALSE;
        goto ErrExit2;
    }

    pInfo->hCyclePal = hPal;

    hPalOld = SelectPalette(hDC, hPal, FALSE);
    RealizePalette(hDC);


#ifndef CYCLETHRD

    bQuit = FALSE;
    while (TRUE && !bQuit) {

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            //sprintf( gtext,"msg = %lx\n", msg.message);
            //OutputDebugString( gtext );

            if ((msg.message == WM_QUIT) || (msg.message == WM_CLOSE) ||
                ((msg.message == WM_SYSCOMMAND) && (msg.wParam == SC_CLOSE))) {
                sprintf( gtext,"msg = %lx, wPram = %lx\n", msg.message, msg.wParam);
                OutputDebugString( gtext );
                bQuit = TRUE;
                PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
            } else {
                if (!TranslateAccelerator( ghwndMain, ghAccel, &msg) &&
                    !TranslateMDISysAccel(  ghwndClient, &msg)          ) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
           }
        }

#else
    bQuit = FALSE;
    while (TRUE) {
        //
        // If parent gets a WM_CLOSE, we will return
        //
        dwWait = WaitForSingleObject(pInfo->hQuitEvent, 0);
        if (dwWait == WAIT_TIMEOUT) {
            MessageBox(ghwndMain, "Cycle Thread Quitting!", "CYCLE Thread", MB_OK);
            break;
        }
#endif
        if (!pInfo->bClrCycle) {
            sprintf( gtext,"NOT to animate palette\n");
            OutputDebugString( gtext );
            bQuit = TRUE;
        }

        if (!bQuit) {
            sprintf( gtext,"About to animate palette\n");
            OutputDebugString( gtext );

            peTemp = plogPat->palPalEntry[10];
            for (i = 0; i < iNumClr; i++) {
                plogPat->palPalEntry[i+10] = plogPat->palPalEntry[i+10+1];
            }
            plogPat->palPalEntry[i-1+10] = peTemp;

            if (!AnimatePalette(hPal, 0, 256, plogPat->palPalEntry)) {
                sprintf( gtext,"Anmiate palette failed\n");
                OutputDebugString( gtext );
            }
            Sleep(50);

//
// This is not needed anymore starting from build 475 14-Jun-1993
//
#if 0
            //
            // Not realizing stops color-cycling after moving back into
            // foreground
            //
            if ((uRC = RealizePalette(hDC)) == GDI_ERROR) {
                sprintf( gtext,"Realize palette failed\n");
                OutputDebugString( gtext );
            }

            if (!UpdateColors(hDC)) {
                sprintf( gtext,"Update Colors failed\n");
                OutputDebugString( gtext );
            }
#endif
        }

    }

    SelectPalette(hDC, hPalOld, 0);
    DeleteObject(hPal);
ErrExit2:
    GlobalFree(plogPat);
ErrExit1:
    return bSuccess;

}


/******************************Public*Routine******************************\
*
* bCycle
*
* Effects: Wrapper for doing color cycling in a separate thread
*          Called from MM_CYCLE
*
* Warnings: presents problem when the MDI child is closed if this thread
*           is not done yet.  We will have to wait until DeleteDC is
*           fully functional.
*
* History:
*  28-May-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL bCycle(HWND hwnd)
{
    HDC     hDC;
    PINFO   pInfo;

    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
        return 0L;
    }

    hDC = GetDC(pInfo->hwnd);
    bChangeDIBColor(hDC, pInfo, MM_CYCLE);
    ReleaseDC(pInfo->hwnd, hDC);

    bReleaseInfoData(hwnd);

    ExitThread(0);
    return TRUE;
}


/******************************Public*Routine******************************\
*
* bColor2Mono
*
* Effects: Create a monochrome bitmap out of the colored one
*          Using a different a source background clr every time it is called
*          Saving the Monochrome bitmap in pInfo
*
* Warnings: It's more straight forward to use GetDIBits than this but
*           that means some big changes to the bChangeDIBColor.
*
* History:
*  31-May-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bColor2Mono(HDC hDC, PINFO pInfo, PBITMAPINFO pbmi) {
    HDC     hDCMemDest, hDCMemSrc;
    HBITMAP hOldBmpDest, hOldBmpSrc;
    INT     iWidth, iHeight, iEntry;
    static INT iCount=0;


    iWidth = pbmi->bmiHeader.biWidth;
    iHeight = pbmi->bmiHeader.biHeight;

    if (pInfo->hBmpMono)
        DeleteObject(pInfo->hBmpMono);

    pInfo->hBmpMono = CreateBitmap(iWidth, iHeight, pbmi->bmiHeader.biPlanes, 1, NULL);
    if (pInfo->hBmpMono == NULL) {
        return(FALSE);
    }

    hDCMemDest = CreateCompatibleDC(hDC);
    hDCMemSrc  = CreateCompatibleDC(hDC);

    if (pInfo->hPal) {
        SelectPalette(hDCMemSrc, pInfo->hPal, FALSE);
        RealizePalette(hDCMemSrc);
    }

    iEntry = 1<<pbmi->bmiHeader.biBitCount;
    SetBkColor(hDCMemSrc, RGB( pbmi->bmiColors[iCount].rgbRed,
                               pbmi->bmiColors[iCount].rgbGreen,
                               pbmi->bmiColors[iCount].rgbBlue) );
    //SetBkColor(hDCMemDest, RGB( 0, 0, 0));
    //SetTextColor(hDCMemDest, RGB( 255, 255, 255));

    iCount++;
    if (iCount >= iEntry)
        iCount = 0;

    hOldBmpDest = SelectObject(hDCMemDest, pInfo->hBmpMono);
    hOldBmpSrc  = SelectObject(hDCMemSrc,  pInfo->hBmpSaved);
    BitBlt(hDCMemDest, 0, 0, iWidth, iHeight, hDCMemSrc, 0, 0, SRCCOPY);

    pInfo->hBmpMono = SelectObject(hDCMemDest, hOldBmpDest);
    SelectObject(hDCMemSrc, hOldBmpSrc);

    DeleteDC(hDCMemDest);
    DeleteDC(hDCMemSrc);

    return(TRUE);

}


/******************************Public*Routine******************************\
*
* iCreatePenFrPal
*
* Effects: Create an array of pens from palette.
*          If device supports palette, then first creates a logical palette
*          with a good spread of color. Then select the logical palette into
*          the DC. Create pen that corresponds to each palette entry.
*          If system does not support palette, then use the system palette.
*
*          prghPen pointer to an array of hPen
*                  If this is NULL, the required size of the array is
*                  returned. If this is not NULL, the array will be filled
*                  with hPens.
*
*          returns the number of hPens created.
*
* Warnings:
*
* History:
*  13-Jun-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
INT iCreatePenFrPal(HDC hDC, PVOID *prghPen, INT iWidth, HPALETTE *phPal)
{
    INT             iNumClr, iResult, i, iEntry;
    PLOGPALETTE     plogPat;
    ULONG           ulSize;

    iResult = 0;

    if (!((GetDeviceCaps(hDC, RASTERCAPS)) & RC_PALETTE)) {

        if (iNumClr = GetSystemPaletteEntries(hDC, 0, 0, NULL)) {
            if (prghPen == NULL) {
                return (iNumClr);
            }

            if (iWidth < 0) {
                MessageBox(GetFocus(), "Invalid pen width!", "Error", MB_OK);
                return 0L;
            }

            for (i = 0; i < iNumClr; i++) {
                prghPen[i] =
                     (PVOID) CreatePen(PS_SOLID, iWidth, PALETTEINDEX(i));
                iResult = i;
#if 0
                SelectObject(hDC, (HPEN)prghPen[i]);
                MoveToEx(hDC, i*iWidth, 0, NULL);
                LineTo(hDC, i*iWidth, 300);
#endif
            }
            return iResult;
        }

        // GetPaletteEntries returns 0
        iNumClr = 1 << GetDeviceCaps(hDC, BITSPIXEL);

        if (iNumClr <= 16) {
            sprintf( gtext,"Assertion failed: iCreatePenFrPal() \n");
            OutputDebugString( gtext);
            sprintf( gtext,"iNumClr <= 16! iNumClr = %d\n", iNumClr);
            OutputDebugString( gtext);
            return (iNumClr);
        }

        // Assuming at least 16 bit device
        //
        iNumClr = 768;      // COLORREF format: 0x00bbggrr r,g,b=[0,255]

        if (prghPen == NULL) {
            return (iNumClr);
        }

        if (iWidth < 0) {
            MessageBox(GetFocus(), "Invalid pen width!", "Error", MB_OK);
            return 0L;
        }

        for (i = 0; i < iNumClr; i++) {
            if (i < 256) {
                prghPen[i] =
                    (PVOID) CreatePen(PS_SOLID, iWidth, RGB(i,0,255-i));
                iResult = i;
#if 0
                SelectObject(hDC, (HPEN)prghPen[i]);
                MoveToEx(hDC, i*iWidth, 0, NULL);
                LineTo(hDC, i*iWidth, 300);
#endif
                continue;
            }

            if (i < 512) {
                prghPen[i] =
                    (PVOID) CreatePen(PS_SOLID, iWidth, RGB(511-i,i-256,0));
                iResult = i;
#if 0
                SelectObject(hDC, (HPEN)prghPen[i]);
                MoveToEx(hDC, i*iWidth, 0, NULL);
                LineTo(hDC, i*iWidth, 300);
#endif
                continue;
            }


            prghPen[i] =
                (PVOID) CreatePen(PS_SOLID, iWidth, RGB(0,767-i,i-512));
            iResult = i;
#if 0
            SelectObject(hDC, (HPEN)prghPen[i]);
            MoveToEx(hDC, i*iWidth, 0, NULL);
            LineTo(hDC, i*iWidth, 300);
#endif
        }

        return iResult;
    }

    //
    // Palette managed device
    //
    iEntry = iNumClr = 1 << GetDeviceCaps(hDC, BITSPIXEL);

    if (iNumClr > 256) {
        sprintf( gtext,"Assertion failed: iCreatePenFrPal() \n");
        OutputDebugString( gtext);
        sprintf( gtext,"iNumClr > 256! iNumClr = %d\n", iNumClr);
        OutputDebugString( gtext);
        iEntry=iNumClr = 256;
    }

    ulSize = sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*iNumClr;

    iNumClr -= GetDeviceCaps(hDC, NUMRESERVED);

    if (prghPen == NULL)
        return iNumClr;

    if ((plogPat = (PLOGPALETTE) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, ulSize)) == NULL) {
        MessageBox(GetFocus(), "Failed in Memory Allocation for plogPat!", "Error", MB_OK);
        goto ErrExit1;
    }

    plogPat->palVersion = 0x300;
    plogPat->palNumEntries = (WORD) iEntry;

    {
    int     iFirst, iSecond, iInc;

        iFirst = iNumClr/3;
        iSecond = iFirst * 2;
        iInc = (iNumClr <= 256) ? 3 : 1;

        for (i = 0; i < iNumClr; i++) {
            if (i < iFirst) {
                plogPat->palPalEntry[i+10].peBlue   = 255-(i+1)*iInc;
                plogPat->palPalEntry[i+10].peGreen  = 0;
                plogPat->palPalEntry[i+10].peRed    = (i+1)*iInc;
                plogPat->palPalEntry[i+10].peFlags  = PC_RESERVED;
                continue;
            }
            if (i < iSecond) {
                plogPat->palPalEntry[i+10].peBlue   = 0;
                plogPat->palPalEntry[i+10].peGreen  = (i+1-iFirst)*iInc;
                plogPat->palPalEntry[i+10].peRed    = 255-(i+1-iFirst)*iInc;
                plogPat->palPalEntry[i+10].peFlags  = PC_RESERVED;
                continue;
            }
            plogPat->palPalEntry[i+10].peBlue   = (i+1-iSecond)*iInc;
            plogPat->palPalEntry[i+10].peGreen  = 255-(i+1-iSecond)*iInc;
            plogPat->palPalEntry[i+10].peRed    = 0;
            plogPat->palPalEntry[i+10].peFlags  = PC_RESERVED;
        }
    }

    DeleteObject(*phPal);

    if ((*phPal = CreatePalette(plogPat)) == (HPALETTE) NULL) {
        MessageBox(GetFocus(), "Failed in creating palette!", "Error", MB_OK);
        goto ErrExit2;
    }
    if (*phPal) {
       SelectPalette(hDC, *phPal, FALSE);
       RealizePalette(hDC);
    }

    for (i = 0; i < iNumClr; i++) {
        HPEN    hPen;

        hPen = CreatePen(PS_SOLID, iWidth, PALETTEINDEX(i+10));
        prghPen[i] = (PVOID) hPen;
        iResult = i;
#if 0
        SelectObject(hDC, hPen);
        MoveToEx(hDC, i*iWidth, 0, NULL);
        LineTo(hDC, i*iWidth, 300);
#endif
    }

ErrExit2:
    GlobalFree(plogPat);
ErrExit1:
    return iResult;

}


/******************************Public*Routine******************************\
*
* HPALETTE CopyPalette
*
* Effects:
*
* Warnings:
*
* History:
*  18-Sep-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

HPALETTE CopyPalette(HPALETTE hPalSrc)
{
    PLOGPALETTE     plogPal;
    PBYTE           pjTmp;
    int             iNumEntries=0;
    HPALETTE        hPal;

    if ((iNumEntries = GetPaletteEntries(hPalSrc, 0, iNumEntries, NULL)) == 0) {
        MessageBox(ghwndMain, "No entry in palette to copy!", "Error", MB_OK);
        return (HPALETTE) NULL;
    }

    if ((plogPal = (PLOGPALETTE)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
            sizeof(DWORD) + sizeof(PALETTEENTRY)*iNumEntries )) == NULL) {
        MessageBox(ghwndMain, "Failed in CopyPalette!", "Error", MB_OK);
        return (HPALETTE) NULL;
    }

    plogPal->palVersion = 0x300;
    plogPal->palNumEntries = (WORD) iNumEntries;
    pjTmp = (PBYTE) plogPal;
    pjTmp += 8;
    GetPaletteEntries(hPalSrc, 0, iNumEntries, (PPALETTEENTRY)pjTmp);
    hPal = CreatePalette(plogPal);

    GlobalFree(plogPal);

    return hPal;
}
