1 (изменено: sanny0112, 2021-05-30 18:18:03)

Тема: AHK: Текст поверх GIF

Доброго времени суток, понадобилось вывести текст поверх .gif. Для вывода анимированной гифки использовал следующую функцию. Чтобы сделать текст поверх картинки применил данный метод. Но, к сожалению, одно с другим не состыкуется и блок текста уходит на задний план. Есть ли у Вас какие-либо идеи как разрешить данную проблему?

+ Код
global ChatText
global ChatIndex := 0

Gui, Font, S10 CDefault , Verdana
Gui, Add, Edit, x0 y364 w720 h20 vChatEnter,

;Gui, Add, Picture, x0 y0 w720 h365 , bg.gif
AddAnimatedGIF(A_ScriptDir "\bg.gif",0,0,720,365)

Gui, Font, S10 CFFFFFF Bold, Arial
Gui, Add, Text, x12 y9 w650 h365 BackgroundTrans vChat,



Gui, Show, x649 y337 h384 w674, Game
Return

GuiClose:
ExitApp

Enter::
{
	IfWinActive, Game
	{
		GuiControlGet, ChatEnter
		print(ChatEnter)
		GuiControl,,ChatEnter,
	}
	else Send {Enter}
	Return
}

F5::Reload

print(Message) {
	if(Message != "") {
		if (ChatText != "") {
			if (ChatIndex > 21) {
				RegExMatch(ChatText, "^.*?\n(.*)", StrDel)
				ChatText := StrDel1 "`n" Message
			}
			else ChatText := ChatText "`n" Message
		}
		else ChatText := Message
		ChatIndex++
		GuiControl,,Chat, %ChatText%
	}
	Return
}

AddAnimatedGIF(imagefullpath , x="", y="", w="", h="", guiname = "1")
{
	global AG1,AG2,AG3,AG4,AG5,AG6,AG7,AG8,AG9,AG10
	static AGcount:=0, pic
	AGcount++
	html := "<html><body style='background-color: transparent' style='overflow:hidden' leftmargin='0' topmargin='0'><img src='" imagefullpath "' width=" w " height=" h " border=0 padding=0></body></html>"
	Gui, AnimGifxx:Add, Picture, vpic, %imagefullpath%
	GuiControlGet, pic, AnimGifxx:Pos
	Gui, AnimGifxx:Destroy
	Gui, %guiname%:Add, ActiveX, % (x = "" ? " " : " x" x ) . (y = "" ? " " : " y" y ) . (w = "" ? " w" picW : " w" w ) . (h = "" ? " h" picH : " h" h ) " vAG" AGcount, Shell.Explorer
	AG%AGcount%.navigate("about:blank")
	AG%AGcount%.document.write(html)
	return "AG" AGcount
}
+ Снимки экрана

Как в приведённом коде
https://i.imgur.com/LPjXLnH.png

Поменял местами блоки вывода GIF и текста
https://i.imgur.com/YN0eb5L.png

Раскомментировал штатный способ добавления картинок и закомментировал функцию (Получил статичную картинку)
https://i.imgur.com/P14991A.png

P.S. Сама гифка

2

Re: AHK: Текст поверх GIF

Отрисовывайте гиф через gdi и там уже производите необходимые вам манипуляции.

3

Re: AHK: Текст поверх GIF

Пример:

#NoEnv
#Include Gdip_All.ahk
SetBatchLines, -1

filePath := "C:\Users\User\Desktop\bg76852ee8ba300133.gif"

exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
Gui, Add, Picture, y10 hwndhPic, % filePath

MyGif := new Gif(filePath, hPic, "Это текст, который будет поверх gif", "Calibri", "Centre y100 r4 s30 cFFFFFFFF")
MyGif.Play()
Gui, Show
return

GuiClose:
   ExitApp

class Gif
{	
   __New(file, hwnd, text := "", font := "Arial", textOptions := "", cycle := true)
   {
      pToken := Gdip_Startup()
      this.file := file
      this.hwnd := hwnd
      this.cycle := cycle
      this.text := text
      this.font := font
      this.textOptions := textOptions
      this.pBitmap := Gdip_CreateBitmapFromFile(this.file)
      Gdip_GetImageDimensions(this.pBitmap, width, height)
      this.width := width, this.height := height
      this.isPlaying := false
      
      DllCall("Gdiplus\GdipImageGetFrameDimensionsCount", "ptr", this.pBitmap, "uptr*", frameDimensions)
      this.SetCapacity("dimensionIDs", 16*frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameDimensionsList", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameCount", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int*", count)
      this.frameCount := count
      this.frameCurrent := -1
      this.frameDelay := this.GetFrameDelay(this.pBitmap)
      this._Play("")
   }

   ; Return a zero-based array, containing the frames delay (in milliseconds)
   GetFrameDelay(pImage) {
      static PropertyTagFrameDelay := 0x5100

      DllCall("Gdiplus\GdipGetPropertyItemSize", "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt*", ItemSize)
      VarSetCapacity(Item, ItemSize, 0)
      DllCall("Gdiplus\GdipGetPropertyItem"    , "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt", ItemSize, "Ptr", &Item)

      PropLen := NumGet(Item, 4, "UInt")
      PropVal := NumGet(Item, 8 + A_PtrSize, "UPtr")

      outArray := []
      Loop, % PropLen//4 {
         if !n := NumGet(PropVal+0, (A_Index-1)*4, "UInt")
            n := 10
         outArray[A_Index-1] := n * 10
      }
      return outArray
   }
   
