[comp.windows.ms] Bitmaps, palettes under Win3.0

rose@stsci.EDU (Jim Rose) (10/26/90)

Bitmaps and Palettes

   The Space Telescope Science Institute has a variety of hardware, and
   lots of capabilities to display the imagery coming from the telescope.
   But alot of these capabilities are expensive, and we felt it would be
   useful to develop an imaging capability using MS Windows on relatively
   inexpensive clones.  The prototype on Win2.1 worked quite well on our
   test systems.  Conversion to Win3.0 has been confusing at best.

   Win3.0 Advantages: Why do this at all?

	Since Win2.1 mostly ignored color tables we were forced to bind
	the software to the hardware devices.  Specifically we were using
	DGIS calls to the device driver to manipulate the hardware palette.
	Conversion to Win3.0 promises greater device independence allowing
	the software to work with any 256 color card, not just DGIS com-
	patible devices.

	A secondary motivation is the improved memory management in Win3.0.
	The objects we deal with are invariably greater than 64K pixels,
	and Win2.1 had problems handling larger objects (eg BitBlt).  We
	had awkward work-arounds, but it is much nicer using huge pointers.

   Bitmaps and DIBitmaps: SelectObject knows something I don't know?

	Device independent bitmaps are new to Win3.0, and the differences
	are still not obvious.  Under Win2.1 we used CreateBitmap,
	CreateCompatibleDC, SelectObject, and BitBlt in that sequence
	to display an image.  All went well so long as we did things
	in chunks of 64K or less.

	Now with Win3.0 that sequence fails.  SelectObject now returns
	a NULL handle when we try to select the bitmap into the compatible
	device context.  We still don't understand why this is the case.
	Does SelectObject know something about the device which makes
	ordinary bitmaps incompatible.  Under what context can you select
	an ordinary bitmap into a device context?

	Still not understanding the problem, we were nevertheless successful
	in using the sequence:
		CreateDIBitmap
		CreateCompatibleDC
		SelectObject
		BitBlt
	That is, SelectObject is much happier with selecting a handle to
	a device independent bitmap, rather than an ordinary one.  Apparently
	there are hBitmap's and hDIBitmap's, but if SelectObject is going
	to complain about the former, what good is it?

	It's not clear what CreateDIBitmap is doing, but it takes a 
	significant amount of time to do it.  It's probably(?) mapping
	the values in the bitmap to color numbers.  But what if the
	bitmap contains (as it does in our case) color numbers already?
	Isn't there a way to tell Windows that it's already been done?

   SelectPalette: For ALL device contexts?

	After several days of displaying very black images, we realized
	that the palette has to be selected into several device
	contexts BEFORE referencing them.

	The sequence above should really read:
		CreatePalette
		GetDC
		SelectPalette
		RealizePalette
		CreateDIBitmap
		ReleaseDC

		...and later...
		hDC = GetDC
		SelectPalette (hDC...)
		RealizePalette (hDC...)
		hBitmapDC = CreateCompatibleDC
		SelectPalette (hBitmapDC...)	
		RealizePalette (hBitmapDC...)
		SelectObject
		BitBlt
		ReleaseDC
	

   BitBlt is still fast; StretchBlt is still slow.  

	Under Win2.1 we found that StretchBlt was so slow that we were
	forced to write our own.  Clearly StretchBlt was doing more than
	we needed.  We wanted only a ZoomBlt where the Zoom factor was
	a integral number (1/4, 1/3, 1/2, 2, 3, 4).  Because of the
	limitations of Win2.1, ZoomBlt handled image sections less than
	64K.

	Now with Win3.0 we are using StretchBlt instead.  Because of the
	time it takes to do a CreateDIBitmap, it is faster (less slow) to
	give the whole task to StretchBlt than to do a Zoom, CreateDIBitmap,
	BitBlt sequence.  Perhaps we are doing something wrong, but it
	is embarrassingly slow to zoom our images right now.
	
   Win3.0 is topsy-turvey.

	We haven't looked into this very far, but Win3.0 changed the default
	origin of our images from the top-left to the bottom-left.  This has
	already made for a little confusion.  Does anyone know whether it
	is difficult to define the origin at the top-left as in Win2.1?

   XORing a Rubberband? Driver specific?

	The standard way of drawing temporary lines and rectangles over
	an image is to XOR the line with the image, and, when you want
	to move the line, XOR it again, and things are back to normal.
	In Win2.1 we used a ROP code of R2_NOT in drawing the lines, and
	things worked just fine.

	In Win3.0 the same code produced quite different results.  The
	underlying image was unaffected by the drawing, but the rubber-
	banded line was not consistently inverted.  Clearly in a world
	of color the meaning of inversion is ambiguous as MS is wont to
	point out often.  But it would be nice to know what the ROP codes
	do in the world of color.  Is it defined?  Is it left up to the
	device driver writers?

	After a good deal of experimentation (using the NEC Graphics
	Engine Windows 3.0 driver) we determined the following: of the
	256 color numbers, Windows reserves the top 20 for its system
	palette (color numbers 236..255).  Our palette consisted of 64
	grey levels assigned to color numbers 0..63.  With our driver,
	the inverse of these grey levels is found at color number 
	235..172 using the R2_NOT rop code.  So we just put the same
	64-level palette at that location, and the rubber-banding worked
	well.

	The question remains whether this scheme will work with other
	drivers, or whether there is a better way to define the inverse
	of a grey-level?

   Standard VGA drivers: making grey levels standard.

	It would be nice if we could at least demonstrate some of the
	capabilities of our imaging software on standard VGA clones with
	the caveat that this would look much nicer if you had 256 colors.
	We tried what we had with the MS distributed VGA.DRV just to see
	what would happen.  We were not amused.

	There were basically two major problems.  The first has to do with
	the definition of the basic color table.  Realizing that VGA will only
	support 16 colors in 640x480 mode, we wanted those 16 colors to
	be grey levels.  Astronomers want to see images as intensity
	gradients, not as reds and oranges, blues and greens.

	Initially our palette of 64 grey levels was being displayed as
	only 4 levels (black, dark grey, light grey, white).  Using the
	Control Panel to define other greys was a useless diversion.
	Win3.0 create fancy patterns for colors not in its base list of
	16.  Those patterns are useful for painting with a brush (see MyPal),
	but are useless for displaying an image where each pixel is different.

	It became apparent that the best we could do would be to create
	a palette containing only those sixteen base colors.  And you can
	find out what the RGB values for those 16 base colors are by running
	the MyPal sample program (click the left button). Now we had an
	image with arbitrary colors.

	The only way we could get grey levels defined as the base 16 colors
	was to bypass Win3.0 and go directly to the BIOS. The following
	code usurps the first 16 of the VGA's 64 color registers, and fills
	them with grey levels:

	void SetVGAGrey ()
	{
	    union REGS regs;
	    int   i,j;
	    for (i=0,j=0; i<16; i++, j+=4) {
		regs.x.ax = 0x1000;  /* set palette register */
		regs.h.bl = i;       /* which register       */
		regs.h.bh = i;       /* color number         */
		int86 (0x10, &regs, &regs);

		regs.x.ax = 0x1010;  /* set individual DAC register */
		regs.x.bx = i;       /* DAC register to set  */
		regs.h.dh = j;       /* red                  */
                regs.h.ch = j;       /* green                */
		regs.h.cl = j;       /* blue                 */
		int86 (0x10, &regs, &regs);
	    }
	}

	Such a drastic approach didn't seem to hurt the Windows system
	colors: they became all grey of course, but there was enough
	distinction that everything was quite readable.

	Running MyPal revealed another curiosity.  It seems that color
	numbers 7 and 8 are switched?  Both MyPal and its icon show
	this reversal.  Our solution was to reverse the definition in
	our logical palette.  That is, instead of color 7 being
	(192, 192, 192), we set it to the old color 8 RGB values
	(128, 128, 128), and vice versa.   All other colors in our palette 
	remained the same as those defined by the VGA.DRV (as displayed 
	by MyPal).

   No Animation is Standard!

	While we were then able to display images using this grey scale
	palette, we were hit with a more severe drawback of Standard VGA.
	The second problem is that the VGA.DRV does not support animating
	these colors. (Or am I mistaken?)

	We defined a palette of 64 levels initially pointing to the 16
	base colors in equal steps (4 levels per color).  We then redefine
	the palette colors and use AnimatePalette to adjust the colors
	seen on the screen.  Works fine with devices which support 256
	colors.  Apparently with the Standard VGA driver however, you
	have to use SetPaletteEntries with the new palette colors, and
	then Re-CreateDIBitmap in order to change the actual assignments.

	This works but is several orders of magnitude slower than animating
	the palette.  It also requires substantially revising the logic of
	what happens when the user wants to adjust the palette.  If you have 
	a 256 color card, a simple AnimatePalette is sufficient. If you
	have just a 16 color monitor you not only have to repaint the screen,
	you have to recreate the bitmap first!   Isn't there an easier way?


Jim Rose			rose@stsci.edu
Space Telescope Science Institute				
3700 San Martin Drive
Baltimore, MD 21218