win32 CreateCompatibleBitmap and Scaling an Image

CreateCompatibleBitmap 函数
创建一个 位图(bitmap),该位图兼容指定的device context对应的device。
creates a bitmap compatible with the device that is associated with the specified device context.

HBITMAP CreateCompatibleBitmap(
  HDC hdc,
  int cx,
  int cy
);

参数:

hdc
指向device context.的句柄
cx
宽度
cy
高度

返回值

如果函数成功,返回值是 兼容的 bitmap的 句柄
the return value is a handle to the compatible bitmap (DDB).
如果函数失败,返回值是NULL

备注

通过CreateCompatibleBitmap函数创建的bitmap的颜色格式,和hdc参数对应的设备的颜色格式是一致的。
bitmap 可以被选择进入内存设备DC,需要该内存设备兼容hdc参数对应的设备。
因为内存设备DC同时支持彩色bitmap和单色bitmap,
当CreateCompatibleBitmap函数的hdc参数 指定的是一个 内存设备DC的时候,
CreateCompatibleBitmap函数返回的bitmap的颜色格式,有可能是彩色的bitmap,也有可能是单色的bitmap。
当CreateCompatibleBitmap函数的hdc参数 指定的是一个 非内存设备DC的时候,
CreateCompatibleBitmap函数返回的bitmap的颜色格式 ,调色板 和 指定的deivce context ,保持一致

注意:当一个内存device context 被创建,它初始化选择进入一个为 1 x 1 单色 bitmap。
如果CreateCompatibleBitmap中使用这个内存device context,返回的bitmap,将会创建为一个单色的bitmap。
如果要创建一个 彩色的bitmap,使用创建内存device context的HDC。看下面的代码:

    HDC memDC = CreateCompatibleDC ( hDC );
    HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight );
    SelectObject ( memDC, memBM );

如果应用 设置nWidth 或者nHeight 为0 ,CreateCompatibleBitmap 返回一个 1x1 像素的单色的bitmap的句柄。

如果 CreateCompatibleBitmap 的hdc参数对应的 deivce context,之前已经选择进去了一个bitmap,该bitmap是通过CreateDIBSection函数创建的一个DIB section。
那么CreateCompatibleBitmap 会创建一个DIB section。

当你不需要bitmap的时候,记得使用DeleteObject 来删除它。

例子: Scaling an Image

https://docs.microsoft.com/zh-cn/windows/desktop/gdi/scaling-an-image

一些应用,按比例放大或者缩小图像的显示。
比如,一个绘图应用,会提供一个放大功能,使能用户按照像素来查看和修改图像。

应用使用StretchBlt函数,缩放图像。这个函数和BitBlt类似,
StretchBlt 从一个源DC的bitmap中复制 bitmap数据到 目标DC的bitmap中去。
和BitBlt函数不同的是,StretchBlt 可以缩放图像。缩放是依照目标和源矩形的尺寸。
如果源矩形比目标矩形大,结果图像会缩小。如果源矩形比目标矩形小,结果图像会放大。

如果目标矩形比源矩形小,StretchBlt会移除图像的一些颜色数据。
移除的方法,和指定的stretch模式有关。看下表:

Stretch mode Method
BLACKONWHITE Performs a logical AND operation on the color data for the eliminated pixels and the color data for the remaining pixels.
WHITEONBLACK Performs a logical OR operation on the color data for the eliminated pixels and the color data for the remaining pixels.
COLORONCOLOR Eliminates the color data of the deleted pixels completely.
HALFTONE Approximates the original (source) color data in the destination.

通过SetStretchBltMode函数设置stretch的模式
下面的例子代码来自一个应用,该应用演示所有的StretchBlt函数支持的4种stretch模式。
使用该代码的方法是:

vs2010 创建一个 win32 应用程序:名字是GDIBitmapScaling,不要选择空工程。
工程创建完毕之后,会生成GDIBitmapScaling.cpp 文件,

将GDIBitmapScaling.cpp 文件的内容,替换为下面的代码:

// GDIBitmapScaling.cpp : 定义应用程序的入口点。
//


