COLOURlovers screensaver
My first take on GDI+:
It downloads and displays the top-rated palettes from colourlovers.com. After each drawn palette, it increments the palette offset and repeats.
- Press
F1 to switch off the circles
- Press
F2 to switch off the text
- Press
F3 to show some debug tooltips
- Press
CTRL+S to save the current image to the Desktop (CL.png) and exit
- Press
SPACE to set the current image as wallpaper and exit
The Code:
Code:
; ++++++++++++++++++++++++++++++++++++++++ Setup
#Persistent
#SingleInstance, Force
#NoEnv
SetBatchLines, -1
SetWorkingDir, %A_ScriptDir%
; ++++++++++++++++++++++++++++++++++++++++
OnExit, Exit
Delimiter := ","
Offset = 0
Circles := Text := 1
Tips = 0
WinWidth := A_ScreenWidth
WinHeight := A_ScreenHeight
; ++++++++++++++++++++++++++++++++++++++++ Start
If Tips
ToolTip, Start!
If pToken := Gdip_Startup()
{
GDIP_RegisterClass(),
hwnd1 := GDIP_CreateWindow()
}
Else
ExitApp
; Prepare the Canvas
hBitmap := CreateDIBSection(WinWidth, WinHeight)
hDC := CreateCompatibleDC()
obm := SelectObject(hDC, hBitmap)
G := Gdip_GraphicsFromhDC(hDC)
If Tips
ToolTip, Setting timer!
GoSub, Draw
SetTimer, Draw, 4000
Return
; ++++++++++++++++++++++++++++++++++++++++ Draw
Draw:
If Tips
ToolTip, Draw!
If Tips
ToolTip, Getting palette!
Palette := GetCL_Palette("TOP", Offset)
Alpha := Dec2Hex("255")
StringSplit, Colour_, Palette, %Delimiter%, %Delimiter%
RectWidth := Ceil(WinWidth / (Colour_0 - 1))
Gdip_SetSmoothingMode(G, 0)
Loop % Colour_0-1 ;%
{
i := A_Index + 1
pBrush := Gdip_BrushCreateSolid(Alpha Colour_%i%)
Gdip_FillRectangle(G, pBrush, xPos, 0, RectWidth, WinHeight)
xPos += RectWidth
}
If Tips
ToolTip, Drawn BG!
Gdip_DeleteBrush(pBrush)
xPos = 0
Offset++
If Circles
{
Gdip_SetSmoothingMode(G, 2)
Loop 500
{
Random, RandColor, 2, %Colour_0%
Random, RandSize, 2, % WinHeight / 8 ;%
Random, RandXPos, 0, %WinWidth%
Random, RandYPos, 0, %WinHeight%
Random, RandAlpha, 255, 255
pBrush := Gdip_BrushCreateSolid(Dec2Hex(RandAlpha) Colour_%RandColor%)
Gdip_FillEllipse(G, pBrush, RandXPos, RandYPos, RandSize, RandSize)
}
If Tips
ToolTip, Drawn circles!
}
Options := "x20 y" (WinHeight - 55) "c" SubStr(Dec2Hex("200"), 3) "ffffff" " r4 s30"
If Text
{
Gdip_SetSmoothingMode(G, 4)
pBrush2 := Gdip_BrushCreateSolid(0xffff0000)
pBrush3 := Gdip_BrushCreateSolid(Dec2Hex("200") "000000")
RectF := Gdip_TextToGraphics(G, Colour_1 "`n" Chr(160), Options, "Arial", WinWidth, WinHeight, 1)
If Tips
ToolTip, %RectF%
StringSplit, RectF_, RectF, |, |
Gdip_FillRoundedRectangle(G, pBrush3, RectF_1, RectF_2, RectF_3, RectF_4/2, 4)
Gdip_TextToGraphics(G, Colour_1 "`n" Chr(160), Options, "Arial", WinWidth, WinHeight)
}
UpdateLayeredWindow(hwnd1, hDC, 0, 0, WinWidth, WinHeight)
Return
; ++++++++++++++++++++++++++++++++++++++++ Functions
DrawCL_Palette(Palette, Delimiter = ",", Alpha = "255")
{
Alpha := Dec2Hex(Alpha)
StringSplit, Colour_, Palette, %Delimiter%, %Delimiter%
RectWidth := Ceil(WinWidth / Colour_0)
Loop %Colour_0%
{
pBrush := Gdip_BrushCreateSolid(Alpha Colour_%A_Index%)
Gdip_FillRectangle(G, pBrush, xPos, 0, RectWidth, WinHeight)
xPos += RectWidth
}
Gdip_DeleteBrush(pBrush)
}
; ++++++++++++++++++++++++++++++++++++++++
GetCL_Palette(Type = "TOP", Offset = "0")
{
global Delimiter
If (Type = "Top")
URL = http://www.colourlovers.com/api/palettes/top&numResults=1&resultOffset=%Offset%
If (Type = "RAND")
URL = http://www.colourlovers.com/api/palettes/random
; URL = http://www.colourlovers.com/api/palettes/top&keywordExact=0&keywords=dark&numResults=1&resultOffset=%Offset%
httpQuery(html, URL, POSTdata)
VarSetCapacity(html, -1)
RegExMatch(html, "iS)\Q<title><![CDATA[\E(.+?)\Q]]></title>\E", titleNode)
RegExMatch(html, "iS)\Q<userName><![CDATA[\E(.+?)\Q]]></userName>\E", userNameNode)
Loop, Parse, html, `n, `n
{
RegExMatch(A_LoopField, "iS)<hex>(.+?)</hex>", hexNode)
If hexNode
Output .= hexNode1 Delimiter
If InStr(A_LoopField, "</palette>")
Output .= "`n"
}
Return titleNode1 " by " userNameNode1 Delimiter SubStr(Output, 1, -2)
}
; ++++++++++++++++++++++++++++++++++++++++
Dec2Hex(Dec)
{
SetFormat, integer, hex
Dec += 0
SetFormat, integer, d
Return Dec
}
; ++++++++++++++++++++++++++++++++++++++++ Labels
^S::
pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap)
Gdip_SaveBitmapToFile(pBitmap, A_Desktop "\CL.png")
Gdip_DisposeImage(pBitmap)
GoSub, Exit
Return
Space::
pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap)
Gdip_SaveBitmapToFile(pBitmap, A_Desktop "\CL.bmp")
Gdip_DisposeImage(pBitmap)
DllCall("SystemParametersInfo", "UInt", 0x14, "UInt", 0, "Str", A_Desktop "\CL.bmp", "UInt", 2)
FileDelete, %A_Desktop%\CL.bmp
GoSub, Exit
Return
F1::
Circles := !Circles
Return
F2::
Text := !Text
Return
F3::
Tips := !Tips
ToolTip
Return
Esc::
Exit:
SelectObject(hDC, obm)
DeleteObject(hBitmap)
DeleteDC(hDC)
Gdip_DeleteGraphics(G)
Gdip_Shutdown(pToken)
ExitApp
Important! You'll need to replace Gdip_TextToGraphics() with my version below.
(I just added a parameter which, when enabled, tells the function to return just the information from Gdip_MeasureString() and not draw the Text)
Edit: Also, this needs httpQuery and it uses some additional Gdip functions, which are now pasted below (thanks tic!)@tic: It would be cool if a means to get the resulting text dimensions directly would be included in the library

