/************************************************************************/
/*                                                                      */
/*     Copyright (c) 1992 Intel Corporation                             */
/*     All Rights Reserved                                              */
/*                                                                      */
/*     INTEL CORPORATION PROPRIETARY INFORMATION                        */
/*                                                                      */
/*     This software is supplied under the terms of a licence           */
/*     agreement with Intel Corporation and may not be copied nor       */
/*     disclosed except in accordance with the terms of that agreement. */
/*                                                                      */
/************************************************************************/

/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   HISTORY:

 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <string.h>
#include <dciddi.h>
#include "dcihdw.h"

// Type conversion
//      Canyon format  Intel format
#define BMP_NONE       NoColorSupport       // unknown type
#define BMP_DIB        NoColorSupport       // DIB
#define BMP_MONO       NoColorSupport       // monochrome
#define BMP_PACKED_4   NoColorSupport       // packed 4 bit, e.g., Fahrenheit
#define BMP_PLANAR_4   NoColorSupport       // VGA or SVGA
#define BMP_INDEX_8    RGB8_CLUT            // palettized driver
#define BMP_5_5_5      RGB16_555            // 32,768 colors
#define BMP_5_6_5      RGB16_565            // XGA, Intel order
#define BMP_PLANAR_16  NoColorSupport       // two planes of one byte each
#define BMP_8_8_8_RGB  RGB24_888            // true color RGB
#define BMP_8_8_8_BGR  NoColorSupport       // true color BGR
#define BMP_5_6_5_M    NoColorSupport       // XGA, Motorola order

// Local macros
#define MAKEWORD(lo, hi) ((WORD)(((BYTE)(lo)) | (((WORD)((BYTE)(hi))) << 8)))
#define SwapBytes(a) ((a >> 8) | (a << 8))

// Global data
extern GLOBALDATA g;

/* Local data
static const int PIXELSPERROW = 256         // for big DIB
               , NBRROWS = 5                // for big DIB
; */

#define PIXELSPERROW  256
#define NBRROWS       5

// Local functions
WORD LOCAL MyConvertBigDIB (HDC hdc,
                            LPVOID lpBMP,    // copy bitmap bits here
                            WORD wSizeBMP    // size of caller's area
                           );
WORD LOCAL MyConvertOneDIB (HDC hdc,
                            LPBITMAPINFO lpbi,
                            LPVOID lpBits,
                            LPVOID lpBMP,
                            WORD wSizeBMP
                           );
BOOL LOCAL MyValidateBGR (LPRGBTRIPLE prgbBMP, WORD wSize);
COLORSUPPORT LOCAL MyValidateIndexed8 (HDC hdc);
COLORSUPPORT LOCAL MyValidateMono (HDC hdc);
COLORSUPPORT LOCAL MyValidatePacked4 (HDC hdc);
BOOL LOCAL MyValidatePlanar16 (HDC hdc);
COLORSUPPORT LOCAL MyValidatePlanar4 (HDC hdc);
BOOL LOCAL MyValidateRGB (LPRGBTRIPLE prgbBMP, WORD wSize);
COLORSUPPORT LOCAL MyValidate16 (HDC hdc);
COLORSUPPORT LOCAL MyValidate24 (HDC hdc);
BOOL LOCAL MyValidate555 (PWORD pwBMP, WORD wSize);
BOOL LOCAL MyValidate565 (PWORD pwBMP, WORD wSize);
BOOL LOCAL MyValidate565M (PWORD pwBMP, WORD wSize);


/******************************************************************************
   FUNCTION:       ValidateBMP (HDC)

   See if we recognize the BMP format for this driver
 ******************************************************************************/
COLORSUPPORT ValidateBMP (HDC hdc)
{
	const UINT  uiStackSize = 12000;
	static      COLORSUPPORT csResult;
	static HDC  hdcSaved;
	HLOCAL 	    hlmem;
	NPSTR  	    npStack;

	
	// save hdc in static variable
	hdcSaved = hdc;

	// allocate heap space for this function's heavy stack requirements
	hlmem = LocalAlloc (LPTR, uiStackSize);
	if (!hlmem)         
	   return BMP_DIB;	// if LocalAlloc failed
	   
	npStack = (NPSTR) hlmem;
	SwitchStackTo (SELECTOROF (GlobalLock ((HGLOBAL) -1))
	             , npStack - (NPSTR) 0 + uiStackSize
	             , npStack - (NPSTR) 0 + 256  // 256 bytes for system calls
	              );

	// run the gauntlet
	if (g.iSizePalette != 0)                  // if palettized
	{
		if (g.iSizePalette == 256 && g.iBitsPerPixel == 8)
		   csResult = MyValidateIndexed8 (hdcSaved);
		else
		{
		   //assert ("Driver is palettized but depth not 8, not supported by BMPs");
		   csResult = BMP_DIB;
		}
		SwitchStackBack ();
		LocalFree (hlmem);
		return csResult;                       // return result to caller
	}  // if palettized

	// not palettized
	if (g.iPlanes > 1)                        // if planar organization
	{
		if (g.iPlanes == 4 && g.iBitsPerPixel == 1)        // if VGA format
			csResult = MyValidatePlanar4 (hdcSaved);
		else if (g.iPlanes == 2 && g.iBitsPerPixel == 8)   // if planar 16
		{
			if (MyValidatePlanar16 (hdcSaved) == FALSE)
			{
				//assert ("BMP validation of planar 16 failed");
				csResult = BMP_DIB;              // validation failed
			}
			else
				csResult = BMP_PLANAR_16;
		}
		else
		{
			//assert ("Driver type is planar but not VGA or 5-5-5, not supported by BMPs");
			csResult = BMP_DIB;
		}

		SwitchStackBack ();
		LocalFree (hlmem);
		return csResult;                       // other planar a mystery
	}

	// not palettized, single plane,
	// depth not 8
	switch (g.iBitsPerPixel)                  // switch on bits per pixel
	{
		case 1:                                // monochrome
			csResult = MyValidateMono (hdcSaved);
			break;
		case 4:                                // monochrome
			csResult = MyValidatePacked4 (hdcSaved);
			break;
		case 16:                               // 2, 3 or 4 bytes per pixel
			csResult = MyValidate16 (hdcSaved);
			break;
		case 24:
			csResult = MyValidate24 (hdcSaved);
			break;
		case 32:
			//assert ("Driver depth 32 not supported by BMPs");
			csResult = BMP_DIB;
			break;
		default:                               // anything else
			//assert ("Unknown driver depth");
			csResult = BMP_DIB;
			break;
	}

	// deallocate the temporary stack and return to caller
	SwitchStackBack ();
	LocalFree (hlmem);
	return csResult;
}  // ValidateBMP