#include "stdafx.h"
#include "GDIBitmapScaling.h"
#include <commctrl.h>
#include <CommDlg.h>


#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
TCHAR szTitle[MAX_LOADSTRING];                    // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);


int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

     // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_GDIBITMAPSCALING, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GDIBITMAPSCALING));

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GDIBITMAPSCALING));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName    = MAKEINTRESOURCE(IDC_GDIBITMAPSCALING);
    wcex.lpszClassName    = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//

#define NEW_DIB_FORMAT(lpbih) (lpbih->biSize != sizeof(BITMAPCOREHEADER))
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_SYSMENU,
      CW_USEDEFAULT, 0, 1024, 768, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

static   HCURSOR hcurSave;

WORD DIBNumColors (LPVOID lpv)
{
    INT                 bits;
    LPBITMAPINFOHEADER  lpbih = (LPBITMAPINFOHEADER)lpv;
    LPBITMAPCOREHEADER  lpbch = (LPBITMAPCOREHEADER)lpv;

    /*  With the BITMAPINFO format headers, the size of the palette
     *  is in biClrUsed, whereas in the BITMAPCORE - style headers, it
     *  is dependent on the bits per pixel ( = 2 raised to the power of
     *  bits/pixel).
     */
    if (NEW_DIB_FORMAT(lpbih)) {
      if (lpbih->biClrUsed != 0)
        return (WORD)lpbih->biClrUsed;
      bits = lpbih->biBitCount;
    }
    else
      bits = lpbch->bcBitCount;

    if (bits > 8) 
      return 0; /* Since biClrUsed is 0, we dont have a an optimal palette */
    else
      return (1 << bits); 
}
/* Macro to determine to round off the given value to the closest byte */
#define WIDTHBYTES(i)   ((((i)+31) >> 5) << 2)
/******************************************************************************
 *                                                                            *
 *  FUNCTION   : DIBInfo(HANDLE hbi, LPBITMAPINFOHEADER lpbih)                *
 *                                                                            *
 *  PURPOSE    : Retrieves the DIB info associated with a CF_DIB              *
 *               format memory block.                                         *
 *                                                                            *
 *  RETURNS    : TRUE  - if successful.                                       *
 *               FALSE - otherwise                                            *
 *                                                                            *
 *****************************************************************************/
BOOL DIBInfo (HANDLE hbi, LPBITMAPINFOHEADER lpbih)
{
    if (hbi){
      *lpbih = *(LPBITMAPINFOHEADER)hbi;

      /* fill in the default fields */
      if (NEW_DIB_FORMAT(lpbih)) {
        if (lpbih->biSizeImage == 0L)
          lpbih->biSizeImage = WIDTHBYTES(lpbih->biWidth*lpbih->biBitCount) * lpbih->biHeight;

        if (lpbih->biClrUsed == 0L)
          lpbih->biClrUsed = DIBNumColors (lpbih);
      }

      return TRUE;
    }
    return FALSE;
}
/* flags for mmioSeek() */
#ifndef SEEK_SET
#define SEEK_SET        0               /* seek to an absolute position */
#define SEEK_CUR        1               /* seek relative to current position */
#define SEEK_END        2               /* seek relative to end of file */
#endif  /* ifndef SEEK_SET */
VOID ReadPackedFileHeader(HFILE hFile, LPBITMAPFILEHEADER lpbmfhdr, LPDWORD lpdwOffset)
{
    *lpdwOffset = _llseek(hFile, 0L, (UINT) SEEK_CUR);
    _hread(hFile, (LPSTR) &lpbmfhdr->bfType, sizeof(WORD)); /* read in bfType*/            
    _hread(hFile, (LPSTR) &lpbmfhdr->bfSize, sizeof(DWORD) * 3); /* read in last 3 dwords*/
}




/* macro to determine if resource is a DIB */
#define ISDIB(bft) ((bft) == BFT_BITMAP)

