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

@ -52,6 +52,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL Windows)
option(MW_USE_DIRECTWRITE "Use DirectWrite" OFF)
endif()
endif()
option(MW_USE_GDI_TEXT "Use GDI for text rendering" ON)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL Linux OR ${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD)
@ -171,6 +172,14 @@ if(MW_USE_DIRECTWRITE)
)
endif()
if(MW_USE_GDI_TEXT)
target_compile_definitions(
Mw
PRIVATE
USE_GDI_TEXT
)
endif()
if(MW_USE_WAYLAND)
function(scan_wayland_protocol_core)
execute_process(

View file

@ -257,8 +257,8 @@ void vulkan_setup(MwWidget handle) {
swapchainCreateInfo.imageExtent = (VkExtent2D){.width = ow, .height = oh},
swapchainCreateInfo.imageArrayLayers = 1,
swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
// th is how we specify no transformation.
swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;

View file

@ -339,19 +339,22 @@ MWDECL void (*MwLLClip)(MwLL handle, MwRect* rect);
MWDECL void MwFLSetup(void);
#ifdef USE_FREETYPE2
MWDECL int MWFL_FT2Setup(void);
MWDECL int MwFL_FT2Setup(void);
#endif
#ifdef USE_STB_TRUETYPE
MWDECL int MwFL_STBTTSetup(void);
#endif
#ifdef USE_DIRECTWRITE
MWDECL int MWFL_DWSetup(void);
MWDECL int MwFL_DWSetup(void);
#endif
#ifdef USE_GDI_TEXT
MWDECL int MwFL_GDISetup(void);
#endif
MWDECL int (*MwFLDrawText)(MwWidget handle, MwFLFont ttf, MwPoint* point, const char* text, MwLLColor color);
MWDECL int (*MwFLTextWidth)(MwFLFont ttf, const char* text);
MWDECL int (*MwFLTextHeight)(MwFLFont ttf, int count);
MWDECL int (*MwFLTextHeightWithText)(MwFLFont ttf, const char * text);
MWDECL int (*MwFLTextHeightWithText)(MwFLFont ttf, const char* text);
MWDECL void* (*MwFLFontLoad)(unsigned char* data, unsigned int size, int px);
MWDECL void (*MwFLFontFree)(void* handle);

View file

@ -17,6 +17,7 @@
#include <Mw/String.h>
#include <Mw/Draw.h>
#include <Mw/Version.h>
#include <Mw/TrueType.h>
#include <Mw/Abstract/Dynamic.h>
#include <Mw/Abstract/Directory.h>

33
include/Mw/TrueType.h Normal file
View file

@ -0,0 +1,33 @@
/*!
* @file Mw/TrueType.h
* @brief TrueType utilities
*/
#ifndef __MW_TRUETYPE_H__
#define __MW_TRUETYPE_H__
#include <Mw/MachDep.h>
#include <Mw/TypeDefs.h>
#ifdef __cplusplus
extern "C" {
#endif
/*!
* @brief Get the TrueType font information
* @param data Font data
* @param size Font size
* @return Information
*/
MWDECL MwTTFInfo MwTTFGetInfo(unsigned char* data, int size);
/*!
* @brief Free the TrueType font information
* @brief info Information
*/
MWDECL void MwTTFFreeInfo(MwTTFInfo info);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -31,6 +31,7 @@ typedef struct _MwSubWindow* MwSubWindow;
typedef struct _MwTab* MwTab;
typedef struct _MwTable* MwTable;
typedef struct _MwMouse MwMouse;
typedef struct _MwTTFInfo* MwTTFInfo;
#ifdef _MILSKO
typedef struct _MwWidget* MwWidget;
#else
@ -247,6 +248,11 @@ struct _MwMouse {
int button;
};
struct _MwTTFInfo {
char* family;
int weight;
};
struct _MwClass {
MwHandlerWithStatus create;
MwHandler destroy;

View file

@ -27,19 +27,17 @@ MwInline void MwWindowMakeBorderless(MwWidget handle, int toggle) {
MwVaWidgetExecute(handle, "mwWindowMakeBorderless", NULL, toggle);
}
/*!
* @brief Whether the window should close
* @param handle Widget
* @return bool
*/
MwInline MwBool MwWindowShouldClose(MwWidget handle) {
MwBool res = MwFALSE;
MwVaWidgetExecute(handle, "mwWindowShouldClose", NULL, &res);
return res;
MwBool res = MwFALSE;
MwVaWidgetExecute(handle, "mwWindowShouldClose", NULL, &res);
return res;
}
#ifdef __cplusplus
}
#endif

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);
}
}