implement gdi text renderer
All checks were successful
pyrite-dev/milsko/pipeline/head This commit looks good

This commit is contained in:
Nishi 2026-07-19 20:37:04 +09:00
commit 8ebc8108ed
20 changed files with 16827 additions and 16830 deletions

View file

@ -64,7 +64,7 @@ char* MwUTF8ToACP(const char* input) {
return MwStringDuplicate(input);
}
wout = malloc(wbytes);
wout = malloc(wbytes + sizeof(wchar_t));
len = wbytes / sizeof(wchar_t);
memset(wout, 0, wbytes);

View file

@ -212,8 +212,8 @@ static void MwLLFreeColorImpl(MwLLColor color) {}
static MwBool lmao = MwFALSE;
static int MwLLPendingImpl(MwLL handle) {
lmao = !lmao;
return lmao;
lmao = !lmao;
return lmao;
}
static void MwLLNextEventImpl(MwLL handle) {
MwLLDispatch(handle, draw, NULL);
@ -256,10 +256,10 @@ static MwBool MwLLDoModernImpl(MwLL handle) {
static void MwLLRaiseImpl(MwLL handle) {}
static void MwLLClipImpl(MwLL handle, MwRect* rect) {}
static int MwLLCairoCallInitImpl(void) {
if(cairo_load_funcs() != 0) {
return 1;
}
return 0;
if(cairo_load_funcs() != 0) {
return 1;
}
return 0;
}
#include "call.c"
CALL(Cairo);

View file

@ -226,16 +226,16 @@ static MwWidget MwCreateWidget_Internal(MwClass widget_class, const char* name,
MwWidget h = malloc(sizeof(*h));
if(name)
h->name = MwStringDuplicate(name);
h->name = MwStringDuplicate(name);
else
h->name = NULL;
h->name = NULL;
h->parent = parent;
h->children = NULL;
if(widget_class != NULL) {
if((h->lowlevel = MwLLCreate(parent == NULL ? NULL : parent->lowlevel, x, y, width, height)) == NULL) {
if(h->name) {
if(h->name) {
free(h->name);
}
free(h);

View file

@ -530,7 +530,7 @@ static CustomTextRenderer* CustomTextRenderer_Create(IDWriteFactory6* write_fact
return obj;
}
int MWFL_DWSetup(void) {
int MwFL_DWSetup(void) {
HRESULT hr;
HANDLE ole32lib;
/* CoInitialize, to my knowledge, is avaliable on every Windows version we actually support. But as of writing, CI fails because a specific mingw won't link to it by default. I'm not gonna waste my time chancing that it will properly link to ole32 either. */
@ -541,7 +541,7 @@ int MWFL_DWSetup(void) {
printf("ole32lib not found, cannot use DirectWrite\n");
return 1;
}
CoInitialize = (HRESULT (*)(LPVOID pvReserved))GetProcAddress(ole32lib, "CoInitialize");
CoInitialize = (HRESULT(*)(LPVOID pvReserved))GetProcAddress(ole32lib, "CoInitialize");
if(!CoInitialize) {
printf("CoInitialize not found, cannot use DirectWrite\n");
return 1;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -159,7 +159,7 @@ static void ft2_MwFontFree(void* handle) {
free(ttf);
}
int MWFL_FT2Setup(void) {
int MwFL_FT2Setup(void) {
/* Try and load FreeType2. If it fails, return 1, signifying we default to stbtt. */
void* ftlib;
#ifdef __APPLE__

148
src/text/gdi.c Normal file
View file

@ -0,0 +1,148 @@
#ifdef USE_GDI_TEXT
#include <Mw/Milsko.h>
struct _MwFLFont {
HANDLE handle;
HFONT font;
HDC dc;
};
static int GDI_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const char* text, MwLLColor color) {
BITMAPINFOHEADER bmih;
int tw;
int th;
HBITMAP hbm;
RGBQUAD* bits = NULL;
HDC hmdc;
char* t = MwUTF8ToACP(text);
unsigned char* px;
int y, x;
MwRect r;
MwLLPixmap p;
tw = MwTextWidth(handle, ttf, text);
th = MwTextHeight(handle, ttf, text);
px = malloc(tw * th * 4);
bmih.biSize = sizeof(bmih);
bmih.biWidth = tw;
bmih.biHeight = -(LONG)th;
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
hbm = CreateDIBSection(ttf->dc, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
hmdc = CreateCompatibleDC(ttf->dc);
SelectObject(hmdc, hbm);
SelectObject(hmdc, ttf->font);
TextOut(hmdc, 0, 0, t, strlen(t));
DeleteDC(hmdc);
for(y = 0; y < th; y++) {
for(x = 0; x < tw; x++) {
unsigned char* opx = &px[(y * tw + x) * 4];
opx[0] = color->common.red;
opx[1] = color->common.green;
opx[2] = color->common.blue;
opx[3] = 255 - bits[y * tw + x].rgbRed;
}
}
p = MwLoadRaw(handle, px, tw, th);
r.x = point->x;
r.y = point->y - th / 2;
r.width = tw;
r.height = th;
MwLLDrawPixmap(handle->lowlevel, &r, p);
MwLLDestroyPixmap(p);
free(px);
DeleteObject(hbm);
free(t);
return 0;
}
static int GDI_MwTextWidth(MwFLFont ttf, const char* text) {
char* t = MwUTF8ToACP(text);
SIZE sz;
GetTextExtentPoint32(ttf->dc, t, strlen(t), &sz);
free(t);
return sz.cx;
}
static int GDI_MwTextHeight(MwFLFont ttf, int count) {
TEXTMETRIC tm;
GetTextMetrics(ttf->dc, &tm);
return tm.tmHeight * count;
}
static void* GDI_MwFontLoad(unsigned char* data, unsigned int size, int px) {
MwFLFont ttf = malloc(sizeof(*ttf));
DWORD c;
MwTTFInfo info;
if((info = MwTTFGetInfo(data, size)) == NULL) {
free(ttf);
return NULL;
}
ttf->dc = GetDC(NULL);
if((ttf->handle = AddFontMemResourceEx(data, size, 0, &c)) == NULL) {
ReleaseDC(NULL, ttf->dc);
MwTTFFreeInfo(info);
free(ttf);
return NULL;
}
if((ttf->font = CreateFont(-px, 0, 0, 0, info->weight, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, info->family)) == NULL) {
RemoveFontMemResourceEx(ttf->handle);
ReleaseDC(NULL, ttf->dc);
MwTTFFreeInfo(info);
free(ttf);
return NULL;
}
MwTTFFreeInfo(info);
SelectObject(ttf->dc, ttf->font);
return ttf;
}
static void GDI_MwFontFree(void* handle) {
MwFLFont ttf = handle;
DeleteObject(ttf->font);
RemoveFontMemResourceEx(ttf->handle);
ReleaseDC(NULL, ttf->dc);
free(ttf);
}
int MwFL_GDISetup(void) {
MwFLDrawText = GDI_MwDrawText;
MwFLTextWidth = GDI_MwTextWidth;
MwFLTextHeight = GDI_MwTextHeight;
MwFLFontLoad = GDI_MwFontLoad;
MwFLFontFree = GDI_MwFontFree;
return 0;
}
#endif

View file

@ -3,7 +3,7 @@
int (*MwFLDrawText)(MwWidget handle, MwFLFont font, MwPoint* point, const char* text, MwLLColor color) = NULL;
int (*MwFLTextWidth)(MwFLFont font, const char* text) = NULL;
int (*MwFLTextHeight)(MwFLFont font, int count) = NULL;
int (*MwFLTextHeightWithText)(MwFLFont ttf, const char * text) = NULL;
int (*MwFLTextHeightWithText)(MwFLFont ttf, const char* text) = NULL;
void* (*MwFLFontLoad)(unsigned char* data, unsigned int size, int px) = NULL;
void (*MwFLFontFree)(void* handle) = NULL;
@ -222,7 +222,7 @@ int MwTextHeight(MwWidget handle, MwFLFont ttf, const char* text) {
if(MwFLTextHeight)
if(!MwGetInteger(handle, MwNbitmapFont) && ttf != NULL && (st = MwFLTextHeight(ttf, c)) != -1) return st;
if(MwFLTextHeightWithText)
if(!MwGetInteger(handle, MwNbitmapFont) && ttf != NULL && (st = MwFLTextHeightWithText(ttf, text)) != -1) return st;
if(!MwGetInteger(handle, MwNbitmapFont) && ttf != NULL && (st = MwFLTextHeightWithText(ttf, text)) != -1) return st;
#endif
return FontHeight * c;
}
@ -300,11 +300,14 @@ static void bitmap_MwDrawText(MwWidget handle, MwPoint* point, const char* text,
typedef int (*call_t)(void);
void MwFLSetup(void) {
call_t calls[] = {
#ifdef USE_GDI_TEXT
MwFL_GDISetup,
#endif
#ifdef USE_DIRECTWRITE
MWFL_DWSetup,
MwFL_DWSetup,
#endif
#ifdef USE_FREETYPE2
MWFL_FT2Setup,
MwFL_FT2Setup,
#endif
#ifdef USE_STB_TRUETYPE
MwFL_STBTTSetup,

142
src/ttf.c Normal file
View file

@ -0,0 +1,142 @@
#include <Mw/Milsko.h>
#define REMAIN (size - (data - old))
MwTTFInfo MwTTFGetInfo(unsigned char* data, int size){
unsigned short n = 0;
int i;
unsigned char* old = data;
MwTTFInfo info = malloc(sizeof(*info));
memset(info, 0, sizeof(*info));
if(size < 12){
MwTTFFreeInfo(info);
return NULL;
}
n |= data[4 + 0];
n = n << 8;
n |= data[4 + 1];
data += 12;
for(i = 0; REMAIN > 0 && i < n; i++){
unsigned int offset = 0;
unsigned int length = 0;
int j;
if(REMAIN < (4 + 4 + 4 + 4)){
MwTTFFreeInfo(info);
return NULL;
}
for(j = 0; j < 4; j++){
offset = offset << 8;
offset |= data[4 + 4 + j];
length = length << 8;
length |= data[4 + 4 + 4 + j];
}
if(memcmp(data, "OS/2", 4) == 0){
if(length >= 68){
unsigned char* rev = data;
unsigned short us_weight = 0;
data = old + offset;
if(REMAIN < 68){
MwTTFFreeInfo(info);
return NULL;
}
for(j = 0; j < 2; j++){
us_weight = us_weight << 8;
us_weight |= data[0x4 + j];
}
info->weight = us_weight;
data = rev;
}
}else if(memcmp(data, "name", 4) == 0){
if(length >= 6){
unsigned short count = 0;
unsigned char* rev = data;
unsigned short str_off = 0;
unsigned char* beg;
data = old + offset;
if(REMAIN < (2 + 2 + 2)){
MwTTFFreeInfo(info);
return NULL;
}
beg = data;
for(j = 0; j < 2; j++){
count = count << 8;
count |= data[2 + j];
str_off = str_off << 8;
str_off |= data[2 + 2 + j];
}
data += 2 + 2 + 2;
for(j = 0; j < count; j++){
unsigned short platform = 0;
unsigned short name = 0;
unsigned short nam_len = 0;
unsigned short nam_off = 0;
int k;
if(REMAIN < (2 + 2 + 2 + 2 + 2 + 2)){
MwTTFFreeInfo(info);
return NULL;
}
for(k = 0; k < 2; k++){
platform = platform << 8;
platform |= data[0 + k];
name = name << 8;
name |= data[2 + 2 + 2 + k];
nam_len = nam_len << 8;
nam_len |= data[2 + 2 + 2 + 2 + k];
nam_off = nam_off << 8;
nam_off |= data[2 + 2 + 2 + 2 + 2 + k];
}
/* FIXME: this does not work with surrogate */
if((platform == 0 || platform == 3) && name == 1){
data = beg + str_off + nam_off;
if(REMAIN < nam_len){
MwTTFFreeInfo(info);
return NULL;
}
if(info->family != NULL) free(info->family);
info->family = malloc(nam_len / 2 + 1);
for(k = 0; k < nam_len / 2; k++) info->family[k] = data[k * 2 + 1];
info->family[k] = 0;
}
data += 2 + 2 + 2 + 2 + 2 + 2;
}
data = rev;
}
}
data += 4 + 4 + 4 + 4;
}
return info;
}
void MwTTFFreeInfo(MwTTFInfo info){
if(info->family != NULL) free(info->family);
free(info);
}

View file

@ -336,7 +336,7 @@ static void draw_normal(MwWidget handle) {
r.width -= MwGetInteger(handle, MwNleftPadding);
#if 0
#if 1
if(align == MwALIGNMENT_CENTER) {
p.x = r.width / 2;
} else if(align == MwALIGNMENT_BEGINNING) {
@ -353,11 +353,11 @@ static void draw_normal(MwWidget handle) {
p.x += 1;
p.y += 1;
MwDrawText(handle, NULL, &p, str, MwALIGNMENT_BEGINNING, shadow);
MwDrawText(handle, NULL, &p, str, MwALIGNMENT_CENTER, shadow);
p.x -= 1;
p.y -= 1;
MwDrawText(handle, NULL, &p, str, MwALIGNMENT_BEGINNING, text);
MwDrawText(handle, NULL, &p, str, MwALIGNMENT_CENTER, text);
MwLLFreeColor(shadow);
MwLLFreeColor(text);

View file

@ -36,7 +36,7 @@ static void mwWindowMakeBorderlessImpl(MwWidget handle, int toggle) {
}
static void mwWindowShouldCloseImpl(MwWidget handle, MwBool* out) {
*out = handle->close;
*out = handle->close;
}
static void func_handler(MwWidget handle, const char* name, void* out, va_list va) {
@ -47,7 +47,7 @@ static void func_handler(MwWidget handle, const char* name, void* out, va_list v
mwWindowMakeBorderlessImpl(handle, toggle);
}
if(strcmp(name, "mwWindowShouldClose") == 0) {
MwBool *out = va_arg(va, MwBool*);
MwBool* out = va_arg(va, MwBool*);
mwWindowShouldCloseImpl(handle, out);
}
}