《GDI+系列教程》第11章 —— 图像的旋转与镜像

本章,我们要搞定图像的旋转与镜像显示。

《GDI+系列教程》第11章 —— 图像的旋转与镜像


1.初始化 GDI+ 并创建 GUI

#SingleInstance, Force
#NoEnv
SetBatchLines, -1

; Uncomment if Gdip.ahk is not in your standard library
#Include, Gdip_All.ahk

gosub, CreateGUI

2.响应图片的旋转

Go:
Go2:
Go3:
Go4:
	; Submit the variables to see the degress to rotate by and whether to flip the image
	; 获得 GUI 控件的参数。
	Gui, 1: +OwnDialogs
	Gui, 1: Submit, NoHide

3.从文件获取图像

	; If the file in the edit field is not a valid image then return
	; 从文件载入图像。
	If !pBitmap := Gdip_CreateBitmapFromFile(File)
		Return

	; We should get the width and height of the image, in case it is too big for the screen then we can resize it to fit nicely
	; 获取图像的原始宽、高、宽高比。
	OriginalWidth := Gdip_GetImageWidth(pBitmap), OriginalHeight := Gdip_GetImageHeight(pBitmap)
	Ratio := OriginalWidth/OriginalHeight

	; If the image has a width larger than 1/2 of the width of the screen or height larger than 1/2 the screen, then we will resize it to be half of the screen
	; 图像如果过大,超过屏幕的1/2,进行旋转时可能超出屏幕显示范围,所以这里缩小一下。
	If (OriginalWidth >= A_ScreenWidth//2) || (OriginalHeight >= A_ScreenHeight//2)
	{
		If (OriginalWidth >= OriginalHeight)
		Width := A_ScreenWidth//2, Height := Width//Ratio
		Else
		Height := A_ScreenHeight//2, Width := Height*Ratio
	}
	Else
	; Width and Height now contain the new dimensions the image on screen will be
	Width := OriginalWidth, Height := OriginalHeight

4.获取图片旋转后的新坐标0点与新宽高

	; When rotating a square image, then the bitmap or canvas will need to be bigger as you can imagine that once rotated then a triangle will be wider than a square
	; We need to know the new dimensions of the image
	; With Gdip_GetRotatedDimensions we can plug in the width and height of the image, and the angle it is to be rotated by
	; The last 2 parameters are the variables in which tio store the new width and height of the rotated image
	; RWidth and RHeight now contain the dimensions of the rotated image
	; 因为图片旋转后的宽高会发生变化,所以先计算旋转后的宽高。
	; Gdip_GetRotatedDimensions(原宽, 原高, 旋转角度, 新宽, 新高) 注意后两个参数是用来接收计算后的结果的,所以不需要往里面传值。
	Gdip_GetRotatedDimensions(Width, Height, Angle, RWidth, RHeight)

	; We rotate an image about the top left corner of the image, however this will result in the image moving off the canvas
	; We can use Gdip_GetRotatedTranslation to find how much the image should be 'shifted' by in the x and y coordinates in order for it to be back on the canvas
	; As with the above function, we plug in the width, height and angle to rotate by
	; The function will then make the last 2 parameters the x and y translation (this is the distance in pixels the image must be shifted by)
	; xTranslation and yTranslation now contain the distance to shift the image by
	; 因为图片的旋转是基于图片左上角为基点的,所以旋转后,图片就会跑出画布。此函数可以计算出偏移的修正量,让图片完整的显示出来。
	; Gdip_GetRotatedTranslation(原宽, 原高, 旋转角度, x, y) 注意后两个参数是用来接收计算后的结果的,所以不需要往里面传值。
	; 正常没有任何旋转的时候 x,y = 0,0 。旋转以后 x,y 的值就是原图左上角在画布上的新坐标。
	Gdip_GetRotatedTranslation(Width, Height, Angle, xTranslation, yTranslation)

	; 理解不了就把下面的代码去掉注释,去观察理解。
	; t=
	; (
	; %Width%					%Height%
	; %RWidth%				%RHeight%
	; %xTranslation%				%yTranslation%
	; )
	; MsgBox, % t

5.根据新宽高创建画布

	; We will now create a gdi bitmap to display the rotated image on the screen (as mentioned previously we must use a gdi bitmap to display things on the screen)
	hbm := CreateDIBSection(RWidth, RHeight)

	; Get a device context compatible with the screen
	hdc := CreateCompatibleDC()

	; Select the bitmap into the device context
	obm := SelectObject(hdc, hbm)

	; Get a pointer to the graphics of the bitmap, for use with drawing functions,
	; and set the InterpolationMode to HighQualityBicubic = 7 so that when resizing the image still looks good
	G := Gdip_GraphicsFromHDC(hdc), Gdip_SetInterpolationMode(G, 7)

6.旋转图像并显示

	; We can now shift our graphics or 'canvas' using the values found with Gdip_GetRotatedTranslation so that the image will be drawn on the canvas
	; 移动画布的位置到之前计算得到的 x,y 。
	; Gdip_TranslateWorldTransform(画布, x偏移量, y偏移量)
	; 注意,后两个值是偏移量。
	; 例如,连续两次运行 Gdip_TranslateWorldTransform(G, 100, 100), Gdip_TranslateWorldTransform(G, 10, -100)
	; 此时的坐标实际上是 x = 100+10, y = 100-100
	Gdip_TranslateWorldTransform(G, xTranslation, yTranslation)

	; We can also rotate the graphics by the angle we desire
	; 旋转画布
	Gdip_RotateWorldTransform(G, Angle)

	; If we wish to flip the image horizontally, then we supply Gdip_ScaleWorldTransform(G, x, y) with a negative x transform
	; We multiply the image by the x and y transform. So multiplying a direction by -1 will flip it in that direction and 1 will do nothing
	; We must then shift the graphics again to ensure the image will be within the 'canvas'
	; You can see that if we wish to flip vertically we supply a negative y transform
	If Horizontal
	; 水平翻转
	; Gdip_ScaleWorldTransform(画布, x乘数, y乘数)
	; 想象水平方向上5个像素:1、2、3、4、5, 乘以 -1 后,就变成了:-1、-2、-3、-4、-5。
	; 所以排列就变成了:-5、-4、-3、-2、-1,实现了水平翻转。
	; 注意,此时虽然x坐标实现了水平的翻转,但也发生了位移,所以还需要使用 Gdip_TranslateWorldTransform() 进行一次移动。
	; 再次注意,水平翻转后,x位移的方向也发生了变化。之前 Gdip_TranslateWorldTransform(G, 100, 0) 是画面往右移动。
	; 此时  Gdip_TranslateWorldTransform(G, -100, 0) 才是往右移动。
	Gdip_ScaleWorldTransform(G, -1, 1), Gdip_TranslateWorldTransform(G, -Width, 0)
	If Vertical
	; 垂直翻转
	Gdip_ScaleWorldTransform(G, 1, -1), Gdip_TranslateWorldTransform(G, 0, -Height)


	; As you will already know....we must draw the image onto the graphics. We want to draw from the top left coordinates of the image (0, 0) to the top left of the graphics (0, 0)
	; We are drawing from the orginal image size to the new size (this may not be different if the image was not larger than half the screen)
	; 这里图像没有发生大小上的缩放。
	Gdip_DrawImage(G, pBitmap, 0, 0, Width, Height, 0, 0, OriginalWidth, OriginalHeight)

	; Even though this is not necessary in this scenario, you should always reset the transforms set on the graphics. This will remove any of the rotations
	; 重置画布的旋转,注意在本例中这不是必须的,但你最好还是这么做,养成好习惯。
	; 之所以在本例不是必须的,是因为这里把所有东西都销毁了。
	; 如果不销毁这些东西,那么像 Gdip_ScaleWorldTransform(G, 1.1, 1) 运行两次,实际就是 (G, 1.1*1.1, 1*1) 。
	Gdip_ResetWorldTransform(G)

	; We will update the hwnd  with the hdc of our gdi bitmap. We are drawing it at the new width and height and in the centre of the screen
	UpdateLayeredWindow(hwnd2, hdc, (A_ScreenWidth-RWidth)//2, (A_ScreenHeight-RHeight)//2, RWidth, RHeight)

	; As always we will dispose of everything we created
	; So we select the object back into the hdc, the delete the bitmap and hdc
	SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
	; We will then dispose of the graphics and bitmap we created
	Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmap)
Return

0.GUI部分

CreateGUI:
	; Start gdi+
	If !pToken := Gdip_Startup()
	{
		MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
		ExitApp
	}
	OnExit, ExitGdip

	; Create a gui where we can select the file we want to rotate, the angle to rotate it by and whether we want to flip it
	; 创建可添加文件的 GUI 。
	Gui, 1: +ToolWindow +AlwaysOnTop
	Gui, 1: Add, Edit, x10 y10 w300 vFile, 
	Gui, 1: Add, Button, x+10 yp+0 w75 gFileSelect Default, &File...
	Gui, 1: Add, Button, x+10 yp+0 w75 gGo, &Go

	; Here is the slider allowing rotation between 0 and 360 degrees
	; 创建 0-360 的滑块控件。
	Gui, 1: Add, Slider, x10 y+10 w300 Tooltip vAngle Range0-360 gGo2, 0

	; Create 2 checkboxes, to select whether we want to flip it horizontally or vertically
	; 创建 水平翻转 与 垂直翻转 两个控件。
	Gui, 1: Add, CheckBox, x+10 yp+0 vHorizontal gGO3, Flip horizontally
	Gui, 1: Add, CheckBox, x+10 yp+0 vVertical gGO4, Flip vertically

	Gui, 1: Show, x0 y0 AutoSize

	; Create a layered window (+E0x80000 : must be used for UpdateLayeredWindow to work!) that is always on top (+AlwaysOnTop), has no taskbar entry or caption
	; This will be used as the 2nd gui so that we can show our image on it
	; 创建 用来显示图片的 GUI 注意编号,这个 GUI 是2号,前面创建的是1号。
	Gui, 2: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs

	; Show the window
	; 显示2号。
	Gui, 2: Show, NA

	; Get a handle to this window we have created in order to update it later
	; 获取2号句柄。
	hwnd2 := WinExist()

	; By placing this OnMessage here. The function WM_LBUTTONDOWN will be called every time the user left clicks on the gui. This can be used for dragging the image
	; 监听消息,实现拖动效果。
	OnMessage(0x201, "WM_LBUTTONDOWN")
Return

;#####################################################################

; This is the function to allow the user to drag the image drawn on the screen (this being gui 2)
; 响应消息,让图片可拖动。
WM_LBUTTONDOWN()
{
	If (A_Gui = 2)
	PostMessage, 0xA1, 2
}

;#####################################################################

; This is a simple subroutine to select images and change the editbox with the image teh user selects
; 选择文件时,跳这里运行。
FileSelect:
	Gui, 1: +OwnDialogs
	Gui, 1: Submit, NoHide

	FileSelectFile, File,,, Select image
	If Errorlevel
		Return
	GuiControl,, File, %File%
	gosub, Go
Return

;#####################################################################

; If the user closes the gui or closes the program then we want to shut down gdi+ and exit the application
Esc::
GuiClose:
ExitGdip:
	Gdip_Shutdown(pToken)
	ExitApp
Return

本章习题:让图像360度立体旋转(就像桌上转硬币一样)。


全部代码与库文件下载地址:

https://ahk.lanzoux.com/b01nypnuh

密码:

给TA捐赠
共{{data.count}}人
人已捐赠
教程

《GDI+系列教程》第10章 —— 屏幕放大镜

2021-2-3 11:37:36

教程

GDI+系列教程目录

2021-2-9 21:42:33

7 条回复 A文章作者 M管理员
  1. sanmaodo

    ?轻松的学习,谢谢! 好像还差个图像放大缩小没开讲

    • 空

      第2章就讲了。

  2. Kevin苏扬

    感谢分享 , 受益!

  3. 快乐就好

    感谢大神的支持。您的好意我心领了。感谢。其实我就是想多交点朋友。圈子里的朋友。

  4. AHK中文社区

    我来到了这里,不错不错,学完之后把影子界面都改造一下

个人中心
购物车
优惠劵
有新私信 私信列表
搜索