//* Written by Microsoft Product Support Services, Windows Developer Support. *
//*****************************************************************************
//   (C) Copyright Microsoft Corp. 1993.  All rights reserved.
//   You have a royalty-free right to use, modify, reproduce and
//   distribute the Sample Files (and/or any modified version) in
//   any way you find useful, provided that you agree that
//   Microsoft has no warranty obligations or liability for any
//   Sample Application Files which are modified.

// Modified by Petr Smilauer, March 1994 for wxWin library purposes!


#include       <io.h>
#include  <windows.h>
#include "cric_prv.h"
#include   "curico.h"

#ifdef GNUWIN32
#include "gnuwin32/extra.h"
#endif

//*****************************************************************************
//* Function : ReadIconFile()                                                 *
//* Purpose  : Reads an icon resource file and creates an icon based on that  *
//*            information.                                                   *
//* Parameters : char *szFileName - The icon resource file.                   *
//* Returns :  A handle to an icon. The handle will be NULL if an icon cannot *
//*            be created for any reason.                                     *
//*****************************************************************************

HICON ReadIconFile( char *szFileName, HINSTANCE hInst, int *W, int *H)
{ HICON   hIcon;
  HANDLE  hDIB;

  if( (hDIB = ReadIcon(szFileName, W, H)) == NULL)
                                          // read the icon DIB from file
    return NULL;
  hIcon = MakeIcon( hDIB, hInst);         // create an icon from DIB
  GlobalFree( hDIB);
  return hIcon;
}

//*****************************************************************************
//* Function : CursorToIcon()                                                 *
//* Purpose  : Reads a cursor resource file and creates an icon based on that *
//*            information.                                                   *
//* Parameters : char *szFileName - The cursor resource file.                 *
//* Returns :  A handle to an icon. The handle will be NULL if an icon cannot *
//*            be created for any reason.                                     *
//* Comments : A cursor is monochrome. So, the resulting icon will also be    *
//*            monochrome.                                                    *
//*****************************************************************************

HICON CursorToIcon( char *szFileName, HINSTANCE hInst, int *W, int *H)
{ HANDLE  hDIB;     // Handle to DIB memory
  HICON   hIcon;    // Handle to Icon

  if( (hDIB = ReadCur( szFileName, NULL, W, H)) == NULL)
                                                    // Read cursor DIB
    return NULL;
  hIcon = MakeIcon( hDIB, hInst);      // make icon from cursor DIB
  GlobalFree( hDIB);
  return hIcon;
}

//*****************************************************************************
//* Function : ReadIcon()                                                     *
//* Purpose  : Reads an icon resource file and extracts the DIB information.  *
//* Parameters : char *szFileName - The icon resource file.                   *
//* Returns :  A handle to a DIB. The handle will be NULL if the resource file*
//*            is corrupt or if memory cannot be allocated for the DIB info.  *
//*****************************************************************************

HANDLE ReadIcon( char *szFileName, int *W, int *H)
{ ICONFILEHEADER iconFileHead;   // ICON file header structure
  ICONFILERES    iconFileRes;    // ICON file resource
  WORD           cbHead,
                 cbRes,
                 cbBits;         // Used for reading in file
  int            hFile;          // File handle
  BITMAPINFO*    lpDIB;          // Pointer to DIB memory
  HANDLE         hDIB;
  int            nWidth      = GetSystemMetrics( SM_CXICON),
                 nHeight     = GetSystemMetrics( SM_CYICON),
                 nDirEntries = 0;

   // Open and read the .ICO file header and the first ICONFILERES
  hFile  = _lopen( szFileName, OF_READ);
  cbHead = _lread( hFile, (LPSTR)&iconFileHead, sizeof(ICONFILEHEADER));
  cbRes  = _lread( hFile, (LPSTR)&iconFileRes, sizeof(ICONFILERES));
  ++nDirEntries;

  if((cbHead != sizeof( ICONFILEHEADER)) || (cbRes != sizeof( ICONFILERES)))
    return NULL;

  // Verify that it's an .ICON file
  if( iconFileHead.wResourceType != 1)
    return NULL;

  // inserted by P.S.
  while( (nDirEntries < iconFileHead.wResourceCount) &&
         ((iconFileRes.bWidth != nWidth) || (iconFileRes.bHeight != nHeight)))
  {
    cbRes = _lread( hFile, (LPSTR )&iconFileRes, sizeof( ICONFILERES));
    if(cbRes != sizeof( ICONFILERES))
      return NULL;
    else
      ++nDirEntries;
  }

  if(W != 0)
    *W = iconFileRes.bWidth;
  if(H != 0)
    *H = iconFileRes.bHeight;

  // Allocate and lock memory to read in the DIB
  hDIB = GlobalAlloc(GHND, iconFileRes.dwDIBSize);
  if(hDIB == NULL)
    return NULL;
#ifdef __WINDOWS_386__
  lpDIB = (BITMAPINFO*)MK_FP32(GlobalLock(hDIB));
#else
  lpDIB = (BITMAPINFO*)GlobalLock(hDIB);
#endif

  // Now read the DIB portion of the file, which follows the
  // end of icon resource table
  _llseek( hFile, iconFileRes.dwDIBOffset, 0);
  cbBits = _lread( hFile, (LPSTR )lpDIB, (WORD )iconFileRes.dwDIBSize);

  // Done reading file
  _lclose(hFile);

  GlobalUnlock( hDIB);

  if( (DWORD )cbBits != iconFileRes.dwDIBSize)
  {
    GlobalFree( hDIB);
    return NULL;
  }
  return hDIB;
}