/******************************************************************************
   FUNCTION:       MyConvertBigDIB (HDC, LPVOID, WORD)

   Convert a thorough DIB for analysis by the caller

   Returns: Number of bytes copied to caller's data area.
******************************************************************************/
WORD LOCAL MyConvertBigDIB (HDC hdc
                          , LPVOID lpBMP     // copy bitmap bits here
                          , WORD wSizeBMP    // size of caller's area
                           )
{
//	const int   BYTESPERPIXEL = 3;
	#define BYTESPERPIXEL 3
	BITMAPINFO  bi;
	char        cPixMap[PIXELSPERROW * BYTESPERPIXEL * NBRROWS];
	char FAR   *pcPixMap = cPixMap;
	char		cClr;
	int 		iRow,        
				iCol;

	// fill in the DIB header
	bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	bi.bmiHeader.biWidth = PIXELSPERROW;      // pixel width
	bi.bmiHeader.biHeight = NBRROWS;
	bi.bmiHeader.biPlanes = 1;                // must be 1
	bi.bmiHeader.biBitCount = BYTESPERPIXEL * 8;
	bi.bmiHeader.biCompression = BI_RGB;      // no compression
	bi.bmiHeader.biSizeImage = PIXELSPERROW * BYTESPERPIXEL * NBRROWS;
	bi.bmiHeader.biXPelsPerMeter = 0;
	bi.bmiHeader.biYPelsPerMeter = 0;
	bi.bmiHeader.biClrUsed = 0;               // no color table
	bi.bmiHeader.biClrImportant = 0;

	// fill in the pixel map
	// Colors are stored as RGBTRIPLEs, which are blue, green, red.
	// Row 1 runs the range of red values, 0-255.
	// Row 2 runs the range of green values, 0-255.
	// Row 3 runs the range of blue values, 0-255.
	// Row 4 runs the range of grey values, 0-255.
	// Row 5 has alternate black and white pixels.
	// DIB rows are bottom to top.  BMP rows are top to bottom.
	
	_fmemset (cPixMap, 0, sizeof cPixMap);    // initialize to zeroes

	// loop once for each column in this row
	for (iCol = 0, cClr = 0; iCol < PIXELSPERROW; iCol++, pcPixMap += BYTESPERPIXEL, cClr ^= 255)
	{
	   pcPixMap [0] = pcPixMap [1] = pcPixMap [2] = cClr;
	}
	
	// loop once for each column in this row
	for (iCol = 0; iCol < PIXELSPERROW; iCol++, pcPixMap += BYTESPERPIXEL)
	{
		pcPixMap [0] = iCol;
		pcPixMap [1] = iCol;
		pcPixMap [2] = iCol;
	}

	// loop once for each row
	for (iRow = 0; iRow < NBRROWS - 2; iRow++)
	{
		// loop once for each column in this row
		for (iCol = 0; iCol < PIXELSPERROW; iCol++, pcPixMap += BYTESPERPIXEL)
		{
			pcPixMap [iRow] = iCol;
		}  
	}
	#undef BYTESPERPIXEL
   // convert the DIB to a BMP
   return MyConvertOneDIB (hdc, &bi, cPixMap, lpBMP, wSizeBMP);
}  // MyConvertBigDIB


/******************************************************************************
   FUNCTION:       MyConvertOneDIB (HDC, LPBITMAPINFO, LPVOID, LPVOID, WORD)

   Convert a DIB to a BMP to validate the BMP format

   Returns: Number of bytes copied to caller's data area.
******************************************************************************/
WORD LOCAL MyConvertOneDIB (HDC hdc,
                            LPBITMAPINFO lpbi,
                            LPVOID lpBits,
                            LPVOID lpBMP,           // copy bitmap bits here
                            WORD wSizeBMP          // size of caller's area
                           )
{
	HBITMAP hbm;
	BITMAP  bmp;
	WORD   wSize;


	hbm = CreateDIBitmap (hdc,
	                      (LPBITMAPINFOHEADER) lpbi,
						  CBM_INIT,
	                      (LPSTR) lpBits,    // bitmap bits
	                       lpbi,              // bitmap header
	                       DIB_RGB_COLORS    // wUsage, no palette
	                      );
	// if CreateDIBitmap failed						  
	if (!hbm)                            
	{
	   //assert ("CreateDIBitmap failed");
	   return 0;
	}

	// get the bitmap bits

	GetObject (hbm, sizeof bmp, (LPSTR) &bmp);
	wSize = bmp.bmHeight * bmp.bmWidthBytes * bmp.bmPlanes;

    // if caller's area is too small	
	if (wSize > wSizeBMP)
	{
		DeleteObject (hbm);                    // delete GDI object
		return 0;
	}

	GetBitmapBits (hbm, (DWORD) wSize, (LPSTR) lpBMP);
	DeleteObject (hbm);                       // delete GDI object

	// return to caller
	return wSize;
}  // MyConvertOneDIB