/* Header signatutes for various resources */
#define BFT_ICON   0x4349   /* 'IC' */
#define BFT_BITMAP 0x4d42   /* 'BM' */
#define BFT_CURSOR 0x5450   /* 'PT' */
HANDLE ReadDIBBitmapInfo (INT hFile)
{
    DWORD              dwOffset;
    HANDLE             hbi = NULL;
    INT                size;
    INT                i;
    WORD               nNumColors;
    LPRGBQUAD          lprgbq;
    BITMAPINFOHEADER   bih;
    BITMAPCOREHEADER   bch;
    LPBITMAPINFOHEADER lpbih;
    BITMAPFILEHEADER   bf;
    DWORD              dwDWMasks= 0;
    DWORD              dwWidth = 0;
    DWORD              dwHeight = 0;
    WORD               wPlanes, wBitCount;

    if (hFile == HFILE_ERROR)
        return NULL;

    /* Read the bitmap file header */
    ReadPackedFileHeader(hFile, &bf, &dwOffset);

    /* Do we have a RC HEADER? */
    if (!ISDIB (bf.bfType)) {    
      bf.bfOffBits = 0L;               
        _llseek(hFile, dwOffset, (UINT)SEEK_SET); /* seek back to beginning of file */
    }

    if (sizeof(bih) != _hread(hFile, (LPSTR)&bih, (UINT)sizeof(bih)))
      return FALSE;

    nNumColors = DIBNumColors (&bih);

    /* Check the nature (BITMAPINFO or BITMAPCORE) of the info. block
     * and extract the field information accordingly. If a BITMAPCOREHEADER, 
     * transfer it's field information to a BITMAPINFOHEADER-style block
     */
    switch (size = (INT)bih.biSize){
        case sizeof (BITMAPINFOHEADER):
            break;

        case sizeof (BITMAPCOREHEADER):

            bch = *(LPBITMAPCOREHEADER)&bih;

            dwWidth   = (DWORD)bch.bcWidth;
            dwHeight  = (DWORD)bch.bcHeight;
            wPlanes   = bch.bcPlanes;
            wBitCount = bch.bcBitCount;

            bih.biSize           = sizeof(BITMAPINFOHEADER);
            bih.biWidth          = dwWidth;
            bih.biHeight         = dwHeight;
            bih.biPlanes         = wPlanes;
            bih.biBitCount       = wBitCount;
            bih.biCompression    = BI_RGB;
            bih.biSizeImage      = 0;
            bih.biXPelsPerMeter  = 0;
            bih.biYPelsPerMeter  = 0;
            bih.biClrUsed        = nNumColors;
            bih.biClrImportant   = nNumColors;

            _llseek(hFile, (LONG)sizeof (BITMAPCOREHEADER) - sizeof (BITMAPINFOHEADER), (UINT)SEEK_CUR);
            break;

        default:
            /* Not a DIB! */
            return NULL;
    }

    /*  Fill in some default values if they are zero */
    if (bih.biSizeImage == 0){
        bih.biSizeImage = WIDTHBYTES((DWORD)bih.biWidth * bih.biBitCount) * bih.biHeight;
    }
    if (bih.biClrUsed == 0)
        bih.biClrUsed = DIBNumColors(&bih);

    /* Allocate for the BITMAPINFO structure and the color table. */
    if ((bih.biBitCount == 16) || (bih.biBitCount == 32))
      dwDWMasks = sizeof(DWORD) * 3;
    hbi = GlobalAlloc (GPTR, (LONG)bih.biSize + nNumColors * sizeof(RGBQUAD) + dwDWMasks);
    if (!hbi)
        return NULL;
    lpbih = (LPBITMAPINFOHEADER)hbi;
    *lpbih = bih;

    /* Get a pointer to the color table */
    lprgbq = (LPRGBQUAD)((LPSTR)lpbih + bih.biSize);
    if (nNumColors){
        if (size == sizeof(BITMAPCOREHEADER)){
            /* Convert a old color table (3 byte RGBTRIPLEs) to a new
             * color table (4 byte RGBQUADs)
             */
            _hread(hFile, (LPSTR)lprgbq, (UINT)nNumColors * sizeof(RGBTRIPLE));

            for (i = nNumColors - 1; i >= 0; i--){
                RGBQUAD rgbq;

                rgbq.rgbRed      = ((RGBTRIPLE*)lprgbq)[i].rgbtRed;
                rgbq.rgbBlue     = ((RGBTRIPLE*)lprgbq)[i].rgbtBlue;
                rgbq.rgbGreen    = ((RGBTRIPLE*)lprgbq)[i].rgbtGreen;
                rgbq.rgbReserved = (BYTE)0;

                lprgbq[i] = rgbq;
            }
        }
        else
            _hread(hFile, (LPSTR)lprgbq, (UINT)nNumColors * sizeof(RGBQUAD));
    } else
        if (dwDWMasks)
           _hread(hFile, (LPSTR)lprgbq, dwDWMasks);

    if (bf.bfOffBits != 0L){
        _llseek(hFile, dwOffset + bf.bfOffBits, (UINT)SEEK_SET);
        }

    return hbi;
}
HGLOBAL GlobalFreeDIB(HGLOBAL hDIB)
{
   LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)hDIB;

   if (!lpbi->biClrImportant)
     return GlobalFree(hDIB);

   if (GlobalFlags((HGLOBAL)lpbi->biClrImportant) == GMEM_INVALID_HANDLE) {
    SetLastError(0);
    return GlobalFree(hDIB);
  } else
    return GlobalFree((HANDLE)lpbi->biClrImportant); 
}
/******************************************************************************
 *                                                                            *
 *  FUNCTION   :  ColorTableSize(LPVOID lpv)                                  *
 *                                                                            *
 *  PURPOSE    :  Calculates the palette size in bytes. If the info. block    *
 *                is of the BITMAPCOREHEADER type, the number of colors is    *
 *                multiplied by 3 to give the palette size, otherwise the     *
 *                number of colors is multiplied by 4.                        *
 *                                                                            *
 *  RETURNS    :  Color table size in number of bytes.                        *
 *                                                                            *
 *****************************************************************************/