//*****************************************************************************
//* Function : MakeIcon()                                                     *
//* Purpose  : Creates an icon based on the DIB info. returned by ReadIcon.   *
//* Parameters : HANDLE hDIB - A handle to the icon's DIB information.        *
//* Returns :  A handle to an Icon. NULL is returned if an icon cannot be     *
//*            successfully created.                                          *
//* Comments : The steps involved in making an icon from a DIB are very       *
//*            similar to those involved in making a cursor from a DIB.       *
//* Steps : 1) Obtain a pointer to the Icon's DIB bits.                       *
//*         2) Divide the DIB'd height with 2 to account for the fact that the*
//*            DIB stores both the XOR and the AND masks, one after the other.*
//*         3) Determine the offset to the XOR bits.                          *
//*         4) Determine the offset to the AND bits.                          *
//*         5) Create a device dependent bitmap with the XOR bits.            *
//*         6) Obtain the device dependent XOR bitmask and save in memory.    *
//*            The AND bitmask is monochrome. Monochrome bits are identical   *
//*            in both the device dependent bitmaps and device independent    *
//*            bitmaps. So, no need to convert the AND bitmask.               *
//*         7) Since a DIB is stored upside down, flip the monochrome AND bits*
//*            by scanlines.                                                  *
//*         8) Use the XOR and AND bits and create an icon with CreateIcon.   * 
//*****************************************************************************

HICON MakeIcon( HANDLE hDIB, HINSTANCE hInst)
{ LPSTR         lpXORbits,
                lpANDbits;    // Pointer to XOR and AND bits
  HBITMAP       hbmXor;       // handle to XOR bitmap
  BITMAP        bmpXor;       // Used to manipulate XOR bitmap
  DWORD         dwBmpSize;    // Size of XOR bitmap
  HANDLE        hXorDDB;
  LPSTR         lpXorDDB;
  LONG          szFlip[32];
  int           j,
                k;
  HDC           hDC;
  HICON         hIcon;
  BITMAPINFO*   lpDIB;

  // 1) Obtain a pointer to the Icon's DIB bits.
#ifdef __WINDOWS_386__
  lpDIB = (BITMAPINFO*)MK_FP32(GlobalLock( hDIB));
#else
  lpDIB = (BITMAPINFO*)GlobalLock( hDIB);
#endif

  // 2) Divide the DIB'd height with 2 to account for the fact that the
  //    DIB stores both the XOR and the AND masks, one after the other.
  lpDIB->bmiHeader.biHeight /= 2;

  // 3) Determine the offset to the XOR bits.
  // To obtain this value, we have to skip the header, and color table
  lpXORbits = (LPSTR )lpDIB + (int )lpDIB->bmiHeader.biSize +
              (DIBNumColors( (LPSTR )lpDIB) * sizeof( RGBQUAD));

  // 4) Determine the offset to the AND bits.
  //    To obtain this value, skip the XOR bits
  lpANDbits = lpXORbits + (int )(lpDIB->bmiHeader.biHeight *
              (WIDTHBYTES ( lpDIB->bmiHeader.biWidth *
                            lpDIB->bmiHeader.biBitCount)));

  // Get a hDC so we can create a bitmap compatible with it
  hDC = CreateDC( "DISPLAY", NULL, NULL, NULL);

  // 5) Create a device dependent bitmap with the XOR bits.
  hbmXor = CreateDIBitmap( hDC, (BITMAPINFOHEADER*)&(lpDIB->bmiHeader),
                           CBM_INIT, lpXORbits, lpDIB, DIB_RGB_COLORS);

  GetObject( hbmXor, sizeof(BITMAP), (LPSTR)&bmpXor);

  dwBmpSize = (DWORD )(bmpXor.bmWidthBytes * bmpXor.bmHeight * bmpXor.bmPlanes);
  hXorDDB = GlobalAlloc( GHND, dwBmpSize);
  if(hXorDDB == NULL)
  {
    // clean up before quitting
    DeleteObject( hbmXor);
    DeleteDC( hDC);
    GlobalUnlock( hDIB);
    return NULL;
  }

#ifdef __WINDOWS_386__
  lpXorDDB = (LPSTR)MK_FP32(GlobalLock( hXorDDB));
#else
  lpXorDDB = (LPSTR)GlobalLock( hXorDDB);
#endif

  // 6) Obtain the device dependent XOR bitmask and save in memory.
  // The AND bitmask is monochrome. Monochrome bits are identical
  // in both the device dependent bitmaps and device independent
  // bitmaps. So, no need to convert the AND bitmask.
  GetBitmapBits( hbmXor, dwBmpSize, lpXorDDB);

  // 7) Since a DIB is stored upside down, flip the monochrome AND bits by scanlines.
  k = (int )lpDIB->bmiHeader.biHeight;
  for( j = 0 ; j < k ; j++, lpANDbits += sizeof(DWORD))
    szFlip[(k - 1) - j] = *(DWORD FAR *)lpANDbits;

  // 8) Use the XOR and AND bits and create an icon with CreateIcon.
  hIcon = CreateIcon( hInst, bmpXor.bmWidth, bmpXor.bmHeight, bmpXor.bmPlanes,
                      bmpXor.bmBitsPixel, (const BYTE *)szFlip, (const BYTE *)lpXorDDB);

  // Clean up before exiting.
  DeleteObject( hbmXor);
  GlobalUnlock( hXorDDB);
  GlobalFree( hXorDDB);
  DeleteDC( hDC);
  GlobalUnlock( hDIB);

  return hIcon;
}

