//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       bmpfile.cxx
//
//  Contents:   CBitmapFile implementation
//
//  Classes:
//
//  Functions:
//
//  History:    4-23-94   KirtD   Created
//
//----------------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::CBitmapFile
//
//  Synopsis:   Constructor
//
//  Arguments:  (none)
//
//  Returns:    nothing
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
CBitmapFile::CBitmapFile ()
{
     //
     // Initialize private members
     //

     //
     // The name
     //

     _pszBitmapFile[0] = '\0';
     _cBitmapFile = 0;

     //
     // The bitmap information
     //

     _cbi = 0;
     _pbi = NULL;

     //
     // The bits
     //

     _cbData = 0;
     _pbData = NULL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::~CBitmapFile
//
//  Synopsis:   Destructor
//
//  Arguments:  (none)
//
//  Returns:    nothing
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
CBitmapFile::~CBitmapFile ()
{
     //
     // Delete any possibly allocated things
     //

     delete _pbi;
     delete _pbData;
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::LoadBitmapFile
//
//  Synopsis:   loads a bitmap file
//
//  Arguments:  [pszFile] -- the file
//
//  Returns:    HRESULT
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
HRESULT
CBitmapFile::LoadBitmapFile (LPSTR pszFile)
{
     HRESULT hr = ResultFromScode(S_OK);
     HANDLE  hFile = INVALID_HANDLE_VALUE;
     HANDLE  hMap = INVALID_HANDLE_VALUE;
     DWORD   dwFileSizeLow = 0;
     DWORD   dwFileSizeHigh = 0;
     LPBYTE  pbuffer = NULL;

     //
     // First open the file
     //

     hFile = CreateFileA(pszFile,
                         GENERIC_READ,
                         FILE_SHARE_READ,
                         NULL,
                         OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL);

     if (hFile == INVALID_HANDLE_VALUE)
     {
          hr = HRESULT_FROM_WIN32(GetLastError());
     }

     //
     // Get the size of the file
     //

     if (SUCCEEDED(hr))
     {
          dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
          if ((dwFileSizeLow == 0xFFFFFFFF) && (GetLastError() != NO_ERROR))
          {
               hr = HRESULT_FROM_WIN32(GetLastError());
          }
          else if (dwFileSizeHigh != 0)
          {
               //
               // Bitmap files can't be greater than 4G
               //

               hr = ResultFromScode(E_FAIL);
          }
     }

     //
     // Create a file mapping object on the file
     //

     if (SUCCEEDED(hr))
     {
          hMap = CreateFileMapping(hFile,
                                   NULL,
                                   PAGE_READONLY,
                                   0,
                                   dwFileSizeLow,
                                   NULL);

          if (hMap == INVALID_HANDLE_VALUE)
          {
               hr = HRESULT_FROM_WIN32(GetLastError());
          }
     }

     //
     // Now map a view of the file into our address space
     //

     if (SUCCEEDED(hr))
     {
          pbuffer = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
          if (pbuffer == NULL)
          {
               hr = HRESULT_FROM_WIN32(GetLastError());
          }
     }

     //
     // Get the bitmap data from the buffer
     //

     if (SUCCEEDED(hr))
     {
          hr = _GetBitmapDataFromBuffer(pbuffer, (ULONG)dwFileSizeLow);
     }

     //
     // Record the file name
     //

     if (SUCCEEDED(hr))
     {
          ULONG cFile;

          //
          // Get the length of the file name
          //

          cFile = strlen(pszFile);

          //
          // Check to see that our buffer is big enough and then copy
          // it.  NOTE that I can use sizeof here since the buffer is
          // in characters which are 1 byte.
          //

          if (cFile < sizeof(_pszBitmapFile))
          {
               strcpy(_pszBitmapFile, pszFile);
               _cBitmapFile = cFile;
          }
          else
          {
               hr = ResultFromScode(E_FAIL);
          }
     }

     //
     // Cleanup
     //

     if (hMap != INVALID_HANDLE_VALUE)
     {
          CloseHandle(hMap);
     }

     if (hFile != INVALID_HANDLE_VALUE)
     {
          CloseHandle(hFile);
     }

     return(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::GetBitmapFileName
//
//  Synopsis:   gets the file name used to set the bitmap
//
//  Arguments:  [pszFile] -- the file
//              [cChar]   -- the length of the buffer in characters
//
//  Returns:    HRESULT
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
HRESULT
CBitmapFile::GetBitmapFileName (LPSTR pszFile, ULONG cChar) const
{
     //
     // Check the length of the receiving buffer, making sure the buffer size
     // includes the null terminator
     //

     if (cChar <= _cBitmapFile)
     {
          return(ResultFromScode(E_INVALIDARG));
     }

     //
     // Copy the string
     //

     strcpy(pszFile, _pszBitmapFile);

     return(ResultFromScode(S_OK));
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::GetBitmapFileNameLength
//
//  Synopsis:   returns _cBitmapFile
//
//  Arguments:  (none)
//
//  Returns:    ULONG
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
ULONG
CBitmapFile::GetBitmapFileNameLength () const
{
     return(_cBitmapFile);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::GetDIBHeight
//
//  Synopsis:   gets the height in pixels of the DIB
//
//  Arguments:  (none)
//
//  Returns:    LONG
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
LONG
CBitmapFile::GetDIBHeight () const
{
     if (_pbi)
     {
          return(_pbi->bmiHeader.biHeight);
     }
     else
     {
          return(0);
     }
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::GetDIBWidth
//
//  Synopsis:   gets the width in pixels of the DIB
//
//  Arguments:  (none)
//
//  Returns:    LONG
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
LONG
CBitmapFile::GetDIBWidth () const
{
     if (_pbi)
     {
          return(_pbi->bmiHeader.biWidth);
     }
     else
     {
          return(0);
     }
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::GetLogicalPalette
//
//  Synopsis:   gets the logical palette from the DIB
//
//  Arguments:  [pplogpal] -- logical palette goes here
//
//  Returns:    HRESULT
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
HRESULT
CBitmapFile::GetLogicalPalette (LPLOGPALETTE *pplogpal) const
{
     HRESULT    hr = ResultFromScode(S_OK);
     LOGPALETTE *plogpal = NULL;
     WORD       cPalEntry;
     LPMALLOC   pMalloc = NULL;

     //
     // First check to see if we have been initialized with a bitmap
     //

     if (_pbi == NULL)
     {
          return(ResultFromScode(E_FAIL));
     }

     //
     // Check to see if this bit count allows a palette
     //

     if (HasPaletteData() == FALSE)
     {
          return(ResultFromScode(E_FAIL));
     }

     //
     // NOTE: We are about to get the number of palette entries we
     //       need to allocate, we do NOT use biClrUsed since we
     //       know that if this field was set the bitmap would
     //       not have been loaded, see BUGBUG in
     //       _GetBitmapDataFromBuffer notes section.  This is
     //       probably a good candidate for an assert.
     //

     //
     // Get the palette entries
     //

     cPalEntry = (WORD) (1 << _pbi->bmiHeader.biBitCount);

     //
     // Allocate a LOGPALETTE
     //

     hr = CoGetMalloc(MEMCTX_TASK, &pMalloc);
     if (SUCCEEDED(hr))
     {
          plogpal = (LOGPALETTE *)pMalloc->Alloc(sizeof(LOGPALETTE)+
                                                 ((cPalEntry-1)*
                                                 sizeof(PALETTEENTRY)));

          if (plogpal == NULL)
          {
               hr = ResultFromScode(E_OUTOFMEMORY);
          }
     }

     //
     // Copy the palette information
     //

     if (SUCCEEDED(hr))
     {
          ULONG cCount;

          plogpal->palVersion = 0x300;
          plogpal->palNumEntries = cPalEntry;

          for (cCount = 0; cCount < cPalEntry; cCount++)
          {
               plogpal->palPalEntry[cCount].peRed = _pbi->bmiColors[cCount].rgbRed;
               plogpal->palPalEntry[cCount].peGreen = _pbi->bmiColors[cCount].rgbGreen;
               plogpal->palPalEntry[cCount].peBlue = _pbi->bmiColors[cCount].rgbBlue;
               plogpal->palPalEntry[cCount].peFlags = PC_NOCOLLAPSE;
          }

          *pplogpal = plogpal;
     }

     //
     // Cleanup
     //

     if (pMalloc)
     {
          pMalloc->Release();
     }

     return(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::CreateDIBInHGlobal
//
//  Synopsis:   creates a DIB i.e. info and data in a GlobalAlloc'd buffer
//
//  Arguments:  [phGlobal] -- handle goes here
//
//  Returns:    HRESULT
//
//  History:    5-06-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
HRESULT
CBitmapFile::CreateDIBInHGlobal (HGLOBAL *phGlobal) const
{
     HRESULT hr = ResultFromScode(S_OK);
     ULONG   cb = 0;
     LPBYTE  pb = NULL;
     HGLOBAL hGlobal = NULL;
     ULONG   cbPalEntry = 0;

     //
     // Check to see if we are initialized
     //

     if (_pbi == NULL)
     {
          return(ResultFromScode(E_FAIL));
     }

     //
     // The size to allocate for the data must be calculated
     // from the following ...
     //

     //
     // The size of the bitmap info plus the size of the data
     //

     cb = _cbi + _cbData;

     //
     // Allocate
     //

     hGlobal = GlobalAlloc(GHND, cb);
     if (hGlobal == NULL)
     {
          hr = HRESULT_FROM_WIN32(GetLastError());
     }

     //
     // Lock the handle
     //

     if (SUCCEEDED(hr))
     {
          pb = (LPBYTE)GlobalLock(hGlobal);
          if (pb == NULL)
          {
               hr = HRESULT_FROM_WIN32(GetLastError());
          }
     }

     //
     // Copy the information
     //

     if (SUCCEEDED(hr))
     {
          //
          // First the bitmap info
          //

          memcpy(pb, _pbi, _cbi);

          //
          // Move pointer but if there is no palette compensate
          // for the extra RGBQUAD
          //

          if (_pbi->bmiHeader.biBitCount == BMP_24_BITSPERPIXEL)
          {
               pb += (_cbi - sizeof(RGBQUAD));
          }
          else
          {
               pb += _cbi;
          }

          //
          // Now the bits
          //

          memcpy(pb, _pbData, _cbData);
     }

     //
     // Cleanup
     //

     //
     // If we locked the handle, unlock it
     //

     if (pb)
     {
          GlobalUnlock(hGlobal);
     }

     //
     // If we succeeded, set the out parameter, if we failed and
     // we had allocated the hGlobal, free it
     //

     if (SUCCEEDED(hr))
     {
          *phGlobal = hGlobal;
     }
     else if (hGlobal)
     {
          GlobalFree(hGlobal);
     }

     return(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::HasPaletteData
//
//  Synopsis:   returns whether or not the dib has palette data
//
//  Arguments:  (none)
//
//  Returns:    BOOL
//
//  History:    5-16-94   kirtd   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
CBitmapFile::HasPaletteData () const
{
     //
     // If we are not initialized return FALSE
     //

     if (_pbi == NULL)
     {
          return(FALSE);
     }

     //
     // If we are a 24, 16 or 32 bpp DIB then we do not have
     // palette data
     //
     // BUGBUG: The case where biClrUsed is set is not dealt
     //         with
     //

     if ((_pbi->bmiHeader.biBitCount == BMP_24_BITSPERPIXEL) ||
         (_pbi->bmiHeader.biBitCount == BMP_16_BITSPERPIXEL) ||
         (_pbi->bmiHeader.biBitCount == BMP_32_BITSPERPIXEL))
     {
          return(FALSE);
     }

     //
     // Otherwise we do have palette data
     //

     return(TRUE);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::GetDIBBits
//
//  Synopsis:   gets the bits
//
//  Arguments:  (none)
//
//  Returns:    LPBYTE
//
//  History:    5-02-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
LPBYTE
CBitmapFile::GetDIBBits ()
{
     return(_pbData);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::GetBitmapInfo
//
//  Synopsis:   gets the bitmap info pointer
//
//  Arguments:  (none)
//
//  Returns:    LPBITMAPINFO
//
//  History:    5-14-94   kirtd   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
LPBITMAPINFO
CBitmapFile::GetBitmapInfo ()
{
     return(_pbi);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::_GetBitmapDataFromBuffer
//
//  Synopsis:   gets the bitmap data from the given buffer
//
//  Arguments:  [pbuffer] -- the buffer
//              [cb]      -- the buffer size
//
//  Returns:    HRESULT
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:      BUGBUG: If biClrUsed is set the bitmap is not loaded
//
//----------------------------------------------------------------------------
HRESULT
CBitmapFile::_GetBitmapDataFromBuffer (LPBYTE pbuffer, ULONG cb)
{
     HRESULT          hr = ResultFromScode(S_OK);
     BITMAPFILEHEADER bmfh;
     BITMAPCOREHEADER *pbch;
     BITMAPINFOHEADER bih;
     LPBYTE           pbStart;
     ULONG            cbi = 0;
     ULONG            cbData;
     LPBITMAPINFO     pbi = NULL;
     LPBYTE           pbData = NULL;
     DWORD            dwSizeOfHeader;

     //
     // Record the starting position
     //

     pbStart = pbuffer;

     //
     // First validate the buffer for size
     //

     if (cb < sizeof(BITMAPFILEHEADER))
     {
          return(ResultFromScode(E_FAIL));
     }

     //
     // Now get the bitmap file header
     //

     memcpy(&bmfh, pbuffer, sizeof(BITMAPFILEHEADER));

     //
     // Validate the information
     //

     hr = _ValidateBitmapFileHeader (&bmfh, cb);

     //
     // Get the next 4 bytes which will represent the size of the
     // next structure and allow us to determine the type
     //

     if (SUCCEEDED(hr))
     {
          pbuffer += sizeof(BITMAPFILEHEADER);
          memcpy(&dwSizeOfHeader, pbuffer, sizeof(DWORD));

          if (dwSizeOfHeader == sizeof(BITMAPCOREHEADER))
          {
               pbch = (BITMAPCOREHEADER *)pbuffer;
               memset(&bih, 0, sizeof(BITMAPINFOHEADER));

               bih.biSize = sizeof(BITMAPINFOHEADER);
               bih.biWidth = pbch->bcWidth;
               bih.biHeight = pbch->bcHeight;
               bih.biPlanes = pbch->bcPlanes;
               bih.biBitCount = pbch->bcBitCount;

               pbuffer += sizeof(BITMAPCOREHEADER);
          }
          else if (dwSizeOfHeader == sizeof(BITMAPINFOHEADER))
          {
               memcpy(&bih, pbuffer, sizeof(BITMAPINFOHEADER));

               pbuffer += sizeof(BITMAPINFOHEADER);
          }
          else
          {
               hr = ResultFromScode(E_FAIL);
          }
     }

     //
     // Check if biClrUsed is set since we do not handle that
     // case at this time
     //

     if (SUCCEEDED(hr))
     {
          if (bih.biClrUsed != 0)
          {
               hr = ResultFromScode(E_FAIL);
          }
     }

     //
     // Now we need to calculate the size of the BITMAPINFO we need
     // to allocate including any palette information
     //

     if (SUCCEEDED(hr))
     {
          //
          // First the size of the header
          //

          cbi = sizeof(BITMAPINFOHEADER);

          //
          // Now the palette
          //

          if (bih.biBitCount == BMP_24_BITSPERPIXEL)
          {
               //
               // Just add on the 1 RGBQUAD for the structure but
               // there is no palette
               //

               cbi += sizeof(RGBQUAD);
          }
          else if ((bih.biBitCount == BMP_16_BITSPERPIXEL) ||
                   (bih.biBitCount == BMP_32_BITSPERPIXEL))
          {
               //
               // Add on the 3 DWORD masks which are used to
               // get the colors out of the data
               //

               cbi += (3 * sizeof(DWORD));
          }
          else
          {
               //
               // Anything else we just use the bit count to calculate
               // the number of entries
               //

               cbi += ((1 << bih.biBitCount) * sizeof(RGBQUAD));
          }

          //
          // Now allocate the BITMAPINFO
          //

          pbi = (LPBITMAPINFO) new BYTE [cbi];
          if (pbi == NULL)
          {
               hr = ResultFromScode(E_OUTOFMEMORY);
          }
     }

     //
     // Fill in the BITMAPINFO data structure and get the bits
     //

     if (SUCCEEDED(hr))
     {
          //
          // First copy the header data
          //

          memcpy(&(pbi->bmiHeader), &bih, sizeof(BITMAPINFOHEADER));

          //
          // Now the palette data
          //

          if (bih.biBitCount == BMP_24_BITSPERPIXEL)
          {
               //
               // No palette data to copy
               //
          }
          else if ((bih.biBitCount == BMP_16_BITSPERPIXEL) ||
                   (bih.biBitCount == BMP_32_BITSPERPIXEL))
          {
               //
               // Copy the 3 DWORD masks
               //

               memcpy(&(pbi->bmiColors), pbuffer, 3*sizeof(DWORD));
          }
          else
          {
               //
               // If we were a BITMAPCOREHEADER type then we have our
               // palette data in the form of RGBTRIPLEs so we must
               // explicitly copy each.  Otherwise we can just memcpy
               // the RGBQUADs
               //

               if (dwSizeOfHeader == sizeof(BITMAPCOREHEADER))
               {
                    ULONG     cPalEntry = (1 << bih.biBitCount);
                    ULONG     cCount;
                    RGBTRIPLE *argbt = (RGBTRIPLE *)pbuffer;

                    for (cCount = 0; cCount < cPalEntry; cCount++)
                    {
                         pbi->bmiColors[cCount].rgbRed =
                                            argbt[cCount].rgbtRed;
                         pbi->bmiColors[cCount].rgbGreen =
                                            argbt[cCount].rgbtGreen;
                         pbi->bmiColors[cCount].rgbBlue =
                                            argbt[cCount].rgbtBlue;

                         pbi->bmiColors[cCount].rgbReserved = 0;
                    }
               }
               else
               {
                    ULONG cbPalette = (1 << bih.biBitCount) * sizeof(RGBQUAD);

                    memcpy(&(pbi->bmiColors), pbuffer, cbPalette);
               }
          }

          //
          // Now find out where the bits are
          //

          pbuffer = pbStart + bmfh.bfOffBits;

          //
          // Get the size to copy
          //

          cbData = cb - bmfh.bfOffBits;

          //
          // Allocate the buffer to hold the bits
          //

          pbData = new BYTE [cbData];
          if (pbData == NULL)
          {
               hr = ResultFromScode(E_OUTOFMEMORY);
          }

          if (SUCCEEDED(hr))
          {
               memcpy(pbData, pbuffer, cbData);
          }
     }

     //
     // If everything succeeded record the data
     //

     if (SUCCEEDED(hr))
     {
          //
          // Record the info
          //

          delete _pbi;
          _cbi = cbi;
          _pbi = pbi;

          //
          // Record the data
          //

          delete _pbData;
          _cbData = cbData;
          _pbData = pbData;
     }
     else
     {
          //
          // Cleanup
          //

          delete pbi;
          delete pbData;
     }

     return(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CBitmapFile::_ValidateBitmapFileHeader
//
//  Synopsis:   validates a bitmap file header
//
//  Arguments:  [pbmfh]  -- bitmap file header
//              [cbFile] -- bitmap file size
//
//  Returns:    HRESULT
//
//  History:    4-23-94   KirtD   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
HRESULT
CBitmapFile::_ValidateBitmapFileHeader (BITMAPFILEHEADER *pbmfh, ULONG cbFile)
{
     //
     // Check for the following,
     //
     // 1. The bfType member contains 'BM'
     // 2. The bfOffset member is NOT greater than the size of the file
     //

     if ((pbmfh->bfType == 0x4d42) && (pbmfh->bfOffBits <= cbFile))
     {
          return(ResultFromScode(S_OK));
     }

     return(ResultFromScode(E_FAIL));
}