/******************************************************************************
   FUNCTION:       MyValidateIndexed8 (HDC)

   Check the BMP format for a palettized driver of depth 8

   Returns: COLORSUPPORT
******************************************************************************/
COLORSUPPORT LOCAL MyValidateIndexed8 (HDC hdc)
{
/*	const WORD    NBRPIXELS = 20,
	              NBRCOLORS = 256;*/
	#define 	  NBRPIXELS 20
	#define		  NBRCOLORS 256
   	PALETTEENTRY  palEntries[NBRCOLORS];
	WORD          wNbrColors;

	struct
	{
	   BITMAPINFO bi;
	   RGBQUAD rgb[NBRCOLORS - 1];
	} bm;
	
	bm.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	bm.bi.bmiHeader.biWidth = NBRPIXELS;      // pixel width
	bm.bi.bmiHeader.biHeight = 1;             // pixel height
	bm.bi.bmiHeader.biPlanes = 1;             // must be 1
	bm.bi.bmiHeader.biBitCount = 8;           // use 8 bits per index
	bm.bi.bmiHeader.biCompression = BI_RGB;   // no compression
	bm.bi.bmiHeader.biSizeImage = NBRPIXELS;  // number of bytes
	bm.bi.bmiHeader.biXPelsPerMeter = 0;
	bm.bi.bmiHeader.biYPelsPerMeter = 0;
	bm.bi.bmiHeader.biClrUsed = NBRCOLORS;
	bm.bi.bmiHeader.biClrImportant = NBRCOLORS;

	// fill in the color table

	wNbrColors = GetSystemPaletteEntries (hdc,
	                                      0,
	                                      NBRCOLORS,
	                                      palEntries
	                                     );
	// if GetSystemPaletteEntries failed										 
	if (wNbrColors != NBRCOLORS)
	{
	  //assert ("GetSystemPaletteEntries did not return 256 colors");
	  return BMP_DIB;
	}  

	#undef  NBRPIXELS 
	#undef  NBRCOLORS 

	// successful return
	return BMP_INDEX_8;
}  // MyValidateIndexed8


/******************************************************************************
   FUNCTION:       MyValidateMono (HDC)

   Check the BMP format for a monochrome driver

   Returns: COLORSUPPORT
******************************************************************************/
COLORSUPPORT LOCAL MyValidateMono (HDC hdc)
{
	#define NBRCOLORS 2
	static char  cPixMap[] =
	{
	  '\x00', '\x00', '\x00', '\x00',
	  '\xff', '\xff', '\xff', '\xff'
	};
	static char  cBMP[] =
	{
	  '\xff', '\xff', '\xff', '\xff',
	  '\x00', '\x00', '\x00', '\x00'
	};
	char cBMPCmp[sizeof cBMP];		   
	struct
	{
	  BITMAPINFO bi;
	  RGBQUAD rgb[NBRCOLORS - 1];
	} bm;


	bm.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	bm.bi.bmiHeader.biWidth = 64;             // pixel width
	bm.bi.bmiHeader.biHeight = 1;             // pixel height
	bm.bi.bmiHeader.biPlanes = 1;             // must be 1
	bm.bi.bmiHeader.biBitCount = 1;           // 1 bit per pixel
	bm.bi.bmiHeader.biCompression = BI_RGB;   // no compression
	bm.bi.bmiHeader.biSizeImage = sizeof cPixMap;   // number of bytes
	bm.bi.bmiHeader.biXPelsPerMeter = 0;
	bm.bi.bmiHeader.biYPelsPerMeter = 0;
	bm.bi.bmiHeader.biClrUsed = NBRCOLORS;    // number of colors in table
	bm.bi.bmiHeader.biClrImportant = NBRCOLORS;

	// fill in the color table
	bm.bi.bmiColors [0].rgbRed = 255;         // 0 is white
	bm.bi.bmiColors [0].rgbGreen = 255;
	bm.bi.bmiColors [0].rgbBlue = 255;
	bm.bi.bmiColors [0].rgbReserved = 0;
	bm.bi.bmiColors [1].rgbRed = 0;           // 1 is black
	bm.bi.bmiColors [1].rgbGreen = 0;
	bm.bi.bmiColors [1].rgbBlue = 0;
	bm.bi.bmiColors [1].rgbReserved = 0;

	// create a BMP from the DIB

	// if BMP fails validation
	if (MyConvertOneDIB (hdc,
	                    (LPBITMAPINFO) &bm,
	                    cPixMap,
	                    &cBMPCmp,
	                    sizeof cBMPCmp 
	                   ) != sizeof cBMPCmp    
	   || _fmemcmp (cBMP, cBMPCmp, sizeof cBMP) != 0)
	{
		//assert ("BMP validation of monochrome failed");
		return BMP_DIB;
	}  // if BMP fails validation

	#undef NBRCOLORS
	// successful return
	return BMP_MONO;
}  // MyValidateMono


