//-----------------------------------------------------------------------------
// DIB.C
//
// This is a collection of useful DIB manipulation/information gathering
// functions.  Many functions are supplied simply to take the burden
// of taking into account whether a DIB is a Win30 style or OS/2 style
// DIB away from the application.
//
// The functions in this module assume that the DIB pointers or handles
// passed to them point to a block of memory in one of two formats:
//
//       a) BITMAPINFOHEADER + color table + DIB bits (3.0 style DIB)
//       b) BITMAPCOREHEADER + color table + DIB bits (OS/2 PM style)
//
// The SDK Reference, Volume 2 describes these data structures.
//
// A number of functions in this module were lifted from SHOWDIB,
// and modified to handle OS/2 DIBs.
//
// The functions in this module could be streamlined (made faster and
// smaller) by removing the OS/2 DIB specific code, and assuming all
// DIBs passed to it are Win30 style DIBs.  The DIB file reading code
// would need to be modified to always convert DIBs to Win30 style
// DIBs.  The only reason this isn't done in DIBView is because DIBView
// was written to test display and printer drivers (which are supposed
// to support OS/2 DIBs wherever they support Win30 style DIBs).  SHOWDIB
// is a great example of how to go about doing this.
//-----------------------------------------------------------------------------

#include <windows.h>
#include <memory.h>
#include "errors.h"
#include "dib.h"

//---------------------------------------------------------------------
//
// Function:   FindDIBBits
//
// Purpose:    Given a pointer to a DIB, returns a pointer to the
//             DIB's bitmap bits.
//
// Parms:      lpbi == pointer to DIB header (either BITMAPINFOHEADER
//                       or BITMAPCOREHEADER)
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

LPSTR FAR PASCAL FindDIBBits (LPSTR lpbi)
{
   return (lpbi + *(LPDWORD)lpbi + PaletteSize (lpbi));
}

//---------------------------------------------------------------------
//
// Function:   DIBNumColors
//
// Purpose:    Given a pointer to a DIB, returns a number of colors in
//             the DIB's color table.
//
// Parms:      lpbi == pointer to DIB header (either BITMAPINFOHEADER
//                       or BITMAPCOREHEADER)
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

WORD FAR PASCAL DIBNumColors (LPSTR lpbi)
{
   WORD wBitCount;


      // If this is a Windows style DIB, the number of colors in the
      //  color table can be less than the number of bits per pixel
      //  allows for (i.e. lpbi->biClrUsed can be set to some value).
      //  If this is the case, return the appropriate value.

   if (IS_WIN30_DIB (lpbi))
      {
      DWORD dwClrUsed;

      dwClrUsed = ((LPBITMAPINFOHEADER) lpbi)->biClrUsed;

      if (dwClrUsed)
         return (WORD) dwClrUsed;
      }


      // Calculate the number of colors in the color table based on
      //  the number of bits per pixel for the DIB.

   if (IS_WIN30_DIB (lpbi))
      wBitCount = ((LPBITMAPINFOHEADER) lpbi)->biBitCount;
   else
      wBitCount = ((LPBITMAPCOREHEADER) lpbi)->bcBitCount;

   switch (wBitCount)
      {
      case 1:
         return 2;

      case 4:
         return 16;

      case 8:
         return 256;

      default:
         return 0;
      }
}

//---------------------------------------------------------------------
//
// Function:   PaletteSize
//
// Purpose:    Given a pointer to a DIB, returns number of bytes
//             in the DIB's color table.
//
// Parms:      lpbi == pointer to DIB header (either BITMAPINFOHEADER
//                       or BITMAPCOREHEADER)
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

WORD FAR PASCAL PaletteSize (LPSTR lpbi)
{
   if (IS_WIN30_DIB (lpbi))
      return (DIBNumColors (lpbi) * sizeof (RGBQUAD));
   else
      return (DIBNumColors (lpbi) * sizeof (RGBTRIPLE));
}

//---------------------------------------------------------------------
//
// Function:   CreateDIBPalette
//
// Purpose:    Given a handle to a DIB, constructs a logical palette,
//             and returns a handle to this palette.
//
//             Stolen almost verbatim from ShowDIB.
//
// Parms:      hDIB == HANDLE to global memory with a DIB header
//                     (either BITMAPINFOHEADER or BITMAPCOREHEADER)
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

