#include <windows.h>
#include <ctype.h>

#define DSPDxax   0x00E20746L

#define RGBBLACK     RGB(0,0,0)
#define RGBWHITE     RGB(255,255,255)

VOID FAR PASCAL ChangeBitmapColorDC (HDC hdcBM, LPBITMAP lpBM,
                                     COLORREF rgbOld, COLORREF rgbNew);



/*************************************************************************
 *
 * ChangeBitmapColor()
 *
 *    This function translates the colors in a bitmap from one
 *    to another.
 *
 * Parameters:
 *
 *    HBITMAP hbmSrc  - Bitmap to process
 *    COLORREF rgbOld - Source color
 *    COLORREF rgbNew - Destination color
 *
 * Return value: none.
 *
 * History:   Date      Author      Reason
 *            6/10/91   CKindel     Created
 *            1/28/92   MarkBad     Split "Graying" functionality out into
 *                                    separate API, added palette param
 *
 *************************************************************************/

VOID ChangeBitmapColor (HBITMAP hbmSrc, COLORREF rgbOld,
                                   COLORREF rgbNew)
{
   HDC hDC;
   HDC hdcMem;
   BITMAP bmBits;

   if (hDC = GetDC(NULL))
   {
      if (hdcMem = CreateCompatibleDC(hDC))
      {
      //
      // Get the bitmap struct needed by ChangeBitmapColorDC()
      //
         GetObject(hbmSrc, sizeof(BITMAP), (LPSTR)&bmBits);

         //
         // Select our bitmap into the memory DC
         //
         hbmSrc = SelectObject(hdcMem, hbmSrc);

         //
         // Translate the sucker
         //
         ChangeBitmapColorDC(hdcMem, &bmBits, rgbOld, rgbNew);

         //
         // Unselect our bitmap before deleting the DC
         //
         hbmSrc = SelectObject(hdcMem, hbmSrc);

         DeleteDC(hdcMem);
      }

      ReleaseDC(NULL, hDC);
   }
}                  /* ChangeBitmapColor()  */


/*************************************************************************
 *
 * ChangeBitmapColorDC()
 *
 * This function makes all pixels in the given DC that have the
 * color rgbOld have the color rgbNew.  This function is used by
 * ChangeBitmapColor().
 *
 * Parameters:
 *
 * HDC hdcBM        - Memory DC containing bitmap
 * LPBITMAP lpBM    - Long pointer to bitmap structure from hdcBM
 * COLORREF rgbOld  - Source color
 * COLORREF rgbNew  - Destination color
 *
 * Return value: none.
 *
 * History:   Date      Author      Reason
 *            6/10/91   CKindel     Created
 *            1/23/92   MarkBad     Added big nifty comments which explain
 *                                  how this works, split bitmap graying 
 *                                  code out
 *
 *************************************************************************/

VOID FAR PASCAL ChangeBitmapColorDC (HDC hdcBM, LPBITMAP lpBM,
                                     COLORREF rgbOld, COLORREF rgbNew)
{
   HDC hdcMask;
   HBITMAP hbmMask, hbmOld;
   HBRUSH hbrOld;

   if (!lpBM)
      return;

   //
   // if the bitmap is mono we have nothing to do
   //

   if (lpBM->bmPlanes == 1 && lpBM->bmBitsPixel == 1)
      return;

   //
   // To perform the color switching, we need to create a monochrome 
   // "mask" which is the same size as our color bitmap, but has all 
   // pixels which match the old color (rgbOld) in the bitmap set to 1.  
   // 
   // We then use the ROP code "DSPDxax" to Blt our monochrome 
   // bitmap to the color bitmap.  "D" is the Destination color 
   // bitmap, "S" is the source monochrome bitmap, and "P" is the 
   // selected brush (which is set to the replacement color (rgbNew)).  
   // "x" and "a" represent the XOR and AND operators, respectively.
   // 
   // The DSPDxax ROP code can be explained as having the following 
   // effect:
   // 
   // "Every place the Source bitmap is 1, we want to replace the 
   // same location in our color bitmap with the new color.  All 
   // other colors we leave as is."
   // 
   // The truth table for DSPDxax is as follows:
   // 
   //       D S P Result
   //       - - - ------
   //       0 0 0   0
   //       0 0 1   0
   //       0 1 0   0
   //       0 1 1   1
   //       1 0 0   1
   //       1 0 1   1
   //       1 1 0   0
   //       1 1 1   1
   // 
   // (Even though the table is assuming monochrome D (Destination color), 
   // S (Source color), & P's (Pattern color), the results apply to color 
   // bitmaps also). 
   //
   // By examining the table, every place that the Source is 1 
   // (source bitmap contains a 1), the result is equal to the 
   // Pattern at that location.  Where S is zero, the result equals 
   // the Destination.  
   // 
   // See Section 11.2 (page 11-4) of the "Reference -- Volume 2" for more
   // information on the Termary Raster Operation codes.
   //

   if (hbmMask = CreateBitmap(lpBM->bmWidth, lpBM->bmHeight, 1, 1, NULL))
   {
      if (hdcMask = CreateCompatibleDC(hdcBM))
      {
      //
      // Select th mask bitmap into the mono DC
      //
         hbmOld = SelectObject(hdcMask, hbmMask);

         // 
         // Create the brush and select it into the source color DC --
         // this is our "Pattern" or "P" color in our DSPDxax ROP.
         //

         hbrOld = SelectObject(hdcBM, CreateSolidBrush(rgbNew));

         // 
         // To create the mask, we will use a feature of BitBlt -- when
         // converting from Color to Mono bitmaps, all Pixels of the
         // background colors are set to WHITE (1), and all other pixels
         // are set to BLACK (0).  So all pixels in our bitmap that are
         // rgbOld color, we set to 1.
         //

         SetBkColor(hdcBM, rgbOld);
         BitBlt(hdcMask, 0, 0, lpBM->bmWidth, lpBM->bmHeight,
                hdcBM, 0, 0, SRCCOPY);

         //
         // Where the mask is 1, lay down the brush, where it is 0, leave
         // the destination.
         //

         SetBkColor(hdcBM, RGBWHITE);
         SetTextColor(hdcBM, RGBBLACK);

         BitBlt(hdcBM, 0, 0, lpBM->bmWidth, lpBM->bmHeight,
                hdcMask, 0, 0, DSPDxax);

         SelectObject(hdcMask, hbmOld);

         hbrOld = SelectObject(hdcBM, hbrOld);
         DeleteObject(hbrOld);

         DeleteDC(hdcMask);
      }
      else
         return;

      DeleteObject(hbmMask);
   }
   else
      return;
}                  /* ChangeBitmapColorDC()  */