   Play()
   {
      this.isPlaying := true
      fn := this._Play.Bind(this)
      this._fn := fn
      SetTimer, % fn, -1
   }
   
   Pause()
   {
      this.isPlaying := false
      fn := this._fn
      SetTimer, % fn, Delete
   }
   
   _Play(mode := "set")
   {
      this.frameCurrent := mod(++this.frameCurrent, this.frameCount)
      DllCall("Gdiplus\GdipImageSelectActiveFrame", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", this.frameCurrent)
      if (this.text != "") {
         G := Gdip_GraphicsFromImage(this.pBitmap)
         Gdip_GetDimensions(this.pBitmap, W, H)
         Gdip_TextToGraphics(G, this.text, this.textOptions, this.font, W, H)
         Gdip_DeleteGraphics(G)
      }
      hBitmap := Gdip_CreateHBITMAPFromBitmap(this.pBitmap)
      GuiControl,, % this.hwnd, HBITMAP: %hBitmap%
      ; SetImage(this.hwnd, hBitmap) ; old variant
      ; DeleteObject(hBitmap)
      if (mode = "set" && this.frameCurrent < (this.cycle ? 0xFFFFFFFF : this.frameCount - 1)) {
         fn := this._fn
         SetTimer, % fn, % -1 * this.frameDelay[this.frameCurrent]
      }
   }
   
   __Delete()
   {
      Gdip_DisposeImage(this.pBitmap)
      Object.Delete("dimensionIDs")
   }
}

Понадобится GDI+ standard library 1.45 by tic, параметр «options» описан в восьмом примере на странице по ссылке.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

4

Re: AHK: Текст поверх GIF

Хотя можно и через ActiveX:

gifFilePath := "C:\Users\User\Desktop\bg76852ee8ba300133.gif"
FixIE()
Size := GetImageSize(gifFilePath)

Gui, Add, ActiveX, % "w" . Size.W . " h" . Size.H . " vDoc", htmlfile
Doc.write("<body style='margin: 0; overflow: hidden;'><img src='" gifFilePath "' width='" Size.W "'></body>")
par := Doc.createElement("p")
par.innerHTML := "Это текст, который будет поверх gif"
styles := par.style
for k, v in { color: "white", fontFamily: "Calibri"
            , fontSize: 30, textAlign: "center"
            , position: "fixed", top: "100px", width: Size.W . "px" }
   styles[k] := v
Doc.body.appendChild(par)
Gui, Show
Return

GuiClose:
   ExitApp

GetImageSize(imageFilePath) {
   if !hBitmap := LoadPicture(imageFilePath, "GDI+")
      throw "Failed to load the image"
   VarSetCapacity(BITMAP, size := 4*4 + A_PtrSize*2, 0)
   DllCall("GetObject", "Ptr", hBitmap, "Int", size, "Ptr", &BITMAP)
   DllCall("DeleteObject", "Ptr", hBitmap)
   Return { W: NumGet(BITMAP, 4, "UInt"), H: NumGet(BITMAP, 8, "UInt") }
}

FixIE() {
   static regKey := "HKCU\Software\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION"
   SplitPath, % A_IsCompiled ? A_ScriptFullPath : A_AhkPath, exeName
   RegRead, value, % regKey, % exeName
   if (value != 11000)
      RegWrite, REG_DWORD, % regKey, % exeName, 11000
   Return !ErrorLevel
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5

Re: AHK: Текст поверх GIF

Поигрался воспроизведением гиф через activex.
Совсем беда - не больше 5 контролов дает создать с анимацией.

imagefullpath := "D:\back.gif"

html := "<html><body><img src='" imagefullpath "'></body></html>"
Count := 5
loop % Count
{
   Gui, Add, ActiveX, w100 h100 va%A_Index%, htmlfile
   a%A_Index%.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=EDGE"">")
   sleep 50
}
loop % Count
   a%A_Index%.write(html)
Gui, Show

6

Re: AHK: Текст поверх GIF

У меня ни на семёрке, ни на десятке нет такого ограничения.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

7

Re: AHK: Текст поверх GIF

Хотя нет, иногда только один- подгружает.
Я имею в виду анимрованные гифы.
Часть из них у меня показывается статичными.

8

Re: AHK: Текст поверх GIF

У меня вот:
 
 https://i.imgur.com/lV1uQny.gif

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

9

Re: AHK: Текст поверх GIF

И похоже это каким-то образом зависит от гифов.
Те, которые с прозрачностью совсем глючат.
https://gifyu.com/image/19Xv

10

Re: AHK: Текст поверх GIF

Еще видно от кеша зависит.
Если вставлять новые, то бывает, что только один подгружается.
https://giphy.com/explore/big

11

Re: AHK: Текст поверх GIF

https://i.imgur.com/BeIOWzn.gif

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

12

Re: AHK: Текст поверх GIF

Кстати, довёл до ума класс с GDIplus:

#NoEnv
SetBatchLines, -1

filePath := "D:\OneDrive\Scripts\GDI+\Gif\gif files\abstract.gif"

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
{
   __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() {                    ; UserFunc will be called with two params: currentFrameIdx and hBitmap
      this.playing := true     ; user is responsible for deleting hBitmap
      this.Frames.PlayFrames()
   }
   Pause() {
      if this.playing {
         this.playing := false
         timer := this.Frames._PlayTimer
         SetTimer, % timer, Delete
      }
   }
   Stop() {
      this.Pause()
      this.PlayFrame(1)
   }
   Prev() {
      this.PlayFrame("prev")
   }
   Next() {
      this.PlayFrame("next")
   }
   PlayFrame(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.Delete("GDIp")
   }
   
   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
            SetTimer, % timer, % "-" this.frameDelay[frameIdx]
         }
         if userFunc := this.userFunc
            %userFunc%(this.currentFrame, this.GetFrame(frameIdx))
      }
      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")
         this.Delete("GDIp")
      }
   }
}