WORD ColorTableSize (LPVOID lpv)
{
    LPBITMAPINFOHEADER lpbih = (LPBITMAPINFOHEADER)lpv;

    if (NEW_DIB_FORMAT(lpbih))
    {
      if (((LPBITMAPINFOHEADER)(lpbih))->biCompression == BI_BITFIELDS)
         /* Remember that 16/32bpp dibs can still have a color table */
         return (sizeof(DWORD) * 3) + (DIBNumColors (lpbih) * sizeof (RGBQUAD));
      else
         return (DIBNumColors (lpbih) * sizeof (RGBQUAD));
    }
    else
      return (DIBNumColors (lpbih) * sizeof (RGBTRIPLE));
}

/******************************************************************************
 *                                                                            *
 *  FUNCTION   :OpenDIB(LPSTR szFilename)                                     *
 *                                                                            *
 *  PURPOSE    :Open a DIB file and create a memory DIB -- a memory handle    *
 *              containing BITMAPINFO, palette data and the bits.             * 
 *                                                                            *
 *  RETURNS    :A handle to the DIB.                                          *
 *                                                                            *
 *****************************************************************************/
HANDLE OpenDIB (LPSTR szFilename)
{
    HFILE               hFile;
    BITMAPINFOHEADER    bih;
    LPBITMAPINFOHEADER  lpbih;
    DWORD               dwLen = 0;
    DWORD               dwBits;
    HANDLE              hDIB;
    HANDLE              hMem;
    OFSTRUCT            of;

    /* Open the file and read the DIB information */
    hFile = OpenFile(szFilename, &of, (UINT)OF_READ);
    if (hFile == HFILE_ERROR)
        return NULL;

    hDIB = ReadDIBBitmapInfo(hFile);
    if (!hDIB)
        return NULL;
    DIBInfo(hDIB, &bih);

    /* Calculate the memory needed to hold the DIB */
    dwBits = bih.biSizeImage;
    dwLen  = bih.biSize + (DWORD)ColorTableSize (&bih) + dwBits;

    /* Try to increase the size of the bitmap info. buffer to hold the DIB */
    hMem = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE);

    if (!hMem){
        GlobalFreeDIB(hDIB);
        hDIB = NULL;
    }
    else
        hDIB = hMem;

    /* Read in the bits */
    if (hDIB){
        lpbih = (LPBITMAPINFOHEADER)hDIB;
        _hread(hFile, (LPSTR)lpbih + (WORD)lpbih->biSize + ColorTableSize(lpbih), dwBits);
    }
    _lclose(hFile);

    return hDIB;
}/* Macros to display/remove hourglass cursor for lengthy operations */
#define StartWait() hcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT))
#define EndWait()   SetCursor(hcurSave)
/******************************************************************************
 *                                                                            *
 *  FUNCTION   : BitmapFromDIB(HANDLE hDIB, HPALETTE hPal)                    *
 *                                                                            *
 *  PURPOSE    : Will create a DDB (Device Dependent Bitmap) given a global   *
 *               handle to a memory block in CF_DIB format                    *
 *                                                                            *
 *  RETURNS    : A handle to the DDB.                                         *
 *                                                                            *
 *****************************************************************************/