/******************************************************************************
   FUNCTION:       MyValidatePacked4 (HDC)

   Check the BMP format for a planar driver of depth 4

   Returns: COLORSUPPORT
******************************************************************************/
COLORSUPPORT LOCAL MyValidatePacked4 (HDC hdc)
{
	#define  NBRCOLORS 16
	static WORD wEscCode;
	static char cPixMap[] =
	{
	  '\x01', '\x23', '\x45', '\x67',
	  '\x89', '\xab', '\xcd', '\xef'
	};
	/* note that cBMP, appropriately reformatted is
	  0x5555 0101 0101 0101 0101
	  0x3333 0011 0011 0011 0011
	  0x0f0f 0000 1111 0000 1111
	  0x00ff 0000 0000 1111 1111
	  which is an enumeration of the 16 color indexes in 4 bit planes
	*/
	struct
	{
		BITMAPINFO bi;
		RGBQUAD rgb[NBRCOLORS - 1];
	} bm;
	PALETTEENTRY    peCLUT[NBRCOLORS];
	LPPALETTEENTRY  lppeWork;
	WORD            wRGBCount;
	LPRGBQUAD       prgb;
	char 			cBMPCmp[sizeof cPixMap];

	bm.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	bm.bi.bmiHeader.biWidth = 16;             // pixel width
	bm.bi.bmiHeader.biHeight = 1;             // pixel height
	bm.bi.bmiHeader.biPlanes = 1;             // must be 1
	bm.bi.bmiHeader.biBitCount = 4;           // use 4 bits per index
	bm.bi.bmiHeader.biCompression = BI_RGB;   // no compression
	bm.bi.bmiHeader.biSizeImage = sizeof cPixMap;   // number of bytes
	bm.bi.bmiHeader.biXPelsPerMeter = 0;
	bm.bi.bmiHeader.biYPelsPerMeter = 0;
	bm.bi.bmiHeader.biClrUsed = NBRCOLORS;    // number of colors in table
	bm.bi.bmiHeader.biClrImportant = NBRCOLORS;

	// check for GETCOLORTABLE escape
	wEscCode = GETCOLORTABLE;

    // if not supported
	if (Escape (hdc,
	            QUERYESCSUPPORT,
	            sizeof wEscCode,
	            (LPSTR) &wEscCode,
	            0                             // lpOutData
	           ) == 0)
	{   
	  	//assert ("GETCOLORTABLE not supported");
		return BMP_DIB;
	}  // if GETCOLORTABLE is not supported

	// get the CLUT
	lppeWork = peCLUT;
	prgb = bm.bi.bmiColors;
	// loop once for each color in table
	for (wRGBCount = 0; wRGBCount < NBRCOLORS; wRGBCount++)
	{
		// if error getting color value
		if (Escape (hdc,
		            GETCOLORTABLE,
		            sizeof wRGBCount,
		            (LPSTR) &wRGBCount,
		            (LPSTR) lppeWork
		           ) <= 0)
		{
			//assert ("Error retrieving a color index");
			return BMP_DIB;
		}
		prgb->rgbRed = lppeWork->peRed;
		prgb->rgbGreen = lppeWork->peGreen;
		prgb->rgbBlue = lppeWork->peBlue;
		prgb->rgbReserved = 0;
		lppeWork->peRed = prgb->rgbBlue;
		lppeWork->peBlue = prgb->rgbRed;
		lppeWork++;
		prgb++;
	}  // loop once for each color in table

	// create a BMP from the DIB
	if (MyConvertOneDIB (hdc,
	                     (LPBITMAPINFO) &bm,
	                     cPixMap,
	                     &cBMPCmp,
	                     sizeof cBMPCmp
	                   ) != sizeof cBMPCmp   // if BMP fails validation
	   || _fmemcmp (cPixMap, cBMPCmp, sizeof cPixMap) != 0)
	{
		//assert ("BMP validation of 4-bit packed failed");
		return BMP_DIB;
	}  // if BMP fails validation
	#undef NBRCOLORS
	// successful return
	return BMP_PACKED_4;
}  // MyValidatePacked4


