Skip to content

Commit 0bd23aa

Browse files
authored
Merge pull request #3326 from pygame-community/ankith26-sdl3-ttf
Port font to SDL3(_ttf)
2 parents 02eb2ca + dc6c168 commit 0bd23aa

File tree

7 files changed

+158
-22
lines changed

7 files changed

+158
-22
lines changed

.github/workflows/build-sdl3.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ jobs:
9797
cmake --build . --config Release --parallel
9898
sudo cmake --install . --config Release
9999
100+
- name: Install SDL3_ttf
101+
if: matrix.os != 'windows-latest'
102+
run: |
103+
git clone https://github.com/libsdl-org/SDL_ttf
104+
cd SDL_ttf
105+
mkdir build
106+
cd build
107+
cmake -DCMAKE_BUILD_TYPE=Release ..
108+
cmake --build . --config Release --parallel
109+
sudo cmake --install . --config Release
110+
100111
- name: Build with SDL3
101112
run: python3 dev.py build --sdl3
102113

buildconfig/download_win_prebuilt.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ def get_urls(x86=True, x64=True):
9898
'2d18b9a4fc2ec0eee80de2a946b088d4e6efd0ee'
9999
],
100100
[
101+
'https://github.com/libsdl-org/SDL_ttf/releases/download/preview-3.1.0/SDL3_ttf-devel-3.1.0-VC.zip',
102+
'34bb4a03c6f0f6c9de3658bac98adc7029830578'
103+
],
104+
[
101105
'https://github.com/libsdl-org/SDL_mixer/releases/download/release-2.8.0/SDL2_mixer-devel-2.8.0-VC.zip',
102106
'a10411644e08cd94f29712f430c7b71c407ae76d',
103107
],
@@ -251,6 +255,18 @@ def copy(src, dst):
251255
'SDL2_ttf-2.24.0'
252256
)
253257
)
258+
copy(
259+
os.path.join(
260+
temp_dir,
261+
'SDL3_ttf-devel-3.1.0-VC/SDL3_ttf-3.1.0'
262+
),
263+
os.path.join(
264+
move_to_dir,
265+
prebuilt_dir,
266+
'SDL3_ttf-3.1.0'
267+
)
268+
)
269+
254270
copy(
255271
os.path.join(
256272
temp_dir,

dev.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
SDL3_ARGS = [
2828
"-Csetup-args=-Dsdl_api=3",
2929
"-Csetup-args=-Dmixer=disabled",
30-
"-Csetup-args=-Dfont=disabled",
3130
]
3231
COVERAGE_ARGS = ["-Csetup-args=-Dcoverage=true"]
3332

meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ if plat == 'win' and host_machine.cpu_family().startswith('x86')
112112
sdl_ver = (sdl_api == 3) ? '3.2.2' : '2.30.12'
113113
sdl_image_ver = (sdl_api == 3) ? '3.1.1' : '2.8.4'
114114
sdl_mixer_ver = '2.8.0'
115-
sdl_ttf_ver = '2.24.0'
115+
sdl_ttf_ver = (sdl_api == 3) ? '3.1.0' : '2.24.0'
116116

117117
dlls = []
118118

src_c/font.c

Lines changed: 119 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,14 @@ static PyObject *
135135
fontmodule_init(PyObject *self, PyObject *_null)
136136
{
137137
if (!font_initialized) {
138+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
139+
if (!TTF_Init())
140+
#else
138141
if (TTF_Init())
142+
#endif
143+
{
139144
return RAISE(pgExc_SDLError, SDL_GetError());
140-
145+
}
141146
font_initialized = 1;
142147
}
143148
Py_RETURN_NONE;
@@ -169,7 +174,11 @@ font_get_height(PyObject *self, PyObject *_null)
169174
}
170175

171176
TTF_Font *font = PyFont_AsFont(self);
177+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
178+
return PyLong_FromLong(TTF_GetFontHeight(font));
179+
#else
172180
return PyLong_FromLong(TTF_FontHeight(font));
181+
#endif
173182
}
174183

175184
static PyObject *
@@ -180,7 +189,11 @@ font_get_descent(PyObject *self, PyObject *_null)
180189
}
181190

