欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Win32汇编系列九,GDI画个寂寞

程序员文章站 2024-03-01 12:17:10
...

前言

图片显示,说简单也简单,说难也难,在html中给img指定一个src、在Android中给ImageView指定一个src,还能各种圆角、缩放,但是,这一切在以前学Window的时候是真的困难,想显示一张图片怎么办?,找各种资料,最终只有一个答案,GDI+,GDI+不知道是啥玩意怎么办,在找资料学,最后会发现,显示一个PNG图片,需要十几行左右,还不包括有圆角等。(这里没说C#)。

但今天说的是GDI,GDI+作为GDI的扩展,增强了很多,也复杂了很多,在后续会慢慢介绍,另外本文需要有Window消息机制和GDI的基础,否则可能有点吃力,在后续会慢慢记录此类文章。

主要函数

LoadImage、CreateCompatibleDC、SelectObject、BitBlt。

LoadImage

顾名思义,是用来加载一个位图,也可以加载图标,光标,但是注意,它只能加载位图,也就是BMP,JPG、PNG,没办法。

HANDLE LoadImageA(
  HINSTANCE hInst,
  LPCSTR    name,
  UINT      type,
  int       cx,
  int       cy,
  UINT      fuLoad
);
  1. hInst:包含要加载的图像的DLL或可执行文件的模块句柄。

  2. name:要加载的图像

  3. type:要加载的图像类型,可以取IMAGE_BITMAP、IMAGE_CURSOR、IMAGE_ICON。

  4. cx、cy:图标或光标的宽高。如果为零的话,并且fuLoad参数为LR_DEFAULTSIZE,那么他会使用SM_CXICON或SM_CXCURSOR系统度量值来设置宽高,如果为零,并且没使用LR_DEFAULTSIZE,则该函数使用实际的资源宽高,所以,如果我们不知道图片高宽,需要设置为0,并且fuLoad不能为LR_DEFAULTSIZE。

  5. type:这个参数有点多,传LR_LOADFROMFILE就行了。

CreateCompatibleDC

创建一个和当前屏幕DC兼容的内存DC,绘制位图的时候,必须先在内存中建立一个和当前设备环境兼容的DC,然后在利用BitBlt函数将位图从内存复制到屏幕DC上,位图才能显示出来。

参数只有一个,现有设备上下文环境的句柄。

HDC CreateCompatibleDC(
  HDC hdc
);

SelectObject

选择一对象到指定的设备上下文环境中,新的对象会替换先前的相同类型的对象。

HGDIOBJ SelectObject(
  HDC     hdc,
  HGDIOBJ h
);
  1. hdc:设备上下文环境的句柄
  2. h:被选择的对象的句柄

BitBlt

这个函数好理解,就是从一个地方把图片贴到另一个地方,并且能指定位置和大小。

BOOL BitBlt(
  HDC   hdc,
  int   x,
  int   y,
  int   cx,
  int   cy,
  HDC   hdcSrc,
  int   x1,
  int   y1,
  DWORD dwRop
);

这里面主要就是hdc、hdcSrc、dwRop参数需要了解,hdc是目标设备上下文的句柄,hdcSrc是源设备上下文的句柄,就是要把hdcSrc的图像复制到hdc中,dwRop是光栅操作的方式,有很多,比如颜色取反,这里就不列举了,选SRCCOPY即可,意思是直接拷贝到hdc中。

其他都是控制位置和大小的参数。

汇编代码

.386 
.model flat,stdcall 
option casemap:none 
include c:\masm32\include\windows.inc 
include c:\masm32\include\user32.inc 
include  c:\masm32\include\msvcrt.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\gdi32.inc

includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\user32.lib       
includelib c:\masm32\lib\msvcrt.lib
printf    PROTO C : dword,:vararg

WinMain proto :DWORD

.DATA                   
ClassName db "MyWindowClass",0       
AppName db "Win32绘制图片",0    
ButtonClassName db "Button",0   
ButtonTitle db "绘制",0
HelloGUI db "HelloGUI",0

Message db "完成",0
MessageTitle db "提示",0
OutFmt db "值=%d",0
OutMessage db "第%d次"
.DATA?              
hInstance HINSTANCE ?
hWindowHdc HDC ?
hButton HWND ?
hWindow HWND ?

ThreadID DWORD ? 
.CONST 
ButtonID equ 1
IMAGE_PATH db 'E:\\test\\imgs\\test.bmp' ,0
.CODE             


start: 

invoke GetModuleHandle, NULL   
mov hInstance,eax 
invoke WinMain, hInstance
invoke ExitProcess, eax       
WinMain proc hInst:HINSTANCE
    LOCAL wc:WNDCLASSEX              
    LOCAL msg:MSG 

    mov   wc.cbSize,SIZEOF WNDCLASSEX   
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, ADDR wc   
    invoke CreateWindowEx,NULL,\ 
                ADDR ClassName,\ 
                ADDR AppName,\ 
                WS_OVERLAPPEDWINDOW,\ 
                0,\ 
                0,\ 
                500,\ 
                500,\ 
                NULL,\ 
                NULL,\ 
                hInst,\ 
                NULL 
    mov   hWindow,eax
    invoke GetDC,eax
    mov  hWindowHdc,eax
    invoke ShowWindow, hWindow,SW_SHOWDEFAULT   
    invoke UpdateWindow, hWindow               

    .WHILE TRUE                                             
		invoke GetMessage, ADDR msg,NULL,0,0 
        .BREAK .IF (!eax) 
        invoke TranslateMessage, ADDR msg 
        invoke DispatchMessage, ADDR msg 
   .ENDW 
    mov     eax,msg.wParam                      
    ret 
WinMain endp 


WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
		LOCAL strRect:RECT
		LOCAL paint:dword
		LOCAL hdcmem:dword
		LOCAL hbitmap:dword
		LOCAL hdc:dword
		.IF uMsg == WM_LBUTTONDOWN
			mov strRect.right,100
			mov strRect.bottom,100
			mov strRect.left,0
			mov strRect.top,0
		
		.ELSEIF uMsg ==WM_PAINT
					invoke BeginPaint, hWindow, paint
					mov hdc,eax
					invoke CreateCompatibleDC,hdc
					mov hdcmem,eax
				
				 	invoke LoadImage,hInstance,offset IMAGE_PATH,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
				 	
				 	mov hbitmap,eax
				 	invoke SelectObject,hdcmem,hbitmap
				 	invoke BitBlt,hWindowHdc,0,0,500,500,hdcmem,0,0,SRCCOPY
					invoke printf,addr OutFmt,hbitmap
					invoke EndPaint,hWindow,paint
				
		.ELSE
			invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
			ret
		.ENDIF
    ret 
WndProc endp 

end start 

Win32汇编系列九,GDI画个寂寞

这里面需要判断消息是不是WM_PAINT,如果是则开始绘制,WM_PAINT是WIndows中最重要的消息之一,它会在第一次创建窗口、改变窗口大小、把窗口从另一个窗口背后移出、最大化、最小化窗口等等时被触发,在这个消息下通常开始处理绘制的逻辑。

如果我们把他放在如WM_SETFOCUS、按钮单击下绘制,那么当窗体重绘的时候图片就会被"擦除",需要再次触发对应事件进行绘制,而在WM_PAINT消息下绘制正好解决了这些问题。

或者可以颜色取反,dwRop值为NOTSRCCOPY

Win32汇编系列九,GDI画个寂寞