<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Серый форум &mdash; AHK: Текст поверх GIF]]></title>
	<link rel="self" href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=16394&amp;type=atom" />
	<updated>2021-06-18T13:08:47Z</updated>
	<generator>PunBB</generator>
	<id>https://forum.script-coding.com/viewtopic.php?id=16394</id>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148510#p148510" />
			<content type="html"><![CDATA[<p>Вряд ли тебе будет интересно, там для игр в основном. Например, к <a href="https://ggloader.com/">этому</a> имею отношение. Но моя работа непосредственно со взаимодействием с игрой не связана.</p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-18T13:08:47Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148510#p148510</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148509#p148509" />
			<content type="html"><![CDATA[<p>А можешь скинуть ссылку на какую-нибудь программу?</p>]]></content>
			<author>
				<name><![CDATA[Malcev]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=26930</uri>
			</author>
			<updated>2021-06-18T11:58:50Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148509#p148509</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148508#p148508" />
			<content type="html"><![CDATA[<p>Я собственноручно никогда продажами не занимался, но заказчики как-то умудряются продавать. <img src="//forum.script-coding.com/img/smilies/smile.png" width="15" height="15" /></p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-18T11:38:45Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148508#p148508</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148507#p148507" />
			<content type="html"><![CDATA[<p>Ну наверное это разумно при условии, что вообще будут покупать. Особенно в России, где практически любой популярный продукт сразу же ломается и выставляется в Варезе на рубоарде.</p>]]></content>
			<author>
				<name><![CDATA[Malcev]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=26930</uri>
			</author>
			<updated>2021-06-18T11:27:54Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148507#p148507</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148506#p148506" />
			<content type="html"><![CDATA[<p>А зачем терять покупателей? Просто для разных систем могут быть разные версии.</p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-18T11:18:38Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148506#p148506</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148504#p148504" />
			<content type="html"><![CDATA[<p>Про семерку, когда говорили, ты говорил, что у тебя клиенты есть, которые сидят на win7.<br />Поэтому, наверное, при конкретном заказе есть резон их операционную систему поддержать.<br />Но изготавливая новый софт вгонять себя в рамки из-за устаревшей системы... Ну имхо то еще развлечение.</p>]]></content>
			<author>
				<name><![CDATA[Malcev]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=26930</uri>
			</author>
			<updated>2021-06-18T08:27:27Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148504#p148504</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148503#p148503" />
			<content type="html"><![CDATA[<p>Да тоже пробовал, да, подтормаживает.<br />Про семерку обсуждали же уже, в России порядка 30% пользователей.</p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-18T08:02:56Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148503#p148503</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148502#p148502" />
			<content type="html"><![CDATA[<div class="quotebox"><blockquote><p>Так скрипт для этого и сделан — пробовать запихнуть много гифов, я даже больше пробовал.</p></blockquote></div><p>Попробуй запихнуть 100 этих гифов и подвигать окно.<br /><a href="https://gifyu.com/image/14sf">https://gifyu.com/image/14sf</a><br /></p><div class="quotebox"><cite>teadrinker пишет:</cite><blockquote><p>Как почему? На семёрке это не работает.</p></blockquote></div><p>Многие производители софта для таких случаев пишут системные требования.<br />Ты бы еще XP вспомнил. <img src="//forum.script-coding.com/img/smilies/smile.png" width="15" height="15" /></p>]]></content>
			<author>
				<name><![CDATA[Malcev]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=26930</uri>
			</author>
			<updated>2021-06-18T08:00:56Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148502#p148502</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148501#p148501" />
			<content type="html"><![CDATA[<p>Так скрипт для этого и сделан — пробовать запихнуть много гифов, я даже больше пробовал.<br />Браузер — это многофункциональная тяжелая программа, тут расход памяти хотя бы оправдан, а мы просто картинки показываем.<br />Как почему? На семёрке это не работает.</p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-18T07:54:59Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148501#p148501</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148499#p148499" />
			<content type="html"><![CDATA[<div class="quotebox"><blockquote><p>Поэтому решение с дочерними layered windows отпадает, как не универсальное</p></blockquote></div><p>Почему не универсальное?<br /></p><div class="quotebox"><blockquote><p>решение с предзагрузкой, думаю, тоже, как отъедающее слишком много памяти</p></blockquote></div><p>У меня отъедает всего в 3 раза больше, чем без предзагрузки.<br />Но цифры смешные по сравнению с тем сколько кушает любой современный броузер.<br />Насчет одного окна - попробуй запихать 100 гифов в одно окно и посмотри на тормоза.</p>]]></content>
			<author>
				<name><![CDATA[Malcev]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=26930</uri>
			</author>
			<updated>2021-06-18T07:46:08Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148499#p148499</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148498#p148498" />
			<content type="html"><![CDATA[<p>На практике — я имею в виду на продажу. Поэтому решение с дочерними layered windows отпадает, как не универсальное, решение с предзагрузкой, думаю, тоже, как отъедающее слишком много памяти. Пришлось бы выбирать из оставшихся, тут в зависимости от конкретных условий задачи.</p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-18T07:39:43Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148498#p148498</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148497#p148497" />
			<content type="html"><![CDATA[<p>Я бы на практике вообще не стал бы пихать много гифов в одно окно.<br />Либо использовал предназначенные для этого контролы типа WebControl, либо отрисовывал бы в дочерних окнах.</p>]]></content>
			<author>
				<name><![CDATA[Malcev]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=26930</uri>
			</author>
			<updated>2021-06-18T03:53:24Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148497#p148497</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148496#p148496" />
			<content type="html"><![CDATA[<p>Ну, на практике я так делать бы не стал — более, чем в 10 раз увеличивается расход памяти. Но попробовал:<br /></p><div class="codebox"><pre><code>#NoEnv
SetBatchLines, -1

filePath := &quot;D:\OneDrive\Scripts\GDI+\Gif\gif files\girl.gif&quot;

AnimateGif.Gifs := {}
exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
Gui, Margin, 5, 5
GuiW := 12 + 5 ; borders + margin
Loop 24 {
   if (A_Index &gt; 1) {
      GuiW += W + 5
      if (GuiW &lt; A_ScreenWidth)
         x := &quot;x+5&quot;, y := &quot;yp&quot;
      else
         x := &quot;xm&quot;, y := &quot;y+5&quot;, GuiW := 12 + 5 + W
   }
   Gui, Add, Pic, % &quot;hwndhGif&quot; . A_Index . &quot; &quot; . (A_Index = 1 ? &quot;&quot; : x . &quot; &quot; . y), % &quot;HBITMAP:&quot; . LoadPicture(filePath, &quot;GDI+&quot;)
   if (A_Index = 1) {
      WinGetPos,,, W,, % &quot;ahk_id&quot; hGif%A_Index%
      GuiW += W
   }
   handler := Func(&quot;OnClick&quot;).Bind(hGif%A_Index%)
   GuiControl, +g, % hGif%A_Index%, % handler
   AnimateGif.Gifs[hGif%A_Index%] := new AnimateGif(filePath, Func(&quot;PlayGif&quot;).Bind(hGif%A_Index%))
   AnimateGif.Gifs[hGif%A_Index%].Play()
}
OnMessage(0x20, &quot;WM_SETCURSOR&quot;)
Gui, Show
Return

GuiClose:
   AnimateGif.Gifs := &quot;&quot;
   ExitApp

PlayGif(hPic, currentFrameIdx, hBitmap) {
   GuiControl,, % hPic, HBITMAP: %hBitmap%
}

OnClick(hPic) {
   if AnimateGif.Gifs[hPic].playing
      AnimateGif.Gifs[hPic].Pause()
   Else
      AnimateGif.Gifs[hPic].Play()
}

WM_SETCURSOR(wp) {
   static hCursor, flags := (LR_DEFAULTSIZE := 0x40) | (LR_SHARED := 0x8000)
        , params := [ &quot;Ptr&quot;, 0, &quot;UInt&quot;, OCR_HAND := 32649
                              , &quot;UInt&quot;, IMAGE_CURSOR := 2
                              , &quot;Int&quot;, 0, &quot;Int&quot;, 0, &quot;UInt&quot;, flags, &quot;Ptr&quot; ]
   (!hCursor &amp;&amp; hCursor := DllCall(&quot;LoadImage&quot;, params*))
   if AnimateGif.Gifs.HasKey(wp)
      Return DllCall(&quot;SetCursor&quot;, &quot;Ptr&quot;, hCursor)
}

class AnimateGif
{
   __New(gifFile, UserFunc := &quot;&quot;, cycle := true) {
      this.GDIp := new GDIplus
      this.pBitmap := this.GDIp.CreateBitmapFromFile(gifFile)
      this.GDIp.GetImageDimensions(this.pBitmap, width, height)
      this.width := width
      this.height := height
      this.Frames := new this._FramesHandling(this.GDIp, this.pBitmap, width, height, UserFunc, cycle)
   }
   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.ShowFrame(1)
   }
   Prev() {
      this.ShowFrame(&quot;prev&quot;)
   }
   Next() {
      this.ShowFrame(&quot;next&quot;)
   }
   ShowFrame(which) {          ; &#039;which&#039; can be &quot;prev&quot;, &quot;next&quot; or &quot;&quot;, or 1-based frame index
      this.Pause()
      (which = &quot;prev&quot; &amp;&amp; this.Frames.currentFrame -= 2)
      (which + 0 &amp;&amp; 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(&quot;Frames&quot;)
      this.GDIp.Release()
   }
   
   class _FramesHandling {
      __New(GDIp, pBitmap, width, height, userFunc, cycle) {
         this.GDIp := GDIp
         this.pBitmap := pBitmap
         this.hDC := DllCall(&quot;GetDC&quot;, &quot;Ptr&quot;, 0, &quot;Ptr&quot;)
         this.DC := new CompatibleDC(this.hDC, width, height)
         this.G := this.GDIp.GraphicsFromHDC(this.DC.hCDC)
         this.userFunc := userFunc
         this.cycle := cycle
         this.GetFrameCount()
         this.GetFrameDelay()
         this.CreateBitmapsArray()
         this._PlayTimer := ObjBindMethod(this, &quot;PlayFrames&quot;)
         this._currentFrame := 1
      }
      currentFrame[] {
         get {
            Return this._currentFrame
         }
         set {
            Return this._currentFrame := value &lt; 1 ? this.frameCount + value : value &gt; this.frameCount ? 1 : value
         }
      }
      PlayFrames() {
         Critical
         frameIdx := ++this.currentFrame - 1
         if ( this.playing &amp;&amp; this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) {
            timer := this._PlayTimer
            SetTimer, % timer, % &quot;-&quot; 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(&quot;dimensionIDs&quot;, size)
         this.pDimensionIDs := this.GetAddress(&quot;dimensionIDs&quot;)
         DllCall(&quot;RtlMoveMemory&quot;, &quot;Ptr&quot;, this.pDimensionIDs, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;Ptr&quot;, size)
         VarSetCapacity(dimensionIDs, 0), dimensionIDs := &quot;&quot;
         this.currentFrame := 0
      }
      GetFrameDelay() {
         this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item)
         len := NumGet(item, 4, &quot;UInt&quot;)
         val := NumGet(item, 8 + A_PtrSize, &quot;UPtr&quot;)
         this.frameDelay := []
         Loop, % len//4 {
            i := A_Index - 1
            n := NumGet(val + i*4, &quot;UInt&quot;) * 10
            this.frameDelay[i] := n ? n : 100
         }
      }
      CreateBitmapsArray() {
         this.CachedBitmaps := []
         Loop % this.frameCount {
            this.GDIp.ImageSelectActiveFrame(this.pBitmap, this.pDimensionIDs, A_Index - 1)
            DllCall(&quot;gdiplus\GdipCreateCachedBitmap&quot;, &quot;Ptr&quot;, this.pBitmap, &quot;Ptr&quot;, this.G, &quot;PtrP&quot;, pCachedBitmap)
            this.CachedBitmaps[A_Index - 1] := pCachedBitmap
         }
      }
      GetFrame(idx) {
         DllCall(&quot;gdiplus\GdipDrawCachedBitmap&quot;, &quot;Ptr&quot;, this.G, &quot;Ptr&quot;, this.CachedBitmaps[idx], &quot;Int&quot;, 0, &quot;Int&quot;, 0)
         Return this.DC.hCBM
      }
      Clear() {
         this.playing := false
         timer := this._PlayTimer
         SetTimer, % timer, Delete
         Loop % this.frameCount
            DllCall(&quot;gdiplus\GdipDeleteCachedBitmap&quot;, &quot;Ptr&quot;, this.CachedBitmaps[A_Index - 1])
         this.Delete(CachedBitmaps)
         this.Delete(&quot;dimensionIDs&quot;)
         this.GDIp.DeleteGraphics(this.G)
         this.DC := &quot;&quot;
         DllCall(&quot;ReleaseDC&quot;, this.hDC)
      }
   }
}

class CompatibleDC
{
   __New(hDC, w, h)  {
      this.hCDC := DllCall(&quot;CreateCompatibleDC&quot;, Ptr, hDC, Ptr)
      this.hCBM := DllCall(&quot;CreateCompatibleBitmap&quot;, Ptr, hDC, Int, w, Int, h, Ptr)
      this.oBM := DllCall(&quot;SelectObject&quot;, Ptr, this.hCDC, Ptr, this.hCBM, Ptr)
   }
   
   __Delete()  {
      DllCall(&quot;SelectObject&quot;, Ptr, this.hCDC, Ptr, this.oBM, Ptr)
      DllCall(&quot;DeleteDC&quot;, Ptr, this.hCDC)
      DllCall(&quot;DeleteObject&quot;, Ptr, this.hCBM)
   }
}

class GDIplus {
   __New() {
      static Instance := &quot;&quot;
      if Instance.references {
         ++Instance.references
         Return Instance
      }
      this.references := 1
      if !DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;LoadLibrary&quot;, &quot;Str&quot;, &quot;gdiplus&quot;)
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall(&quot;gdiplus\GdiplusStartup&quot;, &quot;UPtrP&quot;, pToken, &quot;Ptr&quot;, &amp;si, &quot;Ptr&quot;, 0)
      this.token := pToken
      Return Instance := this
   }
   Release() {
      if --this.references
         Return
      DllCall(&quot;gdiplus\GdiplusShutdown&quot;, &quot;Ptr&quot;, this.token)
      if hModule := DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;FreeLibrary&quot;, &quot;Ptr&quot;, hModule)
   }
   CreateBitmapFromFile(sFile) {
      DllCall(&quot;gdiplus\GdipCreateBitmapFromFile&quot;, &quot;WStr&quot;, sFile, &quot;PtrP&quot;, pBitmap)
      Return pBitmap
   }
   GraphicsFromHDC(hdc) {
       DllCall(&quot;gdiplus\GdipCreateFromHDC&quot;, &quot;Ptr&quot;, hdc, &quot;PtrP&quot;, pGraphics)
       return pGraphics
   }
   CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) {
      DllCall(&quot;gdiplus\GdipCreateHBITMAPFromBitmap&quot;, &quot;Ptr&quot;, pBitmap, &quot;PtrP&quot;, hbm, &quot;Int&quot;, Background)
      Return hbm
   }
   GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
      DllCall(&quot;gdiplus\GdipGetImageWidth&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Width)
      DllCall(&quot;gdiplus\GdipGetImageHeight&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Height)
   }
   GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) {
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, frameDimensions)
      VarSetCapacity(dimensionIDs, size := 16*frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsList&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UInt&quot;, frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UIntP&quot;, count)
      Return count
   }
   GetPropertyItem(pBitmap, tag, ByRef item) {
      DllCall(&quot;gdiplus\GdipGetPropertyItemSize&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UIntP&quot;, size)
      VarSetCapacity(item, size, 0)
      DllCall(&quot;gdiplus\GdipGetPropertyItem&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UInt&quot;, size, &quot;Ptr&quot;, &amp;item)
      Return size
   }
   ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) {
      Return DllCall(&quot;gdiplus\GdipImageSelectActiveFrame&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, pDimensionIDs, &quot;Int&quot;, idx)
   }
   DeleteGraphics(pGraphics) {
      return DllCall(&quot;gdiplus\GdipDeleteGraphics&quot;, &quot;Ptr&quot;, pGraphics)
   }
   DisposeImage(pBitmap) {
      Return DllCall(&quot;gdiplus\GdipDisposeImage&quot;, &quot;Ptr&quot;, pBitmap)
   }
}</code></pre></div><p>Действительно, стало чуть поживее. Но всё равно медленнее, чем GdipDrawImageRectI, и, как оказалось, тоже размазывает области с прозрачностью.</p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-18T01:53:37Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148496#p148496</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148495#p148495" />
			<content type="html"><![CDATA[<p>А зачем ты каждый раз создаешь CachedBitmap, отрисовываешь, удаляешь, а не до запуска скрипта всех их создаешь и потом при необходимости отрисовываешь?</p>]]></content>
			<author>
				<name><![CDATA[Malcev]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=26930</uri>
			</author>
			<updated>2021-06-18T01:29:10Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148495#p148495</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: AHK: Текст поверх GIF]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=148493#p148493" />
			<content type="html"><![CDATA[<p>Понял, как применить CachedBitmap к обычному окну, но разницы не ощутил. Основное время в данном случае занимает отрисовка контрола через </p><div class="codebox"><pre><code>GuiControl,, % hPic, HBITMAP: %hBitmap%</code></pre></div><p>Вот первоначальный вариант с добавленным позиционированием множества контролов:<br /></p><div class="codebox"><pre><code>#NoEnv
SetBatchLines, -1

filePath := &quot;D:\OneDrive\Scripts\GDI+\Gif\gif files\girl.gif&quot;

AnimateGif.Gifs := {}
exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
Gui, Margin, 5, 5
GuiW := 12 + 5 ; borders + margin
Loop 20 {
   if (A_Index &gt; 1) {
      GuiW += W + 5
      if (GuiW &lt; A_ScreenWidth)
         x := &quot;x+5&quot;, y := &quot;yp&quot;
      else
         x := &quot;xm&quot;, y := &quot;y+5&quot;, GuiW := 12 + 5 + W
   }
   Gui, Add, Pic, % &quot;hwndhGif&quot; . A_Index . &quot; &quot; . (A_Index = 1 ? &quot;&quot; : x . &quot; &quot; . y), % &quot;HBITMAP:&quot; . LoadPicture(filePath, &quot;GDI+&quot;)
   if (A_Index = 1) {
      WinGetPos,,, W,, % &quot;ahk_id&quot; hGif%A_Index%
      GuiW += W
   }
   handler := Func(&quot;OnClick&quot;).Bind(hGif%A_Index%)
   GuiControl, +g, % hGif%A_Index%, % handler
   AnimateGif.Gifs[hGif%A_Index%] := new AnimateGif(filePath, Func(&quot;PlayGif&quot;).Bind(hGif%A_Index%))
   AnimateGif.Gifs[hGif%A_Index%].Play()
}
OnMessage(0x20, &quot;WM_SETCURSOR&quot;)
Gui, Show
Return

GuiClose:
   AnimateGif.Gifs := &quot;&quot;
   ExitApp

PlayGif(hPic, currentFrameIdx, hBitmap) {
   GuiControl,, % hPic, HBITMAP: %hBitmap%
}

OnClick(hPic) {
   if AnimateGif.Gifs[hPic].playing
      AnimateGif.Gifs[hPic].Pause()
   Else
      AnimateGif.Gifs[hPic].Play()
}

WM_SETCURSOR(wp) {
   static hCursor, flags := (LR_DEFAULTSIZE := 0x40) | (LR_SHARED := 0x8000)
        , params := [ &quot;Ptr&quot;, 0, &quot;UInt&quot;, OCR_HAND := 32649
                              , &quot;UInt&quot;, IMAGE_CURSOR := 2
                              , &quot;Int&quot;, 0, &quot;Int&quot;, 0, &quot;UInt&quot;, flags, &quot;Ptr&quot; ]
   (!hCursor &amp;&amp; hCursor := DllCall(&quot;LoadImage&quot;, params*))
   if AnimateGif.Gifs.HasKey(wp)
      Return DllCall(&quot;SetCursor&quot;, &quot;Ptr&quot;, hCursor)
}

class AnimateGif
{
   __New(gifFile, UserFunc := &quot;&quot;, 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.ShowFrame(1)
   }
   Prev() {
      this.ShowFrame(&quot;prev&quot;)
   }
   Next() {
      this.ShowFrame(&quot;next&quot;)
   }
   ShowFrame(which) {          ; &#039;which&#039; can be &quot;prev&quot;, &quot;next&quot; or &quot;&quot;, or 1-based frame index
      this.Pause()
      (which = &quot;prev&quot; &amp;&amp; this.Frames.currentFrame -= 2)
      (which + 0 &amp;&amp; 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(&quot;Frames&quot;)
      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, &quot;PlayFrames&quot;)
         this._currentFrame := 1
      }
      currentFrame[] {
         get {
            Return this._currentFrame
         }
         set {
            Return this._currentFrame := value &lt; 1 ? this.frameCount + value : value &gt; this.frameCount ? 1 : value
         }
      }
      PlayFrames() {
         Critical
         frameIdx := ++this.currentFrame - 1
         if ( this.playing &amp;&amp; this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) {
            timer := this._PlayTimer
            SetTimer, % timer, % &quot;-&quot; 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(&quot;dimensionIDs&quot;, size)
         this.pDimensionIDs := this.GetAddress(&quot;dimensionIDs&quot;)
         DllCall(&quot;RtlMoveMemory&quot;, &quot;Ptr&quot;, this.pDimensionIDs, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;Ptr&quot;, size)
         VarSetCapacity(dimensionIDs, 0), dimensionIDs := &quot;&quot;
         this.currentFrame := 0
      }
      GetFrameDelay() {
         this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item)
         len := NumGet(item, 4, &quot;UInt&quot;)
         val := NumGet(item, 8 + A_PtrSize, &quot;UPtr&quot;)
         this.frameDelay := []
         Loop, % len//4 {
            i := A_Index - 1
            n := NumGet(val + i*4, &quot;UInt&quot;) * 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(&quot;dimensionIDs&quot;)
      }
   }
}

class GDIplus {
   __New() {
      static Instance := &quot;&quot;
      if Instance.references {
         ++Instance.references
         Return Instance
      }
      this.references := 1
      if !DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;LoadLibrary&quot;, &quot;Str&quot;, &quot;gdiplus&quot;)
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall(&quot;gdiplus\GdiplusStartup&quot;, &quot;UPtrP&quot;, pToken, &quot;Ptr&quot;, &amp;si, &quot;Ptr&quot;, 0)
      this.token := pToken
      Return Instance := this
   }
   Release() {
      if --this.references
         Return
      DllCall(&quot;gdiplus\GdiplusShutdown&quot;, &quot;Ptr&quot;, this.token)
      if hModule := DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;FreeLibrary&quot;, &quot;Ptr&quot;, hModule)
   }
   CreateBitmapFromFile(sFile) {
      DllCall(&quot;gdiplus\GdipCreateBitmapFromFile&quot;, &quot;WStr&quot;, sFile, &quot;PtrP&quot;, pBitmap)
      Return pBitmap
   }
   CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) {
      DllCall(&quot;gdiplus\GdipCreateHBITMAPFromBitmap&quot;, &quot;Ptr&quot;, pBitmap, &quot;PtrP&quot;, hbm, &quot;Int&quot;, Background)
      Return hbm
   }
   GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
      DllCall(&quot;gdiplus\GdipGetImageWidth&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Width)
      DllCall(&quot;gdiplus\GdipGetImageHeight&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Height)
   }
   GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) {
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, frameDimensions)
      VarSetCapacity(dimensionIDs, size := 16*frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsList&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UInt&quot;, frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UIntP&quot;, count)
      Return count
   }
   GetPropertyItem(pBitmap, tag, ByRef item) {
      DllCall(&quot;gdiplus\GdipGetPropertyItemSize&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UIntP&quot;, size)
      VarSetCapacity(item, size, 0)
      DllCall(&quot;gdiplus\GdipGetPropertyItem&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UInt&quot;, size, &quot;Ptr&quot;, &amp;item)
      Return size
   }
   ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) {
      Return DllCall(&quot;gdiplus\GdipImageSelectActiveFrame&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, pDimensionIDs, &quot;Int&quot;, idx)
   }
   DisposeImage(pBitmap) {
      Return DllCall(&quot;gdiplus\GdipDisposeImage&quot;, &quot;Ptr&quot;, pBitmap)
   }
}</code></pre></div><p>Вот с CachedBitmap:<br /></p><div class="codebox"><pre><code>#NoEnv
SetBatchLines, -1

filePath := &quot;D:\OneDrive\Scripts\GDI+\Gif\gif files\girl.gif&quot;

AnimateGif.Gifs := {}
exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
Gui, Margin, 5, 5
GuiW := 12 + 5 ; borders + margin
Loop 20 {
   if (A_Index &gt; 1) {
      GuiW += W + 5
      if (GuiW &lt; A_ScreenWidth)
         x := &quot;x+5&quot;, y := &quot;yp&quot;
      else
         x := &quot;xm&quot;, y := &quot;y+5&quot;, GuiW := 12 + 5 + W
   }
   Gui, Add, Pic, % &quot;hwndhGif&quot; . A_Index . &quot; &quot; . (A_Index = 1 ? &quot;&quot; : x . &quot; &quot; . y), % &quot;HBITMAP:&quot; . LoadPicture(filePath, &quot;GDI+&quot;)
   if (A_Index = 1) {
      WinGetPos,,, W,, % &quot;ahk_id&quot; hGif%A_Index%
      GuiW += W
   }
   handler := Func(&quot;OnClick&quot;).Bind(hGif%A_Index%)
   GuiControl, +g, % hGif%A_Index%, % handler
   AnimateGif.Gifs[hGif%A_Index%] := new AnimateGif(filePath, Func(&quot;PlayGif&quot;).Bind(hGif%A_Index%))
   AnimateGif.Gifs[hGif%A_Index%].Play()
}
OnMessage(0x20, &quot;WM_SETCURSOR&quot;)
Gui, Show
Return

GuiClose:
   AnimateGif.Gifs := &quot;&quot;
   ExitApp

PlayGif(hPic, currentFrameIdx, hBitmap) {
   GuiControl,, % hPic, HBITMAP: %hBitmap%
}

OnClick(hPic) {
   if AnimateGif.Gifs[hPic].playing
      AnimateGif.Gifs[hPic].Pause()
   Else
      AnimateGif.Gifs[hPic].Play()
}

WM_SETCURSOR(wp) {
   static hCursor, flags := (LR_DEFAULTSIZE := 0x40) | (LR_SHARED := 0x8000)
        , params := [ &quot;Ptr&quot;, 0, &quot;UInt&quot;, OCR_HAND := 32649
                              , &quot;UInt&quot;, IMAGE_CURSOR := 2
                              , &quot;Int&quot;, 0, &quot;Int&quot;, 0, &quot;UInt&quot;, flags, &quot;Ptr&quot; ]
   (!hCursor &amp;&amp; hCursor := DllCall(&quot;LoadImage&quot;, params*))
   if AnimateGif.Gifs.HasKey(wp)
      Return DllCall(&quot;SetCursor&quot;, &quot;Ptr&quot;, hCursor)
}

class AnimateGif
{
   __New(gifFile, UserFunc := &quot;&quot;, cycle := true) {
      this.GDIp := new GDIplus
      this.pBitmap := this.GDIp.CreateBitmapFromFile(gifFile)
      this.GDIp.GetImageDimensions(this.pBitmap, width, height)
      this.width := width
      this.height := height
      this.Frames := new this._FramesHandling(this.GDIp, this.pBitmap, width, height, UserFunc, cycle)
   }
   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.ShowFrame(1)
   }
   Prev() {
      this.ShowFrame(&quot;prev&quot;)
   }
   Next() {
      this.ShowFrame(&quot;next&quot;)
   }
   ShowFrame(which) {          ; &#039;which&#039; can be &quot;prev&quot;, &quot;next&quot; or &quot;&quot;, or 1-based frame index
      this.Pause()
      (which = &quot;prev&quot; &amp;&amp; this.Frames.currentFrame -= 2)
      (which + 0 &amp;&amp; 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(&quot;Frames&quot;)
      this.GDIp.Release()
   }
   
   class _FramesHandling {
      __New(GDIp, pBitmap, width, height, userFunc, cycle) {
         this.GDIp := GDIp
         this.pBitmap := pBitmap
         this.hDC := DllCall(&quot;GetDC&quot;, &quot;Ptr&quot;, 0, &quot;Ptr&quot;)
         this.DC := new CompatibleDC(this.hDC, width, height)
         this.G := this.GDIp.GraphicsFromHDC(this.DC.hCDC)
         this.userFunc := userFunc
         this.cycle := cycle
         this.GetFrameCount()
         this.GetFrameDelay()
         this._PlayTimer := ObjBindMethod(this, &quot;PlayFrames&quot;)
         this._currentFrame := 1
      }
      currentFrame[] {
         get {
            Return this._currentFrame
         }
         set {
            Return this._currentFrame := value &lt; 1 ? this.frameCount + value : value &gt; this.frameCount ? 1 : value
         }
      }
      PlayFrames() {
         Critical
         frameIdx := ++this.currentFrame - 1
         if ( this.playing &amp;&amp; this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) {
            timer := this._PlayTimer
            SetTimer, % timer, % &quot;-&quot; 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(&quot;dimensionIDs&quot;, size)
         this.pDimensionIDs := this.GetAddress(&quot;dimensionIDs&quot;)
         DllCall(&quot;RtlMoveMemory&quot;, &quot;Ptr&quot;, this.pDimensionIDs, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;Ptr&quot;, size)
         VarSetCapacity(dimensionIDs, 0), dimensionIDs := &quot;&quot;
         this.currentFrame := 0
      }
      GetFrameDelay() {
         this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item)
         len := NumGet(item, 4, &quot;UInt&quot;)
         val := NumGet(item, 8 + A_PtrSize, &quot;UPtr&quot;)
         this.frameDelay := []
         Loop, % len//4 {
            i := A_Index - 1
            n := NumGet(val + i*4, &quot;UInt&quot;) * 10
            this.frameDelay[i] := n ? n : 100
         }
      }
      GetFrame(idx) {
         this.GDIp.ImageSelectActiveFrame(this.pBitmap, this.pDimensionIDs, idx)
         DllCall(&quot;gdiplus\GdipCreateCachedBitmap&quot;, &quot;Ptr&quot;, this.pBitmap, &quot;Ptr&quot;, this.G, &quot;PtrP&quot;, pCachedBitmap)
         DllCall(&quot;gdiplus\GdipDrawCachedBitmap&quot;, &quot;Ptr&quot;, this.G, &quot;Ptr&quot;, pCachedBitmap, &quot;Int&quot;, 0, &quot;Int&quot;, 0)
         DllCall(&quot;gdiplus\GdipDeleteCachedBitmap&quot;, &quot;Ptr&quot;, pCachedBitmap)
         Return this.DC.hCBM
      }
      Clear() {
         this.playing := false
         timer := this._PlayTimer
         SetTimer, % timer, Delete
         this.Delete(&quot;dimensionIDs&quot;)
         this.GDIp.DeleteGraphics(this.G)
         this.DC := &quot;&quot;
         DllCall(&quot;ReleaseDC&quot;, this.hDC)
      }
   }
}

class CompatibleDC
{
   __New(hDC, w, h)  {
      this.hCDC := DllCall(&quot;CreateCompatibleDC&quot;, Ptr, hDC, Ptr)
      this.hCBM := DllCall(&quot;CreateCompatibleBitmap&quot;, Ptr, hDC, Int, w, Int, h, Ptr)
      this.oBM := DllCall(&quot;SelectObject&quot;, Ptr, this.hCDC, Ptr, this.hCBM, Ptr)
   }
   
   __Delete()  {
      DllCall(&quot;SelectObject&quot;, Ptr, this.hCDC, Ptr, this.oBM, Ptr)
      DllCall(&quot;DeleteDC&quot;, Ptr, this.hCDC)
      DllCall(&quot;DeleteObject&quot;, Ptr, this.hCBM)
   }
}


class GDIplus {
   __New() {
      static Instance := &quot;&quot;
      if Instance.references {
         ++Instance.references
         Return Instance
      }
      this.references := 1
      if !DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;LoadLibrary&quot;, &quot;Str&quot;, &quot;gdiplus&quot;)
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall(&quot;gdiplus\GdiplusStartup&quot;, &quot;UPtrP&quot;, pToken, &quot;Ptr&quot;, &amp;si, &quot;Ptr&quot;, 0)
      this.token := pToken
      Return Instance := this
   }
   Release() {
      if --this.references
         Return
      DllCall(&quot;gdiplus\GdiplusShutdown&quot;, &quot;Ptr&quot;, this.token)
      if hModule := DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;FreeLibrary&quot;, &quot;Ptr&quot;, hModule)
   }
   CreateBitmapFromFile(sFile) {
      DllCall(&quot;gdiplus\GdipCreateBitmapFromFile&quot;, &quot;WStr&quot;, sFile, &quot;PtrP&quot;, pBitmap)
      Return pBitmap
   }
   GraphicsFromHDC(hdc) {
       DllCall(&quot;gdiplus\GdipCreateFromHDC&quot;, &quot;Ptr&quot;, hdc, &quot;PtrP&quot;, pGraphics)
       return pGraphics
   }
   CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) {
      DllCall(&quot;gdiplus\GdipCreateHBITMAPFromBitmap&quot;, &quot;Ptr&quot;, pBitmap, &quot;PtrP&quot;, hbm, &quot;Int&quot;, Background)
      Return hbm
   }
   GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
      DllCall(&quot;gdiplus\GdipGetImageWidth&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Width)
      DllCall(&quot;gdiplus\GdipGetImageHeight&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Height)
   }
   GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) {
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, frameDimensions)
      VarSetCapacity(dimensionIDs, size := 16*frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsList&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UInt&quot;, frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UIntP&quot;, count)
      Return count
   }
   GetPropertyItem(pBitmap, tag, ByRef item) {
      DllCall(&quot;gdiplus\GdipGetPropertyItemSize&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UIntP&quot;, size)
      VarSetCapacity(item, size, 0)
      DllCall(&quot;gdiplus\GdipGetPropertyItem&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UInt&quot;, size, &quot;Ptr&quot;, &amp;item)
      Return size
   }
   ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) {
      Return DllCall(&quot;gdiplus\GdipImageSelectActiveFrame&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, pDimensionIDs, &quot;Int&quot;, idx)
   }
   DeleteGraphics(pGraphics) {
      return DllCall(&quot;gdiplus\GdipDeleteGraphics&quot;, &quot;Ptr&quot;, pGraphics)
   }
   DisposeImage(pBitmap) {
      Return DllCall(&quot;gdiplus\GdipDisposeImage&quot;, &quot;Ptr&quot;, pBitmap)
   }
}</code></pre></div><p>Зато очень шустро рисует GdipDrawImageRectI, но не поддерживает WS_EX_COMPOSITED | WS_EX_LAYERED, и размазывает области с прозрачностью:<br /></p><div class="codebox"><pre><code>#NoEnv
SetBatchLines, -1

filePath := &quot;D:\OneDrive\Scripts\GDI+\Gif\gif files\girl.gif&quot;

AnimateGif.Gifs := {}
; exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
; Gui, New, +E%exStyles%
Gui, Margin, 5, 5
GuiW := 12 + 5 ; borders + margin
Loop 20 {
   if (A_Index &gt; 1) {
      GuiW += W + 5
      if (GuiW &lt; A_ScreenWidth)
         x := &quot;x+5&quot;, y := &quot;yp&quot;
      else
         x := &quot;xm&quot;, y := &quot;y+5&quot;, GuiW := 12 + 5 + W
   }
   Gui, Add, Pic, % &quot;hwndhGif&quot; . A_Index . &quot; &quot; . (A_Index = 1 ? &quot;&quot; : x . &quot; &quot; . y), % &quot;HBITMAP:&quot; . LoadPicture(filePath, &quot;GDI+&quot;)
   if (A_Index = 1) {
      WinGetPos,,, W,, % &quot;ahk_id&quot; hGif%A_Index%
      GuiW += W
   }
   handler := Func(&quot;OnClick&quot;).Bind(hGif%A_Index%)
   GuiControl, +g, % hGif%A_Index%, % handler
   AnimateGif.Gifs[hGif%A_Index%] := new AnimateGif(filePath, hGif%A_Index%)
   AnimateGif.Gifs[hGif%A_Index%].Play()
}
OnMessage(0x20, &quot;WM_SETCURSOR&quot;)
Gui, Show
Return

GuiClose:
   AnimateGif.Gifs := &quot;&quot;
   ExitApp

OnClick(hPic) {
   if AnimateGif.Gifs[hPic].playing
      AnimateGif.Gifs[hPic].Pause()
   Else
      AnimateGif.Gifs[hPic].Play()
}

WM_SETCURSOR(wp) {
   static hCursor, flags := (LR_DEFAULTSIZE := 0x40) | (LR_SHARED := 0x8000)
        , params := [ &quot;Ptr&quot;, 0, &quot;UInt&quot;, OCR_HAND := 32649
                              , &quot;UInt&quot;, IMAGE_CURSOR := 2
                              , &quot;Int&quot;, 0, &quot;Int&quot;, 0, &quot;UInt&quot;, flags, &quot;Ptr&quot; ]
   (!hCursor &amp;&amp; hCursor := DllCall(&quot;LoadImage&quot;, params*))
   if AnimateGif.Gifs.HasKey(wp)
      Return DllCall(&quot;SetCursor&quot;, &quot;Ptr&quot;, hCursor)
}

class AnimateGif
{
   __New(gifFile, hPic, cycle := true) {
      this.GDIp := new GDIplus
      this.pBitmap := this.GDIp.CreateBitmapFromFile(gifFile)
      this.GDIp.GetImageDimensions(this.pBitmap, width, height)
      this.width := width
      this.height := height
      this.Frames := new this._FramesHandling(this.GDIp, this.pBitmap, hPic, width, height, cycle)
   }
   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(&quot;prev&quot;)
   }
   Next() {
      this.ShowFrame(&quot;next&quot;)
   }
   ShowFrame(which) {          ; &#039;which&#039; can be &quot;prev&quot;, &quot;next&quot; or &quot;&quot;, or 1-based frame index
      this.Pause()
      (which = &quot;prev&quot; &amp;&amp; this.Frames.currentFrame -= 2)
      (which + 0 &amp;&amp; this.Frames.currentFrame := which - 1)
      this.Frames.PlayFrames()
   }
   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(&quot;Frames&quot;)
      this.GDIp.Release()
   }
   
   class _FramesHandling {
      __New(GDIp, pBitmap, hPic, width, height, cycle) {
         this.GDIp := GDIp
         this.pBitmap := pBitmap
         this.G := this.GDIp.GraphicsFromHwnd(hPic)
         this.width := width
         this.height := height
         this.cycle := cycle
         this.GetFrameCount()
         this.GetFrameDelay()
         this._PlayTimer := ObjBindMethod(this, &quot;PlayFrames&quot;)
         this._currentFrame := 1
      }
      currentFrame[] {
         get {
            Return this._currentFrame
         }
         set {
            Return this._currentFrame := value &lt; 1 ? this.frameCount + value : value &gt; this.frameCount ? 1 : value
         }
      }
      PlayFrames() {
         Critical
         frameIdx := ++this.currentFrame - 1
         if ( this.playing &amp;&amp; this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) {
            timer := this._PlayTimer
            SetTimer, % timer, % &quot;-&quot; this.frameDelay[frameIdx]
         }
         this.DrawFrame(frameIdx)
      }
      GetFrameCount() {
         this.frameCount := this.GDIp.GetFrameCount(this.pBitmap, dimensionIDs, size)
         this.SetCapacity(&quot;dimensionIDs&quot;, size)
         this.pDimensionIDs := this.GetAddress(&quot;dimensionIDs&quot;)
         DllCall(&quot;RtlMoveMemory&quot;, &quot;Ptr&quot;, this.pDimensionIDs, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;Ptr&quot;, size)
         VarSetCapacity(dimensionIDs, 0), dimensionIDs := &quot;&quot;
         this.currentFrame := 0
      }
      GetFrameDelay() {
         this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item)
         len := NumGet(item, 4, &quot;UInt&quot;)
         val := NumGet(item, 8 + A_PtrSize, &quot;UPtr&quot;)
         this.frameDelay := []
         Loop, % len//4 {
            i := A_Index - 1
            n := NumGet(val + i*4, &quot;UInt&quot;) * 10
            this.frameDelay[i] := n ? n : 100
         }
      }
      DrawFrame(idx) {
         this.GDIp.ImageSelectActiveFrame(this.pBitmap, this.pDimensionIDs, idx)
         DllCall(&quot;gdiplus\GdipDrawImageRectI&quot;, &quot;Ptr&quot;, this.G, &quot;Ptr&quot;, this.pBitmap, &quot;Int&quot;, 0, &quot;Int&quot;, 0, &quot;Int&quot;, this.width, &quot;Int&quot;, this.height)
      }
      Clear() {
         this.playing := false
         timer := this._PlayTimer
         SetTimer, % timer, Delete
         this.Delete(&quot;dimensionIDs&quot;)
         this.GDIp.DeleteGraphics(this.G)
      }
   }
}

