Waiting Screen with transparent background

Just starting out? Need help? Post your questions and find answers here.
Patrice Terrier
User
User
Posts: 21
Joined: Sun Aug 10, 2003 11:45 am
Location: France
Contact:

Re: Waiting Screen with transparent background

Post by Patrice Terrier »

I have several splash screen animations based on GDImage, working in compositing mode.
unfortunatly none of them written in PureBasic
http://www.objreader.com/index.php?topi ... 37#msg6337
https://depot.webdev.info/resource.awp? ... creen-plus

I am using multi-frame PNG animations, and the key as you did is to use the UpdateLayeredWindow API.
Patrice Terrier
objreader@gmail.com
http://www.objreader.com
Addon: WinLIFT (Skin Engine), GDImage (Graphic library), ObjReader (3D engine)
Mesa
Enthusiast
Enthusiast
Posts: 433
Joined: Fri Feb 24, 2012 10:19 am

Re: Waiting Screen with transparent background

Post by Mesa »

I think there's a bug somewhere in the vectordrawing library:

Code: Select all

Procedure.l SetLayeredWindow(WindowID) ; Mettre l'attribut WS_EX_LAYERED à la fenêtre
	SetWindowLong_(WindowID, #GWL_EXSTYLE, GetWindowLong_(WindowID, #GWL_EXSTYLE) | #WS_EX_LAYERED) ; Mettre l'attribut WS_EX_LAYERED à la fenêtre
EndProcedure

Procedure.l AlphaImageWindow(WindowID, ImageID) ; Mettre une image PNG comme fond d'une fenêtre
	Protected Image_HDC.i, Image_Bitmap.BITMAP, Image_BitmapInfo.BITMAPINFO, ContextOffset.POINT, Blend.BLENDFUNCTION
	Protected Image_Ancienne.i ,xx, yy, x, y, Rouge.l, Vert.l, Bleu.l, AlphaChannel.l
	
	; Précalcul
	Protected Dim Echelle.f($FF)
	For x = 0 To $FF
		Echelle(x) = x / $FF
	Next
	
	; Chargement du HDC
	Image_HDC = CreateCompatibleDC_(#Null)
	Image_Ancienne = SelectObject_(Image_HDC, ImageID)
	
	
	; Dimension de l'image
	GetObject_(ImageID, SizeOf(BITMAP), @Image_Bitmap)
	Image_BitmapInfo\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER)
	Image_BitmapInfo\bmiHeader\biWidth = Image_Bitmap\bmWidth
	Image_BitmapInfo\bmiHeader\biHeight = Image_Bitmap\bmHeight
	Image_BitmapInfo\bmiHeader\biPlanes = 1
	Image_BitmapInfo\bmiHeader\biBitCount = 32
	
	; Zone mémoire pour copier l'image
	xx = Image_Bitmap\bmWidth - 1
	yy = Image_Bitmap\bmHeight - 1
	Protected Dim Image.l(xx, yy)
	
	; Copie de l'image en mémoire
	GetDIBits_(Image_HDC, ImageID, 0, Image_Bitmap\bmHeight, @Image(), @Image_BitmapInfo, #DIB_RGB_COLORS)
	
	; Modification de l'image en mémoire
	For x = 0 To xx
		For y = 0 To yy
			Couleur = Image(x, y)
			AlphaChannel = Couleur >> 24 & $FF
			If AlphaChannel < $FF
				Rouge = (Couleur & $FF) * Echelle(AlphaChannel)
				Vert = (Couleur >> 8 & $FF) * Echelle(AlphaChannel)
				Bleu = (Couleur >> 16 & $FF) * Echelle(AlphaChannel)
				Image(x, y) = Rouge | Vert << 8 | Bleu << 16 | AlphaChannel << 24
			EndIf
		Next
	Next
	
	; Transfert de la mémoire dans la l'image de base
	SetDIBits_(Image_HDC, ImageID, 0, Image_Bitmap\bmHeight, @Image(), @Image_BitmapInfo, #DIB_RGB_COLORS)
	
	; L'image est mise en skin de la fenêtre
	Blend\SourceConstantAlpha = 255 ; niveau de transparence
	Blend\AlphaFormat = 1						; Support de la couche alpha
	Blend\BlendOp = 0
	Blend\BlendFlags = 0
	UpdateLayeredWindow_(WindowID, 0, 0, @Image_BitmapInfo + 4, Image_HDC, @ContextOffset, 0, @Blend, 2)
	
	; Fermeture du HDC
	SelectObject_(Image_HDC, Image_Ancienne)
	DeleteDC_(Image_HDC)
	
EndProcedure

Procedure Rotate(*oldPos.Point, *RotationCenter.Point, angle.f, *newPos.Point)
	Protected xM, yM
	angle = angle * #PI / 180
	xM = *oldPos\x - *RotationCenter\x
	yM = *oldPos\y - *RotationCenter\y
	*newPos\x = Round(xM * Cos(angle) + yM * Sin(angle) + *RotationCenter\x, #PB_Round_Nearest)
	*newPos\y = Round(- xM * Sin(angle) + yM * Cos(angle) + *RotationCenter\y, #PB_Round_Nearest)
EndProcedure


If OpenWindow(0, 0, 0, 200, 200, "", #PB_Window_BorderLess|#PB_Window_ScreenCentered|#PB_Window_Invisible)
	; 	SetWindowColor(0,$FFFFFF)
	SetLayeredWindow(WindowID(0))
	
	
	Define RotationCenter.Point : RotationCenter\x = 100 : RotationCenter\y = 100
	Define startingPos.Point : startingPos\x = 180 : startingPos\y = 100
	Define angle.f = 0
	Define newpos_red.Point, newpos_green.Point
	
	CreateImage(0, 200, 200, 32, #PB_Image_Transparent)
	
	AddWindowTimer(0, 123, 200)
	
	StartDrawing(ImageOutput(0))
	DrawingMode(#PB_2DDrawing_AllChannels)
	Box(0, 0, OutputWidth(), OutputHeight(), $00FFFFFF)
	StopDrawing()
	
	
	StartVectorDrawing(ImageVectorOutput(0))
	For i = 1 To 8
		Rotate(startingPos, RotationCenter, angle, newpos_red)
		AddPathCircle(newpos_red\x, newpos_red\y, 10, 0, 360) : VectorSourceColor(RGBA(255, 255, 0, 255)) : FillPath()
		If angle = 315 : angle = 0 : Else : angle = angle + 45 : EndIf
	Next
	StopVectorDrawing()
	
	AlphaImageWindow(WindowID(0), ImageID(0))
	StickyWindow(0, 1)
	
	HideWindow(0,0)
	
	Repeat
		event = WaitWindowEvent()
		
		If event = #PB_Event_Timer And EventTimer() = 123
			
			; 			AlphaImageWindow(WindowID(0), ImageID(0)); <=======There is a bug somewhere
		EndIf
		
	Until event = #PB_Event_CloseWindow
	
EndIf
M.
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Waiting Screen with transparent background

Post by tatanas »

Different simple test without the transparency :

Code: Select all

Enumeration
	#Horaire
	#AntiHoraire
EndEnumeration

If OpenWindow(0, 0, 0, 200, 200, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

	CanvasGadget(0, 0, 0, 200, 200)
	AddWindowTimer(0, 123, 5)

	Define rotation = #Horaire
	Define start_angle = 0
	Define end_angle = 45
	Define thickness = 20
	Define event
	
	Repeat
		event = WaitWindowEvent()

		If event = #PB_Event_Gadget And EventGadget() = 0 And EventType() = #PB_EventType_LeftDoubleClick
			If rotation = #Horaire : rotation = #AntiHoraire : Else : rotation = #Horaire : EndIf
		EndIf

		If event = #PB_Event_Timer And EventTimer() = 123

			StartVectorDrawing(CanvasVectorOutput(0))
			; cleaning
			AddPathBox(0, 0, VectorOutputWidth(), VectorOutputHeight())
			VectorSourceColor(RGBA(255, 255, 255, 255)) : FillPath()

			; background circle
			AddPathCircle(100, 100, 80, 0, 360)
			VectorSourceColor(RGBA(220, 234, 244, 255))
			StrokePath(thickness)
		
			; updated rotating arc
			AddPathCircle(100, 100, 80, start_angle, end_angle)
			VectorSourceColor(RGBA(148, 178, 228, 255))
			StrokePath(thickness)
			StopVectorDrawing()

			If rotation = #Horaire
				start_angle = start_angle + 2
				end_angle = end_angle + 2
				If start_angle = 360 : start_angle = 0 : end_angle = 45 : EndIf
			Else
				start_angle = start_angle - 2
				end_angle = end_angle - 2
				If start_angle = -360 : start_angle = 0 : end_angle = 45 : EndIf
			EndIf
		EndIf

	Until event = #PB_Event_CloseWindow
EndIf
Is this the right way to do it ? The quality and fluidity is not as good as I thought.
Windows 10 Pro x64
PureBasic 6.20 x64
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Waiting Screen with transparent background

Post by tatanas »

Smoother version (without the WindowTimer) :

Code: Select all

Enumeration
	#Horaire
	#AntiHoraire
EndEnumeration

If OpenWindow(0, 0, 0, 200, 200, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

	CanvasGadget(0, 0, 0, 200, 200)
; 	CreateImage(0, 200, 200)
; 	ImageGadget(1, 0, 0, 200, 200, 0)

	Define rotation = #Horaire
	Define start_angle = 0
	Define end_angle = 45
	Define thickness = 20
	Define event
	Define timer = ElapsedMilliseconds()

	Repeat
		event = WindowEvent()

		If event = #PB_Event_Gadget And EventGadget() = 0 And EventType() = #PB_EventType_LeftDoubleClick
			If rotation = #Horaire : rotation = #AntiHoraire : Else : rotation = #Horaire : EndIf
		EndIf

		If ElapsedMilliseconds() - timer >= 8
			timer = ElapsedMilliseconds()

			StartVectorDrawing(CanvasVectorOutput(0))
			; cleaning
			AddPathBox(0, 0, VectorOutputWidth(), VectorOutputHeight())
			VectorSourceColor(RGBA(255, 255, 255, 255)) : FillPath()

			; background (dark blue)
			AddPathCircle(100, 100, 70, 0, 360)
			VectorSourceColor(RGBA(184, 204, 237, 255))
			FillPath()

			; magnifying glass
			AddPathCircle(100, 100, 16, 0, 360)
			VectorSourceColor(RGBA(255, 255, 255, 255))
			StrokePath(6)
			MovePathCursor(112, 112)
			AddPathLine(128, 128)
			StrokePath(6, #PB_Path_RoundEnd)

			; background circle (light blue)
			AddPathCircle(100, 100, 80, 0, 360)
			VectorSourceColor(RGBA(220, 234, 244, 255))
			StrokePath(thickness)
		
			; updated rotating arc
			AddPathCircle(100, 100, 80, start_angle, end_angle)
			VectorSourceColor(RGBA(148, 178, 228, 255))
			StrokePath(thickness)
			StopVectorDrawing()

; 			SetGadgetState(1, ImageID(0))

			If rotation = #Horaire
				start_angle = start_angle + 2
				end_angle = end_angle + 2
				If start_angle = 360 : start_angle = 0 : end_angle = 45 : EndIf
			Else
				start_angle = start_angle - 2
				end_angle = end_angle - 2
				If start_angle = -360 : start_angle = 0 : end_angle = 45 : EndIf
			EndIf
		EndIf

		Delay(1)

	Until event = #PB_Event_CloseWindow
EndIf
Should I use a canvas or an Image ?
Windows 10 Pro x64
PureBasic 6.20 x64
Mesa
Enthusiast
Enthusiast
Posts: 433
Joined: Fri Feb 24, 2012 10:19 am

Re: Waiting Screen with transparent background

Post by Mesa »

Yes you're right, you can use also WaitWindowEvent(1) without timer it's the 3rd soluce.

The quality is vector>2d>sprite
The speed is sprite>>>2d>vector

You could try the gif solution, pre compute all frame and just display them.

Code: Select all

;
; ------------------------------------------------------------
;
;   PureBasic - ImagePlugin GIF Viewer example file
;
;    (c) Fantaisie Software
;
; ------------------------------------------------------------
;

; Enable the GIF decoder
UseGIFImageDecoder()

; Loading a GIF file
If LoadImage(0, #PB_Compiler_Home+"Examples/Sources/Data/PureBasicLogo.gif")
	
	OpenWindow(0, 100, 100, DesktopUnscaledX(ImageWidth(0)), DesktopUnscaledY(ImageHeight(0)), "GIF viewer")
	
	CanvasGadget(0, 0, 0, ImageWidth(0), ImageHeight(0))
	
	; Add a timer to animate the GIF, starts immediately to display the first frame witout delay
	;   AddWindowTimer(0, 0, 1)
	
	
	;   Pre compute all frames here
	
	Repeat
		Event = WaitWindowEvent(1)
		;     Event = WindowEvent()
		;     If Event = #PB_Event_Timer
		SetImageFrame(0, Frame)
		
		; Each GIF frame can have its own delay, so change the timer accordingly
		;
		;RemoveWindowTimer(0, 0)
		;AddWindowTimer(0, 0, GetImageFrameDelay(0))
		
		If StartDrawing(CanvasOutput(0))
			DrawImage(ImageID(0), 0, 0)
			StopDrawing()
		EndIf
		
		; Go to next frame
		Frame+1
		If Frame >= ImageFrameCount(0) ; Cycle back to first frame, to play in loop
			Frame = 0
		EndIf
		;     EndIf
		;    Delay(1)
	Until Event = #PB_Event_CloseWindow
Else
	Debug "Impossible to load the file: " + Filename$
EndIf

imagegadget is better for the speed and the transparency, it can be transparent itself.
Canvas is better fot the using of the mouse and is not transparent itself.

M.
Axolotl
Addict
Addict
Posts: 802
Joined: Wed Dec 31, 2008 3:36 pm

Re: Waiting Screen with transparent background

Post by Axolotl »

Nice.

You should keep in mind that (at least on windows) the minimum time-out value for timers is 10 ms.
The system has the matter under control, but it's still good to keep that in mind.
MSDN:
If uElapse is less than USER_TIMER_MINIMUM (0x0000000A), the timeout is set to USER_TIMER_MINIMUM. If uElapse is greater than USER_TIMER_MAXIMUM (0x7FFFFFFF), the timeout is set to USER_TIMER_MAXIMUM.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
Post Reply