Seite 1 von 1

GIF mäßige Animationen in GUIs einbauen

Verfasst: 20.10.2006 10:32
von AND51
Hallo!

In meinem Projekt soll das Logo des Programmes oben rechts in der Ecke in einem ImageGadget() angezeigt werden. Erst, wenn das Programm stark arbeitet und z. B. die Festplatte rekursiv durchgeht, soll dieses Standbild anfangen, sich zu verändern. Die Lösung, die ich erarbeitet habe, lässt die Animation genauso aussehen, als sei es ein echtes GIF Bild, obwohl nur hintereinander Einzelbilder abgespielt werden.
Nachteil: Keine Tranzparenz wie bei GIF (es sei denn, man nimmt ICOs)
Vorteile: Jedes Bildformat verwendbar (BMP, ICO, PNG, JPG, TIFF), das bedeutet: Nicht 256 Farben (GIF), sondern 24 Bit Farben!. Außerdem lässt sich, wenn man meinen Code nur geringfügig umbaut, die Wartezeiten bis zum nächsten Bild für jeden Frame der Animation individuell bestimmen. Die Animation ist jederzeit start- und stoppbar.

Was man braucht: 1 ImageGadget, 1 Thread, 1 Mutex Object und die einzelnen Bilder (Frames)

Zur Vorgehensweise: Ich habe alle Frames in einem PAK-Archiv (PB-PackerBefehle) in der Reihenfolge gespeichert, in der sie angezeigt werden.

Code: Alles auswählen

; Das Mutex muss im Hauptprogramm und im Thread genutzt werden können, daher Global
Global logo=CreateMutex()


; Der Thread
Procedure animiertes_logo(temp)
	; In dieser protected LinkedList werden die einzelnen Frames entpackt
	; und deren ImageID()'s alle gespeichert.
	; In meinem Projekt habe ich 6 Frames, also 6 Bilder im PAK-Archiv
	Protected NewList logo_bilder.l()
	
	AddElement(logo_bilder()) : logo_bilder()=ImageID(CatchImage(#PB_Any, NextPackFile()))
	AddElement(logo_bilder()) : logo_bilder()=ImageID(CatchImage(#PB_Any, NextPackFile()))
	AddElement(logo_bilder()) : logo_bilder()=ImageID(CatchImage(#PB_Any, NextPackFile()))
	AddElement(logo_bilder()) : logo_bilder()=ImageID(CatchImage(#PB_Any, NextPackFile()))
	AddElement(logo_bilder()) : logo_bilder()=ImageID(CatchImage(#PB_Any, NextPackFile()))
	AddElement(logo_bilder()) : logo_bilder()=ImageID(CatchImage(#PB_Any, NextPackFile()))
	

	; Diese Schleife muss ForEver ausgeführt werden
	Repeat
		; Zeige den 1. FRame an, wenn Animation nicht abgespielt werden soll
		FirstElement(logo_bilder())
		SetGadgetState(#logo, logo_bilder())
		
		
		; Die Animation wird fortwährend abgespielt, wenn der Thread sich das
		; Mutex ergattern kann
		LockMutex(logo)
			; Spiele einmal alle Frames durch (bei mir sind's 6 Einzelbilder (Frames))
			ResetList(logo_bilder())
			While NextElement(logo_bilder())
				SetGadgetState(#logo, logo_bilder())
				; Wenn man den Code an dieser Stelle erweitert, kann man
				; die Anzeigedauer für jedes Bild selbst bestimmen. Ich brauche
				; für meine 6 Frames abder nur 125 ms für jeden Frame
				Delay(125)
			Wend
		; Mutex wird entsperrt, damit der Hauptthread bei Bedarf sich das Mutex ergattern
		; und somit die Animation stoppen kann.
		UnlockMutex(logo)
	ForEver
EndProcedure


; Öffnen des PAK-Archivs und starten des Threads
If OpenPack("logo.pak")
	; Mutex locken, damit die Animation nicht sofort beginnt.
	LockMutex(logo)
	
	; Wenn Thread gestartet werden, kann, setze seine Priorität etwas niedriger
	; damit er nicht allzuviel die CPU auslastet (kann weggelassen werden)
	Define logo_thread=CreateThread(@animiertes_logo(), 0)
	If logo_thread
		ThreadPriority(logo_thread, 15)
	Else
		; Wenn Thread nicht gestartet werden kann, dann zeige im ImageGadget
		; das erste Bild an
		SetGadgetState(#logo, ImageID(CatchImage(#PB_Any, NextPackFile())))
	EndIf
Else
	MessageRequester("Datei nicht gefunden", "Die Datei 'logo.pak' konnte nicht gefunden werden."+#CRLF$+"Programm wird beendet.", #MB_ICONERROR)
	End
EndIf
Ich hoffe, der Code ist so klar geworden. Wenn die Animation abgespielt werden soll, muss der Hauptthread den Mutex entsperren (freigeben). Somit kriegt der Thread den Mutex, weil er ja ständig darauf wartet. Wenn der Mutex freigegeben wird, lasse ich mein Programm arbeiten (erinnert euch: Ich wollte, dass bei mir die Animation nur dann startet, wenn mein Programm irgendeine Aufgabe ausführen soll). Wenn mein Programm dann fertig ist it arbeiten, locked er wieder den Mutex. Das geht, weil der Thread nach jedem Durchlauf den Mutex wieder entsperrt und zu Beginn der Animation wieder zu sperren versucht.

Der Vorteil: Man kann so gezielt steuern, ob die Animation laufen soll oder nicht. Wenn die Animation stoppen soll, wird dank der ersten zwei Zeilen nach dem Repeat ja immer wieder das erste Bild angezeigt, somit erhält man keine komischen Ergebnisse.


Ich hoffe, der Code bzw. die Idee, die dahinter steckt, hat euch gefallen. :D

Verfasst: 20.10.2006 18:43
von Kekskiller
Also wenn ich das jetzt so richtig verstanden habe, kann ich zu jedem Zeitpunkt die Ani stoppen, auch wenn er in der Delay-Schleife drinne ist...? Hm. Stimmt, auf den Mutes wird ja gewartet, bis er wieder frei ist. Nachteilig finde ich jetzt aber, dass man anscheinend nicht mitten in der Animation anhalten kann. Sollte aber auch nicht dass große Problem sein, schließlich ist so ein Logo ja nicht als 100-Bilder-Ani gedacht.

Nett :allright:

Verfasst: 21.10.2006 15:27
von AND51
Danke!

Dein Problem hatte ich auch vor Augen, ist aber leichter zu lösen, als du denkst!

In dem Beispiel oben kann man die Animation nur zu einem bestimmen Zeitpunkt stoppen: Nämlich, wenn die Animation zu ende ist und wiederholt werden soll.

Statt nun das Mutex bis zum Ende der Animation zu sperren, sollte man LockMutex() nur verwenden, um auf das Mutex zu warten, das Mutex dann sofort wieder entsperren (UnlockMutex()) und dann in der While-Schleife immer TryLockMutex() verwenden.
Verstehste?

Vor jedem neuen Frame einmal den Mutex versuchen sperren, mit TryLockMutex(), wenn es geht, dann soll er weiter die Animation abspielen, wenn nicht, mit Break die While-Schleife umgehend verlassen und damit die Animation sofort zu stoppen.