There are a couple of ways to approach the task of window skinning depending upon the OS you're targeting. If you're developing for Windows 2000/XP only, the layered window functions may be all you need. But what about Windows 9x? No layering is available there. For a skinning method that will work "everywhere" (on Windows, of course) there is the region clipping approach. How does this work? You begin by creating a region object that is the size and shape of the skin you want to use, set the window clipping region to it, and paint your image. That's basically all there is to it. Well, if it's so easy, why doesn't everyone do it? One reason is that some people don't like skins. Another is that even though the concept is simple, the implementation can be somewhat involved. Take the first step, for example:
Create a Region object the size and shape of your skinning image.
There are several Region creation functions, such as CreateRectRgn, CreateEllipticRgn, CreateRoundRectRgn, etc. These will allow you to create an irregular-shaped region, and together with CombineRgn can be used to create a region of any shape you desire. But the difficulty comes in when you need to create a region that is so complex it would take days of hard work to put it all together by hand. So - you need a way to scan an image, choose a transparent colour and create a complex region programmatically. Here is a little utility that will:
- Take a Bitmap image as input
- Let you choose a transparent colour
- Create the region data from the image
- Compress the image data
- Save both components in a custom skin file for later use
Here is an image to test with, but any 24-bit bitmap will do: http://www.networkmaestro.com/skintest.bmp
And here is the code for the skin creation utility:
Code: Select all
;==========================================================
; Project: Skinnamarink (skinning tutorial)
; Program: Custom Skin Creator
; Author: netmaestro
; Date: October 2, 2006
; Target OS: Windows All
; Target Compiler: PureBasic 4.0 and above
; License: Use as you wish, credit appreciated
; but not required
;==========================================================
Procedure ScanRegion(image)
OpenWindow(0,0,0,ImageWidth(image),ImageHeight(image),"Click on Transparent Color",#PB_Window_ScreenCentered)
CreateGadgetList(WindowID(0))
ImageGadget(0,0,0,0,0,ImageID(image))
quit = 0
Repeat
EventID = WaitWindowEvent()
If EventID = #PB_Event_Gadget
If EventGadget() = 0
If EventType() = #PB_EventType_LeftClick
StartDrawing(WindowOutput(0))
transcolor = Point(WindowMouseX(0),WindowMouseY(0))
SetWindowTitle(0, "Current Choice: RGB("+Str(Red(transcolor))+","+Str(Green(transcolor))+","+Str(Blue(transcolor))+")")
result = MessageRequester("","Current Choice = RGB("+Str(Red(transcolor))+","+Str(Green(transcolor))+","+Str(Blue(transcolor))+"), accept?",#MB_YESNOCANCEL|$C0)
Select result
Case 6,2
quit = 1
EndSelect
StopDrawing()
EndIf
EndIf
EndIf
Until quit
CloseWindow(0)
If result = 2 ; user pressed Cancel
End
EndIf
OpenWindow(0,0,0,300,70,"Creating Skin, please wait...",#PB_Window_ScreenCentered)
CreateGadgetList(WindowID(0))
StickyWindow(0, 1)
ProgressBarGadget(0, 0,20,300,20,0,ImageHeight(image),#PB_ProgressBar_Smooth)
SetGadgetColor(0,#PB_Gadget_FrontColor,#Blue)
hRgn = CreateRectRgn_(0,0,ImageWidth(image),ImageHeight(image))
StartDrawing(ImageOutput(image))
For y=0 To ImageHeight(image)
For x=0 To ImageWidth(image)
If Point(x,y) = transcolor
hTmpRgn = CreateRectRgn_(x,y,x+1,y+1)
CombineRgn_(hRgn, hRgn, hTmpRgn, #RGN_XOR)
DeleteObject_(hTmpRgn);
EndIf
Next
SetGadgetState(0,y)
Next
StopDrawing()
CloseWindow(0)
ProcedureReturn hRgn;
EndProcedure
;======================================
; Load the image to be skinned
;======================================
filename$ = OpenFileRequester("Image File Conversion Utility",GetPathPart(GetCurrentDirectory()),"Bitmap |*.bmp",1)
hSkinBmp=LoadImage(#PB_Any, filename$)
If Not IsImage(hSkinBmp)
MessageRequester("Error","Unable to load that image file!",#MB_ICONERROR)
End
EndIf
;====================================================================
; Compress and prepare the image data portion of the skin file
;====================================================================
GetObject_(ImageID(hSkinBmp), SizeOf(BITMAP), @Bmp.BITMAP)
ColorBytesSize = Bmp\bmWidthBytes * Bmp\bmHeight
ImageSize = SizeOf(BITMAPFILEHEADER) + SizeOf(BITMAPINFOHEADER) + ColorBytesSize
*rawimage = AllocateMemory(ImageSize)
*fileheader.BITMAPFILEHEADER = *rawimage
*header.BITMAPINFOHEADER = *rawimage + SizeOf(BITMAPFILEHEADER)
With *fileheader
\bfType = 19778 ; word containing 2 bytes, 'BM' for 'BIT' 'MAP' ;)
\bfSize = ImageSize
\bfOffBits = SizeOf(BITMAPFILEHEADER) + SizeOf(BITMAPINFOHEADER)
EndWith
With *header
\biSize = SizeOf(BITMAPINFOHEADER)
\biWidth = Bmp\bmWidth
\biHeight = Bmp\bmHeight
\biPlanes = 1
\biBitCount = 24
\biCompression = #BI_RGB
EndWith
CopyMemory(Bmp\bmBits, *rawimage + SizeOf(BITMAPFILEHEADER) + SizeOf(BITMAPINFOHEADER), ColorbytesSize)
*PackedImage = AllocateMemory(ImageSize+8)
PackSize = CompressMemory(*rawimage, ImageSize, *PackedImage, ImageSize)
;=======================================================
; Process the image data thru the Region Scanner
;=======================================================
hRegion = ScanRegion(hSkinBmp)
RegionSize = GetRegionData_(hRegion, 0, 0)
*RegionData = AllocateMemory(RegionSize)
GetRegionData_(hRegion,RegionSize,*RegionData)
;=====================================================================
; Save compressed Imagedata and Regiondata in a custom Skin file
;=====================================================================
SaveFile$ = InputRequester("Choose a name for the Skin File","Name: ","test.skn")
If CreateFile(0, SaveFile$)
WriteLong(0, ImageSize)
WriteLong(0, packsize + 12) ; Offset of region data in skin file
WriteLong(0, Regionsize) ; Length of region data in skin file
WriteData(0, *PackedImage, PackSize)
WriteData(0, *RegionData, RegionSize)
CloseFile(0)
Else
MessageRequester("Error","Unable to save the file!",#MB_ICONERROR)
EndIf
End
Code: Select all
Uncompressed image size: 4 bytes (1 long)
Regiondata Offset: 4 bytes (1 long)
Regiondata size: 4 bytes (1 long)
Compressed imagedata: (undetermined size)
Compressed regiondata: (undetermined size)
Code: Select all
;==========================================================
; Project: Skinnamarink (skinning tutorial)
; Program: SkinWindow.pbi
; Author: netmaestro
; Date: October 2, 2006
; Target OS: Windows All
; Target Compiler: PureBasic 4.0 and above
; License: Use as you wish, credit appreciated
; but not required
;==========================================================
Procedure SkinProc(hwnd, msg, wparam, lparam)
Shared oldskinproc, BGBrush
result = CallWindowProc_(oldskinproc, hwnd, msg, wparam, lparam)
Select msg
Case #WM_LBUTTONDOWN
SendMessage_(hwnd, #WM_NCLBUTTONDOWN, #HTCAPTION, 0)
Case #WM_DESTROY
DeleteObject_(BGBrush)
EndSelect
ProcedureReturn Result
EndProcedure
Procedure SkinWindow(hwnd, loc)
Shared oldskinproc, BGBrush
unpackedsize = PeekL(loc)
RegionOffset = PeekL(loc+4)
RegionSize = PeekL(loc + 8)
packedsize = RegionOffset - 12
*unpacked = AllocateMemory(unpackedsize)
UncompressMemory(loc + 12,packedsize, *unpacked,unpackedsize)
Skin = CatchImage(#PB_Any, *unpacked)
FreeMemory(*unpacked)
hRegion = ExtCreateRegion_(0, RegionSize, loc + RegionOffset)
SetWindowRgn_(hWnd, hRegion, #True);
BGBrush = CreatePatternBrush_(ImageID(Skin))
FreeImage(Skin)
SetClassLong_(hwnd, #GCL_HBRBACKGROUND, BGBrush)
InvalidateRect_(hwnd,0,1)
DeleteObject_(hRegion1)
oldskinproc = SetWindowLong_(hwnd, #GWL_WNDPROC, @SkinProc())
SetWindowPos_(hwnd, #HWND_TOPMOST,0,0,0,0,#SWP_NOSIZE|#SWP_NOMOVE)
EndProcedure
Code: Select all
IncludeFile "SkinWindow.pbi"
hWnd=OpenWindow(0,0,0,600,600,"",#PB_Window_ScreenCentered|#PB_Window_BorderLess)
CreateGadgetList(WindowID(0))
ButtonGadget(0,26,26,40,20,"Close")
SkinWindow(WindowID(0), ?skin)
quit = 0
Repeat
EventID = WaitWindowEvent()
Select EventID
Case #PB_Event_Gadget
quit = 1
EndSelect
Until quit
DataSection
skin: IncludeBinary "test.skn"
EndDataSection