// **************************************************************************

//*****************************************************************************
//* Function : ReadCursorFile()                                               *
//* Purpose  : Reads a cursor resource file and creates a cursor based on that*
//*            information.                                                   *
//* Parameters : char *szFileName - The cursor resource file.                 *
//* Returns :  A handle to a cursor. The handle will be NULL if a cursor can't*
//*            be created for any reason.                                     *
//*****************************************************************************

HCURSOR ReadCursorFile( char *szFileName, HINSTANCE hInst, int *W, int *H,
                        int *XHot, int *YHot)
{ HANDLE    hDIB;    // Handle to DIB memory
  HCURSOR   hCursor;
  POINT     ptHotSpot;

  // read cur DIB from file
  if( (hDIB = ReadCur( szFileName, (LPPOINT )&ptHotSpot, W, H)) == NULL)
    return NULL;
  hCursor = MakeCursor( hDIB, (LPPOINT )&ptHotSpot, hInst);//create cur from DIB
  if(XHot != 0)
    *XHot = ptHotSpot.x;
  if(YHot != 0)
    *YHot = ptHotSpot.y;
  GlobalFree( hDIB);
  return ( hCursor);
}

//*****************************************************************************
//* Function : IconToCursor()                                                 *
//* Purpose  : Reads an icon resource file and creates a cursor based on that *
//*            information.                                                   *
//* Parameters : char *szFileName - The icon resource file.                   *
//* Returns :  A handle to a cursor. The handle will be NULL if a cursor can't*
//*            be created for any reason.                                     *
//* Comments : An icon may be in color. So, the DIB has to be forced to be    *
//*            monochrome.                                                    *
//*****************************************************************************

HCURSOR IconToCursor( char *szFileName, HINSTANCE hInst, int XHot, int YHot,
                      int  *W, int *H)
{ HCURSOR   hCursor;
  HANDLE    hDIB;
  POINT     ptHotSpot;

  if( (hDIB = ReadIcon( szFileName, W, H)) == NULL)
                                             //read icon file to get icon DIB
    return NULL;
  // Set the hot spot of the cursor
  ptHotSpot.x = XHot;
  ptHotSpot.y = YHot;
  hCursor     = MakeCursor( hDIB, (LPPOINT )&ptHotSpot, hInst);
                              //create cursor from DIB
  GlobalFree( hDIB);
  return hCursor;
}

//*****************************************************************************
//* Function : ReadCur()                                                      *
//* Purpose  : Reads a cursor resource file and extracts the DIB information. *
//* Parameters : LPSTR szFileName - The cursor resource file.                 *
//* Returns :  A handle to a DIB. The handle will be NULL if the resource file*
//*            is corrupt or if memory cannot be allocated for the DIB info.  *
//*****************************************************************************