class GDIplus {
   __New() {
      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
   }
   __Delete() {
      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)
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

13

Re: AHK: Текст поверх GIF

И у тебя постоянно показываются все гифы?

14

Re: AHK: Текст поверх GIF

Пока не встречал проблемных.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

15

Re: AHK: Текст поверх GIF

Интересно. У меня на 2 компьютерах с разным виндовсом 10 одна и та же ерунда.
Кстати, на оф.форуме тоже пишут:

One problem it has is that if you add more than one animated GIF with it, often one of them would be frozen or wouldn't be shown. But if you want just one, this seems to work well. I don't know if the dll-based version you posted will show multiple GIFs without problems.

https://www.autohotkey.com/boards/viewt … 632#p38632

16

Re: AHK: Текст поверх GIF

Чёрт его знает. У меня с семёркой довольно старый комп, всё ок.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

17 (изменено: Malcev, Сегодня 05:10:27)

Re: AHK: Текст поверх GIF

А вот так у тебя тоже все анимируется на win10?

imagefullpath := "D:\back.gif"

html := "<html><img src='" imagefullpath "'></html>"
Count := 2
loop % Count
{
   Gui, Add, ActiveX, w100 h100 va%A_Index%, about:<!DOCTYPE html><meta http-equiv="X-UA-Compatible" content="IE=edge">
   a%A_Index%.document.write(html)
}
Gui, Show
return
f11::
a2.refresh()
return

У меня вторая гифка анимируется только при зажатии f11.

18

Re: AHK: Текст поверх GIF

Malcev пишет:

У меня вторая гифка анимируется только при зажатии f11.

У меня так же.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

19 (изменено: Malcev, Сегодня 12:38:13)

Re: AHK: Текст поверх GIF

Понял причину.
Гифы не могут быть одинакового пути.
Интересный глюк.
https://docs.microsoft.com/hr-HR/troubl … -instances

imagefullpath1 := "D:\bel\1.gif"
imagefullpath2 := "D:\bel\2.gif"

Count := 2
loop % Count
{
   html := "<html><img src='" imagefullpath%A_Index% "'></html>"
   Gui, Add, ActiveX, w100 h100 va%A_Index%, about:<!DOCTYPE html><meta http-equiv="X-UA-Compatible" content="IE=edge">
   a%A_Index%.document.write(html)
}
Gui, Show

20

Re: AHK: Текст поверх GIF

Интересно, но я бы в любом случае использовал вариант с GDIplus, так как есть возможность управления. На самом деле, в html тоже можно управлять gif через javascript, но довольно сложно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder