|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionWhat is a bitmap and what is a DIB? Is there a difference? Why the Windows API has such functions as Honestly speaking, for a long time, I had no answers for all those questions. My guess is that I was not the only one. All that became perfectly clear for me only after I had to write my own kernel-mode video driver. I believe that every programmer who works with the GDI should know all those answers. 2D-acceleration epopeeWhat is a video card? Obviously, it is a hardware device that knows to hold an image and transmit it to some display device (CRT monitor or etc.). The image is held in the video card's internal memory (video memory), and the video card produces a signal for the CRT's cathode ray at some rate (refresh rate). This is in general; today's video cards know to do much more. What is the size of images in general? How many resources do they take? It can be said approximately that an image 640x480 with 8 bits per pixel takes 300k, 800x600 with 16 bpp - 937.5k, and if we talk about 1024x768 with 24bpp - we reach 2.25Mb. It is a lot! If you want to display such an image, you must transfer more than 2Mb through the bus. Suppose now, you want to animate this image somehow (move it, for example). It would take a lot of system resources. And if you want to make some adjustments to the picture on the screen: you must pull it from the video memory, alter it, and then upload it back. Also, think about the drawing complexity: such an image's drawing involves altering hundreds of thousands of pixels, a significant payload for the CPU. All that led to several inventions in 2D-imaging.
TerminologyBefore we continue, let's agree on the terminology.
I expressly defined the terminology more precisely. This is because functions names in the GDI API are very confusing. From the GDI function names, it could be concluded that a “bitmap” is something that is opposed to a DIB, whereas in fact, a DIB is just a special case of a “bitmap”. In fact, DIBs are opposed to DFBs, both are bitmaps but managed differently. There had to be A word about Windows video driver architecture.The Windows video driver architecture is designed to take as much advantages of the video card’s hardware 2D-accelerations as possible. However, the video card does not have to support all known 2D-acceleration techniques; in fact, it does not have to support any of them at all. Every drawing instruction addressed to the video card ends in some point in the video card’s driver call. This driver is responsible to operate the video card in the proper way (which is manufacturer-dependent) so that the video driver plays the role of the hardware abstraction layer; provides a hardware-independent interface to the hardware. There is a very basic set of functionality that the video driver must implement. It includes setting a specific video mode to the device, and transferring bits to and from the device. So the video card does not have to support 2D-accelerations at all. In addition, there is a set of functionality that the video driver may implement. It includes managing its own bitmaps (DFBs), and supporting several drawing operations on them (and between them) such as bit-block transfer (with or without pixel format conversion), text functions, alpha-blending, stretching, gradient fills, and etc. Every such a function, even if it is reported to the OS by the driver, has a right to refuse to work at any time. What does that mean? First of all, you have to understand that the video card’s driver is involved only for drawing operations on its-managed surfaces, which are the display or a DFB. If you draw something on a DIB, the video card is not involved at all. Suppose now, you call Now, what So that, as we can see, the Windows driver architecture knows to take advantages of the hardware 2D-accelerations, whereas the only thing that is mandatory for the video card is to know to transfer the image to and from the display. DFB vs DIB.After we know the difference between DFB and DIB, let’s discuss their advantages. Drawing operations on DFBs are hardware-accelerated (at least part of them), hence they are usually very fast. Also, transfers between them and between them and the display are very fast too. So, why would we want to use DIBs at all? The reason is that sometimes drawing output we need is so complex that it can’t be expressed in terms of standard GDI functions. In such cases, it is better to have a system-memory pointer to the image’s bits and alter them directly. Well, this is not an easy way, but it gives the best possible performance, and it is used generally by graphics-oriented programs, such as Photoshop, ACDSee, and etc. Another way is to use nothing but GDI API.How do we create a DFB and a DIB? First of all: there is no function that is guaranteed to create a DFB! As we know, the video driver is not required to support DFBs at all. And even if it does, it is allowed to refuse, either because of the video memory shortage or because the pixel format you ask is not supported or not accelerated by the video card. There are functions that attempt to create a DFB. If it fails – they create and return a DIB with adequate parameters. Those functions are BTW, I’ve looked on a couple of video drivers' source code. All of them supported DFBs only with the pixel format identical to the current display’s format, and some of them supported monochrome DFBs regardless of the current video mode. However, there is a function that is guaranteed to create a DIB. And, there is only one such a function. It is called Now, suppose we have a bitmap handle, Useful tips.Suppose you have a bitmap for some owner-draw GUI stuff or animations. And this bitmap won’t change; you are not going to draw on it. Indeed, a DBF would be much more efficient here than a DIB. So, if you create it by yourself – use // This function converts the given bitmap to a DFB. // Returns true if the conversion took place, // false if the conversion either unneeded or unavailable bool ConvertToDFB(HBITMAP& hBitmap) { bool bConverted = false; BITMAP stBitmap; if (GetObject(hBitmap, sizeof(stBitmap), &stBitmap) && stBitmap.bmBits) { // that is a DIB. Now we attempt to create // a DFB with the same sizes, and with the pixel // format of the display (to omit conversions // every time we draw it). HDC hScreen = GetDC(NULL); if (hScreen) { HBITMAP hDfb = CreateCompatibleBitmap(hScreen, stBitmap.bmWidth, stBitmap.bmHeight); if (hDfb) { // now let's ensure what we've created is a DIB. if (GetObject(hDfb, sizeof(stBitmap), &stBitmap) && !stBitmap.bmBits) { // ok, we're lucky. Now we have // to transfer the image to the DFB. HDC hMemSrc = CreateCompatibleDC(NULL); if (hMemSrc) { HGDIOBJ hOldSrc = SelectObject(hMemSrc, hBitmap); if (hOldSrc) { HDC hMemDst = CreateCompatibleDC(NULL); if (hMemDst) { HGDIOBJ hOldDst = SelectObject(hMemDst, hDfb); if (hOldDst) { // transfer the image using BitBlt // function. It will probably end in the // call to driver's DrvCopyBits function. if (BitBlt(hMemDst, 0, 0, stBitmap.bmWidth, stBitmap.bmHeight, hMemSrc, 0, 0, SRCCOPY)) bConverted = true; // success VERIFY(SelectObject(hMemDst, hOldDst)); } VERIFY(DeleteDC(hMemDst)); } VERIFY(SelectObject(hMemSrc, hOldSrc)); } VERIFY(DeleteDC(hMemSrc)); } } if (bConverted) { VERIFY(DeleteObject(hBitmap)); // it's no longer needed hBitmap = hDfb; } else VERIFY(DeleteObject(hDfb)); } ReleaseDC(NULL, hScreen); } } return bConverted; } // This function converts the given bitmap to a DIB. // Returns true if the conversion took place, // false if the conversion either unneeded or unavailable bool ConvertToDIB(HBITMAP& hBitmap) { bool bConverted = false; BITMAP stBitmap; if (GetObject(hBitmap, sizeof(stBitmap), &stBitmap) && !stBitmap.bmBits) { // that is a DFB. Now we attempt to create // a DIB with the same sizes and pixel format. HDC hScreen = GetDC(NULL); if (hScreen) { union { BITMAPINFO stBitmapInfo; BYTE pReserveSpace[sizeof(BITMAPINFO) + 0xFF * sizeof(RGBQUAD)]; }; ZeroMemory(pReserveSpace, sizeof(pReserveSpace)); stBitmapInfo.bmiHeader.biSize = sizeof(stBitmapInfo.bmiHeader); stBitmapInfo.bmiHeader.biWidth = stBitmap.bmWidth; stBitmapInfo.bmiHeader.biHeight = stBitmap.bmHeight; stBitmapInfo.bmiHeader.biPlanes = 1; stBitmapInfo.bmiHeader.biBitCount = stBitmap.bmBitsPixel; stBitmapInfo.bmiHeader.biCompression = BI_RGB; if (stBitmap.bmBitsPixel <= 8) { stBitmapInfo.bmiHeader.biClrUsed = 1 << stBitmap.bmBitsPixel; // This image is paletted-managed. // Hence we have to synthesize its palette. } stBitmapInfo.bmiHeader.biClrImportant = stBitmapInfo.bmiHeader.biClrUsed; PVOID pBits; HBITMAP hDib = CreateDIBSection(hScreen, &stBitmapInfo, DIB_RGB_COLORS, &pBits, NULL, 0); if (hDib) { // ok, we're lucky. Now we have // to transfer the image to the DFB. HDC hMemSrc = CreateCompatibleDC(NULL); if (hMemSrc) { HGDIOBJ hOldSrc = SelectObject(hMemSrc, hBitmap); if (hOldSrc) { HDC hMemDst = CreateCompatibleDC(NULL); if (hMemDst) { HGDIOBJ hOldDst = SelectObject(hMemDst, hDib); if (hOldDst) { if (stBitmap.bmBitsPixel <= 8) { // take the DFB's palette and set it to our DIB HPALETTE hPalette = (HPALETTE) GetCurrentObject(hMemSrc, OBJ_PAL); if (hPalette) { PALETTEENTRY pPaletteEntries[0x100]; UINT nEntries = GetPaletteEntries(hPalette, 0, stBitmapInfo.bmiHeader.biClrUsed, pPaletteEntries); if (nEntries) { ASSERT(nEntries <= 0x100); for (UINT nIndex = 0; nIndex < nEntries; nIndex++) pPaletteEntries[nEntries].peFlags = 0; VERIFY(SetDIBColorTable(hMemDst, 0, nEntries, (RGBQUAD*) pPaletteEntries) == nEntries); } } } // transfer the image using BitBlt function. // It will probably end in the // call to driver's DrvCopyBits function. if (BitBlt(hMemDst, 0, 0, stBitmap.bmWidth, stBitmap.bmHeight, hMemSrc, 0, 0, SRCCOPY)) bConverted = true; // success VERIFY(SelectObject(hMemDst, hOldDst)); } VERIFY(DeleteDC(hMemDst)); } VERIFY(SelectObject(hMemSrc, hOldSrc)); } VERIFY(DeleteDC(hMemSrc)); } if (bConverted) { VERIFY(DeleteObject(hBitmap)); // it's no longer needed hBitmap = hDib; } else VERIFY(DeleteObject(hDib)); } ReleaseDC(NULL, hScreen); } } return bConverted; }
|
||||||||||||||||||||||