HANDLE ReadCur( char *szFileName, LPPOINT lpptHotSpot, int *W, int *H)
{ CURFILEHEADER   curFileHead;  // CURSOR file header structure
  CURFILERES      curFileRes;   // CURSOR file resource
  WORD            cbHead,
                  cbRes,
                  cbBits;       // Used for reading in file
  BITMAPINFO*     lpDIB;        // Pointer to DIB memory
  int             hFile;        // Handle to File
  HANDLE          hDIB;
  int             nWidth      = GetSystemMetrics( SM_CXCURSOR),
                  nHeight     = GetSystemMetrics( SM_CYCURSOR),
                  nDirEntries = 0;

  // Open and read the .ICO file header and the first ICONFILERES
  hFile  = _lopen( szFileName, OF_READ);
  cbHead = _lread( hFile,  (LPSTR )&curFileHead, sizeof( CURFILEHEADER));
  cbRes  = _lread( hFile,  (LPSTR )&curFileRes,  sizeof( CURFILERES));
  ++nDirEntries;

  if((cbHead != sizeof( CURFILEHEADER)) || (cbRes != sizeof( CURFILERES)))
    return NULL;

   // Verify that it's an .CUR file
  if ((curFileRes.bReserved1 != 0) || (curFileHead.wResourceType != 2))
    return NULL;

  // following added by P.S.
  while( (nDirEntries < curFileHead.wResourceCount) &&
         ((curFileRes.bWidth != nWidth) || (curFileRes.bHeight != nHeight)))
  {
    cbRes = _lread( hFile, (LPSTR )&curFileRes, sizeof( CURFILERES));
    if(cbRes != sizeof( CURFILERES))
      return NULL;
    else
      ++nDirEntries;
  }
  if(W != 0)
    *W = curFileRes.bWidth;
  if(H != 0)
    *H = curFileRes.bHeight;


  // Allocate & lock memory to read in the DIB
  hDIB = GlobalAlloc(GHND, curFileRes.dwDIBSize);
  if(hDIB == NULL)
    return NULL;

#ifdef __WINDOWS_386__
  lpDIB = (BITMAPINFO*)MK_FP32(GlobalLock(hDIB));
#else
  lpDIB = (BITMAPINFO*)GlobalLock(hDIB);
#endif

  // Now read the DIB portion of the file, which follows the
  // end of icon resource table
  _llseek( hFile, curFileRes.dwDIBOffset, 0);
  cbBits = _lread( hFile, (LPSTR )lpDIB, (WORD )curFileRes.dwDIBSize);

  // Done reading file
  _lclose(hFile);

  if((DWORD)cbBits != curFileRes.dwDIBSize)
  {
    GlobalUnlock( hDIB);
    GlobalFree( hDIB);
    return NULL;
  }
  if(lpptHotSpot != NULL)  // If it is necessary to know the hot spot
  {
    lpptHotSpot->x = (int )curFileRes.wXHotspot;
    lpptHotSpot->y = (int )curFileRes.wYHotspot;
  }
  GlobalUnlock( hDIB);
  return( hDIB);
}

//*****************************************************************************
//* Function : ColorDDBToMonoDDB()                                            *
//* Purpose  : Converts a color bitmap to a monochrome bitmap.                *
//* Parameters : HBITMAP hbm - The color bitmap.                              *
//* Returns :  A handle to a monochrome bitmap.                               *
//*****************************************************************************
HBITMAP ColorDDBToMonoDDB ( HBITMAP hbm)
{ BITMAP              bm;
  BITMAPINFOHEADER    bi;
  BITMAPINFOHEADER*   lpbi;
  DWORD               dwLen;
  HANDLE              hdib;
  HANDLE              h;
  HDC                 hdc;
  HBITMAP             hbmMono;

  GetObject( hbm, sizeof( bm), (LPSTR )&bm);

  bi.biSize           = sizeof( BITMAPINFOHEADER);    // size of this structure
  bi.biWidth          = bm.bmWidth;                   // bitmap width in pixels
  bi.biHeight         = bm.bmHeight;                  // bitmap height in pixels
  bi.biPlanes         = 1;                            // # of planes always 1 for DIBs
  bi.biBitCount       = bm.bmPlanes * bm.bmBitsPixel; // color bits per pixel
  bi.biCompression    = BI_RGB;                       // no compression
  bi.biSizeImage      = 0;                            // 0 means default size
  bi.biXPelsPerMeter  = 0;                            // not used
  bi.biYPelsPerMeter  = 0;                            // not used
  bi.biClrUsed        = 0;                            // 0 means default colors
  bi.biClrImportant   = 0;                            // 0 means defaults

  dwLen = bi.biSize + PaletteSize((LPSTR)&bi);

  hdc = GetDC( NULL);

  hdib = GlobalAlloc( GHND, dwLen);
  if (hdib == NULL)
  {
    ReleaseDC( NULL, hdc);
    return NULL;
  }

#ifdef __WINDOWS_386__
  lpbi = (BITMAPINFOHEADER*)MK_FP32(GlobalLock( hdib));
#else
  lpbi = (BITMAPINFOHEADER*)GlobalLock( hdib);
#endif

  *lpbi = bi;

  // Call GetDIBits with a NULL lpBits parameter; it will calculate
  // the biSizeImage field.
  GetDIBits( hdc, hbm, 0, (WORD)bi.biHeight,
             NULL, (BITMAPINFO*)lpbi, DIB_RGB_COLORS);

  bi = *lpbi;
  GlobalUnlock( hdib);

  // If the driver did not fill in the biSizeImage field, make one up.
  if(bi.biSizeImage == 0)
    bi.biSizeImage = WIDTHBYTES( (DWORD )bm.bmWidth * bi.biBitCount) * bm.bmHeight;

  // Reallocate the buffer big enough to hold all the bits.
  dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage;
  if( (h = GlobalReAlloc( hdib, dwLen, 0)) != 0)
    hdib = h;
  else
  {
    GlobalFree( hdib);
    ReleaseDC( NULL, hdc);
    return NULL;
  }

  // Call GetDIBits with a NON-NULL lpBits parameter, to actually
  // get the bits this time.

#ifdef __WINDOWS_386__
  lpbi = (BITMAPINFOHEADER*)MK_FP32(GlobalLock( hdib));
#else
  lpbi = (BITMAPINFOHEADER*)GlobalLock( hdib);
#endif

  if( GetDIBits( hdc, hbm, 0, (WORD)bi.biHeight,
                 (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi),
                 (BITMAPINFO*)lpbi, DIB_RGB_COLORS) == 0)
  {
    GlobalUnlock( hdib);
    hdib = NULL;
    ReleaseDC( NULL, hdc);
    return NULL;
  }

  // Finally, create a monochrome DDB, and put the DIB into
  // it. SetDIBits does smart color conversion.
  hbmMono = CreateBitmap((WORD)lpbi->biWidth, (WORD)lpbi->biHeight, 1, 1, NULL);
  SetDIBits( hdc, hbmMono, (WORD)0, (WORD)lpbi->biHeight,
             (LPSTR)lpbi + (int )lpbi->biSize + PaletteSize((LPSTR)lpbi),
             (BITMAPINFO*)lpbi, DIB_RGB_COLORS);

  bi = *lpbi;
  GlobalUnlock( hdib);
  GlobalFree( hdib);

  ReleaseDC(NULL, hdc);
  return hbmMono;
}

