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