182191
TTF_Font *font = PyFont_AsFont(self);
192+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
193+
return PyLong_FromLong(TTF_GetFontDescent(font));
194+
#else
183195
return PyLong_FromLong(TTF_FontDescent(font));
196+
#endif
184197
}
185198

186199
static PyObject *
@@ -191,7 +204,11 @@ font_get_ascent(PyObject *self, PyObject *_null)
191204
}
192205

193206
TTF_Font *font = PyFont_AsFont(self);
207+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
208+
return PyLong_FromLong(TTF_GetFontAscent(font));
209+
#else
194210
return PyLong_FromLong(TTF_FontAscent(font));
211+
#endif
195212
}
196213

197214
static PyObject *
@@ -202,7 +219,11 @@ font_get_linesize(PyObject *self, PyObject *_null)
202219
}
203220

204221
TTF_Font *font = PyFont_AsFont(self);
222+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
223+
return PyLong_FromLong(TTF_GetFontLineSkip(font));
224+
#else
205225
return PyLong_FromLong(TTF_FontLineSkip(font));
226+
#endif
206227
}
207228

208229
static PyObject *
@@ -453,7 +474,10 @@ font_getter_align(PyObject *self, void *closure)
453474
return RAISE_FONT_QUIT_ERROR();
454475
}
455476

456-
#if SDL_TTF_VERSION_ATLEAST(2, 20, 0)
477+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
478+
TTF_Font *font = PyFont_AsFont(self);
479+
return PyLong_FromLong(TTF_GetFontWrapAlignment(font));
480+
#elif SDL_TTF_VERSION_ATLEAST(2, 20, 0)
457481
TTF_Font *font = PyFont_AsFont(self);
458482
return PyLong_FromLong(TTF_GetFontWrappedAlign(font));
459483
#else
@@ -492,7 +516,11 @@ font_setter_align(PyObject *self, PyObject *value, void *closure)
492516
return -1;
493517
}
494518

