Skip to content

Commit 5d62ccb

Browse files
author
vmartinez
committed
Auto generate button images.
Fix memory leak. Update button generator.
1 parent 7a9d8d0 commit 5d62ccb

4 files changed

Lines changed: 216 additions & 19 deletions

File tree

LibreDeck Client.ahk

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
; Requires AutoHotkeyU32
33
;@Ahk2Exe-SetName LibreDeck Client
44
;@Ahk2Exe-SetDescription Macro Panel Client
5-
;@Ahk2Exe-SetVersion 4.0.5
5+
;@Ahk2Exe-SetVersion 4.0.7
66
;@Ahk2Exe-SetCopyright 2026`, elModo7 - VictorDevLog
77
;@Ahk2Exe-SetOrigFilename LibreDeck Client.exe
88
; INITIALIZE
@@ -54,7 +54,7 @@ SetBatchLines, -1
5454
#Include <plugin_system>
5555
#Include <LibreDeckButtonImage>
5656
rutaSplash = ./resources/img/splash.png
57-
global ClientVersionNumber := "4.0.5"
57+
global ClientVersionNumber := "4.0.7"
5858
global ClientVersion := ClientVersionNumber " - elModo7 / VictorDevLog " A_YYYY
5959
SplashScreen(rutaSplash, 3000, 545, 160, 0, 0, true)
6060
global EsVisible = true
@@ -1095,6 +1095,18 @@ GenerarImagenBotonDefault(IdBoton)
10951095
buttonImageStyle := GenerarImagenBotonDefaultSeleccionarEstilo()
10961096
if(buttonImageStyle = "")
10971097
return
1098+
1099+
if (buttonImageStyle == "image") {
1100+
FileSelectFile, imageSelected, 3,, Select image, *.png; *.jpg; *.jpeg; *.bmp; *.gif; *.ico
1101+
if (!ErrorLevel) {
1102+
buttonImagePath := imageSelected
1103+
buttonImageFit := "cover" ; cover | contain | stretch
1104+
} else {
1105+
MsgBox,,Error, Could not generate button image.
1106+
return
1107+
}
1108+
}
1109+
10981110
Gui, 1:Default
10991111

11001112
outDir := A_ScriptDir "\resources\img"
@@ -1118,10 +1130,18 @@ GenerarImagenBotonDefault(IdBoton)
11181130
btnPics[imageName] := ""
11191131
FileDelete, %imagePath%
11201132

1121-
if(!LD_ButtonImage_Render(imagePath, {style:buttonImageStyle, title:buttonImageType, value:buttonImageTitle, subtitle:buttonImageSubtitle}))
1122-
{
1123-
MsgBox,,Error, Could not generate button image.
1124-
return
1133+
if (buttonImageStyle == "image") {
1134+
if(!LD_ButtonImage_Render(imagePath, {style:buttonImageStyle, imagePath:buttonImagePath, imageFit:buttonImageFit, title:buttonImageType, subtitle:buttonImageSubtitle}))
1135+
{
1136+
MsgBox,,Error, Could not generate button image.
1137+
return
1138+
}
1139+
} else {
1140+
if(!LD_ButtonImage_Render(imagePath, {style:buttonImageStyle, title:buttonImageType, value:buttonImageTitle, subtitle:buttonImageSubtitle}))
1141+
{
1142+
MsgBox,,Error, Could not generate button image.
1143+
return
1144+
}
11251145
}
11261146

11271147
btnPics[imageName] := LoadPicture(imagePath)
@@ -1175,7 +1195,7 @@ GenerarImagenBotonDefaultSeleccionarEstilo()
11751195
Gui, ButtonImageStyle:New, +AlwaysOnTop +ToolWindow +OwnDialogs, Button Image Style
11761196
Gui, ButtonImageStyle:Margin, 12, 12
11771197
Gui, ButtonImageStyle:Add, Text, w260, Choose button image style:
1178-
Gui, ButtonImageStyle:Add, DropDownList, vLD_ButtonImage_SelectedStyle w260 Choose6, neon|resident|warning|pokemon|glass|minimal
1198+
Gui, ButtonImageStyle:Add, DropDownList, vLD_ButtonImage_SelectedStyle w260 Choose6, neon|resident|warning|pokemon|glass|minimal|image
11791199
Gui, ButtonImageStyle:Add, Button, x70 y80 w80 Default gButtonImageStyleOK, OK
11801200
Gui, ButtonImageStyle:Add, Button, x160 y80 w80 gButtonImageStyleCancel, Cancel
11811201
Gui, ButtonImageStyle:Show,, Button Image Style
@@ -1837,6 +1857,8 @@ setButtonIcon(buttonId, imagePathOrName) {
18371857
else
18381858
curBtn := CarpetaBoton 15*NumeroPagina+buttonId ".png"
18391859

1860+
if (btnPics[curBtn])
1861+
DllCall("DeleteObject", "ptr", btnPics[curBtn]) ; Dispose image from memory
18401862
btnPics[curBtn] := LoadPicture(imagePathOrName)
18411863
GuiControl, Text, Boton%buttonId%, % btnPics[curBtn] ? "HBITMAP:*" btnPics[curBtn] : imagePathOrName
18421864
if(conf.miniClient)

LibreDeck Client.exe

5.5 KB
Binary file not shown.

lib/LibreDeckButtonImage.ahk

Lines changed: 182 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
; LibreDeckButtonImage.ahk
22
; AutoHotkey v1 library for generating configurable PNG button images.
33
; Self-contained: uses Windows GDI+ directly, no Gdip_All.ahk required.
4-
; Version: 0.1.1
4+
; Version: 0.1.3
55

66
; -----------------------------------------------------------------------------
77
; Public API
@@ -40,6 +40,7 @@ LD_ButtonImage_Render(outputPath, cfg := "") {
4040
valueColor := LD_Color(LD_Cfg(cfg, "valueColor", style.valueColor))
4141
borderColor := LD_Color(LD_Cfg(cfg, "borderColor", style.borderColor))
4242
shadowColor := LD_Color(LD_Cfg(cfg, "shadowColor", style.shadowColor))
43+
transparentOutside := LD_Cfg(cfg, "transparentOutside", style.transparentOutside)
4344

4445
font := LD_Cfg(cfg, "font", "Segoe UI")
4546
titleSize := LD_Cfg(cfg, "titleSize", style.titleSize)
@@ -57,6 +58,13 @@ LD_ButtonImage_Render(outputPath, cfg := "") {
5758
value := LD_Cfg(cfg, "value", "")
5859
subtitle := LD_Cfg(cfg, "subtitle", "")
5960
iconPath := LD_Cfg(cfg, "iconPath", "")
61+
imagePath := LD_Cfg(cfg, "imagePath", LD_Cfg(cfg, "backgroundImagePath", ""))
62+
imageFit := StrLower(Trim(LD_Cfg(cfg, "imageFit", style.imageFit))) ; cover | contain | stretch
63+
imageOpacity := LD_Cfg(cfg, "imageOpacity", style.imageOpacity)
64+
overlayColor := LD_Color(LD_Cfg(cfg, "overlayColor", style.overlayColor))
65+
textShadowColor := LD_Color(LD_Cfg(cfg, "textShadowColor", style.textShadowColor))
66+
textBoxColor := LD_Color(LD_Cfg(cfg, "textBoxColor", style.textBoxColor))
67+
textBoxPadding := LD_Cfg(cfg, "textBoxPadding", style.textBoxPadding)
6068
progress := LD_Cfg(cfg, "progress", "")
6169

6270
pBitmap := LD_Gdip_CreateBitmap(width, height)
@@ -66,14 +74,32 @@ LD_ButtonImage_Render(outputPath, cfg := "") {
6674
G := LD_Gdip_GraphicsFromImage(pBitmap)
6775
LD_Gdip_SetSmoothingMode(G, 4)
6876
LD_Gdip_SetInterpolationMode(G, 7)
69-
LD_Gdip_GraphicsClear(G, bgColor)
7077

71-
if (styleName = "glass")
72-
LD_DrawGlassLayer(G, width, height)
78+
contentX := 4
79+
contentY := 4
80+
contentW := width - 8
81+
contentH := height - 8
82+
83+
; If transparentOutside is enabled, the PNG canvas outside the rounded
84+
; button remains transparent instead of being filled with bgColor.
85+
LD_Gdip_GraphicsClear(G, transparentOutside ? 0x00000000 : bgColor)
7386

7487
if (shadowColor)
7588
LD_FillRoundedRect(G, 7, 8, width - 14, height - 14, radius, shadowColor)
7689

90+
; Paint the actual button background only inside the rounded rectangle.
91+
LD_FillRoundedRect(G, contentX, contentY, contentW, contentH, radius, bgColor)
92+
93+
if (imagePath != "" && FileExist(imagePath)) {
94+
LD_Gdip_SetClipRoundedRect(G, contentX, contentY, contentW, contentH, radius)
95+
LD_DrawImageFileFit(G, imagePath, contentX, contentY, contentW, contentH, imageFit, imageOpacity)
96+
LD_Gdip_ResetClip(G)
97+
if (overlayColor)
98+
LD_FillRoundedRect(G, contentX, contentY, contentW, contentH, radius, overlayColor)
99+
} else if (styleName = "glass") {
100+
LD_DrawGlassLayer(G, width, height)
101+
}
102+
77103
if (border > 0)
78104
LD_DrawRoundedRect(G, borderColor, border, 4, 4, width - 8, height - 8, radius)
79105

@@ -88,14 +114,29 @@ LD_ButtonImage_Render(outputPath, cfg := "") {
88114
LD_DrawImageFile(G, iconPath, iconX, iconY, iconW, iconH)
89115
}
90116

91-
if (title != "")
117+
if (textBoxColor) {
118+
boxY := LD_Cfg(cfg, "textBoxY", titleY - textBoxPadding)
119+
boxH := LD_Cfg(cfg, "textBoxH", height - boxY - 12)
120+
LD_FillRoundedRect(G, 10, boxY, width - 20, boxH, LD_Min(radius, 10), textBoxColor)
121+
}
122+
123+
if (title != "") {
124+
if (textShadowColor)
125+
LD_DrawText(G, title, font, titleSize, textShadowColor, 9, titleY + 2, width - 16, 26, 1, 1, 1)
92126
LD_DrawText(G, title, font, titleSize, textColor, 8, titleY, width - 16, 26, 1, 1, 1)
127+
}
93128

94-
if (value != "")
129+
if (value != "") {
130+
if (textShadowColor)
131+
LD_DrawText(G, value, font, valueSize, textShadowColor, 5, valueY + 2, (valueX ? valueX : width - 8), 54, 1, 1, 1)
95132
LD_DrawText(G, value, font, valueSize, valueColor, 4, valueY, (valueX ? valueX : width - 8), 54, 1, 1, 1)
133+
}
96134

97-
if (subtitle != "")
135+
if (subtitle != "") {
136+
if (textShadowColor)
137+
LD_DrawText(G, subtitle, font, subtitleSize, textShadowColor, 9, subtitleY + 2, width - 16, 22, 1, 1, 0)
98138
LD_DrawText(G, subtitle, font, subtitleSize, textColor, 8, subtitleY, width - 16, 22, 1, 1, 0)
139+
}
99140

100141
if (progress != "")
101142
LD_DrawProgress(G, progress, 14, height - 17, width - 28, 6, accentColor, 0x55333333)
@@ -120,6 +161,16 @@ LD_ButtonImage_RenderInventorySlot(outputPath, itemName, amount := "", iconPath
120161
return LD_ButtonImage_Render(outputPath, cfg)
121162
}
122163

164+
LD_ButtonImage_RenderImageButton(outputPath, imagePath, title := "", subtitle := "", cfg := "") {
165+
if (!IsObject(cfg))
166+
cfg := {}
167+
cfg.style := LD_Cfg(cfg, "style", "image")
168+
cfg.imagePath := imagePath
169+
cfg.title := title
170+
cfg.subtitle := subtitle
171+
return LD_ButtonImage_Render(outputPath, cfg)
172+
}
173+
123174
; -----------------------------------------------------------------------------
124175
; Styles
125176
; -----------------------------------------------------------------------------
@@ -145,6 +196,13 @@ LD_ButtonImage_GetStyle(name) {
145196
s.iconH := 76
146197
s.iconY := 28
147198
s.accentStrip := true
199+
s.imageFit := "cover"
200+
s.imageOpacity := 100
201+
s.overlayColor := "0x00000000"
202+
s.textShadowColor := "0x00000000"
203+
s.textBoxColor := "0x00000000"
204+
s.textBoxPadding := 8
205+
s.transparentOutside := true
148206

149207
if (name = "minimal") {
150208
s.bgColor := "0xFF202020"
@@ -198,6 +256,31 @@ LD_ButtonImage_GetStyle(name) {
198256
s.titleSize := 15
199257
s.valueSize := 30
200258
s.accentStrip := true
259+
} else if (name = "image") {
260+
s.bgColor := "0xFF000000"
261+
s.accentColor := "0xFFFFFFFF"
262+
s.textColor := "0xFFFFFFFF"
263+
s.valueColor := "0xFFFFFFFF"
264+
s.borderColor := "0x99FFFFFF"
265+
s.shadowColor := "0x77000000"
266+
s.titleSize := 18
267+
s.valueSize := 28
268+
s.subtitleSize := 12
269+
s.border := 2
270+
s.radius := 14
271+
s.titleY := 84
272+
s.valueY := 54
273+
s.iconW := 0
274+
s.iconH := 0
275+
s.iconY := 0
276+
s.accentStrip := false
277+
s.imageFit := "cover"
278+
s.imageOpacity := 100
279+
s.overlayColor := "0x33000000"
280+
s.textShadowColor := "0xCC000000"
281+
s.textBoxColor := "0x66000000"
282+
s.textBoxPadding := 8
283+
s.transparentOutside := true
201284
} else if (name = "dark") {
202285
s.bgColor := "0xFF050505"
203286
s.accentColor := "0xFF666666"
@@ -242,6 +325,40 @@ LD_DrawImageFile(G, path, x, y, w, h) {
242325
return true
243326
}
244327

328+
LD_DrawImageFileFit(G, path, x, y, w, h, fit := "cover", opacity := 100) {
329+
pImg := LD_Gdip_CreateBitmapFromFile(path)
330+
if (!pImg)
331+
return false
332+
333+
imgW := LD_Gdip_GetImageWidth(pImg)
334+
imgH := LD_Gdip_GetImageHeight(pImg)
335+
if (imgW <= 0 || imgH <= 0) {
336+
LD_Gdip_DisposeImage(pImg)
337+
return false
338+
}
339+
340+
fit := StrLower(Trim(fit))
341+
if (fit = "stretch") {
342+
dx := x, dy := y, dw := w, dh := h
343+
} else {
344+
scaleX := w / imgW
345+
scaleY := h / imgH
346+
scale := (fit = "contain") ? LD_Min(scaleX, scaleY) : LD_Max(scaleX, scaleY)
347+
dw := Round(imgW * scale)
348+
dh := Round(imgH * scale)
349+
dx := x + Round((w - dw) / 2)
350+
dy := y + Round((h - dh) / 2)
351+
}
352+
353+
if (opacity >= 100)
354+
LD_Gdip_DrawImage(G, pImg, dx, dy, dw, dh)
355+
else
356+
LD_Gdip_DrawImageOpacity(G, pImg, dx, dy, dw, dh, opacity)
357+
358+
LD_Gdip_DisposeImage(pImg)
359+
return true
360+
}
361+
245362
LD_DrawText(G, text, fontName, size, color, x, y, w, h, align := 1, vAlign := 1, bold := 0) {
246363
pBrush := LD_Gdip_BrushCreateSolid(color)
247364
pFamily := LD_Gdip_FontFamilyCreate(fontName)
@@ -287,6 +404,14 @@ LD_Gdip_CreateRoundedRectPath(x, y, w, h, r) {
287404
return pPath
288405
}
289406

407+
LD_Min(a, b) {
408+
return (a < b) ? a : b
409+
}
410+
411+
LD_Max(a, b) {
412+
return (a > b) ? a : b
413+
}
414+
290415
; -----------------------------------------------------------------------------
291416
; Config helpers
292417
; -----------------------------------------------------------------------------
@@ -410,6 +535,56 @@ LD_Gdip_DrawImage(G, pBitmap, x, y, w, h) {
410535
return DllCall("gdiplus\GdipDrawImageRectI", "UPtr", G, "UPtr", pBitmap, "Int", x, "Int", y, "Int", w, "Int", h)
411536
}
412537

538+
LD_Gdip_DrawImageOpacity(G, pBitmap, x, y, w, h, opacity := 100) {
539+
if (opacity < 0)
540+
opacity := 0
541+
if (opacity > 100)
542+
opacity := 100
543+
alpha := opacity / 100.0
544+
545+
VarSetCapacity(matrix, 100, 0)
546+
NumPut(1.0, matrix, 0, "Float")
547+
NumPut(1.0, matrix, 24, "Float")
548+
NumPut(1.0, matrix, 48, "Float")
549+
NumPut(alpha, matrix, 72, "Float")
550+
NumPut(1.0, matrix, 96, "Float")
551+
552+
imgAttr := 0
553+
DllCall("gdiplus\GdipCreateImageAttributes", "UPtrP", imgAttr)
554+
DllCall("gdiplus\GdipSetImageAttributesColorMatrix", "UPtr", imgAttr, "Int", 1, "Int", 1, "UPtr", &matrix, "UPtr", 0, "Int", 0)
555+
556+
DllCall("gdiplus\GdipDrawImageRectRectI"
557+
, "UPtr", G, "UPtr", pBitmap
558+
, "Int", x, "Int", y, "Int", w, "Int", h
559+
, "Int", 0, "Int", 0, "Int", LD_Gdip_GetImageWidth(pBitmap), "Int", LD_Gdip_GetImageHeight(pBitmap)
560+
, "Int", 2, "UPtr", imgAttr, "UPtr", 0, "UPtr", 0)
561+
562+
return DllCall("gdiplus\GdipDisposeImageAttributes", "UPtr", imgAttr)
563+
}
564+
565+
LD_Gdip_GetImageWidth(pBitmap) {
566+
w := 0
567+
DllCall("gdiplus\GdipGetImageWidth", "UPtr", pBitmap, "UIntP", w)
568+
return w
569+
}
570+
571+
LD_Gdip_GetImageHeight(pBitmap) {
572+
h := 0
573+
DllCall("gdiplus\GdipGetImageHeight", "UPtr", pBitmap, "UIntP", h)
574+
return h
575+
}
576+
577+
LD_Gdip_SetClipRoundedRect(G, x, y, w, h, r) {
578+
pPath := LD_Gdip_CreateRoundedRectPath(x, y, w, h, r)
579+
result := DllCall("gdiplus\GdipSetClipPath", "UPtr", G, "UPtr", pPath, "Int", 0)
580+
LD_Gdip_DeletePath(pPath)
581+
return result
582+
}
583+
584+
LD_Gdip_ResetClip(G) {
585+
return DllCall("gdiplus\GdipResetClip", "UPtr", G)
586+
}
587+
413588
LD_Gdip_SaveBitmapToFile(pBitmap, path) {
414589
VarSetCapacity(clsid, 16, 0)
415590
; PNG encoder CLSID: {557CF406-1A04-11D3-9A73-0000F81EF32E}

lib/nm_msg.ahk

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
; Version: 0.3.1
1+
; Version: 0.3.2
22
; Color param is deprecated!
3-
#Include %A_WorkingDir%\lib\talk.ahk
3+
#Include <talk>
44
nmMsg(nmMsg, time:=1, region:=0, color:="FFFFFF")
55
{
66
detectHiddenWindowsPrev := A_DetectHiddenWindows
77
DetectHiddenWindows, On
88
if (WinExist("ahk_exe LibreDeck Client.exe")) {
99
region := region ? "top" : "bottom"
10-
receiver := new talk("LibreDeck Client.exe")
11-
receiver.setVar("incomingNotification", "{""text"": """ nmMsg """, ""duration"": " time*1000 ", ""region"": """ region """}")
12-
receiver.runlabel("showIncomingNotification")
10+
receiver := new talk("ahk_exe LibreDeck Client.exe")
11+
receiver.setVar("incomingNotification", "{""text"": """ nmMsg """, ""duration"": " time*1000 ", ""region"": """ region """}", 0)
12+
receiver.runlabel("showIncomingNotification", 0)
1313
}
1414
DetectHiddenWindows, % detectHiddenWindowsPrev
1515
}

0 commit comments

Comments
 (0)