/******************************************************************************
   FUNCTION:       MyValidatePlanar16 (HDC)

   Check the BMP format for a planar driver of depth 16

   Returns: TRUE  valid
            FALSE invalid
******************************************************************************/
BOOL LOCAL MyValidatePlanar16 (HDC hdc)
{
	#define RED_MASK   0x7c00
	#define GREEN_MASK 0x03e0
	#define BLUE_MASK  0x001f
	#define RED_INCR   0x0400
	#define GREEN_INCR 0x0020
	#define BLUE_INCR  0x0001
	#define BYTESPERPIXEL 2
	
	BYTE 	cBMP[PIXELSPERROW * NBRROWS * BYTESPERPIXEL];
	WORD 	wSizeBMP = MyConvertBigDIB (hdc, cBMP, sizeof cBMP);
	WORD 	wClr=0, wPixel, wIncr;
	LPBYTE 	pcBMP;
	int 	i, j;
	WORD 	wRedLo   = wClr & RED_MASK,
			wRedHi   = wRedLo + RED_INCR,
			wGreenLo = wClr & GREEN_MASK,
			wGreenHi = wGreenLo + GREEN_INCR,
			wBlueLo  = wClr & BLUE_MASK,
			wBlueHi  = wBlueLo + BLUE_INCR;


	// validate the size
	if (wSizeBMP != NBRROWS * PIXELSPERROW * BYTESPERPIXEL)
	  return FALSE;

	// now validate the pixels

	// red
	for (i = 0, wClr = 0, wIncr = RED_INCR    
	    , pcBMP = cBMP; i < 32; i++, wClr += wIncr)
	{
		for (j = 0; j < 8; j++, pcBMP++)
		{
			wPixel = MAKEWORD (pcBMP [0], pcBMP [PIXELSPERROW]);
			if (wPixel != wClr && wPixel != wClr + wIncr)
				return FALSE;
		}
	}
	// green	
	for (i = 0, wClr = 0, wIncr = GREEN_INCR
	    , pcBMP += PIXELSPERROW; i < 32; i++, wClr += wIncr)
	{
		for (j = 0; j < 8; j++, pcBMP++)
		{
			wPixel = MAKEWORD (pcBMP [0], pcBMP [PIXELSPERROW]);
			if (wPixel != wClr && wPixel != wClr + wIncr)
				return FALSE;
		}
	}
	
	// blue
	for (i = 0, wClr = 0, wIncr = BLUE_INCR   
	    , pcBMP += PIXELSPERROW; i < 32; i++, wClr += wIncr)
	{
		  for (j = 0; j < 8; j++, pcBMP++)
		  {
			wPixel = MAKEWORD (pcBMP [0], pcBMP [PIXELSPERROW]);
			if (wPixel != wClr && wPixel != wClr + wIncr)
		        return FALSE;
		  }
	}
	
	// gray	
	for (i = 0, wClr = 0, wIncr = RED_INCR | GREEN_INCR | BLUE_INCR  
		, pcBMP += PIXELSPERROW; i < 32; i++, wClr += wIncr)
	{
		for (j = 0; j < 8; j++, pcBMP++)
		{
			wPixel = MAKEWORD (pcBMP [0], pcBMP [PIXELSPERROW]);
			if (((wPixel & RED_MASK) != wRedLo
				&& (wPixel & RED_MASK) != wRedHi)
				|| ((wPixel & GREEN_MASK) != wGreenLo
				&& (wPixel & GREEN_MASK) != wGreenHi)
				|| ((wPixel & BLUE_MASK) != wBlueLo
				&& (wPixel & BLUE_MASK) != wBlueHi))
				return FALSE;
		}
	}

	// black/white
	for (i = 0, pcBMP += PIXELSPERROW; i < 32; i++)
	{
		for (j = 0, wClr = 0; j < 8; j++, pcBMP++, wClr ^= 0x7fff)
		{
			wPixel = MAKEWORD (pcBMP [0], pcBMP [PIXELSPERROW]);
			if (wPixel != wClr
				&& (wPixel & ~(RED_INCR | GREEN_INCR | BLUE_INCR)) != wClr)
			return FALSE;
		}
	}

	#undef BYTESPERPIXEL
	// successful validation
	return TRUE;
}  // MyValidatePlanar16


/******************************************************************************
   FUNCTION:       MyValidatePlanar4 (HDC)

   Check the BMP format for a planar driver of depth 4

   Returns: COLORSUPPORT
******************************************************************************/
COLORSUPPORT LOCAL MyValidatePlanar4 (HDC hdc)
{
	#define NBRCOLORS 16

	static WORD wEscCode;
	static char cPixMap[] =
	{
	  '\x01', '\x23', '\x45', '\x67',
	  '\x89', '\xab', '\xcd', '\xef'
	};
	static char cBMP[] =
	{
	  '\x55', '\x55', '\x33', '\x33',
	  '\x0f', '\x0f', '\x00', '\xff'
	};
	/* note that cBMP, appropriately reformatted is
	  0x5555 0101 0101 0101 0101
	  0x3333 0011 0011 0011 0011
	  0x0f0f 0000 1111 0000 1111
	  0x00ff 0000 0000 1111 1111
	  which is an enumeration of the 16 color indexes in 4 bit planes
	*/
	struct
	{
		BITMAPINFO bi;
		RGBQUAD rgb[NBRCOLORS - 1];
	} bm;
	PALETTEENTRY peCLUT[NBRCOLORS];
	LPPALETTEENTRY lppeWork;
	WORD wRGBCount;
	LPRGBQUAD prgb;
	char cBMPCmp[sizeof cBMP];
   

	bm.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	bm.bi.bmiHeader.biWidth = 16;             // pixel width
	bm.bi.bmiHeader.biHeight = 1;             // pixel height
	bm.bi.bmiHeader.biPlanes = 1;             // must be 1
	bm.bi.bmiHeader.biBitCount = 4;           // use 4 bits per index
	bm.bi.bmiHeader.biCompression = BI_RGB;   // no compression
	bm.bi.bmiHeader.biSizeImage = sizeof cPixMap;   // number of bytes
	bm.bi.bmiHeader.biXPelsPerMeter = 0;
	bm.bi.bmiHeader.biYPelsPerMeter = 0;
	bm.bi.bmiHeader.biClrUsed = NBRCOLORS;    // number of colors in table
	bm.bi.bmiHeader.biClrImportant = NBRCOLORS;

	// check for GETCOLORTABLE escape
	wEscCode = GETCOLORTABLE;
	if (Escape (hdc,
				QUERYESCSUPPORT,
				sizeof wEscCode,
				(LPSTR) &wEscCode,
				0) == 0)
	{
		//assert ("GETCOLORTABLE not supported");
		return BMP_DIB;
	}
	
	lppeWork = peCLUT;
	prgb = bm.bi.bmiColors;
	// get the CLUT
	for (wRGBCount = 0; wRGBCount < NBRCOLORS; wRGBCount++)
	{
		// if error
		if (Escape (hdc,
					GETCOLORTABLE,
					sizeof wRGBCount,
					(LPSTR) &wRGBCount,
					(LPSTR) lppeWork
					) <= 0)
		{                  
			//assert ("Error retrieving a color index");
			return BMP_DIB;
		}  
		prgb->rgbRed = lppeWork->peRed;
		prgb->rgbGreen = lppeWork->peGreen;
		prgb->rgbBlue = lppeWork->peBlue;
		prgb->rgbReserved = 0;
		lppeWork->peRed = prgb->rgbBlue;
		lppeWork->peBlue = prgb->rgbRed;
		lppeWork++;
		prgb++;
	}

	// create a BMP from the DIB
	if (MyConvertOneDIB (hdc,
						(LPBITMAPINFO) &bm,
						cPixMap,
						&cBMPCmp,
						sizeof cBMPCmp
						) != sizeof cBMPCmp   // if BMP fails validation
	   || _fmemcmp (cBMP, cBMPCmp, sizeof cBMP) != 0)
	{
		//assert ("BMP validation of VGA failed");
		return BMP_DIB;
	}  // if BMP fails validation

	#undef NBRCOLORS
	// successful return
	return BMP_PLANAR_4;
}  // MyValidatePlanar4