519+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
520+
TTF_SetFontWrapAlignment(font, val);
521+
#else
495522
TTF_SetFontWrappedAlign(font, val);
523+
#endif
496524
return 0;
497525
#else
498526
PyErr_SetString(pgExc_SDLError,
@@ -611,28 +639,41 @@ font_render(PyObject *self, PyObject *args, PyObject *kwds)
611639
length string */
612640

613641
if (strlen(astring) == 0) { /* special 0 string case */
642+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
643+
int height = TTF_GetFontHeight(font);
644+
#else
614645
int height = TTF_FontHeight(font);
646+
#endif
615647
surf = PG_CreateSurface(0, height, SDL_PIXELFORMAT_XRGB8888);
616648
}
617649
else { /* normal case */
618650
if (antialias && bg_rgba_obj == Py_None) {
619-
#if SDL_TTF_VERSION_ATLEAST(2, 0, 18)
651+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
652+
surf = TTF_RenderText_Blended_Wrapped(font, astring, 0, foreg,
653+
wraplength);
654+
#elif SDL_TTF_VERSION_ATLEAST(2, 0, 18)
620655
surf = TTF_RenderUTF8_Blended_Wrapped(font, astring, foreg,
621656
wraplength);
622657
#else
623658
surf = TTF_RenderUTF8_Blended(font, astring, foreg);
624659
#endif
625660
}
626661
else if (antialias) {
627-
#if SDL_TTF_VERSION_ATLEAST(2, 0, 18)
662+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
663+
surf = TTF_RenderText_Shaded_Wrapped(font, astring, 0, foreg,
664+
backg, wraplength);
665+
#elif SDL_TTF_VERSION_ATLEAST(2, 0, 18)
628666
surf = TTF_RenderUTF8_Shaded_Wrapped(font, astring, foreg, backg,
629667
wraplength);
630668
#else
631669
surf = TTF_RenderUTF8_Shaded(font, astring, foreg, backg);
632670
#endif
633671
}
634672
else {
635-
#if SDL_TTF_VERSION_ATLEAST(2, 0, 18)
673+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
674+
surf = TTF_RenderText_Solid_Wrapped(font, astring, 0, foreg,
675+
wraplength);
676+
#elif SDL_TTF_VERSION_ATLEAST(2, 0, 18)
636677
surf =
637678
TTF_RenderUTF8_Solid_Wrapped(font, astring, foreg, wraplength);
638679
#else
@@ -642,9 +683,16 @@ font_render(PyObject *self, PyObject *args, PyObject *kwds)
642683
resolve to Render_Solid, that needs to be explicitly handled. */
643684
if (surf != NULL && bg_rgba_obj != Py_None) {
644685
SDL_SetColorKey(surf, 0, 0);
645-
surf->format->palette->colors[0].r = backg.r;
646-
surf->format->palette->colors[0].g = backg.g;
647-
surf->format->palette->colors[0].b = backg.b;
686+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
687+
SDL_Palette *palette = SDL_GetSurfacePalette(surf);
688+
#else
689+
SDL_Palette *palette = surf->format->palette;
690+
#endif
691+
if (palette) {
692+
palette->colors[0].r = backg.r;
693+
palette->colors[0].g = backg.g;
694+
palette->colors[0].b = backg.b;
695+
}
648696
}
649697
}
650698
}
@@ -683,15 +731,24 @@ font_size(PyObject *self, PyObject *text)
683731
return NULL;
684732
}
685733
string = PyBytes_AS_STRING(bytes);
734+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
735+
ecode = TTF_GetStringSize(font, string, 0, &w, &h) ? 0 : -1;
736+
#else
686737
ecode = TTF_SizeUTF8(font, string, &w, &h);
738+
#endif
687739
Py_DECREF(bytes);
688740
if (ecode) {
689741
return RAISE(pgExc_SDLError, TTF_GetError());
690742
}
691743
}
692744
else if (PyBytes_Check(text)) {
693745
string = PyBytes_AS_STRING(text);
694-
if (TTF_SizeText(font, string, &w, &h)) {
746+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
747+
if (!TTF_GetStringSize(font, string, 0, &w, &h))
748+
#else
749+
if (TTF_SizeText(font, string, &w, &h))
750+
#endif
751+
{
695752
return RAISE(pgExc_SDLError, TTF_GetError());
696753
}
697754
}
@@ -737,7 +794,13 @@ font_setter_point_size(PyFontObject *self, PyObject *value, void *closure)
737794
return -1;
738795
}
739796

740-
if (TTF_SetFontSize(font, val) == -1) {
797+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
798+
/* TODO: can consider supporting float in python API */
799+
if (!TTF_SetFontSize(font, (float)val))
800+
#else
801+
if (TTF_SetFontSize(font, val) == -1)
802+
#endif
803+
{
741804
PyErr_SetString(pgExc_SDLError, SDL_GetError());
742805
return -1;
743806
}
@@ -786,7 +849,13 @@ font_set_ptsize(PyObject *self, PyObject *arg)
786849
"point_size cannot be equal to, or less than 0");
787850
}
788851

789-
if (TTF_SetFontSize(font, val) == -1) {
852+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
853+
/* TODO: can consider supporting float in python API */
854+
if (!TTF_SetFontSize(font, (float)val))
855+
#else
856+
if (TTF_SetFontSize(font, val) == -1)
857+
#endif
858+
{
790859
return RAISE(pgExc_SDLError, SDL_GetError());
791860
}
792861
((PyFontObject *)self)->ptsize = val;
@@ -806,7 +875,11 @@ font_getter_name(PyObject *self, void *closure)
806875
}
807876

808877
TTF_Font *font = PyFont_AsFont(self);
878+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
879+
const char *font_name = TTF_GetFontFamilyName(font);
880+
#else
809881
const char *font_name = TTF_FontFaceFamilyName(font);
882+
#endif
810883

811884
return PyUnicode_FromString(font_name ? font_name : "");
812885
}
@@ -819,7 +892,11 @@ font_getter_style_name(PyObject *self, void *closure)
819892
}
820893

821894
TTF_Font *font = PyFont_AsFont(self);
895+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
896+
const char *font_style_name = TTF_GetFontStyleName(font);
897+
#else
822898
const char *font_style_name = TTF_FontFaceStyleName(font);
899+
#endif
823900
return PyUnicode_FromString(font_style_name ? font_style_name : "");
824901
}
825902

