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, ®s, ®s); 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, ®s, ®s); } } 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