//*****************************************************************************
//* Function : MakeCursor()                                                   *
//* Purpose  : Creates a cursor based on the DIB info. returned by ReadCursor.*
//* Parameters : HANDLE hDIB - A handle to the cursor's DIB information.      *
//*              LPPOINT lppt - A pointer to a point struct. indicating the   *
//*                             location of the Cursor's hot spot.            *
//* Returns :  A handle to a cursor. NULL is returned if a cursor cannot be   *
//*            successfully created.                                          *
//* Comments : The steps involved in making a cursor from a DIB are very      *
//*            similar to those involved in making an icon from a DIB.        *
//* Steps : 1) Obtain a pointer to the Cursor's DIB bits.                     *
//*         2) Divide the DIB's height with 2 to account for the fact that the*
//*            DIB stores both the XOR and the AND masks, one after the other.*
//*         3) Determine the offset to the XOR bits.                          *
//*         4) Determine the offset to the AND bits.                          *
//*         5) Create a device dependent bitmap with the XOR bits.            *
//*         6) Obtain the device dependent XOR bitmask and save in memory.    *
//*            The AND bitmask is monochrome. Monochrome bits are identical   *
//*            in both the device dependent bitmaps and device independent    *
//*            bitmaps. So, no need to convert the AND bitmask.               *
//*         7) Since a DIB is stored upside down, flip the monochrome AND bits*
//*            by scanlines.                                                  *
//*         8) Use the XOR and AND bits and create a cursor with CreateCursor.*
//*****************************************************************************