@@ -882,9 +959,16 @@ font_metrics(PyObject *self, PyObject *textobj)
882959
* TTF_GlyphMetrics() seems to return a value for any character,
883960
* using the default invalid character, if the char is not found.
884961
*/
962+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
963+
if (!surrogate && /* conditional and */
964+
TTF_GetGlyphMetrics(font, (Uint16)ch, &minx, &maxx, &miny, &maxy,
965+
&advance))
966+
#else
885967
if (!surrogate && /* conditional and */
886968
!TTF_GlyphMetrics(font, (Uint16)ch, &minx, &maxx, &miny, &maxy,
887-
&advance)) {
969+
&advance))
970+
#endif
971+
{
888972
listitem =
889973
Py_BuildValue("(iiiii)", minx, maxx, miny, maxy, advance);
890974
if (!listitem) {
@@ -912,6 +996,14 @@ font_metrics(PyObject *self, PyObject *textobj)
912996
return list;
913997
}
914998

999+
/* This is taken from the harfbuzz header file. It converts script name in the
1000+
* format expected by sdl2 (a 4 char string) to the format expected by sdl3
1001+
* (a single uint32 tag) */
1002+
#define HB_TAG(c1, c2, c3, c4) \
1003+
((Uint32)((((uint32_t)(c1) & 0xFF) << 24) | \
1004+
(((uint32_t)(c2) & 0xFF) << 16) | \
1005+
(((uint32_t)(c3) & 0xFF) << 8) | ((uint32_t)(c4) & 0xFF)))
1006+
9151007
static PyObject *
9161008
font_set_script(PyObject *self, PyObject *arg)
9171009
{
@@ -935,7 +1027,13 @@ font_set_script(PyObject *self, PyObject *arg)
9351027
"script code must be exactly 4 characters");
9361028
}
9371029

938-
if (TTF_SetFontScriptName(font, script_code) < 0) {
1030+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
1031+
if (!TTF_SetFontScript(font, HB_TAG(script_code[0], script_code[1],
1032+
script_code[2], script_code[3])))
1033+
#else
1034+
if (TTF_SetFontScriptName(font, script_code) < 0)
1035+
#endif
1036+
{
9391037
return RAISE(pgExc_SDLError, SDL_GetError());
9401038
}
9411039
#else
@@ -1013,8 +1111,12 @@ font_set_direction(PyObject *self, PyObject *arg, PyObject *kwarg)
10131111
" to https://github.com/pygame-community/pygame-ce");
10141112
}
10151113
}
1016-
1017-
if (TTF_SetFontDirection(font, dir)) {
1114+
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
1115+
if (!TTF_SetFontDirection(font, dir))
1116+
#else
1117+
if (TTF_SetFontDirection(font, dir))
1118+
#endif
1119+
{
10181120
return RAISE(pgExc_SDLError, SDL_GetError());
10191121
}
10201122

@@ -1183,7 +1285,8 @@ font_init(PyFontObject *self, PyObject *args, PyObject *kwds)
11831285

11841286
Py_BEGIN_ALLOW_THREADS;
11851287
#if SDL_VERSION_ATLEAST(3, 0, 0)
1186-
font = TTF_OpenFontIO(rw, 1, fontsize);
1288+
/* TODO: can consider supporting float in python API */
1289+
font = TTF_OpenFontIO(rw, 1, (float)fontsize);
11871290
#else
11881291
font = TTF_OpenFontRW(rw, 1, fontsize);
11891292
#endif

src_c/font.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
#ifndef PGFONT_INTERNAL_H
22
#define PGFONT_INTERNAL_H
33

4-
#include <SDL_ttf.h>
4+
#ifdef PG_SDL3
5+
#include <SDL3_ttf/SDL_ttf.h>
6+
7+
// SDL3_ttf uses SDL3 error reporting API
8+
#define TTF_GetError SDL_GetError
59

10+
#else
11+
#include <SDL_ttf.h>
12+
#endif
613
/* test font initialization */
714
#define FONT_INIT_CHECK() \
815
if (!(*(int *)PyFONT_C_API[2])) \

0 commit comments

Comments
 (0)