HBITMAP BitmapFromDIB (HANDLE hDIB, HPALETTE  hPal)
{
    LPBITMAPINFOHEADER  lpbih;
    HPALETTE            hPalOld;
    HDC                 hDC;
    HBITMAP             hBitmap;  

    StartWait();

    if (!hDIB)
        return NULL;

    lpbih = (LPBITMAPINFOHEADER)hDIB;

    if (!lpbih)
        return NULL;

    hDC = GetDC(NULL);

    if (hPal){
        hPalOld = SelectPalette(hDC, hPal, FALSE);
        RealizePalette(hDC);     
    }                              

    hBitmap = CreateDIBitmap(hDC, 
                lpbih, 
                CBM_INIT, 
                (LPSTR)lpbih + lpbih->biSize + ColorTableSize(lpbih), 
                (LPBITMAPINFO)lpbih, 
                DIB_RGB_COLORS ); 

    if (hPal)
        SelectPalette(hDC, hPalOld, FALSE);

    ReleaseDC(NULL, hDC);

    EndWait();

    return hBitmap;
}
/******************************************************************************
 *                                                                            *
 *  FUNCTION   : DrawBitmap(HDC hDC, int x, int y,                            *
 *                          HBITMAP hBitmap, DWORD dwROP)                     * 
 *                                                                            *
 *  PURPOSE    : Draws bitmap <hBitmap> at the specified position in DC <hDC> *
 *                                                                            *
 *  RETURNS    : Return value of BitBlt()                                     *
 *                                                                            *
 *****************************************************************************/
BOOL DrawBitmap (HDC hDC, INT x, INT y, HBITMAP hBitmap, DWORD dwROP)
{
    HDC       hDCBits;
    BITMAP    Bitmap;
    BOOL      bResult;

    if (!hDC || !hBitmap)
        return FALSE;

    hDCBits = CreateCompatibleDC(hDC);
    GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
    SelectObject(hDCBits, hBitmap);
    bResult = BitBlt(hDC, x, y, Bitmap.bmWidth, Bitmap.bmHeight, hDCBits, 0, 0, dwROP);
    DeleteDC(hDCBits);

    return bResult;
}

  /*FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)

  PURPOSE:  Processes messages for the main window.

  WM_COMMAND    - process the application menu
  WM_PAINT    - Paint the main window
  WM_DESTROY    - post a quit message and return*/



