Bitmap Basics - A GDI tutorial

转载 2004年09月18日 11:41:00
  • Download Tutorial 1 Source - 42Kb
  • Download Tutorial 2 Source - 10Kb
  • Download Tutorial 3 Source - 28Kb
  • Download Tutorial 4 Source - 29Kb

    Windows GDI Tutorial 1 - Drawing a bitmap

    Bitmaps and palettes are both potentially the most useful part and most confusing parts of the GDI subsystem to neophyte coders. In this, and the following GDI tutorials I will explain how to draw bitmaps onto a window, how to implement bitmap transparency and how to draw animations in a window without any flicker or flashing.

    This being the first tutorial I start by explaining the basic concepts of a device context, and how to properly use them.

    GDI & DC's

    GDI stands for "Graphics Device Interface", DC for "Device Context". The designers of Windows decided that it would be nice to have a single way of drawing to all "things", and thus developed GDI as a universal set of routines that can be used to draw onto a screen, printer, plotter or bitmap image in memory.

    The GDI library is based around an object called a Device Context. A Device Context is a handle to a drawing surface on some device - Device Contexts can typically be obtained for the display device (the entire screen), printers and plotters. Most commonly worked with are window dc's (a display DC that merely represents the area of a single window) and a memory DC that represents a bitmap as a device.

    What these objects all have in common (display, printer, bitmap, etc) is they have some concept of a "drawing surface" where output will appear.

    Associated with a Device context a number of tools that can be used to act on the associated drawing surface: Pens, brushes, fonts etc. In the case of physical devices like a plotter there will be a one to onw mapping of HPEN to physical pen. In the case of the display, or memory DC's, a number of preset pens are provided, and more can be created on the fly as needed.

    BITMAPS

    A Bitmap is the in-memory representation of a drawing surface. By "selecting" a bitmap into a memory DC, the DC then represents that bitmap as a drawing surface, and all the normal GDI operations can be performed on the bitmap. GDI also has a number of functions that can copy areas from the drawing surface of one DC to another, so bitmaps then are a useful way to store images in memory that will later be copied to the display (or other devices).

    A Bitmap that can be slected into a DC is called a "Device Dependent Bitmap" and is represented to the programmer by an HBITMAP handle.

    There is another kind of Bitmap called a "Device Independent Bitmap". This type of bitmap is defined in the windows header files as a number of structs that are filled in by the programmer. Being "device independent" means there is no HBITMAP that can be selected into a "Device context" so GDI operations cannot be performed on this type of bitmap. There are a couple of "DIB" specific functions that can create a DDB (device dependent bitmap) given a DIB, or copy areas from a DIB onto a DC.

    The Tutorial

    In this tutorial (GDI01) I demonstrate how to load a bitmap resource as a device dependent bitmap, and how to display the bitmap on a window. The bitmap is loaded in the applications WM_CREATE handler, and shown in the applications WM_PAINT handler.

    In GDI tutorial 2 I will demonstrate how to implement bitmap transparency using Device Dependent Bitmaps.

    The tutorial comprises a single window that I create in main.cpp. Stored in a global variable is the bitmap handle that is initialized in the OnCreate() function, used in OnPaint(), and destroyed in OnDestroy(). The example bitmap shipped with the tutorial is a 256 color image, and will appear rather flat on a 256 color display. The lack or proper color on 256 color displays will be fixed in tutorial 3 where I intend to discuss palettes.


    Windows GDI Tutorial 2 - Transparency

    Simple bitmap graphics is quite powerfull. Soon however one gets to a situation where one wants to overlap two non-rectangular images. Windows bitmaps are "unfortunately" rectangles so what one needs to do is mark certain areas of the bitmap as "not part of the image". In other words: transparent.

    GDI has no built in transparency support - you have to implement transparency in bitmaps yourself. Specific versions of GDI have had support for transparent areas in a bitmap - NT4 for instance has a specif function, and the VFW kit for Windows 3.11 included an extended devmode option that could be set in a DC to specify that the color set in SetBkColor was to be transparent. These methods however are not compatible with other platforms (notably Windows 95) and should probably be avoided.

    A Simple Method

    The most simple method to implement a bitmap with "transparent" areas is to have two bitmsps. One bitmap specifies the image - and all the "transparent" areas are set to black. The other bitmap is monochrome / black and white. This bitmap has white pixels around the edges of the image. The black pixels forming a siloette of the image.

    GDI supports boolean operations when combinig the contents of DCs surfaces, and we use this to our advantage here. To paint a "transparent" bitmap pair onto a DC the following process is performed:

    1. Blit the monochrome bitmap onto the destination using SRCAND as the raster code. The SRCAND code directs GDI to set each destination pixel as the binary and of the destination pixel and the source pixel. In this case black acts like zero, and white like all 1's: Destination pixels where the source are black become black. Destination pixels where the source is white are left untouched. The effect is of a hole being cut in the destination image.

    2. Blit the color image bitmap onto the destination using SRCPAINT. The SRCPAINT raster code directs GDI to set each destination pixel as the binary OR of the previous destination value and source pixel. Now, due to the previous step, wherever the source has non-black pixels the destination has been zero'd. And zero OR something is that something. So, this step combines the two images seamlessly.

    A Different method

    The above method of having paired bitmaps, a color image bitmap, and a monochrome mask is in terms of GDI operations the simplest. Some people however perfer not to supply a 2nd monochrome mask image: They specify the transparent areas of the bitmap by assigning a certain color to be transparent. This color fills the area around the image, and care must be taken not to use the "magic" color in the image itself.

    Also, special care must be taken when using this kind of bitmap with GDI on low color displays: GDI always creates "compatible" DDBs (and you the programmer always wants to use "compatible" bitmaps) in the format of the display mode. This can result in a loss of color "resolution" and a whole range of colors might be mapped to the magic transparent color. It is therefore best to make sure that the transparent color is one of the twenty system colors that are guaranteed to always exist.

    Monochrome Bitmaps

    Dealing with monochrome bitmaps can at first be tricky due to the lack of explicit documentation dealong with how they interact with color bitmaps. In the example code I Blit directly from a color bitmap to a monochrome bitmap, and later in the reverse direction. To figure out whats going to happen in this situation you have to know how GDI maps monochrome bitmap pixels to color bitmap pixels.

    The background "color" of a bitmap is white, and is stored as binary 0. When combined with a color bitmap via a raster operation (typically in a call to BitBlt) the background pixels in the monochrome bitmap are first mapped to the background color of the color bitmaps DC. This is normally set to white (RGB(255,255,255)), but can easilly be changed by using the SetBKColor() API. The foreground pixels of a monochrome bitmap (binary 1) is mapped to the text color of destination DC - default is black (RGB(0,0,0)), but once again the SetTextColor() API can be used to change that.

    When transferring bytes from a color to a monochrome bitmap, the mapping is simpler. All pixels that are the same color as the background color are mapped to the background color on the mono bitmap (0). All other pixels are demed to be foreground.

    Raster Operations

    Raster operations (SRCPAINT, SRCCOPY) etc are performed only after any mapping has taken place. They are performed bytewise on the image bytes. this is the most efficient means of operation, but it means that logical raster operations performed on 256 color displays will tend to have unexpected results, as any palettes are totally ignored by this process. The default twenty system colors will behave in an expected way, as the system palette has been arranged specially so the mappings work. Instead of simply using the 1st twenty colors, the system palette uses the first ten, and last ten colors, so when a NOT is performed on black (color index 0) the result of the NOT operation (color index 255) is the expected white.

    The Tutorial

    The code demonstrates how to create a monochrome bitmap from the color image. The code uses a simple method to find out what color to use as the transparent color - it checks the color of the top left pixel. The files, when compiled and run should procude a window with an irritating checkerboard pattern. The transparent image is painted over this checker pattern. The files to download are listed at the top of this article.

    The relevent functions in main.cpp are heavily commented. Look in the WM_CREATE handler where the main bitmap is loaded and a monochrome version is generated. The WM_PAINT handler demonstrates how to blit the two bitmaps correctly. WM_DESTROY cleans up the two bitmaps. Also look in the RegisterClass() function of the frame to see where the checkered background is set.

    Whats to come

    In the next gdi tutorial I look at:

    • loading bitmap resources manually.
    • Creating bitmaps and palettes from bitmap resources.
    • Using palettes correctly to ensure color fastness on 256 color display hardware.
    In tutorial 4 I will examine some advanced uses of DIBs. While slower than the DDBs weve been using up till now they are ameanable to a number of tricks that make them quite useful.


    Windows GDI Tutorial 3 - Using Palettes and accessing bitmap resources

    Trying to figure out how to implement palette support can be a quite messy process. The documentation that exists is never entirely clear on what approach one should be taking and what to do when it doesn't work. So...

    Loading resources directly

    Bitmap resources, and bitmap files on disk, are stored in the windows DIB format. As a resource, a bitmap consists of a BITMAPINFO structure describing the bitmap followed by the actual image data as an array of bytes. On disk in a .bmp file, the file starts with a BITMAPFILEHEADER structure, followed by a BITMAPINFO structure. The start of the image data is indicated by a field in the BITMAPFILEHEADER structure, and does not necessairly follow the BITMAPINFO structure directly. This diffrence introduces some annoying incompatiblities when dealing with bitmap resources, and bitmap files.

    The LoadBitmap() function, while simple to use, is too braindead to be used in a situation where your application requires palette support, as it creates all bitmaps using the system default palette which only has 20 colors. While only a problem on 256 color display setups, its a very ugly problem - all your loaded bitmaps are displayed with a mere 20 colors.

    The solution is to use the resource functions to load the bitmaps directly using the resource functions to search the exe file for the bitmap resource, and get pointers to the resource data. As we know the data is stored in DIB format, we can use the CreateDIBitmap() API to create a DDB from the DIB data.

    Palettes and Bitmaps

    A bitmap to windows is just an array of bytes. In 16 bit and higher modes the color information of the bitmap data is encoded directly in the data. In 256 color mode however there is no color information stored in the bitmap. Each byte in the bitmap is simply an index into a palette of colors.

    So, any operations performed on a bitmap will be performed by GDI will be done using the current selected logical palette.

    Please note that the phrase "logical palette" refers to a GDI palette object - refrenced by a HPALETTE handle. The physical palette refers to the state of the actual display device palette.

    Now, the quickest way to blit a bitmap onto the display would be a simple memcpy operation. And GDI does this as much as possible. In order for the results to look pleasing however, the bytes of the bitmap have to match the correct entries in the physical palette. To ensure this GDI, when it first realizes a palette, creates a mapping of logical palette entries to the system palette at the time. GDI expects that the next time the palette is realized it will be able to take the same mapping.

    The bytes in a bitmap then are drawn from this cache table - NOT the logical palette.

    Anyway. The whole subject is very hairy, and all I can suggest is a full reading of all the available dox you can find on palettes if you wish to truly understand the subject.

    The following notes may ease some potential confusion:

    • Realization is a once off thing. Once RealizePalette() has been called on an HDC, that HPALETTE does not need to be realized again until UnRealizePalette() is called, or WM_PALETTECHANGED or WM_QUERYNEWPALETTE indicates the Palette Manager itself has unrealized all palettes and is starting again. You can select and deselect a HPALETTE as many times as you wish without having to call RealizePalette().
    • The last parameter to RealizePalette() can always be set to FALSE. TRUE would only be used if you were realizing a window DC and specifically do not want the palette to get mapped into the physical palette.
    • Always make sure the palette that a bitmap was created with is selected into any DC you select the bitmap into (the order does not matter).
    • BitBlt between memory DCs requires that the target DC has the same palette selected as the source DC.
    • BitBlt from memory DC to Screen DC requires the source palette is realized.


    Windows GDI Tutorial 4 - DIBs

    Welcome to GDI tutorial 4 - In this tutorial I am going to concentrate on Device Independent Bitmaps.

    DIBs and Bitmap resources

    As should hopefully be clear by now, a bitmap resource is stored as a device independent bitmap. The resource data contains the BITMAPINFO struct, followed by an array of bytes. Passing pointers to these two structs allows loaded bitmap resources to be used directly with all windows API functions that work with DIBs.

    A restriction that should be noted: As resources are paged out of the exe or dll file they were loaded from, care should be taken to avoid writing to the memory. Under Win16, all changes written to a resource might be lost if the resource is unlocked and relocked. Under Win32, writing to resource memory causes a memory exception that the operating system handles to create a duplicate resource.

    DIBs and bitmap files

    Bitmap files too can be directly used as a DIB once loaded into memory. The one diffrence between files and resources is that the file starts with a BITMAPFILEHEADER struct. This struct is directly followed by the BITMAPINFO struct containing the information about the DIB and the color table if present. The BITMAPFILEHEADER struct also unfortunatly contains a file offset to the DIBs byte array, so in a bitmap file the byte array might not follow directly after the BITMAPINFO structure.

    Some resource compilers do not handle bitmap files properly if the bitmap data does not follow directly from the BITMAPINFO struct. They write the padded out information into the resource - in that case there is now way for the bitmap loading code to know that there is a gap between the header and bits, and the image appears corrupted.

    Other Image formats and DDBs

    The most efficient way to work with images is to use device dependent bitmaps. GDI stores DDB bitmaps in the format of the display device - thus DDBs are the most efficient way to work with images. GDI however does not let the programmer touch the bits of a DDB directly. GDI does however have a number of functions that allows the programmer to transfer date from DIBs to DDBs and the reverse direction. These transfer operations are slow, as GDI has to translate each pixel on the soure DDB or DIB to its nearest representation on the target DIB or DDB.

    When loading or saving image formats other that bitmaps from DDBs the programmer therefore usually finds theirself working with the data as a DIB.

    GDI provides the following functions to transfer bits from DIBs to DDBs, DDBs to DIBs and DIBs to DCs:

    • CreateDIBitmap() - this function creates a compatible device dependent bitmap, and initializes it with the passed in DIB.
    • GetDIBits() - this functions translates a DDBs data into a DIB that is passed in.
    • SetDIBits() - Like CreateDIBitmap(), this function intializes a DDB using the DIB data that is passed in.
    • SetDIBitsToDevice() - This function copies a DIB directly (translating each pixel of course) onto a display device context.
    • StretchDIBits() - This function is similar to StretchBlt(), it stretches the source onto a DCs surface - the source data is a DIB.

    DIBs and palettes and converting

    When translating data from a DIB onto a DDB in a SetDIBits(), SetDIBitsToDevice(), or StretchDIBits() call, GDI has a lot of work to do. Even more so if the target display device is operating in 256 (or any other palette) mode.

    The logic that GDI uses to convert a pixel is thus: First, GDI resolves the RGB value of the source (DIB) pixel it is converting. If the DIB is itself has a color table, the pixel index is looked up in the color table, and the retrieved RGB color is used in the GDI operation. Now, RGB value in hand, GDI looks up the RGB value and matches it to the closest color found in the device contexts palette. (all the above calls take a device context - The DC is in a couple of cases merely a carrier of an hpalette). The color in the logical palette is then matched to a color in the physical palette, any raster operations (in the case of SetDIBitsToDevice() or StretchDIBits()) are now applied using the physical index, and the result is stored (on the display or target DDB).

    On non palette devices the situation is much simpler. After the RGB value of the DIB pixel is found, it is combined with the target RGB using the given raster operation.

    DIB Transparency

    A common method of implementing transparency with DDBs is to have a color bitmap, and an associated monochrome bitmap containing a mask. The mask bitmap is combined with the target DC using the SRCAND raster operation. All white pixels leave the destination untouched, all black pixels zero the destination - effectivly cutting a black hole in the target. The color bitmap is then combined with the destination using the SRCPAINT raster operation. This operation ORs each color pixel from the color image with a blacked out pixel in the destination. The source image itself is black where the destination has been left, leaving the non-transparent pixels untouched - the transparent pixles now contain the image.

    A similar method is used with DIBs, but the DIB method does not require two complete DIB's *IF* the DIB has a color table. By blitting the same DIB data twice, once SRCAND with a color table intialized with each tranparent color index set to white, and "data" indexes set to black, and once SRCPAINT with the color table set up with data indexes contianing the correct color, and transparent indexes containig black, the same effect is achieved.

    Note: On 256 color displays the DIB method will fail if the logical palette selected into the destination dc does not contain entries for black and white. This is due to the fact (mentioned above) that the DIB pixels are first matched to the closest entriy in the logical palette, and the found entry from the logical palette is then mapped to the physical palette (that always has the 20 system colors including black and white) before the raster operation is performed.

    The Tutorial

    By now you should recognise the simple tutorial. Once again you will see the aeroplane over a background of clouds. This time the aeroplane and clouds are displayed using DIBs. The demo_OnPaint() function has all the fun stuff as usual. In addition to the bmpapi files there is a primitive DIB holder class in dib.cpp and dib.h. The files you will need are at the top of this article.

  • Chris Becke


    Click here to view Chris Becke's online profile.


    Other popular Bitmaps & Palettes articles:

    使用GDI+ 缩放图片

    好久没更新了, 刚好项目用到GDI+来缩放图像,网上搜索了好多关于gdi+缩放图像的例子 ,大多都是加载文件,而我要用的是视频渲染,直接读取的rgb数据流, 废话少说 ,看代码:srcWidth,sr...
    • zhengxinwcking
    • zhengxinwcking
    • 2013年12月29日 14:39
    • 2367

    RenderScript 让你的Android计算速度快的飞上天!

    在上一篇文章Android自动手绘,圆你儿时画家梦! 中结尾提到,我将介绍提升轮廓提取速度相关内容,今天一起学习Android中的RenderScript。看完本文,你将学会如何使用并行计算技术,提高...
    • huachao1001
    • huachao1001
    • 2016年05月28日 20:04
    • 6429

    MiniGUI——使用 GDI 函数

    简介: 本文讲述 MiniGUI 中 GDI 函数及其使用。主要包括:设备上下文的概念、获取和释放;矩形操作和区域操作;基本绘图函数;位图操作函数;逻辑字体操作函数等。...
    • yangsong512
    • yangsong512
    • 2014年09月04日 17:12
    • 1607

    从资源中加载jpg, png到GDI+ Image

    从资源中加载jpg和png文件, 貌似不应该是个大问题, 一google结果一大堆, 却有两个陷阱,trap啊 1, 是Bitmap(RT_BITMAP)类型的图片无法加载, RT_BITMAP...
    • wwqingyue
    • wwqingyue
    • 2014年12月09日 12:43
    • 1641

    GDI+学习及代码总结之------图像的基本处理

    图像的基本操作 在GDI+中,对图像的处理主要靠两个类,Image类和Bitmap类,Bitmap类是在Image类的基础上派生出来的。这里主要讲Image类的使用,Image类支持对BMP, ...
    • harvic880925
    • harvic880925
    • 2013年06月19日 15:37
    • 18372

    GDI+ 在Delphi程序的应用 -- GDI+图像与GDI位图的相互转换

    Delphi的TBitmap封装了Windows的GDI位图,因此,TBitmap只支持bmp格式的图像,但是在Delphi应用程序中,常常会遇到图形格式的转换,如将Delphi位图TBitmap的图...
    • fyl_077
    • fyl_077
    • 2014年01月11日 19:49
    • 726

    GDI+学习及代码总结之------区域

    在GDI+中,对于区域的部分基本上使用了GDI的区域构造函数与合并方法,所以我们先看看GDI中的区域是如何构造与操作的。 GDI中区域构建与操作 一、基本函数 创建矩形区域: HRGN Crea...
    • harvic880925
    • harvic880925
    • 2013年06月17日 20:28
    • 8132

    windows GDI+ 离屏绘制文字图片

    windows GDI+ 离屏绘制文字图片 由于之前都是在linux开发界面,转到windows还真不适应,然后开发工具是VS2015,目的就是不需要把图片文 字绘制到界面上,而是绘制到一张透明图上,...
    • c553110519
    • c553110519
    • 2017年08月01日 17:53
    • 371

    ffmpeg学习二:《FFmpeg Basics》读书笔记(上)

    为了更好的理解ffmpeg工程,官方推荐了一本书:《FFmpeg Basics》。完整的读完这本书,应该对这个工程能有一个基本的理解了。本菜英文不好,姑且从这本书中提炼出一些比较常用的知识,做个笔记吧...
    • u011913612
    • u011913612
    • 2016年11月29日 16:48
    • 1137

    将数组中的RGBA序列绘制出来——GDI、MFC_GDI、GDI+实现

    如果你有一个字节数组,里面存放着R/G/B/A颜色值序列,如何将它所表示的图片绘制在窗口上呢? 之前在论坛上看到有人提了这么一个问题:http://bbs.csdn.net/topics/390663...
    • CharlesSimonyi
    • CharlesSimonyi
    • 2013年12月10日 13:03
    • 2481
    内容举报
    返回顶部
    收藏助手
    不良信息举报
    您举报文章:Bitmap Basics - A GDI tutorial
    举报原因:
    原因补充:

    (最多只允许输入30个字)