带帧数的播放GIF示例.ahk
; https://www.autohotkey.com/boards/viewtopic.php?p=468847#p468847 #NoEnv SetBatchLines -1 SetWorkingDir %A_ScriptDir% filePath := "abstract.gif" FileSelectFile, filePath, , %A_WorkingDir%,选择要播放的GIF,*.gif if (filePath="") ExitApp Menu, Tray, Icon, % "HBITMAP:*" . LoadPicture(filePath, "GDI+ w32 h32") exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000) Gui, New, +E%exStyles% +hwndhGui Gui, Add, Picture, hwndhPic gOnClick, % "HBITMAP:" . LoadPicture(filePath, "GDI+") OnMessage(0x20, Func("WM_SETCURSOR").Bind(hPic)) Gui, Add, Button, xp y+10 w75 h24 gOnClick Default, Play Gui, Add, Button, x+5 yp wp hp gOnClick , Stop Gui, Add, Button, x+5 yp w35 hp gOnClick , < Gui, Add, Button, x+5 yp wp hp gOnClick , > UserFunc := Func("PlayGif").Bind(hGui, hPic, FramesCount := []) MyGif := new AnimateGif(filePath, UserFunc) ; for every frame UserFunc will be called with two params: currentFrameIdx and hBitmap ; user is responsible for deleting hBitmap count := FramesCount[1] := MyGif.framesCount Gui, Show,, % "Frame: " . Format("{:0" . StrLen(count) . "}", 1) . "/" . count Return OnClick: try GuiControl,, Pause, Play Switch A_GuiControl { Case "Stop": MyGif.Stop() Case "<" : MyGif.Prev() Case ">" : MyGif.Next() Default: if MyGif.playing MyGif.Pause() else { GuiControl,, Play, Pause MyGif.Play() } } Return GuiClose: ExitApp PlayGif(hGui, hPic, FramesCount, currentFrameIdx, hBitmap) { GuiControl,, % hPic, HBITMAP: %hBitmap% count := FramesCount[1] frame := Format("{:0" . StrLen(count) . "}", currentFrameIdx) Gui, %hGui%: Show, NA, % "Frame: " . frame . "/" . count } WM_SETCURSOR(hPic, wp) { static hCursor, flags := (LR_DEFAULTSIZE := 0x40) | (LR_SHARED := 0x8000) , params := [ "Ptr", 0, "UInt", OCR_HAND := 32649 , "UInt", IMAGE_CURSOR := 2 , "Int", 0, "Int", 0, "UInt", flags, "Ptr" ] (!hCursor && hCursor := DllCall("LoadImage", params*)) if (wp = hPic) Return DllCall("SetCursor", "Ptr", hCursor) } class AnimateGif { ; UserFunc will be called with two params: currentFrameIdx and hBitmap ; user is responsible for deleting hBitmap __New(gifFile, UserFunc := "", cycle := true) { this.GDIp := new GDIplus this.pBitmap := this.GDIp.CreateBitmapFromFile(gifFile) this.Frames := new this._FramesHandling(this.GDIp, this.pBitmap, UserFunc, cycle) this.GDIp.GetImageDimensions(this.pBitmap, width, height) this.width := width this.height := height } Play() { this.playing := true this.Frames.PlayFrames() } Pause() { if this.playing { this.playing := false timer := this.Frames._PlayTimer SetTimer, % timer, Delete } } Stop() { this.Pause() this.ShowFrame(1) } Prev() { this.ShowFrame("prev") } Next() { this.ShowFrame("next") } ShowFrame(which) { ; 'which' can be "prev", "next" or "", or 1-based frame index this.Pause() (which = "prev" && this.Frames.currentFrame -= 2) (which + 0 && this.Frames.currentFrame := which - 1) this.Frames.PlayFrames() } GetFrameByIndex(idx) { Return hBitmap := this.Frames.GetFrame(idx - 1) } playing[] { get { Return this.Frames.playing } set { Return this.Frames.playing := value } } framesCount[] { get { Return this.Frames.frameCount } } __Delete() { this.Frames.Clear() this.GDIp.DisposeImage(this.pBitmap) this.Delete("Frames") this.GDIp.Release() } class _FramesHandling { __New(GDIp, pBitmap, userFunc, cycle) { this.GDIp := GDIp this.pBitmap := pBitmap this.userFunc := userFunc this.cycle := cycle this.GetFrameCount() this.GetFrameDelay() this._PlayTimer := ObjBindMethod(this, "PlayFrames") this._currentFrame := 1 } currentFrame[] { get { Return this._currentFrame } set { Return this._currentFrame := value < 1 ? this.frameCount + value : value > this.frameCount ? 1 : value } } PlayFrames() { Critical frameIdx := ++this.currentFrame - 1 if ( this.playing && this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) { timer := this._PlayTimer delay := this.frameDelay[frameIdx] ; Emulate behavior of setting 10 ms to 100 ms. (delay == 10) && delay := 100 ; See: https://www.biphelps.com/blog/The-Fastest-GIF-Does-Not-Exist ; Randomize the delay in intervals of 15.6 resolution := 15.6 Random rand, 1, 100000 percentage := mod(delay, resolution) / resolution percentage *= 0.957 ; Higher is faster, lower is slower ; Randomized multiples of resolution if ((rand := rand / 100000) > percentage) res := Floor(delay / resolution) * resolution else res := Ceil(delay / resolution) * resolution SetTimer, % timer, % -1 * res } if userFunc := this.userFunc %userFunc%(this.currentFrame, this.GetFrame(frameIdx)) ; Debug code static start := 0, sum := 0, count := 0, sum2 := 0, count2 := 0 DllCall("QueryPerformanceFrequency", "int64*", frequency:=0) DllCall("QueryPerformanceCounter", "int64*", now:=0) time := (now - start) / frequency * 1000 if (time < 10000) { sum += time count++ average := sum / count sum2 += res count2++ Tooltip % "当前刻度:`t" Round(time, 4) . "`n平均FPS:`t" Round(average, 4) . "`n排队FPS:`t" Round(sum2 / count2, 4) . "`n目标FPS:`t" delay . "`n百分率:`t" percentage ", " rand . "`n下限和上限:`t" Floor(delay / resolution) * resolution ", " Ceil(delay / resolution) * resolution } start := now } GetFrameCount() { this.frameCount := this.GDIp.GetFrameCount(this.pBitmap, dimensionIDs, size) this.SetCapacity("dimensionIDs", size) this.pDimensionIDs := this.GetAddress("dimensionIDs") DllCall("RtlMoveMemory", "Ptr", this.pDimensionIDs, "Ptr", &dimensionIDs, "Ptr", size) VarSetCapacity(dimensionIDs, 0), dimensionIDs := "" this.currentFrame := 0 } GetFrameDelay() { this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item) len := NumGet(item, 4, "UInt") val := NumGet(item, 8 + A_PtrSize, "UPtr") this.frameDelay := [] Loop, % len//4 { i := A_Index - 1 n := NumGet(val + i*4, "UInt") * 10 this.frameDelay[i] := n ? n : 100 } } GetFrame(idx) { this.GDIp.ImageSelectActiveFrame(this.pBitmap, this.pDimensionIDs, idx) Return this.GDIp.CreateHBITMAPFromBitmap(this.pBitmap) } Clear() { this.playing := false timer := this._PlayTimer SetTimer, % timer, Delete this.Delete("dimensionIDs") } } } class GDIplus { __New() { static Instance := "" if Instance.references { ++Instance.references Return Instance } this.references := 1 if !DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr") DllCall("LoadLibrary", "Str", "gdiplus") VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1) DllCall("gdiplus\GdiplusStartup", "UPtrP", pToken, "Ptr", &si, "Ptr", 0) this.token := pToken Return Instance := this } Release() { if --this.references Return DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token) if hModule := DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr") DllCall("FreeLibrary", "Ptr", hModule) } CreateBitmapFromFile(sFile) { DllCall("gdiplus\GdipCreateBitmapFromFile", "WStr", sFile, "PtrP", pBitmap) Return pBitmap } CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) { DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hbm, "Int", Background) Return hbm } GetImageDimensions(pBitmap, ByRef Width, ByRef Height) { DllCall("gdiplus\GdipGetImageWidth", "Ptr", pBitmap, "UIntP", Width) DllCall("gdiplus\GdipGetImageHeight", "Ptr", pBitmap, "UIntP", Height) } GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) { DllCall("gdiplus\GdipImageGetFrameDimensionsCount", "Ptr", pBitmap, "UIntP", frameDimensions) VarSetCapacity(dimensionIDs, size := 16*frameDimensions) DllCall("gdiplus\GdipImageGetFrameDimensionsList", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UInt", frameDimensions) DllCall("gdiplus\GdipImageGetFrameCount", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UIntP", count) Return count } GetPropertyItem(pBitmap, tag, ByRef item) { DllCall("gdiplus\GdipGetPropertyItemSize", "Ptr", pBitmap, "UInt", tag, "UIntP", size) VarSetCapacity(item, size, 0) DllCall("gdiplus\GdipGetPropertyItem", "Ptr", pBitmap, "UInt", tag, "UInt", size, "Ptr", &item) Return size } ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) { Return DllCall("gdiplus\GdipImageSelectActiveFrame", "Ptr", pBitmap, "Ptr", pDimensionIDs, "Int", idx) } DisposeImage(pBitmap) { Return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap) } }
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。
评论(0)