#define ID_LOADBITMAP 1

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    //Handle to a GDI device context
    HDC hDC;
    //Handle to a DDB(device-dependent bitmap)
    HBITMAP hBitmap;


    HFONT hFont;
    NONCLIENTMETRICS ncm={0};
    ncm.cbSize= sizeof(NONCLIENTMETRICS);


    static HWND hwndButton;
    static HWND hwndButtonExit;
    static HANDLE hDIB = NULL;

    char szDirName[MAX_PATH];
    char szFilename[MAX_PATH]="\0";
    char szBitmapName[MAX_PATH]="\\Waterfall.bmp";
    //char szBitmapName[MAX_PATH]="\\tulips256.bmp";
    OPENFILENAMEA ofn;


    switch (message)
    {

    case WM_CREATE:

        //Creates a font from the current theme's caption font
        SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NULL, &ncm, NULL);
        hFont = CreateFontIndirect(&ncm.lfCaptionFont);

        //Gets the device context for the current window
        hDC = GetDC(hWnd);

        //Gets the directory of the current project and loads Waterfall.bmp
        GetCurrentDirectoryA(MAX_PATH, szDirName);
        strcat_s(szDirName, szBitmapName);
        strcat_s(szFilename,szDirName);
        hDIB = OpenDIB(szFilename);
        hBitmap = BitmapFromDIB(hDIB, NULL);

        //Draws Waterfall.bmp as a device dependent bitmap
        DrawBitmap(hDC,0,0,hBitmap,SRCCOPY);
        InvalidateRect(hWnd, NULL, FALSE);
        ReleaseDC(hWnd,hDC);

        //Draws the "Load Bitmap" button
        hwndButton = CreateWindowW(TEXT("button"),TEXT("Load Bitmap"), 
            WS_CHILD | WS_VISIBLE | WS_BORDER, 600, 200, 150,50,
            hWnd, 
            (HMENU)ID_LOADBITMAP, 
            ((LPCREATESTRUCT) lParam)-> hInstance, NULL);

        //Set the font of the button to the theme's caption font
        SendMessage(hwndButton, WM_SETFONT, (WPARAM)hFont, TRUE );


        return 0;


    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);


        //if Load Bitmap button is pressed
        if (wmId == ID_LOADBITMAP && wmEvent == BN_CLICKED)
        {

            //Get the current directory name, and store in szDirName 
            GetCurrentDirectoryA(MAX_PATH, szDirName);

            //Set all structure members to zero. 
            ZeroMemory(&ofn, sizeof(OPENFILENAMEA));

            //Initializing the OPENFILENAMEA structure 
            ofn.lStructSize = sizeof(OPENFILENAMEA);
            ofn.hwndOwner = hWnd;
            ofn.lpstrFilter ="BMP Files (*.BMP)\0 *.BMP\0\0";
            ofn.lpstrFile = szFilename;
            ofn.nMaxFile = sizeof(szFilename);
            ofn.lpstrDefExt = "BMP";
            ofn.lpstrInitialDir = szDirName;
            ofn.Flags = OFN_EXPLORER | OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;



            if (GetOpenFileNameA(&ofn)) {
               hDIB = OpenDIB((LPSTR)szFilename);   
               if (!hDIB)
                  MessageBox(hWnd, TEXT("Unable to load file!"), TEXT("Oops"), MB_ICONSTOP);
            } else {
                if (strlen((const char *)szFilename) != 0)
                  MessageBox(hWnd, TEXT("Unable to load file!"), TEXT("Oops"), MB_ICONSTOP);

                return 0;
            }




            InvalidateRect(hWnd, NULL, FALSE);


        }


        break;
    case WM_PAINT:
        {
            //Initializing arrays for boxes
            POINT pRect1[5] = {{0,0},{400,0},{400,400},{0,400},{0,0}};
            POINT pRect2[5] = {{0,500}, {200, 500}, {200, 700}, {0,700},{0,500}};
            POINT pRect3[5] = {{210,500}, {410, 500}, {410, 700}, {210,700},{210,500}};
            POINT pRect4[5] = {{420,500}, {620, 500}, {620, 700}, {420,700},{420,500}};
            POINT pRect5[5] = {{630,500}, {830, 500}, {830, 700}, {630,700},{630,500}};

            //For the white background
            RECT clientRect;
            HRGN hRegion1;
            HRGN hRegion2;
            HRGN hRegion3;
            HBRUSH hBGBrush;

            //Handle to a logical font
            HFONT hFont;

            //Get the caption font that is currently in use
            SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NULL, &ncm, NULL);
            hFont = CreateFontIndirect(&ncm.lfCaptionFont);

            //Begin drawing
            hDC = BeginPaint(hWnd, &ps);

            //Draw and fill rectangles for the background
            GetClientRect(hWnd, &clientRect);
            hRegion1 = CreateRectRgn(clientRect.left,clientRect.top,clientRect.right,clientRect.bottom);
            hBGBrush = CreateSolidBrush(RGB(255,255,255));
            FillRgn(hDC, hRegion1, hBGBrush);

            //Create an HBITMAP(device dependent bitmap) to be drawn
            hBitmap = BitmapFromDIB(hDIB,NULL);
            //Draw the DDB
            DrawBitmap(hDC,0,0,hBitmap,SRCCOPY);


            if(hDIB)
            {
                hRegion2 = CreateRectRgn(401,0,clientRect.right,401);
                hRegion3 = CreateRectRgn(0,401,clientRect.right,clientRect.bottom);
                FillRgn(hDC,hRegion2,hBGBrush);
                FillRgn(hDC,hRegion3,hBGBrush);
                //Set stretch mode as BLACKONWHITE and then copy from the 
                //source rectangle into the smaller rectangle
                SetStretchBltMode(hDC,BLACKONWHITE);
                StretchBlt(hDC,0,500,200,200,hDC, 0,0,400,400,SRCCOPY);

                //Set stretch mode as WHITEONBLACK and then copy from the 
                //source rectangle into the smaller rectangle
                SetStretchBltMode(hDC,WHITEONBLACK);
                StretchBlt(hDC,210,500,200,200, hDC, 0,0,400,400, SRCCOPY);

                //Set stretch mode as COLORONCOLOR and then copy from the 
                //source rectangle into the smaller rectangle
                SetStretchBltMode(hDC,COLORONCOLOR);
                StretchBlt(hDC,420,500,200,200, hDC, 0,0,400,400, SRCCOPY);

                //Set stretch mode as HALFTONE and then copy from the 
                //source rectangle into the smaller rectangle
                SetStretchBltMode(hDC,HALFTONE);
                StretchBlt(hDC,630,500,200,200, hDC, 0,0,400,400, SRCCOPY);

            }
            //Select the caption font created earlier
            SelectObject(hDC,hFont);

            //Create captions for each demonstration of color loss modes
            TextOut(hDC,50,480,TEXT("BLACKONWHITE"),12);
            TextOut(hDC,250,480,TEXT("WHITEONBLACK"),12);
            TextOut(hDC,460,480,TEXT("COLORONCOLOR"),12);
            TextOut(hDC,680,480,TEXT("HALFTONE"),8);
            DeleteObject(hFont);

            //Selecting the stock object pen to draw with
            SelectObject(hDC, GetStockObject(DC_PEN));
            //The pen is gray
            SetDCPenColor(hDC, RGB(80,80,80));

            //Polylines are drawn from arrays of POINTs
            Polyline(hDC, pRect1, 5);
            Polyline(hDC, pRect2, 5);
            Polyline(hDC, pRect3, 5);
            Polyline(hDC, pRect4, 5);
            Polyline(hDC, pRect5, 5);

            FillRgn(hDC,hRegion2,hBGBrush);
            EndPaint(hWnd, &ps);

            break;
        }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

