原文:DFB vs. DIB

来源:互联网 发布:淘宝双十一抽奖 编辑:程序博客网 时间:2024/04/27 19:04

DFB vs. DIB
By Vladislav Gelfer

The difference between DFB and DIB.
Introduction
What is a bitmap and what is a DIB? Is there a difference? Why the Windows API has such functions as CreateBitmap, CreateCompatibleBitmap, CreateDIBitmap, and CreateDIBSection? And what is the difference between SetBitmapBits and SetDIBits?

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 epopee
What 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.

Video cards that handle drawing instructions. That means that in order to draw "hello world" on the screen, you don't need anymore to synthesize the output image and then upload it to the video memory. Now, you can just send an instruction to the video card's internal processor with all its parameters (font, string, brush, placement, etc.), and the clever video card will do the entire job. You save the CPU clocks (during video card's processor work, the CPU can do other jobs), and you also need to transfer almost nothing through the bus, just the drawing parameters.
Device-managed bitmaps (and frame buffers). Suppose you want to display something above your image and then remove it. Of course, there is no such a thing as 'above', the screen is flat. Theoretically, you have to download the obscured part of your image and store it somewhere (a bitmap), display there another image, and then upload the saved bitmap back. Nowadays, there is no need for it. Video cards have much more video memory than one screen takes. Part of this video memory is used for the so-called device-managed bitmaps. Video cards can perform drawing operations on them, between them, and between them and the screen. Again, there is a great performance advantage.
Terminology
Before we continue, let's agree on the terminology.

Bitmap. It is a kind of a data block that represents a rectangular image. It has sizes and a specific format. Well, historically bitmap ("bit-map") meant a matrix with a specified color depth (bits per pixel), whereas each bit somehow represented a specific pixel's color (or part of it). But under Windows 98/2K, bitmaps can also have compressed formats: JPG, PNG, RLE, so that strictly speaking, bitmaps are not always just matrixes.
Device context (DC). It is just an abstract structure that holds several attributes and handles to other drawing objects. It is not a device, it doesn't consume significant resources. You don't draw on DC. In fact, you draw on either bitmap (which is selected into it), or on the device that is represented by this DC. In fact, it is only needed to supply some default parameters for drawing functions; otherwise every such a function would take dozens of parameters. For instance, when you call TextOut, you supply it only the string and its placement, whereas there are also font, background and foreground colors, fill mode, clipping region, coordinates transform and etc.
Device-independent bitmap (DIB). It is a bitmap with known pixel format. The data it contains (bits) reside in the system memory and can be accessed by the application directly. In other words, to draw "hello world" on it, you can either select it into a DC and call TextOut function (or etc.), or you can directly alter its bits (you must know which). The effect would be the same.
Device-format bitmap (DFB), or device-dependent bitmap (DDB). It is a bitmap that is managed by the hardware device (usually a video card). The manufacturer is free to manage the image the way he wants, so that the pixel format is not defined. Hence, you cannot access the image's data directly. It is also usually kept in the non-system memory (video memory). To draw "hello world" on it, you can use GDI's TextOut function, which ends in the graphics card's driver call.
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 CreateDIBxxx and CreateDFBxxx functions, whereas GDI calls them CreateDIBxxx and CreateBitmapxxx. In some places (but not all), the “Bitmap” term in fact should be replaced by DFB. So – never rely on the function name! Always see the description of what it does.

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 AlphaBlend GDI function, whereas the DC represents either a display or a DFB. First, it checks if the video card supports alpha-blending acceleration (if it does. it must have provided a pointer to its function). If it does – the OS calls the driver-supplied DrvAlphaBlend function, otherwise Windows EngAlphaBlend function is involved. The video card’s driver examines parameters supplied in its DrvAlphaBlend function and decides if it wishes to handle it. If it does – it calls the video card in the hardware-dependent way (through so-called miniport driver); otherwise the driver is free to call the EngAlphaBlend function to do the job, as if it didn’t supply the DrvAlphaBlend at all.

Now, what EngAlphaBlend function does? It must perform somehow the alpha-blending operation, although the video driver didn’t support it. In such a case, the OS breaks the requested operation into simpler tasks and calls the video driver again. For instance, it could synthesize the resulting image within the system memory (DIB), and than ask the video driver to draw that DIB. Or it could convert the source parameters (which could be bitmaps as well) into other formats, eliminating the need for the driver to convert from one format to another, and than retry the DrvAlphaBlend. Again, the driver has a right not to handle the call, and the OS must continue to simplify the driver’s job. In such a way, it can reach a very basic level, which the driver can’t refuse to execute.

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 SetPixel and GetPixel functions, to build the image pixel by pixel. Doing so for DFBs leads to a catastrophic performance degradation, because every such a call involves a heavy transaction, whereas calling those functions for DIBs is OK.

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 CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap (when the DC refers to a DFB or a device), and CreateDIBitmap, although its name is confusing.

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 CreateDIBSection. (Again, never rely on a function name, always see its description!)

Now, suppose we have a bitmap handle, HBITMAP. How do we know what it refers to, a DFB or a DIB? Calling GetObjectType won’t help, because they both are OBJ_BITMAP. Fortunately, there is a way to discover who is who. I discovered it occasionally. You can call GetObject function to fill the BITMAP structure with the bitmap’s parameters. If the bmBits member is NULL – the bitmap is a DFB, otherwise it’s a DIB.

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 CreateBitmap to attempt to create a DFB. But sometimes you receive a bitmap handle from other functions, such as LoadBitmap, LoadImage, and etc. Some of them usually try to create DFBs, and some don’t. In such cases, the following two conversion functions would be useful:

// 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;
}

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝支付密码被锁定怎么办 淘宝支付密码锁定了怎么办 很抱歉手机淘宝停止运行怎么办 苹果手机桌面淘宝图标不见了怎么办 提示网页上有错误怎么办 淘宝定制家具违约两个月怎么办 同行在买家评价下面评论怎么办 淘宝知识产权侵权被投诉了怎么办 闲鱼买家说是假货退货怎么办 日验证失败次数上限怎么办 淘宝卖家发货不全怎么办 子账号登录不上怎么办 淘宝宝贝曝光量少怎么办 淘宝买东西退款卖家拒绝怎么办 华为手机淘宝不能收藏宝贝怎么办 包裹发出不想要了怎么办 淘宝改登录密码忘记了怎么办 三星s8应用闪退怎么办 淘宝购物商家不发货怎么办 淘宝商家迟迟不发货怎么办 买家地址写错了怎么办 买家写错快递地址怎么办 淘宝退款选择服务类型出不来怎么办 网购东西发错了怎么办 拒签商家不退款怎么办 红米手机无响应怎么办 淘宝网登录密码忘记了怎么办 淘宝网密码忘记了怎么办 京东抢购不发货怎么办 微商不想做了怎么办 减肥过程中饿了怎么办 滴滴车龄超过6年怎么办 网络公选课没过怎么办 大学网络课挂了怎么办 淘宝虚拟订单买家恶意退款怎么办 淘宝卖家虚拟发货怎么办 淘宝买虚拟产品被骗了怎么办 哈尔滨暖气低于十八度怎么办 淘客店铺没人买怎么办 淘宝商家不给退货怎么办 淘宝卖家拒绝退款申请怎么办