/******************************************************************************
   FUNCTION:       MyValidate16 (HDC)

   Check the BMP format for a driver of depth 16

   Such a driver has 1 plane, bit depth 16 and 32,768 colors.  Each BMP
   pixel uses two bytes, treated as an Intel-format integer, mapped in
   one of the following formats:

              Direct555                       Direct565
              15-15 0                         15-11 Red
              14-10 Red                       10- 5 Green
               9- 5 Green                      4- 0 Blue
               4- 0 Blue

   Returns: COLORSUPPORT
******************************************************************************/
COLORSUPPORT LOCAL MyValidate16 (HDC hdc)
{
	// create the BMP
	WORD wBMP[PIXELSPERROW * NBRROWS],
		wSizeBMP = MyConvertBigDIB (hdc, wBMP, sizeof wBMP);

	// check for a 5-5-5 format
	if (MyValidate555 ((PWORD) &wBMP, wSizeBMP) == TRUE)
		return BMP_5_5_5;

	// check for a 5-6-5 format
	if (MyValidate565 ((PWORD) &wBMP, wSizeBMP) == TRUE)
		return BMP_5_6_5;

	// check for a 5-6-5 Motorola format
	if (MyValidate565M ((PWORD) &wBMP, wSizeBMP) == TRUE)
		return BMP_5_6_5_M;

	// validation failed
	//assert ("BMP validation of direct 16 failed");
	return BMP_DIB;
}  // MyValidate16


/******************************************************************************
   FUNCTION:       MyValidate24 (HDC)

   Check the BMP format for a driver of depth 24

   Such a driver has 1 plane, bit depth 15 and 32,768 colors.  Each BMP
   pixel uses three bytes, treated as a Motorola-format integer, mapped
   in one of the following formats:

                 RGB                   BGR
                 23-16 Red             23-16 Blue
                 15- 8 Green           15- 8 Green
                  7- 0 Blue             7- 0 Red

   Returns: COLORSUPPORT
******************************************************************************/
COLORSUPPORT LOCAL MyValidate24 (HDC hdc)
{
	// create the BMP
	RGBTRIPLE rgbBMP[PIXELSPERROW * NBRROWS];
	WORD wSizeBMP = MyConvertBigDIB (hdc, rgbBMP, sizeof rgbBMP);

	// check for a forward 8-8-8 format
	if (MyValidateRGB (rgbBMP, wSizeBMP) == TRUE)
		return BMP_8_8_8_RGB;

	// check for a reversed 8-8-8 format
	if (MyValidateBGR (rgbBMP, wSizeBMP) == TRUE)
		return BMP_8_8_8_BGR;

	// validation failed
	//assert ("BMP validation of direct 24 failed");
	return BMP_DIB;
}  // MyValidate24


