The vector engine saga
One of the features required by the host application is for the vector engine to create a snapshot of the viewport at a given size. The current API implementation returns an HBITMAP for use by the client app and uses GDI (through MFC) for rendering the viewport. This in itself was OK, but since the viewport size is declared by the host application and the background for the vector engine is usually a bitmap the rendering engine copies the bitmap onto the viewpoint via GDI, which means the bitmap gets rescaled by GDI. This is a big nono because GDI rescaling is both horribly slow and looks like crap (na?ve rescaling, no nearest-neighbor or bicubic).
Bottom line, I had to rewrite the background rendering to utilize DirectDraw; luckily I've already done this for the actual rendering routines in the engine, and the snapshot generation uses that code. I just had to create a DirectDraw off-screen surface, render to its DC instead of the current compatible DC created from the screen surface, create a bitmap from the off-screen surface and return it.
It took very little time to write the rendering code (particularly after finding a couple of tutorials), but the copied bitmap wouldn't save properly; the resulting BMP file had a black rectangle instead of the rendered image, and when I tried to copy the bitmap to the clipboard I couldn't display it (got a "can't copy data from clipboard" error message from mspaint, and clipbrd wouldn't display anything). It took an additional several hours of beating around the proverbial GDI bush to find a solution, and I still can't figure out why it works:
- Create a DirectDraw surface
- Render image
- Create a compatible DC for the surface (bmpDC)
- Create a compatible bitmap for the surface
- Select the compatible bitmap into bmpDC
- Blit the surface DC onto bmpDC
- Here comes the cinch: call ::GetDIBits to fill a BITMAPINFO structure, then to get the bitmap bits (note: make sure to negate the bitmap height, or you'll get an inverted bitmap)
- Delete the original bitmap
- Recreate the bitmap using ::CreateBitmap with the info from the previous step
- Clean up
- Return the newly created bitmap
What really baffles me is that what I'm doing is effectively creating a device-dependant bitmap (DDB) out of the previously created compatible DDB. If that is the case, why is the newly created bitmap functional (that is, I can save from it and copy it to the clipboard properly)? Why was the original bitmap behavior different? And why, when I tried to ::CreateDIBitmap instead, I consistently got an error where the documentation specifically states the only possibly error is an invalid parameter (there were none that I could find)?
I'm completely baffled by this; the solution outlined above is (aside from being ugly) not supposed to work. Has anyone any idea?