HPALETTE FAR PASCAL CreateDIBPalette (HANDLE hDIB)
{
   LPLOGPALETTE     lpPal;
   HANDLE           hLogPal;
   HPALETTE         hPal = NULL;
   int              i, wNumColors;
   LPSTR            lpbi;
   LPBITMAPINFO     lpbmi;
   LPBITMAPCOREINFO lpbmc;
   BOOL             bWinStyleDIB;

   if (!hDIB)
      return NULL;

   lpbi         = GlobalLock (hDIB);
   lpbmi        = (LPBITMAPINFO) lpbi;
   lpbmc        = (LPBITMAPCOREINFO) lpbi;
   wNumColors   = DIBNumColors (lpbi);
   bWinStyleDIB = IS_WIN30_DIB (lpbi);

   if (wNumColors)
      {
      hLogPal = GlobalAlloc (GHND, sizeof (LOGPALETTE) +
                             sizeof (PALETTEENTRY) * wNumColors);

      if (!hLogPal)
         {
         DIBError (ERR_CREATEPAL);
         GlobalUnlock (hDIB);
         return NULL;
         }

      lpPal = (LPLOGPALETTE) GlobalLock (hLogPal);

      lpPal->palVersion    = PALVERSION;
      lpPal->palNumEntries = wNumColors;

      for (i = 0;  i < wNumColors;  i++)
         {
         if (bWinStyleDIB)
            {
            lpPal->palPalEntry[i].peRed   = lpbmi->bmiColors[i].rgbRed;
            lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen;
            lpPal->palPalEntry[i].peBlue  = lpbmi->bmiColors[i].rgbBlue;
            lpPal->palPalEntry[i].peFlags = 0;
            }
         else
            {
            lpPal->palPalEntry[i].peRed   = lpbmc->bmciColors[i].rgbtRed;
            lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen;
            lpPal->palPalEntry[i].peBlue  = lpbmc->bmciColors[i].rgbtBlue;
            lpPal->palPalEntry[i].peFlags = 0;
            }
         }

      hPal = CreatePalette (lpPal);

      if (!hPal)
         DIBError (ERR_CREATEPAL);

      GlobalUnlock (hLogPal);
      GlobalFree   (hLogPal);
   }

   GlobalUnlock (hDIB);

   return hPal;
}

//---------------------------------------------------------------------
//
// Function:   DIBHeight
//
// Purpose:    Given a pointer to a DIB, returns its height.  Note
//             that it returns a DWORD (since a Win30 DIB can have
//             a DWORD in its height field), but under Win30, the
//             high order word isn't used!
//
// Parms:      lpDIB == pointer to DIB header (either BITMAPINFOHEADER
//                       or BITMAPCOREHEADER)
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

DWORD FAR PASCAL DIBHeight (LPSTR lpDIB)
{
   LPBITMAPINFOHEADER lpbmi;
   LPBITMAPCOREHEADER lpbmc;

   lpbmi = (LPBITMAPINFOHEADER) lpDIB;
   lpbmc = (LPBITMAPCOREHEADER) lpDIB;

   if (lpbmi->biSize == sizeof (BITMAPINFOHEADER))
      return lpbmi->biHeight;
   else
      return (DWORD) lpbmc->bcHeight;
}

//---------------------------------------------------------------------
//
// Function:   DIBWidth
//
// Purpose:    Given a pointer to a DIB, returns its width.  Note
//             that it returns a DWORD (since a Win30 DIB can have
//             a DWORD in its width field), but under Win30, the
//             high order word isn't used!
//
// Parms:      lpDIB == pointer to DIB header (either BITMAPINFOHEADER
//                       or BITMAPCOREHEADER)
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

DWORD FAR PASCAL DIBWidth (LPSTR lpDIB)
{
   LPBITMAPINFOHEADER lpbmi;
   LPBITMAPCOREHEADER lpbmc;

   lpbmi = (LPBITMAPINFOHEADER) lpDIB;
   lpbmc = (LPBITMAPCOREHEADER) lpDIB;

   if (lpbmi->biSize == sizeof (BITMAPINFOHEADER))
      return lpbmi->biWidth;
   else
      return (DWORD) lpbmc->bcWidth;
}

//---------------------------------------------------------------------
//
// Function:   DIBLoad
//
// Purpose:    Given a pointer to a DIB filename, will open that file
//             and read the DIB data into a globally allocated memory
//             block.
//
// Parms:      lpszDIBName == pointer to a string containing the name
//                (any valid DOS filename) of the DIB file
//
// Returns:    Will return a handle to the global memory block if
//			   successful.  Will return NULL otherwise.
//
// History:    Date     Reason
//             3/10/92  Created
//
//---------------------------------------------------------------------

