• Please review our updated Terms and Rules here

EGA/VGA 16 color dithering

resman

Veteran Member
Joined
Jan 1, 2014
Messages
604
Location
Lake Tahoe
About 30 years ago I wrote a few Windows display drivers for various HW platforms. In doing so, one algorithm I always defaulted to the Microsoft implementation was the RGB dithered brush generation. Always short on time and the sample code from MS, either x86 assembly or even the C code, was enough to make a grown man cry, I never got around to implementing my own. Playing around with VGA code last week I decided to revisit the dithered brush code. After a little investigation, I settled on a very simple approach that has me wishing I'd spent the time on it back in the day. I put a github project together here: https://github.com/dschmenk/DITHER16

Here are some samples:
compaqs.jpg
racecar.jpg

Edit:
The forum shrunk the images to the point of in distinguishing the dither. Better images are on GitHub.
 
Last edited:
That dithering pattern looks extremely similar to the Chromagraphics dithering pattern used by the Futura 100 on color wax thermal printers

The patterns allowed 300 dpi printers to have cleaner more color accurate results with a similar look to a 600dpu laser.

Good job.
 
Yeah, this looks good. I think Atkinson will always be my favorite dither algorithm (there's a phrase I never imagined I'd find myself writing,) but this is a nice-looking ordered dither.

Never realized that the dither algorithm itself was a part of the display driver. Now I'm kinda curious if various manufacturers ever rolled their own, and what you'd get if you tried displaying the same image on various drivers...
 
That dithering pattern looks extremely similar to the Chromagraphics dithering pattern used by the Futura 100 on color wax thermal printers

The patterns allowed 300 dpi printers to have cleaner more color accurate results with a similar look to a 600dpu laser.

Good job.

Thanks. The pattern itself is a standard 4x4, but the epiphany was to break the algorithm into two halves, 0%-50% brightness and 50%-100% brightness. With only 8 colors, the algorithm is quite straight forward.
 
Yeah, this looks good. I think Atkinson will always be my favorite dither algorithm (there's a phrase I never imagined I'd find myself writing,) but this is a nice-looking ordered dither.

Never realized that the dither algorithm itself was a part of the display driver. Now I'm kinda curious if various manufacturers ever rolled their own, and what you'd get if you tried displaying the same image on various drivers...

The VGA Windows driver does a full 8x8 pattern, but I don't think it really buys you much. I doubt anyone bothered implementing their own algorithm and I'm sure all the SVGA drivers implemented the same code. I don't think the benchmarks were affected by the dithering code which would have been the real reason to spend effort on it.
 
The patterns are nice but the colour reproduction isn't very accurate. It doesn't look like you're doing any kind of gamma correction, which may be part of this. It also looks like you're not taking into account the specific RGB values in the target colour palette. This is particularly noticeable on the yellow bar of "Color Bars #2" - the dithered bar is brown rather than matching the yellow in the source image. There's a really good, in-depth article at https://bisqwit.iki.fi/story/howto/dither/jy/ which covers these issues. I will be interested to see if this inspires you to make some improvements!

Another problem you might find is that the standard EGA 200-line RGBI palette does not completely cover the sRGB gamut. Consider completely saturated red (255,0,0). The closest RGBI colours (255,85,85) and (170,0,0) don't protrude right into that corner, and no combination of those with other colours will ever make a more saturated red. So you may find that colour accuracy and detail reproduction suffer. Avoiding this is, I think, mostly a matter of choosing source images carefully. Though it is possible that a clever pre-processing step could subtly regrade the images so that they look nice and lie within the RGBI gamut. I'd be really interested to hear about any developments along those lines, as I may want to steal them for my own (error-diffused based) image converter.

For VGA and 350-line EGA, you can reprogram the palette to include the 8 corners of the sRGB cube which will enable you to reproduce all colours. It'd be an interesting exercise to try to figure out which other 8 colours (for EGA and for VGA) enable the best possible dithered reproduction of images. Windows uses 2 shades of grey and dark versions of the 6 saturated corner colours - I'm not convinced that this is optimal, though (and it is also really ugly compared to the RGBI colours). That's another thing I'd be interested to hear about if anyone does some playing around with it!
 
Thank for the link, reenigne, I hadn't seen that one before.

So just to clarify, this is an algorithm to dither to an idealized RGBI palette. There is no question the default colors for the VGA mode 12h are far from idealized. That mess of a color for yellow is abysmal. I really should have reprogrammed the colors for a better match. However, the code would really have been used for generating a brush for filling in large areas of the screen, not creating a dithered representation of an RGB image. Doing so was just a way to validate the speed and relative accuracy of the algorithm. An RGBI palette does present some interesting challenges to get decent results, i.e. the Windows driver doesn't use 2 shades of grey when dithering, as including the darker grey is rather difficult. It does provide the dark grey as a "closest match", though.

For dithering an RGB image, there are much better approaches than building a 4x4 brush, then extracting the pixel from the brush like I did here. I tried some other approaches that sub-divided the RGB cube into sections based on the RGBI values, but I couldn't get a consistent brush pattern. It might have been a better approach for converting an RGB image with error diffusion. Rethinking the color matches using different color models might provide better results, too. That's where building a dithered brush to the RGBI palette was greatly simplified by using the HSV cone model.
 
Went back to the dither code to address a few issues. First, reprogrammed the palette registers so that yellow looks like yellow. Then tweak the dither matrices to get rid of the flat spot in the middle of the transition from dim to bright colors. Finally, add code to include the dark grey color to the available dither colors.

To improve the quality of the displayed image, i added a gamma value to adjust the RGB colors in the sample application. It really helped out my cheap, washed out LCD panel. The DOSBox screen capture doesn't really do the updates justice (added to the GitHub page).
 
Back
Top