HCURSOR MakeCursor( HANDLE hDIB, LPPOINT lpptHotSpot, HINSTANCE hInst)
{ LPSTR         lpXORbits,
                lpANDbits;    // Pointer to XOR and AND bits
  HBITMAP       hbmXor;       // handle to XOR bitmap
  BITMAP        bmpXor;       // Used to manipulate XOR bitmap
  DWORD         dwBmpSize;    // Size of XOR bitmap
  HCURSOR       hCursor;
  HANDLE        hXorDDB;
  LPSTR         lpXorDDB;
  LONG          szFlip[32];
  int           j,
                k;
  HDC           hDC;
  BITMAPINFO*   lpDIB;

  // 1) Obtain a pointer to the Cursor's DIB bits.
#ifdef __WINDOWS_386__
  lpDIB = (BITMAPINFO*)MK_FP32(GlobalLock( hDIB));
#else
  lpDIB = (BITMAPINFO*)GlobalLock( hDIB);
#endif

  // 2) Divide the DIB's height with 2 to account for the fact that the
  //    DIB stores both the XOR and the AND masks, one after the other.
  lpDIB->bmiHeader.biHeight /= 2;

  // 3) Determine the offset to the XOR bits.
  //    To obtain this value, we have to skip the header, and color table
  lpXORbits = (LPSTR )lpDIB + (int )lpDIB->bmiHeader.biSize +
              (DIBNumColors((LPSTR)lpDIB) * sizeof(RGBQUAD));

  // 4) Determine the offset to the AND bits
  //    To obtain this value, skip the XOR bits
  lpANDbits = lpXORbits + (int )( lpDIB->bmiHeader.biHeight *
              (WIDTHBYTES( lpDIB->bmiHeader.biWidth *
                           lpDIB->bmiHeader.biBitCount)));

  // Get a hDC so we can create a bitmap compatible with it
  hDC = CreateDC( "DISPLAY", NULL, NULL, NULL);

  // 5) Create a device dependent bitmap with the XOR bits.
  hbmXor = CreateBitmap( (int )lpDIB->bmiHeader.biWidth,
                         (int )lpDIB->bmiHeader.biHeight, 1, 1, NULL);
  SetDIBits( hDC, hbmXor, 0, (WORD)lpDIB->bmiHeader.biHeight, lpXORbits,
             lpDIB, DIB_RGB_COLORS);
  GetObject( hbmXor, sizeof( BITMAP), (LPSTR )&bmpXor);

  dwBmpSize = (DWORD )(bmpXor.bmWidthBytes * bmpXor.bmHeight * bmpXor.bmPlanes);
  hXorDDB = GlobalAlloc( GHND, dwBmpSize);
  if(hXorDDB == NULL)
  {  // clean up before quitting
    DeleteObject( hbmXor);
    DeleteDC( hDC);
    GlobalUnlock( hDIB);
    return NULL;
  }
#ifdef __WINDOWS_386__
  lpXorDDB = (LPSTR)MK_FP32(GlobalLock( hXorDDB));
#else
  lpXorDDB = (LPSTR)GlobalLock( hXorDDB);
#endif

  // 6) Obtain the device dependent XOR bitmask and save in memory.
  //    The AND bitmask is monochrome. Monochrome bits are identical
  //    in both the device dependent bitmaps and device independent
  //    bitmaps. So, no need to convert the AND bitmask.
  GetBitmapBits( hbmXor, dwBmpSize, lpXorDDB);

  // 7) Since a DIB is stored upside down, flip the monochrome AND bits by scanlines.
  k = (int)lpDIB->bmiHeader.biHeight;
  for( j = 0 ; j < k; j++, lpANDbits += sizeof( DWORD))
    szFlip[(k - 1) - j] = *(DWORD FAR *)lpANDbits;

  // 8) Use the XOR and AND bits and create a cursor with CreateCursor.
  hCursor = CreateCursor( hInst, lpptHotSpot->x, lpptHotSpot->y,
                          bmpXor.bmWidth, bmpXor.bmHeight, (LPSTR)szFlip, lpXorDDB);

  // Clean up before exiting.
  DeleteObject( hbmXor);
  GlobalUnlock( hXorDDB);
  GlobalFree( hXorDDB);
  DeleteDC( hDC);
  GlobalUnlock( hDIB);

  return hCursor;
}

//*****************************************************************************
//* Function : PaletteSize()                                                  *
//* Purpose  : Calculates the palette size in bytes. If the info. block is of *
//*            the BITMAPCOREHEADER type, the number of colors is multiplied  *
//*            by sizeof(RGBTRIPLE) to give the palette size, otherwise the   *
//*            number of colors is multiplied by sizeof(RGBQUAD).             *
//* Parameters : LPSTR pv - pointer to the BITMAPINFOHEADER                   *
//* Returns :  The size of the palette.                                       *
//*****************************************************************************

WORD PaletteSize( LPSTR pv)
{ BITMAPINFOHEADER*   lpbi;
  WORD                NumColors;

  lpbi      = (BITMAPINFOHEADER*)pv;
  NumColors = DIBNumColors((LPSTR )lpbi);

  if(lpbi->biSize == sizeof( BITMAPCOREHEADER))  // OS/2 style DIBs
    return NumColors * sizeof( RGBTRIPLE);
  else
    return NumColors * sizeof( RGBQUAD);
}

//*****************************************************************************
//* Function : DIBNumColors()                                                 *
//* Purpose  : This function calculates the number of colors in the DIB's     *
//*            color table by finding the bits per pixel for the DIB (whether *
//*            Win3.0 or OS/2-style DIB). If bits per pixel is 1: colors=2,   *
//*            if 4: colors=16, if 8: colors=256, if 24, no colors in color   *
//*            table.                                                         *
//* Parameters : LPSTR lpbi - pointer to packed-DIB memory block.             *
//* Returns :  The number of colors in the color table.                       *
//*****************************************************************************