以上代码,运行效果如下:
这里写图片描述

完整的工程代码如下:
https://download.csdn.net/download/wowocpp/10503082

以上代码有空用 MFC实现一遍,其中的HFILE OpenFile 也不推荐使用,推荐使用CreateFile

Bitmap Functions
https://docs.microsoft.com/zh-cn/windows/desktop/gdi/bitmap-functions
Using Bitmaps
https://docs.microsoft.com/zh-cn/windows/desktop/gdi/using-bitmaps
Capturing an Image
Scaling an Image
Storing an Image
https://docs.microsoft.com/zh-cn/windows/desktop/gdi/storing-an-image

Alpha Blending a Bitmap
Drawing a Shaded Rectangle
Drawing a Shaded Triangle
Testing a Printer for JPEG or PNG Support
Sizing a JPEG or PNG Image

https://msdn.microsoft.com/zh-cn/ff0a5ae3-ae2e-4417-b5e5-0f9871c03964
Bitmaps
A bitmap is a graphical object used to create, manipulate (scale, scroll, rotate, and paint), and store images as files on a disk. This overview describes the bitmap classes and bitmap operations.

About Bitmaps
Using Bitmaps
Bitmap Reference

CreateCompatibleDC 与 CreateCompatibleBitmap 小小结

https://www.cnblogs.com/open-fu/p/3337810.html
通常使用CreateCompatibleBitmap时候都会用到CreateCompatibleDC。而是用CreateCompatibleDC的目的不是为CreateCompatibleBitmap而产生,它更多为了建立内存设备环境起一个绘图操作与显示设备之间的缓冲作用,而CreateCompatibleBitmap 是为扩展内存设备环境的图像空间