class GDIplus {
   __New() {
      static Instance := &quot;&quot;
      if Instance.references {
         ++Instance.references
         Return Instance
      }
      this.references := 1
      if !DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;LoadLibrary&quot;, &quot;Str&quot;, &quot;gdiplus&quot;)
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall(&quot;gdiplus\GdiplusStartup&quot;, &quot;UPtrP&quot;, pToken, &quot;Ptr&quot;, &amp;si, &quot;Ptr&quot;, 0)
      this.token := pToken
      Return Instance := this
   }
   Release() {
      if --this.references
         Return
      DllCall(&quot;gdiplus\GdiplusShutdown&quot;, &quot;Ptr&quot;, this.token)
      if hModule := DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;gdiplus&quot;, &quot;Ptr&quot;)
         DllCall(&quot;FreeLibrary&quot;, &quot;Ptr&quot;, hModule)
   }
   CreateBitmapFromFile(sFile) {
      DllCall(&quot;gdiplus\GdipCreateBitmapFromFile&quot;, &quot;WStr&quot;, sFile, &quot;PtrP&quot;, pBitmap)
      Return pBitmap
   }
   CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) {
      DllCall(&quot;gdiplus\GdipCreateHBITMAPFromBitmap&quot;, &quot;Ptr&quot;, pBitmap, &quot;PtrP&quot;, hbm, &quot;Int&quot;, Background)
      Return hbm
   }
   GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
      DllCall(&quot;gdiplus\GdipGetImageWidth&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Width)
      DllCall(&quot;gdiplus\GdipGetImageHeight&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, Height)
   }
   GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) {
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;UIntP&quot;, frameDimensions)
      VarSetCapacity(dimensionIDs, size := 16*frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameDimensionsList&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UInt&quot;, frameDimensions)
      DllCall(&quot;gdiplus\GdipImageGetFrameCount&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, &amp;dimensionIDs, &quot;UIntP&quot;, count)
      Return count
   }
   GetPropertyItem(pBitmap, tag, ByRef item) {
      DllCall(&quot;gdiplus\GdipGetPropertyItemSize&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UIntP&quot;, size)
      VarSetCapacity(item, size, 0)
      DllCall(&quot;gdiplus\GdipGetPropertyItem&quot;, &quot;Ptr&quot;, pBitmap, &quot;UInt&quot;, tag, &quot;UInt&quot;, size, &quot;Ptr&quot;, &amp;item)
      Return size
   }
   ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) {
      Return DllCall(&quot;gdiplus\GdipImageSelectActiveFrame&quot;, &quot;Ptr&quot;, pBitmap, &quot;Ptr&quot;, pDimensionIDs, &quot;Int&quot;, idx)
   }
   GraphicsFromHwnd(hwnd) {
      DllCall(&quot;Gdiplus\GdipCreateFromHWND&quot;, &quot;Ptr&quot;, hwnd, &quot;PtrP&quot;, pGraphics)
      Return pGraphics
   }
   DeleteGraphics(pGraphics) {
      return DllCall(&quot;gdiplus\GdipDeleteGraphics&quot;, &quot;Ptr&quot;, pGraphics)
   }
   DisposeImage(pBitmap) {
      Return DllCall(&quot;gdiplus\GdipDisposeImage&quot;, &quot;Ptr&quot;, pBitmap)
   }
}</code></pre></div>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2021-06-17T17:46:07Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=148493#p148493</id>
		</entry>
</feed>