WORD DIBNumColors ( LPSTR pv)
{ int                 bits;
  BITMAPINFOHEADER*   lpbi;
  BITMAPCOREHEADER*   lpbc;

  lpbi = ((BITMAPINFOHEADER*)pv);     // assume win 3.0 style DIBs
  lpbc = ((BITMAPCOREHEADER*)pv);     // assume OS/2 style DIBs

  // With the BITMAPINFO format headers, the size of the palette
  // is in biClrUsed, whereas in the BITMAPCORE - style headers, it
  // is dependent on the bits per pixel ( = 2 raised to the power of
  // bits/pixel).
    
  if(lpbi->biSize != sizeof( BITMAPCOREHEADER))
  {
    if(lpbi->biClrUsed != 0)
      return (WORD)lpbi->biClrUsed;
    bits = lpbi->biBitCount;
  }
  else
    bits = lpbc->bcBitCount;

  switch( bits)
  {
    case 1:
      return 2;
    case 4:
      return 16;
    case 8:
      return 256;
    default:
      // A 24 bitcount DIB has no color table
      return 0;
  }
}


// ******************************************************************
BOOL fGetXPixmap( BOOL fIsIcon, char *szFileName, HINSTANCE hInst,
                  char cData[], int &width, int &height)
{ HDC       hdc,
            hdcMemory;
  HBITMAP   hbmp,
            holdbmp;
  int       i,
            j,
            w,
            h;
  BYTE     *s,
            cByte,
            cMask;
  COLORREF  rgb;
  HCURSOR   hIconOrCursor = fIsIcon ?
                      IconToCursor( szFileName, hInst, 0, 0, &w, &h)
                    : ReadCursorFile( szFileName, hInst, &w, &h, 0, 0);
  int       sum;

  if(hIconOrCursor == 0)
    return FALSE;

  hdc       = GetDC( GetDesktopWindow());
  hdcMemory = CreateCompatibleDC( hdc);
  hbmp      = CreateCompatibleBitmap( hdc, w, h);
  holdbmp   = (HBITMAP) SelectObject( hdcMemory, hbmp);
  PatBlt( hdcMemory, 0, 0, w, h, BLACKNESS); // or use WHITENESS??
  DrawIcon( hdcMemory, 0, 0, hIconOrCursor); //using HCURSOR with DrawIcon is OK

  // the data retrieval follows:
  width  = w;
  height = h;
  for( j = 0, s = (BYTE *)cData ; j < h ; ++j)
    for( i = 0 ; i < w ; ++i, cMask >>= 1)
    {
      if( (i % 8) == 0)
      {
        cByte = 0;
        cMask = 0x80;
      }
      rgb  = GetPixel( hdcMemory, i, j);
      sum  = (int )(rgb & 0xFFL);
      sum += (int )((rgb & 0xFF00L) >> 8);
      sum += (int )((rgb & 0xFF0000L) >> 16);
      if(sum > 381)
        cByte = cByte | cMask;
      if( (i % 8) == 7)
      {
        *s = cByte;
        ++s;
      }
    }
  SelectObject( hdcMemory, holdbmp);
  DeleteDC( hdcMemory);
  ReleaseDC( GetDesktopWindow(), hdc);
  DestroyCursor( hIconOrCursor);
  DeleteObject( hbmp);
  return TRUE;
}

