Ich habe schon öfter C-Funktionalität via #pragma BEGINDUMP in FiveWin eingebaut – perfekte Kombi. Ich poste hier einen Ansatz von ChatGPT. Ich selbst brauche die Funktion aktuell nicht und habe sie daher nicht getestet. Vielleicht hilft es jemandem beim Ausprobieren.
Short answer:
In Win32/FiveWin gibt es kein natives SVG-Brush. Man kann das SVG aber zur Laufzeit in ein 32-bpp DIB/HBITMAP rasterisieren (z. B. mit NanoSVG) und es dann als Hintergrund malen (empfohlen: im bEraseBkGnd Handler), oder alternativ als TBrush verwenden (kachelt).
Code (Harbour + #pragma, NanoSVG):
Benötigt die Header nanosvg.h und nanosvgrast.h (zlib-Lizenz) im Projektinclude.
/* Demo: SVG als Fenster-Hintergrund (ohne Kacheln) */
FUNCTION Main()
LOCAL oWnd, cSvg := "bg.svg"
DEFINE WINDOW oWnd TITLE "SVG background via #pragma"
/* Bei Größenänderung neu rendern */
oWnd:bResized := { || _SetSvgBackground( oWnd, cSvg ) }
/* GDI-Handle beim Schließen freigeben */
oWnd:bDestroy := { || IF oWnd:cargo != NIL .AND. oWnd:cargo != 0 ; HB_DELETE_GDI( oWnd:cargo ) ; ENDIF }
/* Hintergrund selbst zeichnen, nicht kacheln */
oWnd:bEraseBkGnd := { | hDC | ;
IF(oWnd:cargo != NIL .AND. oWnd:cargo != 0, ;
HB_PAINTBITMAPFIT( hDC, oWnd:cargo, oWnd:nClientWidth(), oWnd:nClientHeight() ), NIL ), ;
1 }
ACTIVATE WINDOW oWnd CENTERED
RETURN NIL
STATIC FUNCTION _SetSvgBackground( oWnd, cSvg )
LOCAL nW := Max( oWnd:nClientWidth(), 1 )
LOCAL nH := Max( oWnd:nClientHeight(), 1 )
LOCAL hOld := IIF( ValType( oWnd:cargo ) == "N", oWnd:cargo, 0 )
LOCAL hBmp := HB_HBITMAPFROMSVG( cSvg, nW, nH ) // erstellt 32bpp DIB passend zur Clientgröße
IF hBmp != 0
oWnd:cargo := hBmp
IF hOld != 0 .AND. hOld != hBmp
HB_DELETE_GDI( hOld )
ENDIF
oWnd:Refresh()
ENDIF
RETURN NIL
#pragma BEGINDUMP
#include <windows.h>
#include <stdint.h>
#include "hbapi.h"
/* ---- NanoSVG: lege diese beiden Header in dein Projekt ---- */
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h" /* https://github.com/memononen/nanosvg */
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
/* ----------------------------------------------------------- */
/* Hilfsfunktion: RGBA -> BGRA über weißem Hintergrund (Brushs ignorieren Alpha) */
static void rgba_to_bgra_over_white(uint8_t* dstBGRA, const uint8_t* srcRGBA, int pixels){
for (int i = 0; i < pixels; ++i){
uint8_t r = srcRGBA[i*4+0], g = srcRGBA[i*4+1], b = srcRGBA[i*4+2], a = srcRGBA[i*4+3];
/* over white: c' = c*a + 255*(1-a) */
uint8_t R = (uint8_t)((r * a + 255 * (255 - a)) / 255);
uint8_t G = (uint8_t)((g * a + 255 * (255 - a)) / 255);
uint8_t B = (uint8_t)((b * a + 255 * (255 - a)) / 255);
dstBGRA[i*4+0] = B;
dstBGRA[i*4+1] = G;
dstBGRA[i*4+2] = R;
dstBGRA[i*4+3] = 0xFF;
}
}
/* Render SVG-Datei in 32bpp top-down DIB, Größe w x h, "cover"-Skalierung */
static HBITMAP HBitmapFromSvg(const char* path, int w, int h){
if (!path || w <= 0 || h <= 0) return NULL;
NSVGimage* img = nsvgParseFromFile(path, "px", 96.0f);
if (!img) return NULL;
float sx = (float) w / (img->width > 0 ? img->width : 1.0f);
float sy = (float) h / (img->height > 0 ? img->height : 1.0f);
float scale = (sx > sy) ? sx : sy; /* cover; für "contain": (sx < sy) ? sx : sy */
BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h; /* top-down */
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
void* bits = NULL;
HBITMAP hbm = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
if (!hbm){ nsvgDelete(img); return NULL; }
/* NanoSVG rendert in RGBA */
uint8_t* rgba = (uint8_t*) GlobalAlloc(GPTR, (SIZE_T) w * h * 4);
if (!rgba){ DeleteObject(hbm); nsvgDelete(img); return NULL; }
ZeroMemory(rgba, (SIZE_T) w * h * 4);
NSVGrasterizer* rast = nsvgCreateRasterizer();
if (!rast){ GlobalFree(rgba); DeleteObject(hbm); nsvgDelete(img); return NULL; }
nsvgRasterize(rast, img, 0.0f, 0.0f, scale, rgba, w, h, w * 4);
rgba_to_bgra_over_white((uint8_t*) bits, rgba, w * h);
nsvgDeleteRasterizer(rast);
GlobalFree(rgba);
nsvgDelete(img);
return hbm;
}
/* Export: Harbour -> HBITMAP aus SVG */
HB_FUNC( HB_HBITMAPFROMSVG ){
const char* path = hb_parc(1);
int w = hb_parni(2), h = hb_parni(3);
HBITMAP hbmp = HBitmapFromSvg(path, w, h);
hb_retptr( (void*) hbmp );
}
/* Export: Bitmap passend in Zielrechteck malen (ohne Kacheln) */
HB_FUNC( HB_PAINTBITMAPFIT ){
HDC hdc = (HDC) hb_parptr(1);
HBITMAP h = (HBITMAP) hb_parptr(2);
int w = hb_parni(3);
int hgt = hb_parni(4);
if (!hdc || !h || w <= 0 || hgt <= 0) return;
BITMAP bm; GetObject(h, sizeof(bm), &bm);
HDC mem = CreateCompatibleDC(hdc);
HGDIOBJ old = SelectObject(mem, h);
SetStretchBltMode(hdc, HALFTONE);
SetBrushOrgEx(hdc, 0, 0, NULL);
StretchBlt(hdc, 0, 0, w, hgt, mem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
SelectObject(mem, old);
DeleteDC(mem);
}
/* Export: GDI-Objekt löschen */
HB_FUNC( HB_DELETE_GDI ){
HGDIOBJ h = (HGDIOBJ) hb_parptr(1);
if (h) DeleteObject(h);
}
#pragma ENDDUMP
inweise:
-
NanoSVG deckt viel, aber nicht alles aus der SVG-Welt (komplexe Filter/Masks evtl. vereinfacht). Für maximale SVG-Treue auf Win10+ ginge auch eine Direct2D-SVG-Variante; ist aber deutlich mehr COM-Plumbing.
-
Wenn ihr lieber ein klassisches Brush (mit Kacheln) wollt, könnt ihr das HBITMAP in TBrush() stecken. Für vollflächig skaliert ohne Kacheln ist der gezeigte bEraseBkGnd-Weg sauberer.
Die zlib-Lizenz ist kostenlos (keine Gebühren, keine Royalties) und sehr permissiv.
Was erlaubt ist:
-
Kommerzielle Nutzung, Weitergabe, statisches/dynamisches Linken, Änderungen, Closed-Source-Produkte.
-
Kein Copyleft: Du musst deinen Code nicht offenlegen.
Was du beachten musst:
-
Herkunft nicht falsch darstellen (nicht so tun, als hättest du das Original geschrieben).
-
Änderungen kennzeichnen (wenn du den Quelltext änderst).
-
Lizenzhinweis im Source beibehalten (Notice darf aus dem Source nicht entfernt/verändert werden).
Praxis-Tipp für dein Projekt:
-
Lege die Original-LICENSE (z. B. von NanoSVG) in deinen third_party/-Ordner.
-
Falls du Dateien geändert hast: oben im Header kurz „Modified by …“ + Datum notieren.
-
Optional eine THIRD-PARTY-NOTICES.txt beilegen – schadet nie.