Code:
Gdip_TextToGraphics(pGraphics, Text, Options, Font="Arial", Width="", Height="", MeasureOnly="")
{
IWidth := Width, IHeight:= Height
RegExMatch(Options, "i)X([\-0-9]+)(p*)", xpos)
RegExMatch(Options, "i)Y([\-0-9]+)(p*)", ypos)
RegExMatch(Options, "i)W([0-9]+)(p*)", Width)
RegExMatch(Options, "i)H([0-9]+)(p*)", Height)
RegExMatch(Options, "i)C(?!(entre|enter))([a-f0-9]{8})", Colour)
RegExMatch(Options, "i)Top|Up|Bottom|Down|vCentre|vCenter", vPos)
RegExMatch(Options, "i)R([0-9])", Rendering)
RegExMatch(Options, "i)S([0-9]+)(p*)", Size)
If !(IWidth && IHeight) && (xpos2 || ypos2 || Width2 || Height2 || Size2)
Return, -1
Style := 0, Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
Loop, Parse, Styles, |
{
If RegExMatch(Options, "\b" A_loopField)
Style |= (A_LoopField != "StrikeOut") ? (A_Index-1) : 8
}
Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right"
Loop, Parse, Alignments, |
{
If RegExMatch(Options, "\b" A_loopField)
Align |= A_Index//2.1 ; 0|0|1|1|2|2
}
xpos := (xpos1 != "") ? xpos2 ? IWidth*(xpos1/100) : xpos1 : 0
ypos := (ypos1 != "") ? ypos2 ? IHeight*(ypos1/100) : ypos1 : 0
Width := Width1 ? Width2 ? IWidth*(Width1/100) : Width1 : IWidth
Height := Height1 ? Height2 ? IHeight*(Height1/100) : Height1 : IHeight
Colour := "0x" (Colour2 ? Colour2 : "ff000000")
Rendering := ((Rendering1 >= 0) && (Rendering1 <= 4)) ? Rendering1 : 4
Size := (Size1 > 0) ? Size2 ? IHeight*(Size1/100) : Size1 : 12
hFamily := Gdip_FontFamilyCreate(Font)
hFont := Gdip_FontCreate(hFamily, Size, Style)
hFormat := Gdip_StringFormatCreate(0x4000)
pBrush := Gdip_BrushCreateSolid(Colour)
If !(hFamily && hFont && hFormat && pBrush && pGraphics)
Return, !pGraphics ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0
CreateRectF(RC, xpos, ypos, Width, Height)
Gdip_SetStringFormatAlign(hFormat, Align)
Gdip_SetTextRenderingHint(pGraphics, Rendering)
ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
If MeasureOnly
{
Gdip_DeleteBrush(pBrush)
Gdip_DeleteStringFormat(hFormat)
Gdip_DeleteFont(hFont)
Gdip_DeleteFontFamily(hFamily)
Return ReturnRC
}
If vPos
{
StringSplit, ReturnRC, ReturnRC, |
If (vPos = "vCentre") || (vPos = "vCenter")
ypos := (Height-ReturnRC4)//2
Else If (vPos = "Top") || (vPos = "Up")
ypos := 0
Else If (vPos = "Bottom") || (vPos = "Down")
ypos := Height-ReturnRC4
CreateRectF(RC, xpos, ypos, Width, ReturnRC4)
}
E := Gdip_DrawString(pGraphics, Text, hFont, hFormat, pBrush, RC)
Gdip_DeleteBrush(pBrush)
Gdip_DeleteStringFormat(hFormat)
Gdip_DeleteFont(hFont)
Gdip_DeleteFontFamily(hFamily)
Return, E ? E : ReturnRC
}
; ++++++++++++++++++++++++++++++++++++++++
; additional GDIP functions, to register a new GDIP Canvas class
; and work on a GUI which is NOT created by Gui* Commands
; initially written by derRaphael for tic's gdip library on nov 30, 2008
; licensed under the terms of gdip.ahk's license
; Inspiration to this came from this thread:
; majkinetor's [tutorial] Creating windows without GUI commands
; http://www.autohotkey.com/forum/viewtopic.php?t=22904
GDIP_CreateWindow(lpWindowName="dR's GDIP Canvas") {
return DllCall("CreateWindowEx" ; This function actually creates the window and returns its hWnd
, "uInt",dwExStyle:=0x80088, "Str", lpClassName:="GDIP_Canvas"
, "Str", lpWindowName, "uInt", dwStyle:=0x940A0000
, "Int", x, "Int", y, "Int", nWidth, "Int", nHeight, "uInt", hWndParent
, "uInt",hMenu, "uInt", hInstance, "uInt", lpParam)
}
GDIP_RegisterClass(){ ; needs to be called only once, so it can register the class
cName := "GDIP_Canvas", WndProc := RegisterCallback("GDIP_WndProc", "F")
VarSetCapacity(WndClass, 40, 0)
NumPut(WndProc, WndClass, 4) ; style / lpfnWndProc
; If below is NOT specified, the app must take care of assigning proper IDC_* Cursors
; the provoded method assigns standard cursor for the window
NumPut((hCursor := DllCall( "LoadCursor", "Uint",0, "Int", IDC_ARROW:=32512 )), WndClass , 24)
NumPut(&cName, WndClass, 36) ; lpszClassName
return DllCall("RegisterClass", "uint", &WndClass )
}
/*
; Additional WndClass - http://msdn.microsoft.com/en-us/library/ms633576(VS.85).aspx
NumPut(0, WndClass, 0) ; style see http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx
NumPut(0, WndClass, 8) ; cbClsExtra
NumPut(0, WndClass, 12) ; cbWndExtra
NumPut(0, WndClass, 16) ; hInstance
NumPut(0, WndClass, 20) ; hIcon
NumPut(0, WndClass, 28) ; hbrBackground (brush background)
NumPut(0, WndClass, 32) ; lpszMenuName
*/
; The callback for our new registered class
GDIP_WndProc(hwnd, uMsg, wParam, lParam) { ; This one can handle additional WndMsgs
if (uMsg=0x111) {
DllCall("DestroyWindow", "uint", hwnd)
}
return, DllCall("DefWindowProc", "uint", hwnd, "uint", umsg, "uint", wParam, "uint", lParam)
}