// Added from scavenged internet code, JACS 23/6/95
HCURSOR MakeCursorFromBitmap(HINSTANCE hInst, HBITMAP hBitmap, POINT *pPoint)
{
  HDC hDCColor, hDCMono;
  HDC hDC;
  HBITMAP   hBmpOld;
  HBITMAP   hAndBmp;
  HBITMAP   hXorBmp;
  HCURSOR   hNewCursor;
  BITMAP    bm;
  DWORD     dwBytes;
  NPSTR     andBits;
  NPSTR     xorBits;

  hDC = GetDC(NULL);
  hDCColor = CreateCompatibleDC(hDC);
  hDCMono = CreateCompatibleDC(hDC);
  hAndBmp = CreateCompatibleBitmap(hDCMono, 32, 32);
  hXorBmp = CreateCompatibleBitmap(hDCMono, 32, 32);

  hBmpOld = (HBITMAP) SelectObject(hDCColor, hBitmap);
  SelectObject(hDCMono, hAndBmp);
  SetBkColor(hDCColor, RGB(191, 191, 191));

  BitBlt(hDCMono, 0, 0, 32, 32, hDCColor, 0, 0, SRCCOPY);

  // Now we have the AND Mask

  GetObject(hAndBmp, sizeof(BITMAP), (LPSTR) &bm);
  dwBytes = (bm.bmWidthBytes * bm.bmHeight);
  andBits = (NPSTR) LocalAlloc(LPTR, dwBytes);
  GetBitmapBits(hAndBmp, dwBytes, andBits);

  SelectObject(hDCMono, hXorBmp);
  SetBkColor(hDCColor, RGB(0, 0, 0));

  BitBlt(hDCMono, 0, 0, 32, 32, hDCColor, 0, 0, SRCCOPY);

  // Now we have the XOR Mask

  GetObject(hXorBmp, sizeof(BITMAP), (LPSTR) &bm);
  dwBytes = (bm.bmWidthBytes * bm.bmHeight);
  xorBits = (NPSTR) LocalAlloc(LPTR, dwBytes);
  GetBitmapBits(hXorBmp, dwBytes, xorBits);

  if (pPoint->x > 32)
    pPoint->x = 32;
  if (pPoint->y > 32)
    pPoint->y = 32;

  hNewCursor = CreateCursor(hInst,
                            pPoint->x, pPoint->y, 32, 32, andBits, xorBits);

  SelectObject(hDCColor, hBmpOld);
  SelectObject(hDCMono, hBmpOld);
  DeleteDC(hDCColor);
  DeleteDC(hDCMono);
  DeleteObject(hAndBmp);
  DeleteObject(hXorBmp);
  ReleaseDC(NULL, hDC);
#ifndef WIN32
  LocalUnlock(LocalHandle((WORD) andBits));
  LocalUnlock(LocalHandle((WORD) xorBits));
  LocalFree(LocalHandle((WORD) andBits));
  LocalFree(LocalHandle((WORD) xorBits));
#else
  LocalUnlock(LocalHandle((LPCVOID) andBits));
  LocalUnlock(LocalHandle((LPCVOID) xorBits));
  LocalFree(LocalHandle((LPCVOID) andBits));
  LocalFree(LocalHandle((LPCVOID) xorBits));
#endif
  return hNewCursor;
}

/*
 * This doesn't work: just gives us a grey square. Ideas, anyone?
 */
 
HICON MakeIconFromBitmap(HINSTANCE hInst, HBITMAP hBitmap)
{
  HDC hDCColor, hDCMono;
  HDC hDC;
  HBITMAP   hBmpOld;
  HBITMAP   hAndBmp;
  HBITMAP   hXorBmp;
  HICON     hNewIcon;
  BITMAP    bm;
  DWORD     dwBytes;
  NPSTR     andBits;
  NPSTR     xorBits;

  hDC = GetDC(NULL);
  hDCColor = CreateCompatibleDC(hDC);
  hDCMono = CreateCompatibleDC(hDC);
  hAndBmp = CreateCompatibleBitmap(hDCMono, 32, 32);
  hXorBmp = CreateCompatibleBitmap(hDCMono, 32, 32);

  hBmpOld = (HBITMAP) SelectObject(hDCColor, hBitmap);
  SelectObject(hDCMono, hAndBmp);
  SetBkColor(hDCColor, RGB(191, 191, 191));

  BitBlt(hDCMono, 0, 0, 32, 32, hDCColor, 0, 0, SRCCOPY);

  // Now we have the AND Mask

  GetObject(hAndBmp, sizeof(BITMAP), (LPSTR) &bm);
  dwBytes = (bm.bmWidthBytes * bm.bmHeight);
  andBits = (NPSTR) LocalAlloc(LPTR, dwBytes);
  GetBitmapBits(hAndBmp, dwBytes, andBits);

  SelectObject(hDCMono, hXorBmp);
  SetBkColor(hDCColor, RGB(0, 0, 0));

  BitBlt(hDCMono, 0, 0, 32, 32, hDCColor, 0, 0, SRCCOPY);

  // Now we have the XOR Mask

  GetObject(hXorBmp, sizeof(BITMAP), (LPSTR) &bm);
  dwBytes = (bm.bmWidthBytes * bm.bmHeight);
  xorBits = (NPSTR) LocalAlloc(LPTR, dwBytes);
  GetBitmapBits(hXorBmp, dwBytes, xorBits);

  hNewIcon = CreateIcon(hInst, 1, 4, 32, 32, (unsigned char *)andBits, (unsigned char *)xorBits);

  SelectObject(hDCColor, hBmpOld);
  SelectObject(hDCMono, hBmpOld);
  DeleteDC(hDCColor);
  DeleteDC(hDCMono);
  DeleteObject(hAndBmp);
  DeleteObject(hXorBmp);
  ReleaseDC(NULL, hDC);
#ifndef WIN32
  LocalUnlock(LocalHandle((WORD) andBits));
  LocalUnlock(LocalHandle((WORD) xorBits));
  LocalFree(LocalHandle((WORD) andBits));
  LocalFree(LocalHandle((WORD) xorBits));
#else
  LocalUnlock(LocalHandle((LPCVOID) andBits));
  LocalUnlock(LocalHandle((LPCVOID) xorBits));
  LocalFree(LocalHandle((LPCVOID) andBits));
  LocalFree(LocalHandle((LPCVOID) xorBits));
#endif
  return hNewIcon;
}