/******************************************************************************
   FUNCTION:       MyValidate555 (PWORD, WORD)

   Check for 16-bit 555 format

   The ATI 16-bit driver is creative.  A reasonable driver maps a
   primary color value x = 0-247 to x/8.  ATI maps the color to x/8 or
   x/8 + 1, based on x mod 8. For red, ATI uses the higher value for x/8
   = 1, 3, 4, 6 and 7; for green, 4, 6 and 7; for blue, 3, 5, 6 and 7.
   Values 248-255 map to x/8.  An earlier version driver, WIN3-72B.DRV,
   used 5 and 7 for red; 2, 5, 6 and 7 for green; 2, 3, 5 and 6 for
   blue.

   Yet another 6 x 4 x 15 ATI Wonder XL driver (W31-72B.DRV, 159,024
   bytes, 28Jul92, 14:24:58) sets bit 15 on for red values with
   saturation >= 50%.  We now mask out bit 15 before testing.

   Returns: TRUE  valid
            FALSE invalid
******************************************************************************/
BOOL LOCAL MyValidate555 (PWORD pwBMP, WORD wSize)
{
	#define RED_MASK   0x7c00
	#define GREEN_MASK 0x03e0
	#define BLUE_MASK  0x001f
	#define RGB_MASK   (RED_MASK | GREEN_MASK | BLUE_MASK)
	#define RED_INCR   0x0400
	#define GREEN_INCR 0x0020
	#define BLUE_INCR  0x0001
	int i, j;
	WORD wClr,
		 wIncr,
		 wRedLo,
		 wRedHi,
		 wGreenLo,
		 wGreenHi,
		 wBlueLo,
		 wBlueHi;


	// validate the size
	if (wSize != NBRROWS * PIXELSPERROW * 2)
		return FALSE;

	// now validate the pixels
	// red
	wClr = 0;
	wIncr = RED_INCR;
	for (i = 0; i < 32; i++, wClr += wIncr)
		for (j = 0; j < 8; j++, pwBMP++)
			if ((*pwBMP & RGB_MASK) != wClr
			 && (*pwBMP & RGB_MASK) != wClr + wIncr)
				return FALSE;
	// green
	wClr = 0;
	wIncr = GREEN_INCR;
	for (i = 0; i < 32; i++, wClr += wIncr)
		for (j = 0; j < 8; j++, pwBMP++)
			if ((*pwBMP & RGB_MASK) != wClr
			 && (*pwBMP & RGB_MASK) != wClr + wIncr)
				return FALSE;

	// blue
	wClr = 0;
	wIncr = BLUE_INCR;
	for (i = 0; i < 32; i++, wClr += wIncr)
		for (j = 0; j < 8; j++, pwBMP++)
		if ((*pwBMP & RGB_MASK) != wClr
			 && (*pwBMP & RGB_MASK) != wClr + wIncr)
			return FALSE;
	// gray
	wClr = 0;
	wIncr = RED_INCR | GREEN_INCR | BLUE_INCR; 
	for (i = 0; i < 32; i++, wClr += wIncr)
	{
		wRedLo   = wClr & RED_MASK;
		wRedHi   = wRedLo + RED_INCR;
		wGreenLo = wClr & GREEN_MASK;
		wGreenHi = wGreenLo + GREEN_INCR;
		wBlueLo  = wClr & BLUE_MASK;
		wBlueHi  = wBlueLo + BLUE_INCR;

		for (j = 0; j < 8; j++, pwBMP++)
		{
			if ((((*pwBMP & RGB_MASK) & RED_MASK) != wRedLo
				&& ((*pwBMP & RGB_MASK) & RED_MASK) != wRedHi)
				|| (((*pwBMP & RGB_MASK) & GREEN_MASK) != wGreenLo
				&& ((*pwBMP & RGB_MASK) & GREEN_MASK) != wGreenHi)
				|| (((*pwBMP & RGB_MASK) & BLUE_MASK) != wBlueLo
				&& ((*pwBMP & RGB_MASK) & BLUE_MASK) != wBlueHi))
				return FALSE;
		}
	}
	
	// black/white
	for (i = 0; i < 32; i++)
	{
		wClr = 0;
		for (j = 0; j < 8; j++)
		{
			if (i == 0 && j == 0)
			{  					 // bypass apparent problem with
				pwBMP++; 		 //  Diamond SpeedStar 24
				wClr ^= 0x7fff;			             
				continue;
			}                        
			if ((*pwBMP & RGB_MASK) != wClr
				&& ((*pwBMP & RGB_MASK)
				& ~(RED_INCR | GREEN_INCR | BLUE_INCR)) != wClr)
				return FALSE;
			pwBMP++; 
			wClr ^= 0x7fff;
		}
	}

	// successful validation
	return TRUE;
}  // MyValidate555


/******************************************************************************
   FUNCTION:       MyValidate565 (PWORD, WORD)

   Check for 16-bit 565 format

   Returns: TRUE  valid
            FALSE invalid
******************************************************************************/
BOOL LOCAL MyValidate565 (PWORD pwBMP, WORD wSize)
{
	int i, j;
	WORD wClr;

	// validate the size
	if (wSize != NBRROWS * PIXELSPERROW * 2)
		return FALSE;

// now validate the pixels
	// red
	for (i = 0, wClr = 0; i < 32; i++, wClr += 0x800)
		for (j = 0; j < 8; j++)
			if (*pwBMP++ != wClr)
				return FALSE;
	// green
	for (i = 0, wClr = 0; i < 64; i++, wClr += 0x20)
		for (j = 0; j < 4; j++)
			if (*pwBMP++ != wClr)
				return FALSE;
	// blue
	for (i = 0, wClr = 0; i < 32; i++, wClr += 1)
		for (j = 0; j < 8; j++)
			if (*pwBMP++ != wClr)
				return FALSE;
	// gray
	for (i = 0, wClr = 0; i < 32; i++)  
	{
		for (j = 0; j < 4; j++)
			if (*pwBMP++ != wClr)
				return FALSE;
		wClr += 0x20;
		for (j = 0; j < 4; j++)
			if (*pwBMP++ != wClr)
				return FALSE;
		wClr += 0x821;
	}

	// black/white
	for (i = 0; i < 32; i++)                           
		for (j = 0, wClr = 0; j < 8; j++, wClr ^= 0xffff)
			if (*pwBMP++ != wClr)
				return FALSE;

	// successful validation
	return TRUE;
}  // MyValidate565