HANDLE FAR PASCAL DIBLoad (LPSTR lpszDIBName)
{
    unsigned           fh;
    LPBITMAPINFOHEADER lpbi;
    OFSTRUCT           of;
    BITMAPFILEHEADER   bf;
    WORD               nNumColors;
	WORD			   offBits;
    HANDLE			   hDIBInfo = NULL;
    char *			   szMsg;
    HANDLE             result = NULL;         /* assume failure */

    /* Open the file and get a handle to it's BITMAPINFO */

    hDIBInfo = GlobalAlloc(GMEM_MOVEABLE,
	               (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)));
	if (hDIBInfo == NULL) {
		wsprintf((LPSTR) szMsg, "Not enough memory to load '%s'", lpszDIBName);
        MessageBox((HWND) NULL, (LPSTR) szMsg, "Error", MB_ICONSTOP | MB_OK);
		return (NULL);
	}

    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIBInfo);

    fh = OpenFile (lpszDIBName, &of, OF_READ);
    if (fh == -1) {
        wsprintf((LPSTR) szMsg, "Can't open file '%s'", lpszDIBName);
        MessageBox((HWND) NULL, (LPSTR) szMsg, "Error", MB_ICONSTOP | MB_OK);
        return (NULL);
    }

    /* read the BITMAPFILEHEADER */
    if (sizeof (bf) != _lread (fh, (LPSTR)&bf, sizeof (bf)))
        goto ErrExit;

    if (bf.bfType != 0x4d42)    /* 'BM' */
        goto ErrExit;

    if (sizeof(BITMAPINFOHEADER) != _lread (fh, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER)))
        goto ErrExit;

    /* don't even deal with OS/2 bitmaps */
    if (!IS_WIN30_DIB(lpbi)) {
		wsprintf((LPSTR) szMsg, "OS/2 bitmaps (%s) not supported!", lpszDIBName);
        MessageBox((HWND) NULL, (LPSTR) szMsg, "Error", MB_ICONSTOP | MB_OK);
        goto ErrExit;
    }

    if (!(nNumColors = (WORD)lpbi->biClrUsed))
    {
        /* no color table for 24-bit, default size otherwise */
        if (lpbi->biBitCount != 24)
            nNumColors = 1 << lpbi->biBitCount; /* standard size table */
    }

    /*  fill in some default values if they are zero */
    if (lpbi->biClrUsed == 0)
        lpbi->biClrUsed = (DWORD)nNumColors;

    if (lpbi->biSizeImage == 0)
    {
        lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3)
                         * lpbi->biHeight;
    }

    /* get a proper-sized buffer for header, color table and bits */
    GlobalUnlock(hDIBInfo);
    hDIBInfo = GlobalReAlloc(hDIBInfo, lpbi->biSize +
                                        nNumColors * sizeof(RGBQUAD) +
                                        lpbi->biSizeImage, 0);
    if (!hDIBInfo) {     /* can't resize buffer for loading */
		wsprintf((LPSTR) szMsg, "Not enough memory to load '%s'", lpszDIBName);
        MessageBox((HWND) NULL, (LPSTR) szMsg, "Error", MB_ICONSTOP | MB_OK);
        goto ErrExit;
    }

    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIBInfo);

    /* read the color table */
    _lread (fh, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD));

    /* offset to the bits from start of DIB header */
    offBits = (WORD)lpbi->biSize + nNumColors * sizeof(RGBQUAD);

    if (bf.bfOffBits != 0L)
    {
        _llseek(fh,bf.bfOffBits, SEEK_SET);
    }
    if (lpbi->biSizeImage == lread(fh, (LPSTR)lpbi + offBits, lpbi->biSizeImage))
        result = hDIBInfo;

ErrExit:
    _lclose(fh);
    GlobalUnlock(hDIBInfo);
    return(result);
}

/**************** PRIVATE ROUTINE TO READ MORE THAN 64K *********************/
/****************************************************************************
 *                                                                          *
 *  FUNCTION   : lread(int fh, VOID FAR *pv, DWORD ul)                      *
 *                                                                          *
 *  PURPOSE    : Reads data in steps of 32k till all the data has been read.*
 *                                                                          *
 *  RETURNS    : 0 - If read did not proceed correctly.                     *
 *               number of bytes read otherwise.                            *
 *                                                                          *
 ****************************************************************************/
DWORD PASCAL lread (int fh, VOID far *pv, DWORD ul)
{
    DWORD     ulT = ul;
    BYTE huge *hp = pv;

    while (ul > (DWORD)MAXREAD) {
        if (_lread(fh, (LPSTR)hp, (WORD)MAXREAD) != MAXREAD)
                return 0;
        ul -= MAXREAD;
        hp += MAXREAD;
    }
    if (_lread(fh, (LPSTR)hp, (WORD)ul) != (WORD)ul)
        return 0;
    return ulT;
}