CreateComptibleDC在mfc与sdk编程中参数不一样,前者是CDC*,后者是hdc(通常大多数绘图在sdk中是hdc,而mfc是cdc*),但作用都一样,都是为了创建一个与设备环境上下文兼容的内存设备环境(可以理解成一个与设备一样的环境),但值得注意的是这样创建出来的内存设备环境的图像空间尺寸是很小的,通常是1*1像素大小,而且还是单色的(没想通搞啥,可能为了兼容以前设备或者程序),因此需要调用SelectObject函数来加载位图bitmap,这样加载的位图尺寸大小就相当于了内存设备环境尺寸大小。接下来才可以进行一系列绘图操作。

对于CreateCompatibleBitmap函数,是为了创建与指定的设备环境相关的设备兼容的位图。有时候会觉得直接从资源里加载位图资源即可,何必直接创建呢?但有时是必要的,比如你想直接对屏幕操作,又想避免闪烁。这时候就可以Create一个内存DC,使用CreateCompatibleBitmap产生一个bitmap,然后内存DC使用SelectObject加载bitmap,这样内存中才有一个固定大小的图像空间,其次再使用bitblt把屏幕copy到内存DC中,这样子你可以在内存dc中进行各种绘图操作。当然你也可以用在内存缓冲中完成对一张图片的操作(如缩放、透明等),也有必要create一个bitmap

另外参考可以见:

http://blog.csdn.net/hhygcy/article/details/4073975 这个写得挺详细的~

http://fengqing888.blog.163.com/blog/static/3301141620091019104353119/ 这个前半部分可以看看即可,后半部分确实没那么大吸引力~

PS:

1、对于CreateCompatibleBitmap 初始化颜色,我实践一下,屏幕显示的是黑色

2、(第二个链接中有这样一段原话,没有修改)由CreateCompatibleBitmap 函数创建的位图的颜色格式与由参数hdc标识的设备的颜色格式匹配。该位图可以选入任意一个与原设备兼容的内存设备环境中。由于内存设备环境允许彩色和单色两种位图。因此当指定的设备环境是内存设备环境时,由CreateCompatibleBitmap函数返回的位图格式不一定相同。然而为非内存设备环境创建的兼容位图通常拥有相同的颜色格式,并且使用与指定的设备环境一样的色彩调色板 其实说的不是太清楚,CreateCompatilbeBitmap 参数中可以选择的hdc可以是内存设备环境或者物理设备环境,而选择的hdc会决定你所创建bitmap的颜色格式,如果你选择内存环境,若是是单色的,所创建的bitmap也是单色的;如果是彩色的,也是彩色的。选择物理设备环境,则是彩色的。其实第一个链接就是对其中进行探讨,我觉得看到这边不明白可以直接看msdn:http://msdn.microsoft.com/en-us/library/windows/desktop/dd183488(v=vs.85).aspx   里面很详细。

具体例子如下: 我们可以使用

HDC hDC = GetDC( NULL );
HDC hDC2 = CreateCompatibleDC( hDC );
HBITMAP hBM = CreateCompatibleBitmap( hDC, 400, 400 );
SelectObject( hDC2, hBM );
也可以这样使用:

HDC hDC = GetDC( NULL );
HDC hDC2 = CreateCompatibleDC( hDC );
HBITMAP hBM = CreateCompatibleBitmap( hDC2, 400, 400 );
SelectObject( hDC2, hBM );
前者code的hdc2的颜色格式应该是彩色,而后者是单色的。

猜你喜欢

转载自blog.csdn.net/wowocpp/article/details/80817175