/******************************************************************************
   FUNCTION:       MyValidate565M (PWORD, WORD)

   Check for 16-bit 565 format in Motorola order

   Returns: TRUE  valid
            FALSE invalid
******************************************************************************/
BOOL LOCAL MyValidate565M (PWORD pwBMP, WORD wSize)
{
   int i, j;
   WORD wClr;

	// validate the size
	if (wSize != NBRROWS * PIXELSPERROW * 2)
		return FALSE;

	// now validate the pixels
	// red
	for (i = 0, wClr = 0; i < 32; i++, wClr += 0x800)  
		for (j = 0; j < 8; j++)
			if (*pwBMP++ != SwapBytes (wClr))
				return FALSE;
	// green
	for (i = 0, wClr = 0; i < 64; i++, wClr += 0x20)   
		for (j = 0; j < 4; j++)
			if (*pwBMP++ != SwapBytes (wClr))
				return FALSE;
	// blue
	for (i = 0, wClr = 0; i < 32; i++, wClr += 1)      
		for (j = 0; j < 8; j++)
			if (*pwBMP++ != SwapBytes (wClr))
			return FALSE;
	// gray
	for (i = 0, wClr = 0; i < 32; i++)  
	{
		for (j = 0; j < 4; j++)
			if (*pwBMP++ != SwapBytes (wClr))
				return FALSE;
		wClr += 0x20;
		for (j = 0; j < 4; j++)
			if (*pwBMP++ != SwapBytes (wClr))
				return FALSE;
		wClr += 0x821;
	}
	
	// black/white
	for (i = 0; i < 32; i++)                           
		for (j = 0, wClr = 0; j < 8; j++, wClr ^= 0xffff)
			if (*pwBMP++ != SwapBytes (wClr))
				return FALSE;

	// successful validation
	return TRUE;
}  // MyValidate565M


/******************************************************************************
   FUNCTION:       MyValidateRGB (LPRGBTRIPLE, WORD)

   Check for 24-bit RGB format

   Returns: TRUE  valid
            FALSE invalid
******************************************************************************/
BOOL LOCAL MyValidateRGB (LPRGBTRIPLE prgb, WORD wSize)
{
	WORD i;
	BYTE bClr;


	// validate the size
	if (wSize != NBRROWS * PIXELSPERROW * 3)
		return FALSE;

	// now validate the pixels
	// note RGBTRIPLE is defined as blue, green, red
	
	// red	
	for (i = 0; i < 256; i++, prgb++)         
		if (prgb->rgbtBlue != i
			|| prgb->rgbtGreen != 0
			|| prgb->rgbtRed != 0)
			return FALSE;
	// green
	for (i = 0; i < 256; i++, prgb++)         
		if (prgb->rgbtBlue != 0
			|| prgb->rgbtGreen != i
			|| prgb->rgbtRed != 0)
			return FALSE;
	// blue
	for (i = 0; i < 256; i++, prgb++)         
		if (prgb->rgbtBlue != 0
			|| prgb->rgbtGreen != 0
			|| prgb->rgbtRed != i)
			return FALSE;
	// gray
	for (i = 0; i < 256; i++, prgb++)         
		if (prgb->rgbtBlue != i
			|| prgb->rgbtGreen != i
			|| prgb->rgbtRed != i)
			return FALSE;

	// black/white
	// Skip value i = 0, as the first byte in the DIB appears to be trashed
	//  by the drivers for the Diamond SpeedStar 24 and the Diamond SpeedStar
	//  24X.
	for (i = 1, bClr = 255, prgb++; i < 256; i++, prgb++, bClr ^= 255)
		if (prgb->rgbtBlue != bClr
			|| prgb->rgbtGreen != bClr
			|| prgb->rgbtRed != bClr)
			return FALSE;

	// successful validation
	return TRUE;
}  // MyValidateRGB


/******************************************************************************
   FUNCTION:       MyValidateBGR (LPRGBTRIPLE, WORD)

   Check for 24-bit BGR format

   Returns: TRUE  valid
            FALSE invalid
******************************************************************************/
BOOL LOCAL MyValidateBGR (LPRGBTRIPLE prgb, WORD wSize)
{
WORD i;
BYTE bClr;


	// validate the size
	if (wSize != NBRROWS * PIXELSPERROW * 3)
		return FALSE;

	// now validate the pixels
	// note RGBTRIPLE is defined as blue, green, red
	// red
	for (i = 0; i < 256; i++, prgb++)         
		if (prgb->rgbtBlue != 0
			|| prgb->rgbtGreen != 0
			|| prgb->rgbtRed != i)
			return FALSE;

	// green
	for (i = 0; i < 256; i++, prgb++)         
		if (prgb->rgbtBlue != 0
			|| prgb->rgbtGreen != i
			|| prgb->rgbtRed != 0)
			return FALSE;

	// blue
	for (i = 0; i < 256; i++, prgb++)         
		if (prgb->rgbtBlue != i
			|| prgb->rgbtGreen != 0
			|| prgb->rgbtRed != 0)
			return FALSE;
	// gray
	for (i = 0; i < 256; i++, prgb++)        
		if (prgb->rgbtBlue != i
			|| prgb->rgbtGreen != i
			|| prgb->rgbtRed != i)
			return FALSE;

	// black/white
	for (i = 0, bClr = 0; i < 256; i++, prgb++, bClr ^= 255)
		if (prgb->rgbtBlue != bClr
			|| prgb->rgbtGreen != bClr
			|| prgb->rgbtRed != bClr)
			return FALSE;

	// successful validation
	return TRUE;
}  // MyValidateBGR

