[Windows] Skin Window with PNG -> Alpha channel support!

Share your advanced PureBasic knowledge/code with the community.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

[Windows] Skin Window with PNG -> Alpha channel support!

Post by c4s »

This code can be used to skin a window with a png image. The advantage of this solution is that it supports the alpha channel of the image. The trick is to use 2 synchronous windows - one for the background image and another one for gadgets etc.

Code: Select all

EnableExplicit

#AC_SRC_OVER = $0
#AC_SRC_ALPHA = $1
#ULW_ALPHA = $2


Macro WindowAlphaInit(WindowNr)
	SetWindowLongPtr_(WindowID(WindowNr), #GWL_EXSTYLE, GetWindowLongPtr_(WindowID(WindowNr), #GWL_EXSTYLE) | #WS_EX_LAYERED)
EndMacro

Macro WindowAlphaColor(WindowNr, Color)
	SetLayeredWindowAttributes_(WindowID(WindowNr), Color, 0, #LWA_COLORKEY)
EndMacro

Procedure WindowAlphaImage(WindowNr, ImageNr, Alpha=$FF)
	Protected ImageID, X, Y, Width, Height
	Protected hDC, bmi.BITMAPINFO, pt.POINT, Blend.BLENDFUNCTION
	Protected Color, R, G, B, A
	Protected Result = #False

	If IsImage(ImageNr) = 0 Or ImageDepth(ImageNr) <> 32 : ProcedureReturn Result : EndIf


	ImageID = ImageID(ImageNr)
	Width = ImageWidth(ImageNr)
	Height = ImageHeight(ImageNr)

	; Precalculate scale:
	Protected Dim Scale.f($FF)
	For X = 0 To $FF
		Scale(X) = X / $FF
	Next

	; Prepare array size:
	Protected Dim Image.l(Width - 1, Height - 1)


	hDC = StartDrawing(ImageOutput(ImageNr))
	If hDC
		With bmi\bmiHeader
			\biSize = SizeOf(BITMAPINFOHEADER)
			\biWidth = Width
			\biHeight = Height
			\biPlanes = 1
			\biBitCount = 32
			\biCompression = #BI_RGB
		EndWith

		GetDIBits_(hDC, ImageID, 0, Height, @Image(), @bmi, #DIB_RGB_COLORS)  ; Copy image to memory

		; Modify memory:
		For Y = 0 To Height - 1
			For X = 0 To Width - 1
				Color = Image(X, Y)
				A = Alpha(Color)
				If A < $FF
					R = Red(Color) * Scale(A)
					G = Green(Color) * Scale(A)
					B = Blue(Color) * Scale(A)
					Image(X, Y) = RGBA(R, G, B, A)
				EndIf
			Next
		Next

		SetDIBits_(hDC, ImageID, 0, Height, @Image(), @bmi, #DIB_RGB_COLORS)  ; Copy memory back to image


		; Set image to window background:
		With Blend
			\BlendOp = #AC_SRC_OVER
			\BlendFlags = 0
			\SourceConstantAlpha = Alpha
			\AlphaFormat = #AC_SRC_ALPHA
		EndWith

		Result = UpdateLayeredWindow_(WindowID(WindowNr), #Null, #Null, @bmi + SizeOf(Long), hDC, @pt, 0, @Blend, #ULW_ALPHA)

		StopDrawing()
	EndIf

	ProcedureReturn Result
EndProcedure

Procedure MainCallback(hWnd, uMsg, wParam, lParam)
	Protected Result = #PB_ProcessPureBasicEvents       

	Select uMsg               
		Case #WM_SIZE, #WM_MOVE, #WM_PAINT         
			ResizeWindow(0, WindowX(1), WindowY(1), #PB_Ignore, #PB_Ignore)  ; Synchronize both windows
	EndSelect   

	ProcedureReturn Result
EndProcedure


UsePNGImageDecoder()
Define File.s = OpenFileRequester("Load the window background image", "", "PNG|*.png", 0)
If File And LoadImage(123, File)

	If OpenWindow(0, 0, 0, ImageWidth(123), ImageHeight(123), "Alpha Window Test", #PB_Window_BorderLess | #PB_Window_Invisible | #PB_Window_ScreenCentered)  ; Just has the image
		WindowAlphaInit(0)
		WindowAlphaImage(0, 123)

		If OpenWindow(1, 0, 0, ImageWidth(123), ImageHeight(123), "", #PB_Window_BorderLess | #PB_Window_Invisible | #PB_Window_ScreenCentered, WindowID(0))  ; Just has all gadgets etc.
			SetWindowColor(1, $FF00FF)
			WindowAlphaInit(1)
			WindowAlphaColor(1, $FF00FF)

			ButtonGadget(3, (ImageWidth(123) - 150) / 2, (ImageHeight(123) - 30) / 2, 150, 30, "Close window")  ; Button centered to the image

			SetWindowCallback(@MainCallback(), 1)

			HideWindow(0, #False)
			HideWindow(1, #False)


			Repeat
				Select WaitWindowEvent()
					Case #PB_Event_Gadget
						If EventGadget() = 3
							Break
						EndIf
					Case #WM_LBUTTONDOWN
						SendMessage_(WindowID(1), #WM_NCLBUTTONDOWN, #HTCAPTION, 0)
				EndSelect
			ForEver
		EndIf
	EndIf
EndIf
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Re: [Windows] Skin Window with PNG -> Alpha channel support!

Post by ricardo »

Very good one!!

Is there any way to avoid that little "jump" when opening the window with the button? There is some flicker or something that shows, not much, but could be nice to find a way to avoid it.

Best Regards
ARGENTINA WORLD CHAMPION
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Re: [Windows] Skin Window with PNG -> Alpha channel support!

Post by ricardo »

I am here right now: Ricardo's Aero


Image

Not using external images.
I paint my own png with alpha transparency, draw some gradients to give a more modern and glass like effect to the transparent border and put a windows as child of the alphablended one.

Any correction and suggestion are welcome! :)
I want to get this code to the next level, so we can create modern GUIs without the need of external images, etc.

Code: Select all


UsePNGImageDecoder()
Global hWidth, hHeight, hBorder
hHeight = 600
hWidth = 800
hBorder  = 30

Structure myBITMAPINFO 
	bmiHeader.BITMAPINFOHEADER 
	bmiColors.RGBQUAD[1] 
EndStructure 




Procedure MainCallback(hWnd, uMsg, wParam, lParam)
	Shared hBorder
	Protected Result = #PB_ProcessPureBasicEvents       
	Select uMsg               
		Case #WM_SIZE, #WM_MOVE, #WM_PAINT         
			ResizeWindow(0, WindowX(1)-hBorder+2, WindowY(1)-hBorder+2, #PB_Ignore, #PB_Ignore)  ; Synchronize both windows
		Default
	EndSelect   
	ProcedureReturn Result
EndProcedure

Procedure PegarVentana(hWidth,hHeight, hBorder)
	OpenWindow(1,0,0,hWidth+8,hHeight+4,"Ok",#PB_Window_WindowCentered|#PB_Window_BorderLess,WindowID(0))
	SetWindowCallback(@MainCallback(), 1)
EndProcedure




If OpenWindow(0,0,0,hWidth,hHeight+50,"Test",#PB_Window_BorderLess|#PB_Window_Invisible|#PB_Window_ScreenCentered)
	ButtonGadget(222,100,100,100,100,"abc")
	SetWindowLong_(WindowID(0), #GWL_EXSTYLE, GetWindowLong_(WindowID(0), #GWL_EXSTYLE) | #WS_EX_LAYERED ) 
    If CreateImage(0, hWidth, hHeight,32) 
		StartDrawing(ImageOutput(0))
			
			Opacity = 50
			DrawingMode(#PB_2DDrawing_AlphaChannel)      
			Box(0,0,hWidth,hBorder,RGBA(125,0,125,Opacity))
			Box(0,0,hBorder,hHeight,RGBA(125,0,125,Opacity))
			Box(hWidth-hBorder,0,hBorder,hHeight,RGBA(125,0,125,Opacity))
			Box(0,hHeight-hBorder,hWidth,hBorder,RGBA(125,0,125,Opacity))
			
			DrawingMode(#PB_2DDrawing_Default )      
			Box(0, 0, hWidth, hHeight, RGB(0,0,55))

			DrawingMode(#PB_2DDrawing_Gradient)     ;To give the transparent window a modern style 
			BackColor($333333)
			GradientColor(0.001, $666666)
			GradientColor(0.003, $191919)
			GradientColor(0.145, $292929)

			
			FrontColor($000000)
			
			LinearGradient(0, 0,0, hHeight)    
			Box(0,0,hWidth,hHeight) 
			
			
		StopDrawing() 
		
		
		hdc = StartDrawing(ImageOutput(I0)) 
			ContextOffset.point 
			BlendMode.BLENDFUNCTION 
			BitmapInfo.BITMAP 
			GetObject_(ImageID(0), SizeOf(BITMAP), @BitmapInfo) 
			BlendMode\SourceConstantAlpha = 255 
			BlendMode\AlphaFormat = 1
			UpdateLayeredWindow_(WindowID(0), 0, 0, @BitmapInfo+4, hdc, @ContextOffset, 0, @BlendMode, 2) 
	EndIf
	
	PegarVentana(hWidth - (hBorder*2),hHeight-(hBorder*2), hBorder)
	HideWindow(0,0)
	
	Repeat
		eventID=WaitWindowEvent()
		
		Select eventID
			Case #WM_LBUTTONDOWN 
				SendMessage_(WindowID(1), #WM_NCLBUTTONDOWN, #HTCAPTION, 0) 
			Case #PB_Event_Gadget
				Select EventGadget()
					Case 2
				EndSelect
				
		EndSelect
		
	Until eventID=#PB_Event_CloseWindow  Or GetAsyncKeyState_(#VK_ESCAPE) 
EndIf

ARGENTINA WORLD CHAMPION
Nituvious
Addict
Addict
Posts: 1027
Joined: Sat Jul 11, 2009 4:57 am
Location: United States

Re: [Windows] Skin Window with PNG -> Alpha channel support!

Post by Nituvious »

ricardo wrote:I am here right now: Ricardo's Aero


Image

Not using external images.
I paint my own png with alpha transparency, draw some gradients to give a more modern and glass like effect to the transparent border and put a windows as child of the alphablended one.

Any correction and suggestion are welcome! :)
I want to get this code to the next level, so we can create modern GUIs without the need of external images, etc.

Code: Select all


UsePNGImageDecoder()
Global hWidth, hHeight, hBorder
hHeight = 600
hWidth = 800
hBorder  = 30

Structure myBITMAPINFO 
	bmiHeader.BITMAPINFOHEADER 
	bmiColors.RGBQUAD[1] 
EndStructure 




Procedure MainCallback(hWnd, uMsg, wParam, lParam)
	Shared hBorder
	Protected Result = #PB_ProcessPureBasicEvents       
	Select uMsg               
		Case #WM_SIZE, #WM_MOVE, #WM_PAINT         
			ResizeWindow(0, WindowX(1)-hBorder+2, WindowY(1)-hBorder+2, #PB_Ignore, #PB_Ignore)  ; Synchronize both windows
		Default
	EndSelect   
	ProcedureReturn Result
EndProcedure

Procedure PegarVentana(hWidth,hHeight, hBorder)
	OpenWindow(1,0,0,hWidth+8,hHeight+4,"Ok",#PB_Window_WindowCentered|#PB_Window_BorderLess,WindowID(0))
	SetWindowCallback(@MainCallback(), 1)
EndProcedure




If OpenWindow(0,0,0,hWidth,hHeight+50,"Test",#PB_Window_BorderLess|#PB_Window_Invisible|#PB_Window_ScreenCentered)
	ButtonGadget(222,100,100,100,100,"abc")
	SetWindowLong_(WindowID(0), #GWL_EXSTYLE, GetWindowLong_(WindowID(0), #GWL_EXSTYLE) | #WS_EX_LAYERED ) 
    If CreateImage(0, hWidth, hHeight,32) 
		StartDrawing(ImageOutput(0))
			
			Opacity = 50
			DrawingMode(#PB_2DDrawing_AlphaChannel)      
			Box(0,0,hWidth,hBorder,RGBA(125,0,125,Opacity))
			Box(0,0,hBorder,hHeight,RGBA(125,0,125,Opacity))
			Box(hWidth-hBorder,0,hBorder,hHeight,RGBA(125,0,125,Opacity))
			Box(0,hHeight-hBorder,hWidth,hBorder,RGBA(125,0,125,Opacity))
			
			DrawingMode(#PB_2DDrawing_Default )      
			Box(0, 0, hWidth, hHeight, RGB(0,0,55))

			DrawingMode(#PB_2DDrawing_Gradient)     ;To give the transparent window a modern style 
			BackColor($333333)
			GradientColor(0.001, $666666)
			GradientColor(0.003, $191919)
			GradientColor(0.145, $292929)

			
			FrontColor($000000)
			
			LinearGradient(0, 0,0, hHeight)    
			Box(0,0,hWidth,hHeight) 
			
			
		StopDrawing() 
		
		
		hdc = StartDrawing(ImageOutput(I0)) 
			ContextOffset.point 
			BlendMode.BLENDFUNCTION 
			BitmapInfo.BITMAP 
			GetObject_(ImageID(0), SizeOf(BITMAP), @BitmapInfo) 
			BlendMode\SourceConstantAlpha = 255 
			BlendMode\AlphaFormat = 1
			UpdateLayeredWindow_(WindowID(0), 0, 0, @BitmapInfo+4, hdc, @ContextOffset, 0, @BlendMode, 2) 
	EndIf
	
	PegarVentana(hWidth - (hBorder*2),hHeight-(hBorder*2), hBorder)
	HideWindow(0,0)
	
	Repeat
		eventID=WaitWindowEvent()
		
		Select eventID
			Case #WM_LBUTTONDOWN 
				SendMessage_(WindowID(1), #WM_NCLBUTTONDOWN, #HTCAPTION, 0) 
			Case #PB_Event_Gadget
				Select EventGadget()
					Case 2
				EndSelect
				
		EndSelect
		
	Until eventID=#PB_Event_CloseWindow  Or GetAsyncKeyState_(#VK_ESCAPE) 
EndIf

That's pretty cool and innovative!
If you're trying to get a true Windows Aero look you will need to use the DWM library, though.

Here are some articles that will help you:
http://msdn.microsoft.com/en-us/library ... S.85).aspx
and
http://msdn.microsoft.com/en-us/magazine/cc163435.aspx
▓▓▓▓▓▒▒▒▒▒░░░░░
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Re: [Windows] Skin Window with PNG -> Alpha channel support!

Post by ricardo »

Nituvious wrote: That's pretty cool and innovative!
If you're trying to get a true Windows Aero look you will need to use the DWM library, though.

Here are some articles that will help you:
http://msdn.microsoft.com/en-us/library ... S.85).aspx
and
http://msdn.microsoft.com/en-us/magazine/cc163435.aspx
Thanks.
Im not really trying to go to real Aero, because im just playinh with the idea of skinning my own apps.
Im now playing with adding some background and controls, just to see how it shows. Also this do not need Aero :)

Im palying with gradients to see how to draw something in background.

Image

As i dont want to distract this thread with my own code, because its for the code that starts the thread, i just show some image.
ARGENTINA WORLD CHAMPION
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: [Windows] Skin Window with PNG -> Alpha channel support!

Post by netmaestro »

BERESHEIT
Post Reply