From a99a72e831c5a02df3b2d390a6a75bf3b3e0227b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 19 Oct 2024 09:58:38 -0700 Subject: [PATCH 01/14] Added SDL_textengine.h as a public header. --- CMakeLists.txt | 1 + Xcode/SDL_ttf.xcodeproj/project.pbxproj | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c846553..09717ce6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,7 @@ if(SDLTTF_INSTALL) RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library ) install(FILES + "${CMAKE_CURRENT_SOURCE_DIR}/include/SDL3_ttf/SDL_textengine.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/SDL3_ttf/SDL_ttf.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3_ttf" COMPONENT devel ) diff --git a/Xcode/SDL_ttf.xcodeproj/project.pbxproj b/Xcode/SDL_ttf.xcodeproj/project.pbxproj index d7fb834b..bed4b848 100644 --- a/Xcode/SDL_ttf.xcodeproj/project.pbxproj +++ b/Xcode/SDL_ttf.xcodeproj/project.pbxproj @@ -61,6 +61,7 @@ BE48FD5F07AFA17000BB41DA /* SDL_ttf.h in Headers */ = {isa = PBXBuildFile; fileRef = 1014BAEA010A4B677F000001 /* SDL_ttf.h */; settings = {ATTRIBUTES = (Public, ); }; }; BE48FD6207AFA17000BB41DA /* SDL_ttf.c in Sources */ = {isa = PBXBuildFile; fileRef = F567D67A01CD962A01F3E8B9 /* SDL_ttf.c */; }; F307EE29282738F8003915D7 /* svg.c in Sources */ = {isa = PBXBuildFile; fileRef = F307EE28282738F8003915D7 /* svg.c */; }; + F33F083D2CC41C810062C26D /* SDL_textengine.h in Headers */ = {isa = PBXBuildFile; fileRef = F33F083C2CC41C810062C26D /* SDL_textengine.h */; settings = {ATTRIBUTES = (Public, ); }; }; F364A5B82620E1A200325ECE /* FTL.TXT in Resources */ = {isa = PBXBuildFile; fileRef = F364A5B72620E1A200325ECE /* FTL.TXT */; }; F364A5C42620E22400325ECE /* ReadMe.txt in Resources */ = {isa = PBXBuildFile; fileRef = F364A5C32620E22400325ECE /* ReadMe.txt */; }; F3696FE4278F7107003A7F94 /* sdf.c in Sources */ = {isa = PBXBuildFile; fileRef = F3696FE3278F7107003A7F94 /* sdf.c */; }; @@ -196,6 +197,7 @@ BE48FD6707AFA17000BB41DA /* SDL3_ttf.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDL3_ttf.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BE48FD8307AFA29000BB41DA /* SDL3.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SDL3.framework; sourceTree = ""; }; F307EE28282738F8003915D7 /* svg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = svg.c; path = ../external/freetype/src/svg/svg.c; sourceTree = ""; }; + F33F083C2CC41C810062C26D /* SDL_textengine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_textengine.h; sourceTree = ""; }; F364A5B72620E1A200325ECE /* FTL.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = FTL.TXT; path = ../../../external/freetype/docs/FTL.TXT; sourceTree = ""; }; F364A5C32620E22400325ECE /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReadMe.txt; sourceTree = ""; }; F3696FE3278F7107003A7F94 /* sdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sdf.c; path = ../external/freetype/src/sdf/sdf.c; sourceTree = ""; }; @@ -298,9 +300,11 @@ 0153844A006D81B07F000001 /* Public Headers */ = { isa = PBXGroup; children = ( + F33F083C2CC41C810062C26D /* SDL_textengine.h */, 1014BAEA010A4B677F000001 /* SDL_ttf.h */, ); name = "Public Headers"; + path = ../include/SDL3_ttf; sourceTree = ""; }; 034768DDFF38A45A11DB9C8B /* Products */ = { @@ -542,6 +546,7 @@ F3F7BDF72CB6FD6700C984AF /* SDL_hashtable.h in Headers */, F3F7BDF82CB6FD6700C984AF /* stb_rect_pack.h in Headers */, BE48FD5F07AFA17000BB41DA /* SDL_ttf.h in Headers */, + F33F083D2CC41C810062C26D /* SDL_textengine.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; From b168d2cf6a225901aafb5ac4a7fb944e16468658 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 19 Oct 2024 10:43:13 -0700 Subject: [PATCH 02/14] Updated documentation --- include/SDL3_ttf/SDL_ttf.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index a4b81f31..e4dd12c2 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -114,15 +114,10 @@ typedef struct TTF_Font TTF_Font; * Initialize SDL_ttf. * * You must successfully call this function before it is safe to call any - * other function in this library, with one exception: a human-readable error - * message can be retrieved from SDL_GetError() if this function fails. + * other function in this library. * - * SDL must be initialized before calls to functions in this library, because - * this library uses utility functions from the SDL library. - * - * It is safe to call this more than once; the library keeps a counter of init - * calls, and decrements it on each call to TTF_Quit, so you must pair your - * init and quit calls. + * It is safe to call this more than once, and each successful TTF_Init() + * call should be paired with a matching TTF_Quit() call. * * \returns true on success or false on failure; call SDL_GetError() for more * information. From b9b9bf133dadd6677926ecda2d31b5ace838559c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 19 Oct 2024 12:55:27 -0700 Subject: [PATCH 03/14] Added TTF_SetTextPosition() and TTF_GetTextPosition() This allows you to lay out multiple text objects within a single text area. Also switched text objects to always wrap on newlines. --- examples/showfont.c | 42 +++++- include/SDL3_ttf/SDL_textengine.h | 2 + include/SDL3_ttf/SDL_ttf.h | 145 ++++++++++++++++--- src/SDL_ttf.c | 227 ++++++++++++------------------ src/SDL_ttf.sym | 2 + 5 files changed, 262 insertions(+), 156 deletions(-) diff --git a/examples/showfont.c b/examples/showfont.c index fe804f49..fda33db4 100644 --- a/examples/showfont.c +++ b/examples/showfont.c @@ -105,6 +105,16 @@ static void DrawScene(Scene *scene) } } +static void AdjustTextOffset(TTF_Text *text, int xoffset, int yoffset) +{ + int x, y; + + TTF_GetTextPosition(text, &x, &y); + x += xoffset; + y += yoffset; + TTF_SetTextPosition(text, x, y); +} + static void HandleKeyDown(Scene *scene, SDL_Event *event) { int style, outline; @@ -197,16 +207,36 @@ static void HandleKeyDown(Scene *scene, SDL_Event *event) TTF_SetFontStyle(scene->font, style); break; + case SDLK_LEFT: + if (event->key.mod & SDL_KMOD_CTRL) { + AdjustTextOffset(scene->edit->text, -1, 0); + } + break; + + case SDLK_RIGHT: + if (event->key.mod & SDL_KMOD_CTRL) { + AdjustTextOffset(scene->edit->text, 1, 0); + } + break; + case SDLK_UP: - /* Increase font size */ - ptsize = TTF_GetFontSize(scene->font); - TTF_SetFontSize(scene->font, ptsize + 1.0f); + if (event->key.mod & SDL_KMOD_CTRL) { + AdjustTextOffset(scene->edit->text, 0, -1); + } else { + /* Increase font size */ + ptsize = TTF_GetFontSize(scene->font); + TTF_SetFontSize(scene->font, ptsize + 1.0f); + } break; case SDLK_DOWN: - /* Decrease font size */ - ptsize = TTF_GetFontSize(scene->font); - TTF_SetFontSize(scene->font, ptsize - 1.0f); + if (event->key.mod & SDL_KMOD_CTRL) { + AdjustTextOffset(scene->edit->text, 0, 1); + } else { + /* Decrease font size */ + ptsize = TTF_GetFontSize(scene->font); + TTF_SetFontSize(scene->font, ptsize - 1.0f); + } break; case SDLK_ESCAPE: diff --git a/include/SDL3_ttf/SDL_textengine.h b/include/SDL3_ttf/SDL_textengine.h index 0a50c704..99222e95 100644 --- a/include/SDL3_ttf/SDL_textengine.h +++ b/include/SDL3_ttf/SDL_textengine.h @@ -109,6 +109,8 @@ struct TTF_TextData bool needs_layout_update; /**< True if the layout needs to be updated */ TTF_TextLayout *layout; /**< Cached layout information, read-only. */ + int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */ + int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */ int w; /**< The width of this text, in pixels, read-only. */ int h; /**< The height of this text, in pixels, read-only. */ int num_ops; /**< The number of drawing operations to render this text, read-only. */ diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index e4dd12c2..19d73539 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -1472,12 +1472,7 @@ extern SDL_DECLSPEC void SDLCALL TTF_DestroyRendererTextEngine(TTF_TextEngine *e /** * Create a text object from UTF-8 text and a text engine. * - * This will not word-wrap the string; you'll get a surface with a single line - * of text, as long as the string requires. You can use - * TTF_CreateText_Wrapped() instead if you need to wrap the output to multiple - * lines. - * - * This will not wrap on newline characters. + * This function is equivalent to `TTF_CreateText_Wrapped(engine, font, text, 0)` and will wrap on newline characters. * * \param engine the text engine to use when creating the text object, may be * NULL. @@ -1534,7 +1529,10 @@ extern SDL_DECLSPEC TTF_Text * SDLCALL TTF_CreateText_Wrapped(TTF_TextEngine *en * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. * - * \since This function is available since SDL 3.0.0. + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL TTF_GetTextProperties(TTF_Text *text); @@ -1545,6 +1543,11 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL TTF_GetTextProperties(TTF_Text *tex * \param engine the text engine to use for drawing. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngine *engine); @@ -1554,6 +1557,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngin * \param text the TTF_Text to query. * \returns the TTF_TextEngine used by the text on success or NULL on failure; * call SDL_GetError() for more information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text); @@ -1566,6 +1574,11 @@ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text); * * \param text the TTF_Text to modify. * \param font the font to use, may be NULL. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font); @@ -1575,9 +1588,44 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font) * \param text the TTF_Text to query. * \returns the TTF_Font used by the text on success or NULL on failure; call * SDL_GetError() for more information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text); +/** + * Set the position of a text object. + * + * This can be used to position multiple text objects within a single wrapping text area. + * + * \param text the TTF_Text to modify. + * \param x the x offset of the upper left corner of this text in pixels. + * \param y the y offset of the upper left corner of this text in pixels. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + */ +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int y); + +/** + * Get the position of a text object. + * + * \param text the TTF_Text to query. + * \param x a pointer filled in with the x offset of the upper left corner of this text in pixels, may be NULL. + * \param y a pointer filled in with the y offset of the upper left corner of this text in pixels, may be NULL. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + */ +extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int *y); + /** * Set the UTF-8 text used by a text object. * @@ -1587,6 +1635,11 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text); * text. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *string, size_t length); @@ -1603,6 +1656,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *s * text. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t length); @@ -1615,6 +1673,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset * text. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char *string, size_t length); @@ -1630,6 +1693,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char * remainder of the string. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset, int length); @@ -1637,28 +1705,33 @@ extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset * Set whether wrapping is enabled on a text object. * * \param text the TTF_Text to modify. - * \param wrap true if wrapping should be enabled, false if it should be - * disabled. * \param wrapLength the maximum width in pixels, 0 to wrap on newline - * characters, or -1 to leave wrapLength unchanged. + * characters. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ -extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, bool wrap, int wrapLength); +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLength); /** * Get whether wrapping is enabled on a text object. * * \param text the TTF_Text to query. - * \param wrap a pointer filled in with true if wrapping is enabled, false if - * it is disabled, may be NULL. * \param wrapLength a pointer filled in with the maximum width in pixels or 0 - * if the text is being wrapped on newline characters, may - * be NULL. + * if the text is being wrapped on newline characters. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ -extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, bool *wrap, int *wrapLength); +extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLength); /** * Get the size of a text object. @@ -1673,6 +1746,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, bool *wrap, * NULL. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSize(TTF_Text *text, int *w, int *h); @@ -1727,6 +1805,11 @@ typedef struct TTF_SubString * offset. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubString(TTF_Text *text, int offset, TTF_SubString *substring); @@ -1745,6 +1828,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubString(TTF_Text *text, int offset * offset. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForLine(TTF_Text *text, int line, TTF_SubString *substring); @@ -1761,6 +1849,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForLine(TTF_Text *text, int * call SDL_GetError() for more information. This is a single * allocation that should be freed with SDL_free() when it is no * longer needed. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC TTF_SubString ** SDLCALL TTF_GetTextSubStringsForRange(TTF_Text *text, int offset, int length, int *count); @@ -1778,6 +1871,11 @@ extern SDL_DECLSPEC TTF_SubString ** SDLCALL TTF_GetTextSubStringsForRange(TTF_T * the given point. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForPoint(TTF_Text *text, int x, int y, TTF_SubString *substring); @@ -1791,6 +1889,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForPoint(TTF_Text *text, in * \param substring the TTF_SubString to query. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_GetPreviousTextSubString(TTF_Text *text, const TTF_SubString *substring, TTF_SubString *previous); @@ -1805,6 +1908,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetPreviousTextSubString(TTF_Text *text, co * \param next a pointer filled in with the next substring. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_GetNextTextSubString(TTF_Text *text, const TTF_SubString *substring, TTF_SubString *next); @@ -1818,6 +1926,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetNextTextSubString(TTF_Text *text, const * \param text the TTF_Text to update. * \returns true on success or false on failure; call SDL_GetError() for more * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. */ extern SDL_DECLSPEC bool SDLCALL TTF_UpdateText(TTF_Text *text); diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c index 9e6ef9c1..8ffe7f55 100644 --- a/src/SDL_ttf.c +++ b/src/SDL_ttf.c @@ -3121,6 +3121,10 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i char_count += 1; } if (cw >= measure_width) { + if (cw > measure_width) { + // The last character didn't fit + font->pos_len -= 1; + } break; } } @@ -3342,7 +3346,7 @@ static bool CharacterIsNewLine(Uint32 c) return false; } -static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int wrapLength, TTF_Line **lines, int *num_lines, int *w, int *h) +static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int xoffset, int wrapLength, TTF_Line **lines, int *num_lines, int *w, int *h) { int width, height; int i, numLines = 0, rowHeight; @@ -3403,51 +3407,61 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int strLines[numLines].length = left; ++numLines; - if (!TTF_MeasureString(font, spot, left, wrapLength, &extent, &max_count)) { + int measure_width = wrapLength; + if (measure_width > 0) { + measure_width = SDL_max(measure_width - xoffset, 1); + } + if (!TTF_MeasureString(font, spot, left, measure_width, &extent, &max_count)) { SDL_SetError("Error measure text"); goto done; } if (wrapLength != 0) { - if (max_count == 0) { + if (max_count == 0 && numLines > 1) { max_count = 1; } } - while (left > 0) { - int is_delim; - Uint32 c = SDL_StepUTF8((const char **)&spot, &left); + if (max_count > 0) { + while (left > 0) { + int is_delim; + Uint32 c = SDL_StepUTF8((const char **)&spot, &left); - if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { - continue; - } + if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { + continue; + } - char_count += 1; + char_count += 1; + + // With wrapLength == 0, normal text rendering but newline aware + is_delim = (wrapLength > 0) ? CharacterIsDelimiter(c) : CharacterIsNewLine(c); - // With wrapLength == 0, normal text rendering but newline aware - is_delim = (wrapLength > 0) ? CharacterIsDelimiter(c) : CharacterIsNewLine(c); + // Record last delimiter position + if (is_delim) { + save_text = spot; + save_length = left; + // Break, if new line + if (c == '\n' || (c == '\r' && *spot != '\n')) { + break; + } + } - // Record last delimiter position - if (is_delim) { - save_text = spot; - save_length = left; - // Break, if new line - if (c == '\n' || (c == '\r' && *spot != '\n')) { + // Break, if reach the limit + if (char_count == max_count) { break; } } - // Break, if reach the limit - if (char_count == max_count) { - break; + // Cut at last delimiter/new lines, otherwise in the middle of the word + if (save_text && left > 0) { + spot = save_text; + left = save_length; } } - // Cut at last delimiter/new lines, otherwise in the middle of the word - if (save_text && left > 0) { - spot = save_text; - left = save_length; - } + // First line is complete, start the next at offset 0 + xoffset = 0; + } while (left > 0); // Trim whitespace from the wrapped lines and newlines from unwrapped lines @@ -3522,7 +3536,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int bool TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrapLength, int *w, int *h) { - return GetWrappedLines(font, text, length, wrapLength, NULL, NULL, w, h); + return GetWrappedLines(font, text, length, 0, wrapLength, NULL, NULL, w, h); } static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength, const render_mode_t render_mode) @@ -3533,7 +3547,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text int i, numLines = 0; TTF_Line *strLines = NULL; - if (!GetWrappedLines(font, text, length, wrapLength, &strLines, &numLines, &width, &height)) { + if (!GetWrappedLines(font, text, length, 0, wrapLength, &strLines, &numLines, &width, &height)) { return NULL; } @@ -3637,7 +3651,6 @@ SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t struct TTF_TextLayout { - bool wrap; int wrap_length; int *lines; }; @@ -3649,7 +3662,7 @@ typedef struct TTF_InternalText TTF_TextLayout layout; } TTF_InternalText; -static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, bool wrap, int wrapLength) +static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength) { if (engine && engine->version < sizeof(*engine)) { // Update this to handle older versions of this interface @@ -3672,7 +3685,6 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char * result->color.a = 1.0f; result->internal->needs_layout_update = true; result->internal->engine = engine; - result->internal->layout->wrap = wrap; result->internal->layout->wrap_length = wrapLength; if (text && *text) { if (length == 0) { @@ -3696,12 +3708,12 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char * TTF_Text *TTF_CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length) { - return CreateText(engine, font, text, length, false, -1); + return CreateText(engine, font, text, length, 0); } TTF_Text *TTF_CreateText_Wrapped(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength) { - return CreateText(engine, font, text, length, true, wrapLength); + return CreateText(engine, font, text, length, wrapLength); } static int SDLCALL SortClusters(const void *a, const void *b) @@ -3796,81 +3808,6 @@ static int CalculateClusterLengths(TTF_Text *text, TTF_SubString *clusters, int } static bool LayoutText(TTF_Text *text) -{ - TTF_Font *font = text->internal->font; - size_t length = SDL_strlen(text->text); - TTF_DrawOperation *ops = NULL; - int num_ops = 0, max_ops; - TTF_SubString *clusters = NULL, *cluster; - int num_clusters = 0; - int xstart, ystart, width = 0, height = 0; - bool result = false; - - if (!TTF_Size_Internal(font, text->text, length, &width, &height, &xstart, &ystart, NO_MEASUREMENT) || !width) { - return true; - } - - max_ops = font->pos_len; - if (TTF_HANDLE_STYLE_UNDERLINE(font)) { - ++max_ops; - } - if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { - ++max_ops; - } - - ops = (TTF_DrawOperation *)SDL_calloc(max_ops, sizeof(*ops)); - if (!ops) { - goto done; - } - - clusters = (TTF_SubString *)SDL_calloc(font->num_clusters + 2, sizeof(*clusters)); - if (!clusters) { - goto done; - } - - // Create the text drawing operations - if (!Render_Line_TextEngine(font, xstart, ystart, width, height, ops, &num_ops, clusters, &num_clusters, 0, 0)) { - goto done; - } - - cluster = &clusters[num_clusters++]; - cluster->flags = TTF_SUBSTRING_LINE_END; - cluster->offset = (int)SDL_strlen(text->text); - - cluster = &clusters[num_clusters++]; - cluster->flags = TTF_SUBSTRING_TEXT_END; - cluster->offset = (int)SDL_strlen(text->text); - - // Apply underline or strikethrough style, if needed - if (TTF_HANDLE_STYLE_UNDERLINE(font)) { - Draw_Line_TextEngine(font, width, height, 0, ystart + font->underline_top_row, width, font->line_thickness, ops, &num_ops); - } - - if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { - Draw_Line_TextEngine(font, width, height, 0, ystart + font->strikethrough_top_row, width, font->line_thickness, ops, &num_ops); - } - - num_clusters = CalculateClusterLengths(text, clusters, num_clusters, length, NULL); - - result = true; - -done: - if (result) { - text->num_lines = 1; - text->internal->w = width; - text->internal->h = height; - text->internal->num_ops = num_ops; - text->internal->ops = ops; - text->internal->num_clusters = num_clusters; - text->internal->clusters = clusters; - } else { - SDL_free(ops); - SDL_free(clusters); - } - return result; -} - -static bool LayoutTextWrapped(TTF_Text *text) { TTF_Font *font = text->internal->font; int wrapLength = text->internal->layout->wrap_length; @@ -3884,9 +3821,10 @@ static bool LayoutTextWrapped(TTF_Text *text) int *lines = NULL; bool result = false; - if (!GetWrappedLines(font, text->text, length, wrapLength, &strLines, &numLines, &width, &height)) { + if (!GetWrappedLines(font, text->text, length, text->internal->x, wrapLength, &strLines, &numLines, &width, &height)) { return true; } + height += text->internal->y; if (TTF_HANDLE_STYLE_UNDERLINE(font)) { ++extra_ops; @@ -3931,6 +3869,8 @@ static bool LayoutTextWrapped(TTF_Text *text) // Move to i-th line ystart += i * font->lineskip; + ystart += text->internal->y; + // Control left/right/center align of each bit of text if (font->horizontal_align == TTF_HORIZONTAL_ALIGN_RIGHT) { xoffset = (width - line_width); @@ -3941,6 +3881,10 @@ static bool LayoutTextWrapped(TTF_Text *text) } xoffset = SDL_max(0, xoffset); + if (i == 0) { + xoffset += text->internal->x; + } + // Allocate space for the operations on this line additional_ops = (font->pos_len + extra_ops); new_ops = (TTF_DrawOperation *)SDL_realloc(ops, (max_ops + additional_ops) * sizeof(*new_ops)); @@ -4096,6 +4040,38 @@ TTF_Font *TTF_GetTextFont(TTF_Text *text) return text->internal->font; } +bool TTF_SetTextPosition(TTF_Text *text, int x, int y) +{ + TTF_CHECK_POINTER("text", text, false); + + if (x != text->internal->x || y != text->internal->y) { + text->internal->x = x; + text->internal->y = y; + text->internal->needs_layout_update = true; + } + return true; +} + +bool TTF_GetTextPosition(TTF_Text *text, int *x, int *y) +{ + if (x) { + *x = 0; + } + if (y) { + *y = 0; + } + + TTF_CHECK_POINTER("text", text, false); + + if (x) { + *x = text->internal->x; + } + if (y) { + *y = text->internal->y; + } + return true; +} + bool TTF_SetTextString(TTF_Text *text, const char *string, size_t length) { TTF_CHECK_POINTER("text", text, false); @@ -4218,38 +4194,27 @@ bool TTF_DeleteTextString(TTF_Text *text, int offset, int length) return true; } -bool TTF_SetTextWrapping(TTF_Text *text, bool wrap, int wrapLength) +bool TTF_SetTextWrapping(TTF_Text *text, int wrapLength) { TTF_CHECK_POINTER("text", text, false); - if (wrap == text->internal->layout->wrap && - (wrapLength < 0 || wrapLength == text->internal->layout->wrap_length)) { + if (wrapLength == text->internal->layout->wrap_length) { return true; } - text->internal->layout->wrap = wrap; - if (wrapLength >= 0) { - text->internal->layout->wrap_length = wrapLength; - } - + text->internal->layout->wrap_length = SDL_max(wrapLength, 0); text->internal->needs_layout_update = true; return true; } -bool TTF_GetTextWrapping(TTF_Text *text, bool *wrap, int *wrapLength) +bool TTF_GetTextWrapping(TTF_Text *text, int *wrapLength) { - if (wrap) { - *wrap = false; - } if (wrapLength) { - *wrapLength = -1; + *wrapLength = 0; } TTF_CHECK_POINTER("text", text, false); - if (wrap) { - *wrap = text->internal->layout->wrap; - } if (wrapLength) { *wrapLength = text->internal->layout->wrap_length; } @@ -4545,7 +4510,7 @@ bool TTF_GetTextSubStringForPoint(TTF_Text *text, int x, int y, TTF_SubString *s #endif const TTF_SubString *closest = NULL; int closest_dist = INT_MAX; - int wrap_cost = (text->internal->layout->wrap ? 100 : 1); + int wrap_cost = 100; SDL_Point point = { x, y }; for (int i = 0; i < text->internal->num_clusters; ++i) { const TTF_SubString *cluster = &text->internal->clusters[i]; @@ -4673,14 +4638,8 @@ bool TTF_UpdateText(TTF_Text *text) text->internal->h = 0; if (text->internal->font && text->text) { - if (text->internal->layout->wrap) { - if (!LayoutTextWrapped(text)) { - return false; - } - } else { - if (!LayoutText(text)) { - return false; - } + if (!LayoutText(text)) { + return false; } } diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym index dffea6e6..351142b8 100644 --- a/src/SDL_ttf.sym +++ b/src/SDL_ttf.sym @@ -45,6 +45,7 @@ SDL3_ttf_0.0.0 { TTF_GetStringSizeWrapped; TTF_GetTextEngine; TTF_GetTextFont; + TTF_GetTextPosition; TTF_GetTextSize; TTF_GetTextSubString; TTF_GetTextSubStringForLine; @@ -81,6 +82,7 @@ SDL3_ttf_0.0.0 { TTF_SetFontWrapAlignment; TTF_SetTextEngine; TTF_SetTextFont; + TTF_SetTextPosition; TTF_SetTextString; TTF_SetTextWrapping; TTF_UpdateText; From 7436b67f039167ab340c04d807ed1352f5881656 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 19 Oct 2024 13:01:15 -0700 Subject: [PATCH 04/14] Don't trim whitespace on the last line of text. Fixes https://github.com/libsdl-org/SDL_ttf/issues/414 --- src/SDL_ttf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c index 8ffe7f55..4add63b2 100644 --- a/src/SDL_ttf.c +++ b/src/SDL_ttf.c @@ -3475,7 +3475,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int if (line->text[line->length - 1] == '\r') { --line->length; } - } else { + } else if (i < (numLines - 1)) { while (line->length > 0 && CharacterIsDelimiter(line->text[line->length - 1])) { --line->length; From b3c1c658bc4857ea8fa08f62ef9fb5eeb46c31b6 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sat, 19 Oct 2024 20:14:44 +0000 Subject: [PATCH 05/14] Sync SDL3_ttf wiki -> header [ci skip] --- include/SDL3_ttf/SDL_ttf.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index 19d73539..1ccadbcc 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -116,8 +116,8 @@ typedef struct TTF_Font TTF_Font; * You must successfully call this function before it is safe to call any * other function in this library. * - * It is safe to call this more than once, and each successful TTF_Init() - * call should be paired with a matching TTF_Quit() call. + * It is safe to call this more than once, and each successful TTF_Init() call + * should be paired with a matching TTF_Quit() call. * * \returns true on success or false on failure; call SDL_GetError() for more * information. @@ -1472,7 +1472,8 @@ extern SDL_DECLSPEC void SDLCALL TTF_DestroyRendererTextEngine(TTF_TextEngine *e /** * Create a text object from UTF-8 text and a text engine. * - * This function is equivalent to `TTF_CreateText_Wrapped(engine, font, text, 0)` and will wrap on newline characters. + * This function is equivalent to `TTF_CreateText_Wrapped(engine, font, text, + * 0)` and will wrap on newline characters. * * \param engine the text engine to use when creating the text object, may be * NULL. @@ -1599,7 +1600,8 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text); /** * Set the position of a text object. * - * This can be used to position multiple text objects within a single wrapping text area. + * This can be used to position multiple text objects within a single wrapping + * text area. * * \param text the TTF_Text to modify. * \param x the x offset of the upper left corner of this text in pixels. @@ -1616,8 +1618,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int * Get the position of a text object. * * \param text the TTF_Text to query. - * \param x a pointer filled in with the x offset of the upper left corner of this text in pixels, may be NULL. - * \param y a pointer filled in with the y offset of the upper left corner of this text in pixels, may be NULL. + * \param x a pointer filled in with the x offset of the upper left corner of + * this text in pixels, may be NULL. + * \param y a pointer filled in with the y offset of the upper left corner of + * this text in pixels, may be NULL. * * \threadsafety This function should be called on the thread that created the * text. From 01ff187c2fca936083fb177c0f43a9a6748a5058 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 19 Oct 2024 19:48:58 -0700 Subject: [PATCH 06/14] Added TTF_SetTextWrapWhitespaceVisible() and TTF_TextWrapWhitespaceVisible() When you're editing text, you want whitespace to be visible. When you're rendering it, you normally want it to be eliminated around line breaks. This is the default, to match previous rendering behavior. Fixes https://github.com/libsdl-org/SDL_ttf/issues/414 --- examples/editbox.c | 5 +++- include/SDL3_ttf/SDL_ttf.h | 30 ++++++++++++++++++++ src/SDL_ttf.c | 58 +++++++++++++++++++++++++++++++++----- src/SDL_ttf.sym | 2 ++ 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/examples/editbox.c b/examples/editbox.c index b2028178..8cd239e9 100644 --- a/examples/editbox.c +++ b/examples/editbox.c @@ -218,7 +218,7 @@ static void SaveCandidates(EditBox *edit, const SDL_Event *event) } *dst = '\0'; - edit->candidates = TTF_CreateText_Wrapped(TTF_GetTextEngine(edit->text), edit->font, candidate_text, 0, 0); + edit->candidates = TTF_CreateText(TTF_GetTextEngine(edit->text), edit->font, candidate_text, 0); SDL_free(candidate_text); if (edit->candidates) { SDL_copyp(&edit->candidates->color, &edit->text->color); @@ -342,6 +342,9 @@ EditBox *EditBox_Create(SDL_Window *window, SDL_Renderer *renderer, TTF_TextEngi edit->highlight1 = -1; edit->highlight2 = -1; + /* Show the whitespace when wrapping, so it can be edited */ + TTF_SetTextWrapWhitespaceVisible(edit->text, true); + #ifdef TEST_SURFACE_ENGINE /* Grab the window surface if we want to test the surface text engine. * This isn't strictly necessary, we can still use the renderer if it's diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index 1ccadbcc..f78f22fb 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -1737,6 +1737,36 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLen */ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLength); +/** + * Set whether whitespace should be visible when wrapping a text object. + * + * If the whitespace is visible, it will take up space for purposes of alignment and wrapping. This is good for editing, but looks better when centered or aligned if whitespace around line wrapping is hidden. This defaults false. + * + * \param text the TTF_Text to modify. + * \param visible true to show whitespace when wrapping text, false to hide it. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + */ +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible); + +/** + * Return whether whitespace is shown when wrapping a text object. + * + * \param text the TTF_Text to query. + * \returns true if whitespace is shown when wrapping text, or false otherwise. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + */ +extern SDL_DECLSPEC bool SDLCALL TTF_TextWrapWhitespaceVisible(TTF_Text *text); + /** * Get the size of a text object. * diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c index 4add63b2..ec342eec 100644 --- a/src/SDL_ttf.c +++ b/src/SDL_ttf.c @@ -3346,7 +3346,7 @@ static bool CharacterIsNewLine(Uint32 c) return false; } -static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int xoffset, int wrapLength, TTF_Line **lines, int *num_lines, int *w, int *h) +static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int xoffset, int wrapLength, bool trim_whitespace, TTF_Line **lines, int *num_lines, int *w, int *h) { int width, height; int i, numLines = 0, rowHeight; @@ -3400,6 +3400,19 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int strLines = new_lines; } + if (trim_whitespace && spot > text && spot[-1] != '\n') { + const char *next_spot = spot; + size_t next_left = left; + for (;;) { + Uint32 c = SDL_StepUTF8(&next_spot, &next_left); + if (c == 0 || (c != ' ' && c != '\t')) { + break; + } + spot = next_spot; + left = next_left; + } + } + if (numLines > 0) { strLines[numLines - 1].length = spot - strLines[numLines - 1].text; } @@ -3417,6 +3430,9 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int } if (wrapLength != 0) { + // The first line can be empty if we have a text position that's + // at the edge of the wrap length, but subsequent lines should have + // at least one character per line. if (max_count == 0 && numLines > 1) { max_count = 1; } @@ -3425,7 +3441,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int if (max_count > 0) { while (left > 0) { int is_delim; - Uint32 c = SDL_StepUTF8((const char **)&spot, &left); + Uint32 c = SDL_StepUTF8(&spot, &left); if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { continue; @@ -3464,18 +3480,24 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int } while (left > 0); - // Trim whitespace from the wrapped lines and newlines from unwrapped lines for (i = 0; i < numLines; ++i) { TTF_Line *line = &strLines[i]; if (line->length == 0) { continue; } + + // The line doesn't include any delimiter that caused it to be wrapped. if (CharacterIsNewLine(line->text[line->length - 1])) { --line->length; if (line->text[line->length - 1] == '\r') { --line->length; } - } else if (i < (numLines - 1)) { + } else if (i < (numLines - 1) && + CharacterIsDelimiter(line->text[line->length - 1])) { + --line->length; + } + + if (trim_whitespace) { while (line->length > 0 && CharacterIsDelimiter(line->text[line->length - 1])) { --line->length; @@ -3536,7 +3558,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int bool TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrapLength, int *w, int *h) { - return GetWrappedLines(font, text, length, 0, wrapLength, NULL, NULL, w, h); + return GetWrappedLines(font, text, length, 0, wrapLength, true, NULL, NULL, w, h); } static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength, const render_mode_t render_mode) @@ -3547,7 +3569,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text int i, numLines = 0; TTF_Line *strLines = NULL; - if (!GetWrappedLines(font, text, length, 0, wrapLength, &strLines, &numLines, &width, &height)) { + if (!GetWrappedLines(font, text, length, 0, wrapLength, true, &strLines, &numLines, &width, &height)) { return NULL; } @@ -3652,6 +3674,7 @@ SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t struct TTF_TextLayout { int wrap_length; + bool wrap_whitespace_visible; int *lines; }; @@ -3811,6 +3834,7 @@ static bool LayoutText(TTF_Text *text) { TTF_Font *font = text->internal->font; int wrapLength = text->internal->layout->wrap_length; + bool trim_whitespace = !text->internal->layout->wrap_whitespace_visible; size_t length = SDL_strlen(text->text); int i, width = 0, height = 0, numLines = 0; TTF_Line *strLines = NULL; @@ -3821,7 +3845,7 @@ static bool LayoutText(TTF_Text *text) int *lines = NULL; bool result = false; - if (!GetWrappedLines(font, text->text, length, text->internal->x, wrapLength, &strLines, &numLines, &width, &height)) { + if (!GetWrappedLines(font, text->text, length, text->internal->x, wrapLength, trim_whitespace, &strLines, &numLines, &width, &height)) { return true; } height += text->internal->y; @@ -4221,6 +4245,26 @@ bool TTF_GetTextWrapping(TTF_Text *text, int *wrapLength) return true; } +bool TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible) +{ + TTF_CHECK_POINTER("text", text, false); + + if (visible == text->internal->layout->wrap_whitespace_visible) { + return true; + } + + text->internal->layout->wrap_whitespace_visible = visible; + text->internal->needs_layout_update = true; + return true; +} + +bool TTF_GetTextWrapSpaceTrimming(TTF_Text *text) +{ + TTF_CHECK_POINTER("text", text, false); + + return text->internal->layout->wrap_whitespace_visible; +} + bool TTF_GetTextSize(TTF_Text *text, int *w, int *h) { if (w) { diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym index 351142b8..c5bb0e1b 100644 --- a/src/SDL_ttf.sym +++ b/src/SDL_ttf.sym @@ -84,7 +84,9 @@ SDL3_ttf_0.0.0 { TTF_SetTextFont; TTF_SetTextPosition; TTF_SetTextString; + TTF_SetTextWrapWhitespaceVisible; TTF_SetTextWrapping; + TTF_TextWrapWhitespaceVisible; TTF_UpdateText; TTF_Version; TTF_WasInit; From cb0e12a6157556aad7e2aa229e9e8bc6bd386700 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 19 Oct 2024 20:32:57 -0700 Subject: [PATCH 07/14] Added TTF_SetTextColor() and TTF_GetTextColor() People expected there to be an API function to set the text color. Voila! --- examples/editbox.c | 4 +- examples/showfont.c | 5 +- include/SDL3_ttf/SDL_textengine.h | 1 + include/SDL3_ttf/SDL_ttf.h | 125 +++++++++++++++++++++++++++++- src/SDL_renderer_textengine.c | 2 +- src/SDL_surface_textengine.c | 10 +-- src/SDL_ttf.c | 100 +++++++++++++++++++++++- src/SDL_ttf.sym | 4 + 8 files changed, 235 insertions(+), 16 deletions(-) diff --git a/examples/editbox.c b/examples/editbox.c index 8cd239e9..f5f701a9 100644 --- a/examples/editbox.c +++ b/examples/editbox.c @@ -221,7 +221,9 @@ static void SaveCandidates(EditBox *edit, const SDL_Event *event) edit->candidates = TTF_CreateText(TTF_GetTextEngine(edit->text), edit->font, candidate_text, 0); SDL_free(candidate_text); if (edit->candidates) { - SDL_copyp(&edit->candidates->color, &edit->text->color); + float r, g, b, a; + TTF_GetTextColorFloat(edit->text, &r, &g, &b, &a); + TTF_SetTextColorFloat(edit->candidates, r, g, b, a); } else { ClearCandidates(edit); } diff --git a/examples/showfont.c b/examples/showfont.c index fda33db4..9ff9c6d0 100644 --- a/examples/showfont.c +++ b/examples/showfont.c @@ -550,10 +550,7 @@ int main(int argc, char *argv[]) editRect.w -= 8.0f; scene.edit = EditBox_Create(scene.window, scene.renderer, engine, font, &editRect); if (scene.edit) { - scene.edit->text->color.r = forecol->r / 255.0f; - scene.edit->text->color.g = forecol->g / 255.0f; - scene.edit->text->color.b = forecol->b / 255.0f; - scene.edit->text->color.a = forecol->a / 255.0f; + TTF_SetTextColor(scene.edit->text, forecol->r, forecol->g, forecol->b, forecol->a); EditBox_Insert(scene.edit, message); } diff --git a/include/SDL3_ttf/SDL_textengine.h b/include/SDL3_ttf/SDL_textengine.h index 99222e95..0bf12399 100644 --- a/include/SDL3_ttf/SDL_textengine.h +++ b/include/SDL3_ttf/SDL_textengine.h @@ -106,6 +106,7 @@ typedef struct TTF_TextLayout TTF_TextLayout; struct TTF_TextData { TTF_Font *font; /**< The font used by this text, read-only. */ + SDL_FColor color; /**< The color of the text, read-only. */ bool needs_layout_update; /**< True if the layout needs to be updated */ TTF_TextLayout *layout; /**< Cached layout information, read-only. */ diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index f78f22fb..3dcce873 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -1341,7 +1341,6 @@ typedef struct TTF_TextData TTF_TextData; typedef struct TTF_Text { char *text; /**< A copy of the text used to create this text object, useful for layout and debugging. This will be freed automatically when the object is destroyed. */ - SDL_FColor color; /**< The color of the text, read-write. You can change this anytime. */ int num_lines; /**< The number of lines in the text, 0 if it's empty */ int refcount; /**< Application reference count, used when freeing surface */ @@ -1549,6 +1548,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL TTF_GetTextProperties(TTF_Text *tex * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextEngine */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngine *engine); @@ -1563,6 +1564,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngin * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_SetTextEngine */ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text); @@ -1580,6 +1583,8 @@ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text); * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextFont */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font); @@ -1594,9 +1599,99 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font) * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_SetTextFont */ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text); +/** + * Set the color of a text object. + * + * The default text color is white (255, 255, 255, 255). + * + * \param text the TTF_Text to modify. + * \param r the red color value in the range of 0-255. + * \param g the green color value in the range of 0-255. + * \param b the blue color value in the range of 0-255. + * \param a the alpha value in the range of 0-255. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextColor + * \sa TTF_SetTextColorFloat + */ +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextColor(TTF_Text *text, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/** + * Set the color of a text object. + * + * The default text color is white (1.0f, 1.0f, 1.0f, 1.0f). + * + * \param text the TTF_Text to modify. + * \param r the red color value, normally in the range of 0-1. + * \param g the green color value, normally in the range of 0-1. + * \param b the blue color value, normally in the range of 0-1. + * \param a the alpha value in the range of 0-1. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextColorFloat + * \sa TTF_SetTextColor + */ +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextColorFloat(TTF_Text *text, float r, float g, float b, float a); + +/** + * Get the color of a text object. + * + * \param text the TTF_Text to query. + * \param r a pointer filled in with the red color value in the range of 0-255, may be NULL. + * \param g a pointer filled in with the green color value in the range of 0-255, may be NULL. + * \param b a pointer filled in with the blue color value in the range of 0-255, may be NULL. + * \param a a pointer filled in with the alpha value in the range of 0-255, may be NULL. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextColorFloat + * \sa TTF_SetTextColor + */ +extern SDL_DECLSPEC bool SDLCALL TTF_GetTextColor(TTF_Text *text, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a); + +/** + * Get the color of a text object. + * + * \param text the TTF_Text to query. + * \param r a pointer filled in with the red color value, normally in the range of 0-1, may be NULL. + * \param g a pointer filled in with the green color value, normally in the range of 0-1, may be NULL. + * \param b a pointer filled in with the blue color value, normally in the range of 0-1, may be NULL. + * \param a a pointer filled in with the alpha value in the range of 0-1, may be NULL. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should be called on the thread that created the + * text. + * + * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextColor + * \sa TTF_SetTextColorFloat + */ +extern SDL_DECLSPEC bool SDLCALL TTF_GetTextColorFloat(TTF_Text *text, float *r, float *g, float *b, float *a); + /** * Set the position of a text object. * @@ -1611,6 +1706,8 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text); * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextPosition */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int y); @@ -1627,6 +1724,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_SetTextPosition */ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int *y); @@ -1644,6 +1743,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_AppendTextString + * \sa TTF_DeleteTextString + * \sa TTF_InsertTextString */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *string, size_t length); @@ -1665,6 +1768,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *s * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_AppendTextString + * \sa TTF_DeleteTextString + * \sa TTF_SetTextString */ extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t length); @@ -1682,6 +1789,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_DeleteTextString + * \sa TTF_InsertTextString + * \sa TTF_SetTextString */ extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char *string, size_t length); @@ -1702,6 +1813,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_AppendTextString + * \sa TTF_InsertTextString + * \sa TTF_SetTextString */ extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset, int length); @@ -1718,6 +1833,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_GetTextWrapping */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLength); @@ -1734,6 +1851,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLen * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_SetTextWrapping */ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLength); @@ -1751,6 +1870,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLe * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_TextWrapWhitespaceVisible */ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible); @@ -1764,6 +1885,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text * text. * * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_SetTextWrapWhitespaceVisible */ extern SDL_DECLSPEC bool SDLCALL TTF_TextWrapWhitespaceVisible(TTF_Text *text); diff --git a/src/SDL_renderer_textengine.c b/src/SDL_renderer_textengine.c index 386597dc..d8e53663 100644 --- a/src/SDL_renderer_textengine.c +++ b/src/SDL_renderer_textengine.c @@ -887,7 +887,7 @@ bool TTF_DrawRendererText(TTF_Text *text, float x, float y) SDL_RenderGeometryRaw(renderer, sequence->texture, sequence->positions, 2 * sizeof(float), - &text->color, 0, + &text->internal->color, 0, sequence->texcoords, 2 * sizeof(float), sequence->num_rects * 4, sequence->indices, sequence->num_rects * 6, sizeof(*sequence->indices)); diff --git a/src/SDL_surface_textengine.c b/src/SDL_surface_textengine.c index f8a70754..8730a276 100644 --- a/src/SDL_surface_textengine.c +++ b/src/SDL_surface_textengine.c @@ -344,11 +344,11 @@ bool TTF_DrawSurfaceText(TTF_Text *text, int x, int y, SDL_Surface *surface) return true; } - if (text->color.r != data->fcolor.r || - text->color.g != data->fcolor.g || - text->color.b != data->fcolor.b || - text->color.a != data->fcolor.a) { - UpdateColor(data, &text->color); + if (text->internal->color.r != data->fcolor.r || + text->internal->color.g != data->fcolor.g || + text->internal->color.b != data->fcolor.b || + text->internal->color.a != data->fcolor.a) { + UpdateColor(data, &text->internal->color); } for (int i = 0; i < data->num_ops; ++i) { diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c index ec342eec..37b04bb3 100644 --- a/src/SDL_ttf.c +++ b/src/SDL_ttf.c @@ -3702,10 +3702,10 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char * result->internal = &mem->internal; result->internal->layout = &mem->layout; result->internal->font = font; - result->color.r = 1.0f; - result->color.g = 1.0f; - result->color.b = 1.0f; - result->color.a = 1.0f; + result->internal->color.r = 1.0f; + result->internal->color.g = 1.0f; + result->internal->color.b = 1.0f; + result->internal->color.a = 1.0f; result->internal->needs_layout_update = true; result->internal->engine = engine; result->internal->layout->wrap_length = wrapLength; @@ -4064,6 +4064,98 @@ TTF_Font *TTF_GetTextFont(TTF_Text *text) return text->internal->font; } +bool TTF_SetTextColor(TTF_Text *text, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + const float fR = (float)r / 255.0f; + const float fG = (float)g / 255.0f; + const float fB = (float)b / 255.0f; + const float fA = (float)a / 255.0f; + + return TTF_SetTextColorFloat(text, fR, fG, fB, fA); +} + +bool TTF_SetTextColorFloat(TTF_Text *text, float r, float g, float b, float a) +{ + TTF_CHECK_POINTER("text", text, false); + + text->internal->color.r = r; + text->internal->color.g = g; + text->internal->color.b = b; + text->internal->color.a = a; + return true; +} + +bool TTF_GetTextColor(TTF_Text *text, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) +{ + float fR = 1.0f, fG = 1.0f, fB = 1.0f, fA = 1.0f; + + if (!TTF_GetTextColorFloat(text, &fR, &fG, &fB, &fA)) { + if (r) { + *r = 255; + } + if (g) { + *g = 255; + } + if (b) { + *b = 255; + } + if (b) { + *b = 255; + } + return false; + } + + if (r) { + *r = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f); + } + if (g) { + *g = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f); + } + if (b) { + *b = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f); + } + if (a) { + *a = (Uint8)SDL_roundf(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f); + } + return true; +} + +bool TTF_GetTextColorFloat(TTF_Text *text, float *r, float *g, float *b, float *a) +{ + SDL_FColor color; + + if (r) { + *r = 1.0f; + } + if (g) { + *g = 1.0f; + } + if (b) { + *b = 1.0f; + } + if (a) { + *a = 1.0f; + } + + TTF_CHECK_POINTER("text", text, false); + + color = text->internal->color; + + if (r) { + *r = color.r; + } + if (g) { + *g = color.g; + } + if (b) { + *b = color.b; + } + if (a) { + *a = color.a; + } + return true; +} + bool TTF_SetTextPosition(TTF_Text *text, int x, int y) { TTF_CHECK_POINTER("text", text, false); diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym index c5bb0e1b..4aab9e0f 100644 --- a/src/SDL_ttf.sym +++ b/src/SDL_ttf.sym @@ -43,6 +43,8 @@ SDL3_ttf_0.0.0 { TTF_GetPreviousTextSubString; TTF_GetStringSize; TTF_GetStringSizeWrapped; + TTF_GetTextColor; + TTF_GetTextColorFloat; TTF_GetTextEngine; TTF_GetTextFont; TTF_GetTextPosition; @@ -80,6 +82,8 @@ SDL3_ttf_0.0.0 { TTF_SetFontSizeDPI; TTF_SetFontStyle; TTF_SetFontWrapAlignment; + TTF_SetTextColor; + TTF_SetTextColorFloat; TTF_SetTextEngine; TTF_SetTextFont; TTF_SetTextPosition; From e7e333e7727a2084a60401c7b64e1b1148288d0d Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Sun, 20 Oct 2024 14:33:50 +0300 Subject: [PATCH 08/14] cmake: require C99 for example programs, as they use C99 features. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 09717ce6..131ec3fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -380,6 +380,9 @@ if(SDLTTF_SAMPLES) sdl_add_warning_options(${prog} WARNING_AS_ERROR ${SDLTTF_WERROR}) target_link_libraries(${prog} PRIVATE SDL3_ttf::${sdl3_ttf_target_name}) target_link_libraries(${prog} PRIVATE ${sdl3_target_name}) + if("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES) + target_compile_features(${prog} PRIVATE c_std_99) + endif() if(SDLTTF_SAMPLES_INSTALL) install(TARGETS ${prog} From ecf0069127225a8570ae721a5e0b096e46c16c14 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sun, 20 Oct 2024 12:35:27 +0000 Subject: [PATCH 09/14] Sync SDL3_ttf wiki -> header [ci skip] --- include/SDL3_ttf/SDL_ttf.h | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index 3dcce873..3671fd08 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -1654,10 +1654,14 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextColorFloat(TTF_Text *text, float r, * Get the color of a text object. * * \param text the TTF_Text to query. - * \param r a pointer filled in with the red color value in the range of 0-255, may be NULL. - * \param g a pointer filled in with the green color value in the range of 0-255, may be NULL. - * \param b a pointer filled in with the blue color value in the range of 0-255, may be NULL. - * \param a a pointer filled in with the alpha value in the range of 0-255, may be NULL. + * \param r a pointer filled in with the red color value in the range of + * 0-255, may be NULL. + * \param g a pointer filled in with the green color value in the range of + * 0-255, may be NULL. + * \param b a pointer filled in with the blue color value in the range of + * 0-255, may be NULL. + * \param a a pointer filled in with the alpha value in the range of 0-255, + * may be NULL. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1675,10 +1679,14 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextColor(TTF_Text *text, Uint8 *r, Uint * Get the color of a text object. * * \param text the TTF_Text to query. - * \param r a pointer filled in with the red color value, normally in the range of 0-1, may be NULL. - * \param g a pointer filled in with the green color value, normally in the range of 0-1, may be NULL. - * \param b a pointer filled in with the blue color value, normally in the range of 0-1, may be NULL. - * \param a a pointer filled in with the alpha value in the range of 0-1, may be NULL. + * \param r a pointer filled in with the red color value, normally in the + * range of 0-1, may be NULL. + * \param g a pointer filled in with the green color value, normally in the + * range of 0-1, may be NULL. + * \param b a pointer filled in with the blue color value, normally in the + * range of 0-1, may be NULL. + * \param a a pointer filled in with the alpha value in the range of 0-1, may + * be NULL. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1859,10 +1867,14 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLe /** * Set whether whitespace should be visible when wrapping a text object. * - * If the whitespace is visible, it will take up space for purposes of alignment and wrapping. This is good for editing, but looks better when centered or aligned if whitespace around line wrapping is hidden. This defaults false. + * If the whitespace is visible, it will take up space for purposes of + * alignment and wrapping. This is good for editing, but looks better when + * centered or aligned if whitespace around line wrapping is hidden. This + * defaults false. * * \param text the TTF_Text to modify. - * \param visible true to show whitespace when wrapping text, false to hide it. + * \param visible true to show whitespace when wrapping text, false to hide + * it. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1879,7 +1891,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text * Return whether whitespace is shown when wrapping a text object. * * \param text the TTF_Text to query. - * \returns true if whitespace is shown when wrapping text, or false otherwise. + * \returns true if whitespace is shown when wrapping text, or false + * otherwise. * * \threadsafety This function should be called on the thread that created the * text. From 3adac8ac7ca3be6a5f73a23d930c9f7449f99189 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 20 Oct 2024 16:01:36 -0700 Subject: [PATCH 10/14] Don't use linear interpolation for text We're not scaling it, so nearest sampling will give the clearest text. --- src/SDL_renderer_textengine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SDL_renderer_textengine.c b/src/SDL_renderer_textengine.c index d8e53663..8ae21e2e 100644 --- a/src/SDL_renderer_textengine.c +++ b/src/SDL_renderer_textengine.c @@ -193,6 +193,7 @@ static AtlasTexture *CreateAtlas(SDL_Renderer *renderer) DestroyAtlas(atlas); return NULL; } + SDL_SetTextureScaleMode(atlas->texture, SDL_SCALEMODE_NEAREST); int num_nodes = ATLAS_TEXTURE_SIZE / 4; atlas->packing_nodes = (stbrp_node *)SDL_calloc(num_nodes, sizeof(*atlas->packing_nodes)); From 40e606fb1e83d632139185d8672b34d2db0a629d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 21 Oct 2024 02:42:09 -0700 Subject: [PATCH 11/14] Cleanup and polish on the new TTF_Text API * showfont uses the API to show the caption * showfont now has an -editbox command line option to show the editbox * TTF_MeasureString() returns width in pixels and max_length in bytes * Removed TTF_CreateText_Wrapped() * TTF_SetTextWrapping() has been renamed TTF_SetTextWrapWidth() * TTF_GetTextWrapping() has been renamed TTF_GetTextWrapWidth() Fixes https://github.com/libsdl-org/SDL_ttf/issues/418 --- examples/editbox.c | 5 +- examples/showfont.c | 116 ++++++++-------- examples/testapp.c | 6 +- include/SDL3_ttf/SDL_ttf.h | 219 +++++++++++++---------------- src/SDL_ttf.c | 273 ++++++++++++++++--------------------- src/SDL_ttf.sym | 5 +- 6 files changed, 277 insertions(+), 347 deletions(-) diff --git a/examples/editbox.c b/examples/editbox.c index f5f701a9..53f1449a 100644 --- a/examples/editbox.c +++ b/examples/editbox.c @@ -335,7 +335,7 @@ EditBox *EditBox_Create(SDL_Window *window, SDL_Renderer *renderer, TTF_TextEngi edit->window = window; edit->renderer = renderer; edit->font = font; - edit->text = TTF_CreateText_Wrapped(engine, font, NULL, 0, (int)SDL_floorf(rect->w)); + edit->text = TTF_CreateText(engine, font, NULL, 0); if (!edit->text) { EditBox_Destroy(edit); return NULL; @@ -344,6 +344,9 @@ EditBox *EditBox_Create(SDL_Window *window, SDL_Renderer *renderer, TTF_TextEngi edit->highlight1 = -1; edit->highlight2 = -1; + /* Wrap the editbox text within the editbox area */ + TTF_SetTextWrapWidth(edit->text, (int)SDL_floorf(rect->w)); + /* Show the whitespace when wrapping, so it can be edited */ TTF_SetTextWrapWhitespaceVisible(edit->text, true); diff --git a/examples/showfont.c b/examples/showfont.c index 9ff9c6d0..244abad1 100644 --- a/examples/showfont.c +++ b/examples/showfont.c @@ -40,7 +40,7 @@ #define TTF_SHOWFONT_USAGE \ -"Usage: %s [-textengine surface|renderer] [-shaded] [-blended] [-wrapped] [-b] [-i] [-u] [-s] [-outline size] [-hintlight|-hintmono|-hintnone] [-nokerning] [-wrap] [-align left|center|right] [-fgcol r,g,b,a] [-bgcol r,g,b,a] .ttf [ptsize] [text]\n" +"Usage: %s [-textengine surface|renderer] [-shaded] [-blended] [-wrapped] [-b] [-i] [-u] [-s] [-outline size] [-hintlight|-hintmono|-hintnone] [-nokerning] [-wrap] [-align left|center|right] [-fgcol r,g,b,a] [-bgcol r,g,b,a] [-editbox] .ttf [ptsize] [text]\n" typedef enum { @@ -61,8 +61,8 @@ typedef struct { SDL_Surface *window_surface; SDL_Renderer *renderer; TTF_Font *font; - SDL_Texture *caption; - SDL_FRect captionRect; + TTF_Text *caption; + SDL_Rect captionRect; SDL_Texture *message; SDL_FRect messageRect; TextEngine textEngine; @@ -96,7 +96,20 @@ static void DrawScene(Scene *scene) EditBox_Draw(scene->edit); } - SDL_RenderTexture(renderer, scene->caption, NULL, &scene->captionRect); + switch (scene->textEngine) { + case TextEngineSurface: + /* Flush the renderer so we can draw directly to the window surface */ + SDL_FlushRenderer(renderer); + TTF_DrawSurfaceText(scene->caption, scene->captionRect.w, scene->captionRect.h, scene->window_surface); + break; + case TextEngineRenderer: + TTF_DrawRendererText(scene->caption, (float)scene->captionRect.x, (float)scene->captionRect.y); + break; + default: + SDL_assert(!"Unknown text engine"); + break; + } + SDL_RenderTexture(renderer, scene->message, NULL, &scene->messageRect); SDL_RenderPresent(renderer); @@ -269,26 +282,20 @@ int main(int argc, char *argv[]) SDL_Color *backcol; SDL_Event event; TTF_TextEngine *engine = NULL; - TextRenderMethod rendermethod; - int renderstyle; - int outline; - int hinting; - int kerning; - int wrap; + TextRenderMethod rendermethod = TextRenderShaded; + int renderstyle = TTF_STYLE_NORMAL; + int outline = 0; + int hinting = TTF_HINTING_NORMAL; + int kerning = 1; + bool wrap = false; TTF_HorizontalAlignment align = TTF_HORIZONTAL_ALIGN_LEFT; - int dump; + bool editbox = false; + bool dump = false; char *message, string[128]; - /* Look for special execution mode */ - dump = 0; - /* Look for special rendering types */ SDL_zero(scene); - rendermethod = TextRenderShaded; - renderstyle = TTF_STYLE_NORMAL; - outline = 0; - hinting = TTF_HINTING_NORMAL; - kerning = 1; - wrap = 0; + scene.textEngine = TextEngineRenderer; + /* Default is black and white */ forecol = &black; backcol = &white; @@ -341,7 +348,7 @@ int main(int argc, char *argv[]) kerning = 0; } else if (SDL_strcmp(argv[i], "-wrap") == 0) { - wrap = 1; + wrap = true; } else if (SDL_strcmp(argv[i], "-align") == 0 && argv[i+1]) { ++i; @@ -356,9 +363,6 @@ int main(int argc, char *argv[]) return (1); } } else - if (SDL_strcmp(argv[i], "-dump") == 0) { - dump = 1; - } else if (SDL_strcmp(argv[i], "-fgcol") == 0 && argv[i+1]) { int r, g, b, a = SDL_ALPHA_OPAQUE; if (SDL_sscanf(argv[++i], "%d,%d,%d,%d", &r, &g, &b, &a) < 3) { @@ -380,6 +384,12 @@ int main(int argc, char *argv[]) backcol->g = (Uint8)g; backcol->b = (Uint8)b; backcol->a = (Uint8)a; + } else + if (SDL_strcmp(argv[i], "-editbox") == 0) { + editbox = true; + } else + if (SDL_strcmp(argv[i], "-dump") == 0) { + dump = true; } else { SDL_Log(TTF_SHOWFONT_USAGE, argv0); return(1); @@ -467,24 +477,32 @@ int main(int argc, char *argv[]) Cleanup(2); } - /* Show which font file we're looking at */ - SDL_snprintf(string, sizeof(string), "Font file: %s", argv[0]); /* possible overflow */ - switch (rendermethod) { - case TextRenderShaded: - text = TTF_RenderText_Shaded(font, string, 0, *forecol, *backcol); + switch (scene.textEngine) { + case TextEngineSurface: + engine = TTF_CreateSurfaceTextEngine(); + if (!engine) { + SDL_Log("Couldn't create surface text engine: %s\n", SDL_GetError()); + Cleanup(2); + } break; - case TextRenderBlended: - text = TTF_RenderText_Blended(font, string, 0, *forecol); + case TextEngineRenderer: + engine = TTF_CreateRendererTextEngine(scene.renderer); + if (!engine) { + SDL_Log("Couldn't create renderer text engine: %s\n", SDL_GetError()); + Cleanup(2); + } + break; + default: break; } - if (text != NULL) { - scene.captionRect.x = 4.0f; - scene.captionRect.y = 4.0f; - scene.captionRect.w = (float)text->w; - scene.captionRect.h = (float)text->h; - scene.caption = SDL_CreateTextureFromSurface(scene.renderer, text); - SDL_DestroySurface(text); - } + + /* Show which font file we're looking at */ + SDL_snprintf(string, sizeof(string), "Font file: %s", argv[0]); /* possible overflow */ + scene.caption = TTF_CreateText(engine, font, string, 0); + TTF_SetTextColor(scene.caption, forecol->r, forecol->g, forecol->b, forecol->a); + scene.captionRect.x = 4; + scene.captionRect.y = 4; + TTF_GetTextSize(scene.caption, &scene.captionRect.w, &scene.captionRect.h); /* Render and center the message */ if (argc > 2) { @@ -521,23 +539,7 @@ int main(int argc, char *argv[]) SDL_Log("Font is generally %d big, and string is %d big\n", TTF_GetFontHeight(font), text->h); - switch (scene.textEngine) { - case TextEngineSurface: - engine = TTF_CreateSurfaceTextEngine(); - if (!engine) { - SDL_Log("Couldn't create surface text engine: %s\n", SDL_GetError()); - } - break; - case TextEngineRenderer: - engine = TTF_CreateRendererTextEngine(scene.renderer); - if (!engine) { - SDL_Log("Couldn't create renderer text engine: %s\n", SDL_GetError()); - } - break; - default: - break; - } - if (engine) { + if (editbox) { scene.textRect.x = 8.0f; scene.textRect.y = scene.captionRect.y + scene.captionRect.h + 4.0f; scene.textRect.w = WIDTH / 2 - scene.textRect.x * 2; @@ -601,7 +603,7 @@ int main(int argc, char *argv[]) default: break; } - SDL_DestroyTexture(scene.caption); + TTF_DestroyText(scene.caption); SDL_DestroyTexture(scene.message); Cleanup(0); diff --git a/examples/testapp.c b/examples/testapp.c index 7950b276..57cd4590 100644 --- a/examples/testapp.c +++ b/examples/testapp.c @@ -1064,9 +1064,11 @@ int main(void) break; } } else if (textengine_mode == 1) { - text_obj = TTF_CreateText_Wrapped(engine_surface, font, text, 0, wrap_size); + text_obj = TTF_CreateText(engine_surface, font, text, 0); + TTF_SetTextWrapWidth(text_obj, wrap_size); } else { - text_obj = TTF_CreateText_Wrapped(engine_renderer, font, text, 0, wrap_size); + text_obj = TTF_CreateText(engine_renderer, font, text, 0); + TTF_SetTextWrapWidth(text_obj, wrap_size); } if (print_elapsed_ticks) { diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index 3671fd08..43043c50 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -993,15 +993,15 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetStringSize(TTF_Font *font, const char *t * specified string will take to fully render. * * Text is wrapped to multiple lines on line endings and on word boundaries if - * it extends beyond `wrapLength` in pixels. + * it extends beyond `wrap_width` in pixels. * - * If wrapLength is 0, this function will only wrap on newline characters. + * If wrap_width is 0, this function will only wrap on newline characters. * * \param font the font to query. * \param text text to calculate, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated * text. - * \param wrapLength the maximum width or 0 to wrap on newline characters. + * \param wrap_width the maximum width or 0 to wrap on newline characters. * \param w will be filled with width, in pixels, on return. * \param h will be filled with height, in pixels, on return. * \returns true on success or false on failure; call SDL_GetError() for more @@ -1012,13 +1012,13 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetStringSize(TTF_Font *font, const char *t * * \since This function is available since SDL_ttf 3.0.0. */ -extern SDL_DECLSPEC bool SDLCALL TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrapLength, int *w, int *h); +extern SDL_DECLSPEC bool SDLCALL TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrap_width, int *w, int *h); /** * Calculate how much of a UTF-8 string will fit in a given width. * * This reports the number of characters that can be rendered before reaching - * `measure_width`. + * `max_width`. * * This does not need to render the string to do this calculation. * @@ -1026,10 +1026,9 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetStringSizeWrapped(TTF_Font *font, const * \param text text to calculate, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated * text. - * \param measure_width maximum width, in pixels, available for the string. - * \param extent on return, filled with latest calculated width. - * \param count on return, filled with number of characters that can be - * rendered. + * \param max_width maximum width, in pixels, available for the string, or 0 for unbounded width. + * \param measured_width a pointer filled in with the width, in pixels, of the string that will fit, may be NULL. + * \param measured_length a pointer filled in with the length, in bytes, of the string that will fit, may be NULL. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1038,7 +1037,7 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetStringSizeWrapped(TTF_Font *font, const * * \since This function is available since SDL_ttf 3.0.0. */ -extern SDL_DECLSPEC bool SDLCALL TTF_MeasureString(TTF_Font *font, const char *text, size_t length, int measure_width, int *extent, int *count); +extern SDL_DECLSPEC bool SDLCALL TTF_MeasureString(TTF_Font *font, const char *text, size_t length, int max_width, int *measured_width, size_t *measured_length); /** * Render UTF-8 text at high quality to a new 8-bit surface. @@ -1083,9 +1082,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, * surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if - * it extends beyond `wrapLength` in pixels. + * it extends beyond `wrap_width` in pixels. * - * If wrapLength is 0, this function will only wrap on newline characters. + * If wrap_width is 0, this function will only wrap on newline characters. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. @@ -1093,7 +1092,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, * text. * \param fg the foreground color for the text. * \param bg the background color for the text. - * \param wrapLength the maximum width of the text surface or 0 to wrap on + * \param wrap_width the maximum width of the text surface or 0 to wrap on * newline characters. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * @@ -1106,7 +1105,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, * \sa TTF_RenderText_LCD_Wrapped * \sa TTF_RenderText_Shaded */ -extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength); +extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width); /** * Render a single UNICODE codepoint at high quality to a new 8-bit surface. @@ -1175,16 +1174,16 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, * new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if - * it extends beyond `wrapLength` in pixels. + * it extends beyond `wrap_width` in pixels. * - * If wrapLength is 0, this function will only wrap on newline characters. + * If wrap_width is 0, this function will only wrap on newline characters. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated * text. * \param fg the foreground color for the text. - * \param wrapLength the maximum width of the text surface or 0 to wrap on + * \param wrap_width the maximum width of the text surface or 0 to wrap on * newline characters. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * @@ -1197,7 +1196,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, * \sa TTF_RenderText_LCD_Wrapped * \sa TTF_RenderText_Shaded_Wrapped */ -extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, int wrapLength); +extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, int wrap_width); /** * Render a single UNICODE codepoint at high quality to a new ARGB surface. @@ -1266,9 +1265,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD(TTF_Font *font, con * returns the new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if - * it extends beyond `wrapLength` in pixels. + * it extends beyond `wrap_width` in pixels. * - * If wrapLength is 0, this function will only wrap on newline characters. + * If wrap_width is 0, this function will only wrap on newline characters. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. @@ -1276,7 +1275,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD(TTF_Font *font, con * text. * \param fg the foreground color for the text. * \param bg the background color for the text. - * \param wrapLength the maximum width of the text surface or 0 to wrap on + * \param wrap_width the maximum width of the text surface or 0 to wrap on * newline characters. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * @@ -1289,7 +1288,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD(TTF_Font *font, con * \sa TTF_RenderText_LCD * \sa TTF_RenderText_Shaded_Wrapped */ -extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength); +extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width); /** * Render a single UNICODE codepoint at LCD subpixel quality to a new ARGB @@ -1334,7 +1333,6 @@ typedef struct TTF_TextData TTF_TextData; * \since This struct is available since SDL_ttf 3.0.0. * * \sa TTF_CreateText - * \sa TTF_CreateText_Wrapped * \sa TTF_GetTextProperties * \sa TTF_DestroyText */ @@ -1386,7 +1384,6 @@ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_CreateSurfaceTextEngine(void); * * \sa TTF_CreateSurfaceTextEngine * \sa TTF_CreateText - * \sa TTF_CreateText_Wrapped */ extern SDL_DECLSPEC bool SDLCALL TTF_DrawSurfaceText(TTF_Text *text, int x, int y, SDL_Surface *surface); @@ -1446,7 +1443,6 @@ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_CreateRendererTextEngine(SDL_Re * * \sa TTF_CreateRendererTextEngine * \sa TTF_CreateText - * \sa TTF_CreateText_Wrapped */ extern SDL_DECLSPEC bool SDLCALL TTF_DrawRendererText(TTF_Text *text, float x, float y); @@ -1471,9 +1467,6 @@ extern SDL_DECLSPEC void SDLCALL TTF_DestroyRendererTextEngine(TTF_TextEngine *e /** * Create a text object from UTF-8 text and a text engine. * - * This function is equivalent to `TTF_CreateText_Wrapped(engine, font, text, - * 0)` and will wrap on newline characters. - * * \param engine the text engine to use when creating the text object, may be * NULL. * \param font the font to render with. @@ -1488,40 +1481,10 @@ extern SDL_DECLSPEC void SDLCALL TTF_DestroyRendererTextEngine(TTF_TextEngine *e * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_CreateText_Wrapped * \sa TTF_DestroyText */ extern SDL_DECLSPEC TTF_Text * SDLCALL TTF_CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length); -/** - * Create a text object from word-wrapped UTF-8 text and a text engine. - * - * Text is wrapped to multiple lines on line endings and on word boundaries if - * it extends beyond `wrapLength` in pixels. - * - * If wrapLength is 0, this function will only wrap on newline characters. - * - * \param engine the text engine to use when creating the text object, may be - * NULL. - * \param font the font to render with. - * \param text the text to use, in UTF-8 encoding. - * \param length the length of the text, in bytes, or 0 for null terminated - * text. - * \param wrapLength the maximum width of the text surface or 0 to wrap on - * newline characters. - * \returns a TTF_Text object or NULL on failure; call SDL_GetError() for more - * information. - * - * \threadsafety This function should be called on the thread that created the - * font. - * - * \since This function is available since SDL_ttf 3.0.0. - * - * \sa TTF_CreateText - * \sa TTF_DestroyText - */ -extern SDL_DECLSPEC TTF_Text * SDLCALL TTF_CreateText_Wrapped(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength); - /** * Get the properties associated with a text object. * @@ -1738,12 +1701,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int *y); /** - * Set the UTF-8 text used by a text object. + * Set whether wrapping is enabled on a text object. * * \param text the TTF_Text to modify. - * \param string the UTF-8 text to use, may be NULL. - * \param length the length of the text, in bytes, or 0 for null terminated - * text. + * \param wrap_width the maximum width in pixels, 0 to wrap on newline + * characters. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1752,23 +1714,16 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_AppendTextString - * \sa TTF_DeleteTextString - * \sa TTF_InsertTextString + * \sa TTF_GetTextWrapWidth */ -extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *string, size_t length); +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWidth(TTF_Text *text, int wrap_width); /** - * Insert UTF-8 text into a text object. + * Get whether wrapping is enabled on a text object. * - * \param text the TTF_Text to modify. - * \param offset the offset, in bytes, from the beginning of the string if >= - * 0, the offset from the end of the string if < 0. Note that - * this does not do UTF-8 validation, so you should only insert - * at UTF-8 sequence boundaries. - * \param string the UTF-8 text to insert. - * \param length the length of the text, in bytes, or 0 for null terminated - * text. + * \param text the TTF_Text to query. + * \param wrap_width a pointer filled in with the maximum width in pixels or 0 + * if the text is being wrapped on newline characters. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1777,19 +1732,21 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *s * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_AppendTextString - * \sa TTF_DeleteTextString - * \sa TTF_SetTextString + * \sa TTF_SetTextWrapWidth */ -extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t length); +extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapWidth(TTF_Text *text, int *wrap_width); /** - * Append UTF-8 text to a text object. + * Set whether whitespace should be visible when wrapping a text object. + * + * If the whitespace is visible, it will take up space for purposes of + * alignment and wrapping. This is good for editing, but looks better when + * centered or aligned if whitespace around line wrapping is hidden. This + * defaults false. * * \param text the TTF_Text to modify. - * \param string the UTF-8 text to insert. - * \param length the length of the text, in bytes, or 0 for null terminated - * text. + * \param visible true to show whitespace when wrapping text, false to hide + * it. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1798,42 +1755,33 @@ extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_DeleteTextString - * \sa TTF_InsertTextString - * \sa TTF_SetTextString + * \sa TTF_TextWrapWhitespaceVisible */ -extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char *string, size_t length); +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible); /** - * Delete UTF-8 text from a text object. + * Return whether whitespace is shown when wrapping a text object. * - * \param text the TTF_Text to modify. - * \param offset the offset, in bytes, from the beginning of the string if >= - * 0, the offset from the end of the string if < 0. Note that - * this does not do UTF-8 validation, so you should only delete - * at UTF-8 sequence boundaries. - * \param length the length of text to delete, in bytes, or -1 for the - * remainder of the string. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \param text the TTF_Text to query. + * \returns true if whitespace is shown when wrapping text, or false + * otherwise. * * \threadsafety This function should be called on the thread that created the * text. * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_AppendTextString - * \sa TTF_InsertTextString - * \sa TTF_SetTextString + * \sa TTF_SetTextWrapWhitespaceVisible */ -extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset, int length); +extern SDL_DECLSPEC bool SDLCALL TTF_TextWrapWhitespaceVisible(TTF_Text *text); /** - * Set whether wrapping is enabled on a text object. + * Set the UTF-8 text used by a text object. * * \param text the TTF_Text to modify. - * \param wrapLength the maximum width in pixels, 0 to wrap on newline - * characters. + * \param string the UTF-8 text to use, may be NULL. + * \param length the length of the text, in bytes, or 0 for null terminated + * text. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1842,16 +1790,23 @@ extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_GetTextWrapping + * \sa TTF_AppendTextString + * \sa TTF_DeleteTextString + * \sa TTF_InsertTextString */ -extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLength); +extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *string, size_t length); /** - * Get whether wrapping is enabled on a text object. + * Insert UTF-8 text into a text object. * - * \param text the TTF_Text to query. - * \param wrapLength a pointer filled in with the maximum width in pixels or 0 - * if the text is being wrapped on newline characters. + * \param text the TTF_Text to modify. + * \param offset the offset, in bytes, from the beginning of the string if >= + * 0, the offset from the end of the string if < 0. Note that + * this does not do UTF-8 validation, so you should only insert + * at UTF-8 sequence boundaries. + * \param string the UTF-8 text to insert. + * \param length the length of the text, in bytes, or 0 for null terminated + * text. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1860,21 +1815,19 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLen * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_SetTextWrapping + * \sa TTF_AppendTextString + * \sa TTF_DeleteTextString + * \sa TTF_SetTextString */ -extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLength); +extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t length); /** - * Set whether whitespace should be visible when wrapping a text object. - * - * If the whitespace is visible, it will take up space for purposes of - * alignment and wrapping. This is good for editing, but looks better when - * centered or aligned if whitespace around line wrapping is hidden. This - * defaults false. + * Append UTF-8 text to a text object. * * \param text the TTF_Text to modify. - * \param visible true to show whitespace when wrapping text, false to hide - * it. + * \param string the UTF-8 text to insert. + * \param length the length of the text, in bytes, or 0 for null terminated + * text. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -1883,25 +1836,35 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLe * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_TextWrapWhitespaceVisible + * \sa TTF_DeleteTextString + * \sa TTF_InsertTextString + * \sa TTF_SetTextString */ -extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible); +extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char *string, size_t length); /** - * Return whether whitespace is shown when wrapping a text object. + * Delete UTF-8 text from a text object. * - * \param text the TTF_Text to query. - * \returns true if whitespace is shown when wrapping text, or false - * otherwise. + * \param text the TTF_Text to modify. + * \param offset the offset, in bytes, from the beginning of the string if >= + * 0, the offset from the end of the string if < 0. Note that + * this does not do UTF-8 validation, so you should only delete + * at UTF-8 sequence boundaries. + * \param length the length of text to delete, in bytes, or -1 for the + * remainder of the string. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * * \threadsafety This function should be called on the thread that created the * text. * * \since This function is available since SDL_ttf 3.0.0. * - * \sa TTF_SetTextWrapWhitespaceVisible + * \sa TTF_AppendTextString + * \sa TTF_InsertTextString + * \sa TTF_SetTextString */ -extern SDL_DECLSPEC bool SDLCALL TTF_TextWrapWhitespaceVisible(TTF_Text *text); +extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset, int length); /** * Get the size of a text object. diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c index 37b04bb3..6908a729 100644 --- a/src/SDL_ttf.c +++ b/src/SDL_ttf.c @@ -344,7 +344,7 @@ typedef enum { } render_mode_t; #define NO_MEASUREMENT \ - 0, NULL, NULL + false, 0, NULL, NULL static bool Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want_pixmap, int want_color, int want_lcd, int want_subpixel, int translation, c_glyph **out_glyph, TTF_Image **out_image); @@ -2917,7 +2917,7 @@ bool TTF_GetGlyphKerning(TTF_Font *font, Uint32 previous_ch, Uint32 ch, int *ker return true; } -static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, int *w, int *h, int *xstart, int *ystart, int measure_width, int *extent, int *count) +static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, int *w, int *h, int *xstart, int *ystart, bool measure_width, int max_width, int *measured_width, size_t *measured_length) { int x = 0; int pos_x, pos_y; @@ -2941,16 +2941,18 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i #endif int prev_advance = 0; - // Measurement mode - int char_count = 0; - int current_width = 0; - if (w) { *w = 0; } if (h) { *h = 0; } + if (measured_width) { + *measured_width = 0; + } + if (measured_length) { + *measured_length = 0; + } TTF_CHECK_INITIALIZED(false); TTF_CHECK_POINTER("font", font, false); @@ -2959,6 +2961,9 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i if (!length) { length = SDL_strlen(text); } + if (measured_length) { + *measured_length = length; + } maxy = font->height; @@ -3116,14 +3121,13 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i if (measure_width) { int cw = SDL_max(maxx, FT_FLOOR(x + prev_advance)) - minx; cw += 2 * font->outline; - if (cw <= measure_width) { - current_width = cw; - char_count += 1; - } - if (cw >= measure_width) { - if (cw > measure_width) { - // The last character didn't fit - font->pos_len -= 1; + if (!max_width || cw <= max_width) { + if (measured_width) { + *measured_width = cw; + } + } else { + if (measured_length) { + *measured_length = (size_t)offset; } break; } @@ -3164,30 +3168,6 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i *h += 2 * font->outline; } - // Measurement mode - if (measure_width) { - if (extent) { - *extent = current_width; - } - if (count) { -#if TTF_USE_HARFBUZZ - if ((unsigned int)char_count == glyph_count) { - /* The higher level code doesn't know about ligatures, - * so if we've covered all the glyphs, report the full - * string length. - * - * If we have to line wrap somewhere in the middle, we - * might be off by the number of ligatures, but there - * isn't an easy way around that without using hb_buffer - * at that level instead. - */ - *count = (int)SDL_utf8strlen(text); - } else -#endif - *count = char_count; - } - } - #if TTF_USE_HARFBUZZ if (hb_buffer) { hb_buffer_destroy(hb_buffer); @@ -3209,9 +3189,9 @@ bool TTF_GetStringSize(TTF_Font *font, const char *text, size_t length, int *w, return TTF_Size_Internal(font, text, length, w, h, NULL, NULL, NO_MEASUREMENT); } -bool TTF_MeasureString(TTF_Font *font, const char *text, size_t length, int width, int *extent, int *count) +bool TTF_MeasureString(TTF_Font *font, const char *text, size_t length, int max_width, int *measured_width, size_t *measured_length) { - return TTF_Size_Internal(font, text, length, NULL, NULL, NULL, NULL, width, extent, count); + return TTF_Size_Internal(font, text, length, NULL, NULL, NULL, NULL, true, max_width, measured_width, measured_length); } static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, const render_mode_t render_mode) @@ -3346,7 +3326,7 @@ static bool CharacterIsNewLine(Uint32 c) return false; } -static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int xoffset, int wrapLength, bool trim_whitespace, TTF_Line **lines, int *num_lines, int *w, int *h) +static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int xoffset, int wrap_width, bool trim_whitespace, TTF_Line **lines, int *num_lines, int *w, int *h) { int width, height; int i, numLines = 0, rowHeight; @@ -3363,8 +3343,8 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int TTF_CHECK_INITIALIZED(false); TTF_CHECK_POINTER("font", font, false); TTF_CHECK_POINTER("text", text, false); - if (wrapLength < 0) { - return SDL_InvalidParamError("wrapLength"); + if (wrap_width < 0) { + return SDL_InvalidParamError("wrap_width"); } if (!length) { @@ -3382,16 +3362,15 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int size_t left = length; do { - int extent = 0, max_count = 0, char_count = 0; const char *save_text = NULL; size_t save_length = (size_t)(-1); if (numLines >= maxNumLines) { TTF_Line *new_lines; - if (wrapLength == 0) { + if (wrap_width == 0) { maxNumLines += 32; } else { - maxNumLines += (width / wrapLength) + 1; + maxNumLines += (width / wrap_width) + 1; } new_lines = (TTF_Line *)SDL_realloc(strLines, maxNumLines * sizeof (*strLines)); if (new_lines == NULL) { @@ -3420,59 +3399,52 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int strLines[numLines].length = left; ++numLines; - int measure_width = wrapLength; - if (measure_width > 0) { - measure_width = SDL_max(measure_width - xoffset, 1); + int max_width = wrap_width; + if (max_width > 0) { + max_width = SDL_max(max_width - xoffset, 1); } - if (!TTF_MeasureString(font, spot, left, measure_width, &extent, &max_count)) { + size_t max_length = 0; + if (!TTF_MeasureString(font, spot, left, max_width, NULL, &max_length)) { SDL_SetError("Error measure text"); goto done; } - if (wrapLength != 0) { + if (wrap_width != 0) { // The first line can be empty if we have a text position that's // at the edge of the wrap length, but subsequent lines should have // at least one character per line. - if (max_count == 0 && numLines > 1) { - max_count = 1; + if (max_length == 0 && numLines > 1) { + max_length = 1; } } - if (max_count > 0) { - while (left > 0) { - int is_delim; - Uint32 c = SDL_StepUTF8(&spot, &left); + const char *end = spot + max_length; + while (spot < end) { + int is_delim; + Uint32 c = SDL_StepUTF8(&spot, &left); - if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { - continue; - } - - char_count += 1; + if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { + continue; + } - // With wrapLength == 0, normal text rendering but newline aware - is_delim = (wrapLength > 0) ? CharacterIsDelimiter(c) : CharacterIsNewLine(c); + // With wrap_width == 0, normal text rendering but newline aware + is_delim = (wrap_width > 0) ? CharacterIsDelimiter(c) : CharacterIsNewLine(c); - // Record last delimiter position - if (is_delim) { - save_text = spot; - save_length = left; - // Break, if new line - if (c == '\n' || (c == '\r' && *spot != '\n')) { - break; - } - } - - // Break, if reach the limit - if (char_count == max_count) { + // Record last delimiter position + if (is_delim) { + save_text = spot; + save_length = left; + // Break, if new line + if (c == '\n' || (c == '\r' && *spot != '\n')) { break; } } + } - // Cut at last delimiter/new lines, otherwise in the middle of the word - if (save_text && left > 0) { - spot = save_text; - left = save_length; - } + // Cut at last delimiter/new lines, otherwise in the middle of the word + if (save_text && left > 0) { + spot = save_text; + left = save_length; } // First line is complete, start the next at offset 0 @@ -3508,7 +3480,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int rowHeight = SDL_max(height, font->lineskip); - if (wrapLength == 0) { + if (wrap_width == 0) { // Find the max of all line lengths if (numLines > 1) { width = 0; @@ -3524,10 +3496,10 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int } } else { if (numLines <= 1 && font->horizontal_align == TTF_HORIZONTAL_ALIGN_LEFT) { - // Don't go above wrapLength if you have only 1 line which hasn't been cut - width = SDL_min((int)wrapLength, width); + // Don't go above wrap_width if you have only 1 line which hasn't been cut + width = SDL_min((int)wrap_width, width); } else { - width = wrapLength; + width = wrap_width; } } height = rowHeight + font->lineskip * (numLines - 1); @@ -3556,12 +3528,12 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int return result; } -bool TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrapLength, int *w, int *h) +bool TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrap_width, int *w, int *h) { - return GetWrappedLines(font, text, length, 0, wrapLength, true, NULL, NULL, w, h); + return GetWrappedLines(font, text, length, 0, wrap_width, true, NULL, NULL, w, h); } -static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength, const render_mode_t render_mode) +static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width, const render_mode_t render_mode) { Uint32 color; int width, height; @@ -3569,7 +3541,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text int i, numLines = 0; TTF_Line *strLines = NULL; - if (!GetWrappedLines(font, text, length, 0, wrapLength, true, &strLines, &numLines, &width, &height)) { + if (!GetWrappedLines(font, text, length, 0, wrap_width, true, &strLines, &numLines, &width, &height)) { return NULL; } @@ -3656,19 +3628,19 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text return NULL; } -SDL_Surface* TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength) +SDL_Surface* TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width) { - return TTF_Render_Wrapped_Internal(font, text, length, fg, bg, wrapLength, RENDER_SHADED); + return TTF_Render_Wrapped_Internal(font, text, length, fg, bg, wrap_width, RENDER_SHADED); } -SDL_Surface* TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, int wrapLength) +SDL_Surface* TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, int wrap_width) { - return TTF_Render_Wrapped_Internal(font, text, length, fg, fg /* unused */, wrapLength, RENDER_BLENDED); + return TTF_Render_Wrapped_Internal(font, text, length, fg, fg /* unused */, wrap_width, RENDER_BLENDED); } -SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength) +SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width) { - return TTF_Render_Wrapped_Internal(font, text, length, fg, bg, wrapLength, RENDER_LCD); + return TTF_Render_Wrapped_Internal(font, text, length, fg, bg, wrap_width, RENDER_LCD); } struct TTF_TextLayout @@ -3685,7 +3657,7 @@ typedef struct TTF_InternalText TTF_TextLayout layout; } TTF_InternalText; -static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength) +TTF_Text *TTF_CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length) { if (engine && engine->version < sizeof(*engine)) { // Update this to handle older versions of this interface @@ -3708,7 +3680,6 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char * result->internal->color.a = 1.0f; result->internal->needs_layout_update = true; result->internal->engine = engine; - result->internal->layout->wrap_length = wrapLength; if (text && *text) { if (length == 0) { length = SDL_strlen(text); @@ -3729,16 +3700,6 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char * return result; } -TTF_Text *TTF_CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length) -{ - return CreateText(engine, font, text, length, 0); -} - -TTF_Text *TTF_CreateText_Wrapped(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength) -{ - return CreateText(engine, font, text, length, wrapLength); -} - static int SDLCALL SortClusters(const void *a, const void *b) { TTF_SubString *A = (TTF_SubString *)a; @@ -3833,7 +3794,7 @@ static int CalculateClusterLengths(TTF_Text *text, TTF_SubString *clusters, int static bool LayoutText(TTF_Text *text) { TTF_Font *font = text->internal->font; - int wrapLength = text->internal->layout->wrap_length; + int wrap_width = text->internal->layout->wrap_length; bool trim_whitespace = !text->internal->layout->wrap_whitespace_visible; size_t length = SDL_strlen(text->text); int i, width = 0, height = 0, numLines = 0; @@ -3845,7 +3806,7 @@ static bool LayoutText(TTF_Text *text) int *lines = NULL; bool result = false; - if (!GetWrappedLines(font, text->text, length, text->internal->x, wrapLength, trim_whitespace, &strLines, &numLines, &width, &height)) { + if (!GetWrappedLines(font, text->text, length, text->internal->x, wrap_width, trim_whitespace, &strLines, &numLines, &width, &height)) { return true; } height += text->internal->y; @@ -4188,6 +4149,53 @@ bool TTF_GetTextPosition(TTF_Text *text, int *x, int *y) return true; } +bool TTF_SetTextWrapWidth(TTF_Text *text, int wrap_width) +{ + TTF_CHECK_POINTER("text", text, false); + + if (wrap_width == text->internal->layout->wrap_length) { + return true; + } + + text->internal->layout->wrap_length = SDL_max(wrap_width, 0); + text->internal->needs_layout_update = true; + return true; +} + +bool TTF_GetTextWrapWidth(TTF_Text *text, int *wrap_width) +{ + if (wrap_width) { + *wrap_width = 0; + } + + TTF_CHECK_POINTER("text", text, false); + + if (wrap_width) { + *wrap_width = text->internal->layout->wrap_length; + } + return true; +} + +bool TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible) +{ + TTF_CHECK_POINTER("text", text, false); + + if (visible == text->internal->layout->wrap_whitespace_visible) { + return true; + } + + text->internal->layout->wrap_whitespace_visible = visible; + text->internal->needs_layout_update = true; + return true; +} + +bool TTF_TextWrapWhitespaceVisible(TTF_Text *text) +{ + TTF_CHECK_POINTER("text", text, false); + + return text->internal->layout->wrap_whitespace_visible; +} + bool TTF_SetTextString(TTF_Text *text, const char *string, size_t length) { TTF_CHECK_POINTER("text", text, false); @@ -4310,53 +4318,6 @@ bool TTF_DeleteTextString(TTF_Text *text, int offset, int length) return true; } -bool TTF_SetTextWrapping(TTF_Text *text, int wrapLength) -{ - TTF_CHECK_POINTER("text", text, false); - - if (wrapLength == text->internal->layout->wrap_length) { - return true; - } - - text->internal->layout->wrap_length = SDL_max(wrapLength, 0); - text->internal->needs_layout_update = true; - return true; -} - -bool TTF_GetTextWrapping(TTF_Text *text, int *wrapLength) -{ - if (wrapLength) { - *wrapLength = 0; - } - - TTF_CHECK_POINTER("text", text, false); - - if (wrapLength) { - *wrapLength = text->internal->layout->wrap_length; - } - return true; -} - -bool TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible) -{ - TTF_CHECK_POINTER("text", text, false); - - if (visible == text->internal->layout->wrap_whitespace_visible) { - return true; - } - - text->internal->layout->wrap_whitespace_visible = visible; - text->internal->needs_layout_update = true; - return true; -} - -bool TTF_GetTextWrapSpaceTrimming(TTF_Text *text) -{ - TTF_CHECK_POINTER("text", text, false); - - return text->internal->layout->wrap_whitespace_visible; -} - bool TTF_GetTextSize(TTF_Text *text, int *w, int *h) { if (w) { diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym index 4aab9e0f..a0e2d867 100644 --- a/src/SDL_ttf.sym +++ b/src/SDL_ttf.sym @@ -5,7 +5,6 @@ SDL3_ttf_0.0.0 { TTF_CreateRendererTextEngine; TTF_CreateSurfaceTextEngine; TTF_CreateText; - TTF_CreateText_Wrapped; TTF_DeleteTextString; TTF_DestroyRendererTextEngine; TTF_DestroySurfaceTextEngine; @@ -53,7 +52,7 @@ SDL3_ttf_0.0.0 { TTF_GetTextSubStringForLine; TTF_GetTextSubStringForPoint; TTF_GetTextSubStringsForRange; - TTF_GetTextWrapping; + TTF_GetTextWrapWidth; TTF_Init; TTF_InsertTextString; TTF_MeasureString; @@ -89,7 +88,7 @@ SDL3_ttf_0.0.0 { TTF_SetTextPosition; TTF_SetTextString; TTF_SetTextWrapWhitespaceVisible; - TTF_SetTextWrapping; + TTF_SetTextWrapWidth; TTF_TextWrapWhitespaceVisible; TTF_UpdateText; TTF_Version; From 9de716d0af1558d4a377a343eb68510a27b179c8 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Mon, 21 Oct 2024 10:02:28 +0000 Subject: [PATCH 12/14] Sync SDL3_ttf wiki -> header [ci skip] --- include/SDL3_ttf/SDL_ttf.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index 43043c50..ab7c492b 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -1026,9 +1026,12 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetStringSizeWrapped(TTF_Font *font, const * \param text text to calculate, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated * text. - * \param max_width maximum width, in pixels, available for the string, or 0 for unbounded width. - * \param measured_width a pointer filled in with the width, in pixels, of the string that will fit, may be NULL. - * \param measured_length a pointer filled in with the length, in bytes, of the string that will fit, may be NULL. + * \param max_width maximum width, in pixels, available for the string, or 0 + * for unbounded width. + * \param measured_width a pointer filled in with the width, in pixels, of the + * string that will fit, may be NULL. + * \param measured_length a pointer filled in with the length, in bytes, of + * the string that will fit, may be NULL. * \returns true on success or false on failure; call SDL_GetError() for more * information. * From a0ed8b647fcbd88832213e928e624c24ed117a68 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 24 Oct 2024 09:09:05 -0700 Subject: [PATCH 13/14] Re-added solid text rendering It turns out this is useful for pixel fonts Fixes https://github.com/libsdl-org/SDL_ttf/issues/423 --- docs/README-migration.md | 8 +- examples/showfont.c | 13 ++- examples/testapp.c | 40 +++++---- include/SDL3_ttf/SDL_ttf.h | 138 +++++++++++++++++++++++++++++++ src/SDL_ttf.c | 163 ++++++++++++++++++++++++++++++------- src/SDL_ttf.sym | 3 + 6 files changed, 314 insertions(+), 51 deletions(-) diff --git a/docs/README-migration.md b/docs/README-migration.md index f56e3ece..bb609986 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -36,8 +36,6 @@ Several functions have been renamed. We have provided a handy semantic patch to In general we have switched to using UTF8 in the API. Functions which had 3 variants, for Latin-1, UTF-8, and UCS2, now accept UTF-8 text. In addition, those functions now have an optional length parameter which allows you to render substrings. -The solid color rendering functions have been removed in favor of the higher quality shaded and blended functions. - The alpha in background colors is now transparent if it's equal to 0. The following functions have been renamed: @@ -58,12 +56,15 @@ The following functions have been renamed: * TTF_RenderGlyph32_Blended() => TTF_RenderGlyph_Blended() * TTF_RenderGlyph32_LCD() => TTF_RenderGlyph_LCD() * TTF_RenderGlyph32_Shaded() => TTF_RenderGlyph_Shaded() +* TTF_RenderGlyph32_Solid() => TTF_RenderGlyph_Solid() * TTF_RenderUTF8_Blended() => TTF_RenderText_Blended() * TTF_RenderUTF8_Blended_Wrapped() => TTF_RenderText_Blended_Wrapped() * TTF_RenderUTF8_LCD() => TTF_RenderText_LCD() * TTF_RenderUTF8_LCD_Wrapped() => TTF_RenderText_LCD_Wrapped() * TTF_RenderUTF8_Shaded() => TTF_RenderText_Shaded() * TTF_RenderUTF8_Shaded_Wrapped() => TTF_RenderText_Shaded_Wrapped() +* TTF_RenderUTF8_Solid() => TTF_RenderText_Solid() +* TTF_RenderUTF8_Solid_Wrapped() => TTF_RenderText_Solid_Wrapped() * TTF_SetFontScriptName() => TTF_SetFontScript() * TTF_SetFontWrappedAlign() => TTF_SetFontWrapAlignment() * TTF_SizeText() => TTF_GetTextSize() @@ -81,7 +82,6 @@ The following functions have been removed: * TTF_OpenFontIndexDPI() - replaced with TTF_OpenFontWithProperties() * TTF_OpenFontIndexDPIIO() - replaced with TTF_OpenFontWithProperties() * TTF_OpenFontIndexIO() - replaced with TTF_OpenFontWithProperties() -* TTF_RenderGlyph32_Solid() * TTF_RenderGlyph_Solid() * TTF_RenderText_Solid() * TTF_RenderText_Solid_Wrapped() @@ -93,8 +93,6 @@ The following functions have been removed: * TTF_RenderUNICODE_Shaded_Wrapped() * TTF_RenderUNICODE_Solid() * TTF_RenderUNICODE_Solid_Wrapped() -* TTF_RenderUTF8_Solid() -* TTF_RenderUTF8_Solid_Wrapped() * TTF_SizeUNICODE() The following symbols have been renamed: diff --git a/examples/showfont.c b/examples/showfont.c index 244abad1..a9284579 100644 --- a/examples/showfont.c +++ b/examples/showfont.c @@ -40,7 +40,7 @@ #define TTF_SHOWFONT_USAGE \ -"Usage: %s [-textengine surface|renderer] [-shaded] [-blended] [-wrapped] [-b] [-i] [-u] [-s] [-outline size] [-hintlight|-hintmono|-hintnone] [-nokerning] [-wrap] [-align left|center|right] [-fgcol r,g,b,a] [-bgcol r,g,b,a] [-editbox] .ttf [ptsize] [text]\n" +"Usage: %s [-textengine surface|renderer] [-solid] [-shaded] [-blended] [-wrapped] [-b] [-i] [-u] [-s] [-outline size] [-hintlight|-hintmono|-hintnone] [-nokerning] [-wrap] [-align left|center|right] [-fgcol r,g,b,a] [-bgcol r,g,b,a] [-editbox] .ttf [ptsize] [text]\n" typedef enum { @@ -51,6 +51,7 @@ typedef enum typedef enum { + TextRenderSolid, TextRenderShaded, TextRenderBlended } TextRenderMethod; @@ -311,6 +312,9 @@ int main(int argc, char *argv[]) return(1); } } else + if (SDL_strcmp(argv[i], "-solid") == 0) { + rendermethod = TextRenderSolid; + } else if (SDL_strcmp(argv[i], "-shaded") == 0) { rendermethod = TextRenderShaded; } else @@ -511,6 +515,13 @@ int main(int argc, char *argv[]) message = DEFAULT_TEXT; } switch (rendermethod) { + case TextRenderSolid: + if (wrap) { + text = TTF_RenderText_Solid_Wrapped(font, message, 0, *forecol, 0); + } else { + text = TTF_RenderText_Solid(font, message, 0, *forecol); + } + break; case TextRenderShaded: if (wrap) { text = TTF_RenderText_Shaded_Wrapped(font, message, 0, *forecol, *backcol, 0); diff --git a/examples/testapp.c b/examples/testapp.c index 57cd4590..8c364a2c 100644 --- a/examples/testapp.c +++ b/examples/testapp.c @@ -112,7 +112,7 @@ static void help(void) SDL_Log("t : ticks elapsed for 50 rendering"); SDL_Log("d : display normal texture, no screen update, stream texture "); SDL_Log("r : start/stop random test"); - SDL_Log("m : render mode Blended/Shaded"); + SDL_Log("m : render mode Solid/Blended/Shaded"); SDL_Log("x : text engine None/Surface/Renderer"); SDL_Log("n : change direction"); SDL_Log("9/0 : -/+ alpha color fg"); @@ -191,10 +191,10 @@ static int print_elapsed_ticks = 0; static int update_screen_mode = 0; static int save_to_bmp = 0; -/* RENDER_BLENDED = 0, RENDER_SHADED = 1, RENDER_LCD = 2 */ +/* RENDER_SOLID = 0, RENDER_BLENDED = 1, RENDER_SHADED = 2, RENDER_LCD = 3 } */ static int render_mode = -1; static int render_mode_overwrite; -static const char *render_mode_desc[] = { "Blended", "Shaded", "LCD" }; +static const char *render_mode_desc[] = { "Solid", "Blended", "Shaded", "LCD" }; static const int render_mode_count = SDL_arraysize(render_mode_desc); static int textengine_mode = 0; @@ -698,6 +698,10 @@ int main(void) seed=1641805930; replay=1; font_style=9; kerning=1; sdf=1; wrap=0; wrap_size=661; w_align=2; outline=0; curr_size=20; render_mode=3; curr_str=14; curr_font=1777; hinting=1; fg_alpha=65; // light LCD #endif + //seed=1673390190; replay=1; font_style=12; kerning=0; sdf=1; wrap=0; wrap_size=91; w_align=0; outline=0; curr_size=16; render_mode=0; curr_str=14; curr_font=1288; hinting=0; fg_alpha=77; // none Solid + + //seed=1673390190; replay=1; font_style=12; kerning=0; sdf=0; wrap=0; wrap_size=91; w_align=0; outline=0; curr_size=30; render_mode=1; curr_str=14; curr_font=1288; hinting=0; fg_alpha=77; // none Solid + //seed=1673390190; replay=1; font_style=9; kerning=1; sdf=0; wrap=1; wrap_size=94; w_align=2; outline=7; curr_size=42; render_mode=1; curr_str=75; curr_font=1997; hinting=1; fg_alpha=90; // light Blended if (replay) { @@ -957,15 +961,17 @@ int main(void) if (textengine_mode == 0) { - switch (render_mode) - { + switch (render_mode) { case 0: - text_surface = TTF_RenderText_Blended(font, text, 0, textcol); + text_surface = TTF_RenderText_Solid(font, text, 0, textcol); break; case 1: - text_surface = TTF_RenderText_Shaded(font, text, 0, textcol, boardcol); + text_surface = TTF_RenderText_Blended(font, text, 0, textcol); break; case 2: + text_surface = TTF_RenderText_Shaded(font, text, 0, textcol, boardcol); + break; + case 3: #if defined(HAVE_LCD) text_surface = TTF_RenderText_LCD(font, text, 0, textcol, boardcol); #else @@ -996,15 +1002,17 @@ int main(void) } if (textengine_mode == 0) { - switch (render_mode) - { + switch (render_mode) { case 0: - text_surface = TTF_RenderText_Blended(font, text, 0, textcol); + text_surface = TTF_RenderText_Solid(font, text, 0, textcol); break; case 1: - text_surface = TTF_RenderText_Shaded(font, text, 0, textcol, boardcol); + text_surface = TTF_RenderText_Blended(font, text, 0, textcol); break; case 2: + text_surface = TTF_RenderText_Shaded(font, text, 0, textcol, boardcol); + break; + case 3: #if defined(HAVE_LCD) text_surface = TTF_RenderText_LCD(font, text, 0, textcol, boardcol); #else @@ -1047,15 +1055,17 @@ int main(void) } if (textengine_mode == 0) { - switch (render_mode) - { + switch (render_mode) { case 0: - text_surface = TTF_RenderText_Blended_Wrapped(font, text, 0, textcol, wrap_size); + text_surface = TTF_RenderText_Solid_Wrapped(font, text, 0, textcol, wrap_size); break; case 1: - text_surface = TTF_RenderText_Shaded_Wrapped(font, text, 0, textcol, boardcol, wrap_size); + text_surface = TTF_RenderText_Blended_Wrapped(font, text, 0, textcol, wrap_size); break; case 2: + text_surface = TTF_RenderText_Shaded_Wrapped(font, text, 0, textcol, boardcol, wrap_size); + break; + case 3: #if defined(HAVE_LCD) text_surface = TTF_RenderText_LCD_Wrapped(font, text, 0, textcol, boardcol, wrap_size); #else diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h index ab7c492b..9e00cf75 100644 --- a/include/SDL3_ttf/SDL_ttf.h +++ b/include/SDL3_ttf/SDL_ttf.h @@ -757,6 +757,108 @@ typedef enum TTF_Direction TTF_DIRECTION_BTT /* Bottom to Top */ } TTF_Direction; +/** + * Render UTF-8 text at fast quality to a new 8-bit surface. + * + * This function will allocate a new 8-bit, palettized surface. The surface's + * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel + * will be set to the text color. + * + * This will not word-wrap the string; you'll get a surface with a single line + * of text, as long as the string requires. You can use + * TTF_RenderText_Solid_Wrapped() instead if you need to wrap the output to + * multiple lines. + * + * This will not wrap on newline characters. + * + * You can render at other quality levels with TTF_RenderText_Shaded, + * TTF_RenderText_Blended, and TTF_RenderText_LCD. + * + * \param font the font to render with. + * \param text text to render, in UTF-8 encoding. + * \param length the length of the text, in bytes, or 0 for null terminated + * text. + * \param fg the foreground color for the text. + * \returns a new 8-bit, palettized surface, or NULL if there was an error. + * + * \threadsafety This function should be called on the thread that created the + * font. + * + * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_RenderText_Blended + * \sa TTF_RenderText_LCD + * \sa TTF_RenderText_Shaded + * \sa TTF_RenderText_Solid + * \sa TTF_RenderText_Solid_Wrapped + */ +extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid(TTF_Font *font, const char *text, size_t length, SDL_Color fg); + +/** + * Render word-wrapped UTF-8 text at fast quality to a new 8-bit surface. + * + * This function will allocate a new 8-bit, palettized surface. The surface's + * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel + * will be set to the text color. + * + * Text is wrapped to multiple lines on line endings and on word boundaries if + * it extends beyond `wrapLength` in pixels. + * + * If wrapLength is 0, this function will only wrap on newline characters. + * + * You can render at other quality levels with TTF_RenderText_Shaded_Wrapped, + * TTF_RenderText_Blended_Wrapped, and TTF_RenderText_LCD_Wrapped. + * + * \param font the font to render with. + * \param text text to render, in UTF-8 encoding. + * \param length the length of the text, in bytes, or 0 for null terminated + * text. + * \param fg the foreground color for the text. + * \param wrapLength the maximum width of the text surface or 0 to wrap on + * newline characters. + * \returns a new 8-bit, palettized surface, or NULL if there was an error. + * + * \threadsafety This function should be called on the thread that created the + * font. + * + * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_RenderText_Blended_Wrapped + * \sa TTF_RenderText_LCD_Wrapped + * \sa TTF_RenderText_Shaded_Wrapped + * \sa TTF_RenderText_Solid + */ +extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, int wrapLength); + +/** + * Render a single 32-bit glyph at fast quality to a new 8-bit surface. + * + * This function will allocate a new 8-bit, palettized surface. The surface's + * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel + * will be set to the text color. + * + * The glyph is rendered without any padding or centering in the X direction, + * and aligned normally in the Y direction. + * + * You can render at other quality levels with TTF_RenderGlyph_Shaded, + * TTF_RenderGlyph_Blended, and TTF_RenderGlyph_LCD. + * + * \param font the font to render with. + * \param ch the character to render. + * \param fg the foreground color for the text. + * \returns a new 8-bit, palettized surface, or NULL if there was an error. + * + * \threadsafety This function should be called on the thread that created the + * font. + * + * \since This function is available since SDL_ttf 3.0.0. + * + * \sa TTF_RenderGlyph_Blended + * \sa TTF_RenderGlyph_LCD + * \sa TTF_RenderGlyph_Shaded + */ +extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Solid(TTF_Font *font, Uint32 ch, SDL_Color fg); + /** * Set direction to be used for text shaping by a font. * @@ -1057,6 +1159,9 @@ extern SDL_DECLSPEC bool SDLCALL TTF_MeasureString(TTF_Font *font, const char *t * * This will not wrap on newline characters. * + * You can render at other quality levels with TTF_RenderText_Solid, + * TTF_RenderText_Blended, and TTF_RenderText_LCD. + * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated @@ -1073,6 +1178,7 @@ extern SDL_DECLSPEC bool SDLCALL TTF_MeasureString(TTF_Font *font, const char *t * \sa TTF_RenderText_Blended * \sa TTF_RenderText_LCD * \sa TTF_RenderText_Shaded_Wrapped + * \sa TTF_RenderText_Solid */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg); @@ -1089,6 +1195,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, * * If wrap_width is 0, this function will only wrap on newline characters. * + * You can render at other quality levels with TTF_RenderText_Solid_Wrapped, + * TTF_RenderText_Blended_Wrapped, and TTF_RenderText_LCD_Wrapped. + * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated @@ -1107,6 +1216,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, * \sa TTF_RenderText_Blended_Wrapped * \sa TTF_RenderText_LCD_Wrapped * \sa TTF_RenderText_Shaded + * \sa TTF_RenderText_Solid_Wrapped */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width); @@ -1121,6 +1231,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * + * You can render at other quality levels with TTF_RenderGlyph_Solid, + * TTF_RenderGlyph_Blended, and TTF_RenderGlyph_LCD. + * * \param font the font to render with. * \param ch the codepoint to render. * \param fg the foreground color for the text. @@ -1134,6 +1247,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font * * \sa TTF_RenderGlyph_Blended * \sa TTF_RenderGlyph_LCD + * \sa TTF_RenderGlyph_Solid */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Shaded(TTF_Font *font, Uint32 ch, SDL_Color fg, SDL_Color bg); @@ -1151,6 +1265,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Shaded(TTF_Font *font, * * This will not wrap on newline characters. * + * You can render at other quality levels with TTF_RenderText_Solid, + * TTF_RenderText_Shaded, and TTF_RenderText_LCD. + * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated @@ -1166,6 +1283,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Shaded(TTF_Font *font, * \sa TTF_RenderText_Blended_Wrapped * \sa TTF_RenderText_LCD * \sa TTF_RenderText_Shaded + * \sa TTF_RenderText_Solid */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, const char *text, size_t length, SDL_Color fg); @@ -1181,6 +1299,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, * * If wrap_width is 0, this function will only wrap on newline characters. * + * You can render at other quality levels with TTF_RenderText_Solid_Wrapped, + * TTF_RenderText_Shaded_Wrapped, and TTF_RenderText_LCD_Wrapped. + * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated @@ -1198,6 +1319,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, * \sa TTF_RenderText_Blended * \sa TTF_RenderText_LCD_Wrapped * \sa TTF_RenderText_Shaded_Wrapped + * \sa TTF_RenderText_Solid_Wrapped */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, int wrap_width); @@ -1211,6 +1333,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Fon * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * + * You can render at other quality levels with TTF_RenderGlyph_Solid, + * TTF_RenderGlyph_Shaded, and TTF_RenderGlyph_LCD. + * * \param font the font to render with. * \param ch the codepoint to render. * \param fg the foreground color for the text. @@ -1223,6 +1348,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Fon * * \sa TTF_RenderGlyph_LCD * \sa TTF_RenderGlyph_Shaded + * \sa TTF_RenderGlyph_Solid */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font, Uint32 ch, SDL_Color fg); @@ -1240,6 +1366,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font * * This will not wrap on newline characters. * + * You can render at other quality levels with TTF_RenderText_Solid, + * TTF_RenderText_Shaded, and TTF_RenderText_Blended. + * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated @@ -1256,6 +1385,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font * \sa TTF_RenderText_Blended * \sa TTF_RenderText_LCD_Wrapped * \sa TTF_RenderText_Shaded + * \sa TTF_RenderText_Solid */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg); @@ -1272,6 +1402,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD(TTF_Font *font, con * * If wrap_width is 0, this function will only wrap on newline characters. * + * You can render at other quality levels with TTF_RenderText_Solid_Wrapped, + * TTF_RenderText_Shaded_Wrapped, and TTF_RenderText_Blended_Wrapped. + * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param length the length of the text, in bytes, or 0 for null terminated @@ -1290,6 +1423,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD(TTF_Font *font, con * \sa TTF_RenderText_Blended_Wrapped * \sa TTF_RenderText_LCD * \sa TTF_RenderText_Shaded_Wrapped + * \sa TTF_RenderText_Solid_Wrapped */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width); @@ -1304,6 +1438,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD_Wrapped(TTF_Font *f * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * + * You can render at other quality levels with TTF_RenderGlyph_Solid, + * TTF_RenderGlyph_Shaded, and TTF_RenderGlyph_Blended. + * * \param font the font to render with. * \param ch the codepoint to render. * \param fg the foreground color for the text. @@ -1317,6 +1454,7 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD_Wrapped(TTF_Font *f * * \sa TTF_RenderGlyph_Blended * \sa TTF_RenderGlyph_Shaded + * \sa TTF_RenderGlyph_Solid */ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_LCD(TTF_Font *font, Uint32 ch, SDL_Color fg, SDL_Color bg); diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c index 6908a729..4951c04e 100644 --- a/src/SDL_ttf.c +++ b/src/SDL_ttf.c @@ -170,12 +170,13 @@ static SDL_INLINE int hasNEON(void) #define DIVIDE_BY_255(x) DIVIDE_BY_255_SIGNED(x, 1) -#define CACHED_METRICS 0x10 +#define CACHED_METRICS 0x20 -#define CACHED_PIXMAP 0x01 -#define CACHED_COLOR 0x02 -#define CACHED_LCD 0x04 -#define CACHED_SUBPIX 0x08 +#define CACHED_BITMAP 0x01 +#define CACHED_PIXMAP 0x02 +#define CACHED_COLOR 0x04 +#define CACHED_LCD 0x08 +#define CACHED_SUBPIX 0x10 typedef struct { @@ -192,6 +193,7 @@ typedef struct { typedef struct cached_glyph { int stored; FT_UInt index; + TTF_Image bitmap; TTF_Image pixmap; int sz_left; int sz_top; @@ -338,6 +340,7 @@ static struct TTF_CHECK_POINTER("font", font, errval) typedef enum { + RENDER_SOLID = 0, RENDER_SHADED, RENDER_BLENDED, RENDER_LCD @@ -347,7 +350,7 @@ typedef enum { false, 0, NULL, NULL -static bool Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want_pixmap, int want_color, int want_lcd, int want_subpixel, int translation, c_glyph **out_glyph, TTF_Image **out_image); +static bool Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want_bitmap, int want_pixmap, int want_color, int want_lcd, int want_subpixel, int translation, c_glyph **out_glyph, TTF_Image **out_image); #if defined(USE_DUFFS_LOOP) @@ -1160,7 +1163,7 @@ static int Get_Alignment(void) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-value" #endif -#define BUILD_RENDER_LINE(NAME, IS_BLENDED, IS_BLENDED_OPAQUE, IS_LCD, WP_WC, WS, BLIT_GLYPH_BLENDED_OPAQUE_OPTIM, BLIT_GLYPH_BLENDED_OPTIM, BLIT_GLYPH_OPTIM) \ +#define BUILD_RENDER_LINE(NAME, IS_BLENDED, IS_BLENDED_OPAQUE, IS_LCD, WB_WP_WC, WS, BLIT_GLYPH_BLENDED_OPAQUE_OPTIM, BLIT_GLYPH_BLENDED_OPTIM, BLIT_GLYPH_OPTIM) \ \ static bool Render_Line_##NAME(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) \ { \ @@ -1174,7 +1177,7 @@ static bool Render_Line_##NAME(TTF_Font *font, SDL_Surface *textbuf, int xstart, int y = font->pos_buf[i].y; \ TTF_Image *image; \ \ - if (Find_GlyphByIndex(font, idx, WP_WC, WS, x & 63, NULL, &image)) { \ + if (Find_GlyphByIndex(font, idx, WB_WP_WC, WS, x & 63, NULL, &image)) { \ int above_w, above_h; \ Uint32 dstskip; \ Sint32 srcskip; /* Can be negative */ \ @@ -1270,18 +1273,20 @@ static bool Render_Line_##NAME(TTF_Font *font, SDL_Surface *textbuf, int xstart, } \ \ -#define PIXMAP CACHED_PIXMAP, 0, 0 -#define COLOR 0, CACHED_COLOR, 0 -#define LCD 0, 0, CACHED_LCD +#define BITMAP CACHED_BITMAP, 0, 0, 0 +#define PIXMAP 0, CACHED_PIXMAP, 0, 0 +#define COLOR 0, 0, CACHED_COLOR, 0 +#define LCD 0, 0, 0, CACHED_LCD #define SUBPIX CACHED_SUBPIX -// BUILD_RENDER_LINE(NAME, IS_BLENDED, IS_BLENDED_OPAQUE, WANT_PIXMAP_COLOR_LCD, WANT_SUBPIXEL, BLIT_GLYPH_BLENDED_OPAQUE_OPTIM, BLIT_GLYPH_BLENDED_OPTIM, BLIT_GLYPH_OPTIM) +// BUILD_RENDER_LINE(NAME, IS_BLENDED, IS_BLENDED_OPAQUE, WANT_BITMAP_PIXMAP_COLOR_LCD, WANT_SUBPIXEL, BLIT_GLYPH_BLENDED_OPAQUE_OPTIM, BLIT_GLYPH_BLENDED_OPTIM, BLIT_GLYPH_OPTIM) #if defined(HAVE_SSE2_INTRINSICS) BUILD_RENDER_LINE(SSE_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_SSE ) BUILD_RENDER_LINE(SSE_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_SSE , ) BUILD_RENDER_LINE(SSE_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_SSE , , ) +BUILD_RENDER_LINE(SSE_Solid , 0, 0, 0, BITMAP, 0 , , , BG_SSE ) BUILD_RENDER_LINE(SSE_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_SSE ) BUILD_RENDER_LINE(SSE_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_SSE , ) BUILD_RENDER_LINE(SSE_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_SSE , , ) @@ -1293,6 +1298,7 @@ BUILD_RENDER_LINE(SSE_LCD_SP , 0, 0, 1, LCD, SUBPIX, BUILD_RENDER_LINE(NEON_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_NEON ) BUILD_RENDER_LINE(NEON_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_NEON, ) BUILD_RENDER_LINE(NEON_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_NEON, , ) +BUILD_RENDER_LINE(NEON_Solid , 0, 0, 0, BITMAP, 0 , , , BG_NEON ) BUILD_RENDER_LINE(NEON_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_NEON ) BUILD_RENDER_LINE(NEON_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_NEON, ) BUILD_RENDER_LINE(NEON_Blended_Opaque_SP, 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_NEON, , ) @@ -1304,6 +1310,7 @@ BUILD_RENDER_LINE(NEON_LCD_SP , 0, 0, 1, LCD, SUBPIX, BUILD_RENDER_LINE(64_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_64 ) BUILD_RENDER_LINE(64_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_32 , ) BUILD_RENDER_LINE(64_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_32 , , ) +BUILD_RENDER_LINE(64_Solid , 0, 0, 0, BITMAP, 0 , , , BG_64 ) BUILD_RENDER_LINE(64_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_64 ) BUILD_RENDER_LINE(64_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_32 , ) BUILD_RENDER_LINE(64_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_32 , , ) @@ -1313,6 +1320,7 @@ BUILD_RENDER_LINE(64_LCD_SP , 0, 0, 1, LCD, SUBPIX, BUILD_RENDER_LINE(32_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_32 ) BUILD_RENDER_LINE(32_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_32 , ) BUILD_RENDER_LINE(32_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_32 , , ) +BUILD_RENDER_LINE(32_Solid , 0, 0, 0, BITMAP, 0 , , , BG_32 ) BUILD_RENDER_LINE(32_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_32 ) BUILD_RENDER_LINE(32_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_32 , ) BUILD_RENDER_LINE(32_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_32 , , ) @@ -1322,6 +1330,7 @@ BUILD_RENDER_LINE(32_LCD_SP , 0, 0, 1, LCD, SUBPIX, BUILD_RENDER_LINE(8_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG ) BUILD_RENDER_LINE(8_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended , ) BUILD_RENDER_LINE(8_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque , , ) +BUILD_RENDER_LINE(8_Solid , 0, 0, 0, BITMAP, 0 , , , BG ) BUILD_RENDER_LINE(8_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG ) BUILD_RENDER_LINE(8_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended , ) BUILD_RENDER_LINE(8_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque , , ) @@ -1334,6 +1343,7 @@ BUILD_RENDER_LINE(8_LCD_SP , 0, 0, 1, LCD, SUBPIX, static int (*Render_Line_SDF_Shaded)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; BUILD_RENDER_LINE(SDF_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_SDF , ) BUILD_RENDER_LINE(SDF_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_SDF , , ) +static int (*Render_Line_SDF_Solid)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; static int (*Render_Line_SDF_Shaded_SP)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; BUILD_RENDER_LINE(SDF_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_SDF , ) BUILD_RENDER_LINE(SDF_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_SDF , , ) @@ -1349,6 +1359,8 @@ static bool Render_Line(const render_mode_t render_mode, int subpixel, TTF_Font { // Render line (pos_buf) to textbuf at (xstart, ystart) + // Subpixel with RENDER_SOLID doesn't make sense. + // (and 'cached->subpixel.translation' would need to distinguish bitmap/pixmap). int is_opaque = (fg.a == SDL_ALPHA_OPAQUE); #define Call_Specific_Render_Line(NAME) \ @@ -1372,12 +1384,14 @@ static bool Render_Line(const render_mode_t render_mode, int subpixel, TTF_Font return Render_Line_##NAME##_Blended_SP(font, textbuf, xstart, ystart, &fg); \ } \ } \ - } else /*if (render_mode == RENDER_LCD)*/ { \ + } else if (render_mode == RENDER_LCD) { \ if (subpixel == 0) { \ return Render_Line_##NAME##_LCD(font, textbuf, xstart, ystart, &fg); \ } else { \ return Render_Line_##NAME##_LCD_SP(font, textbuf, xstart, ystart, &fg); \ } \ + } else { \ + return Render_Line_##NAME##_Solid(font, textbuf, xstart, ystart, NULL); \ } #if TTF_USE_SDF @@ -1426,7 +1440,7 @@ static bool Render_Line_TextEngine(TTF_Font *font, int xstart, int ystart, int w int offset = font->pos_buf[i].offset; c_glyph *glyph; - if (Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, &glyph, NULL)) { + if (Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, 0, &glyph, NULL)) { int above_w, above_h; int glyph_x = 0; int glyph_y = 0; @@ -1532,9 +1546,9 @@ static SDL_Surface *AllocateAlignedPixels(size_t width, size_t height, SDL_Pixel * Considerer also for instance, that when we read 1 block of 16 bytes from source, for the blended * format (bbp == 4), it writes(and reads) 4 blocks of 16 in the dest, like BG_Blended_SSE()). * - * Remark: for Shaded, block ratio read/write is 1:1. + * Remark: for Solid/Shaded, block ratio read/write is 1:1. * For Color / LCD / SDF, it is byte vs byte or int. They are also fallback for - * Shaded/Blend, when it isn't contained in textbuf, see clip_glyph() + * Solid/Shaded/Blend, when it isn't contained in textbuf, see clip_glyph() * * So the pitch must contain "width * bytes_per_pixel", plus in the * worst case, writing at last pixel (1 * bytes_per_pixel), an extra "alignment * bytes_per_pixel". @@ -1607,6 +1621,33 @@ static SDL_Surface *AllocateAlignedPixels(size_t width, size_t height, SDL_Pixel return textbuf; } +static SDL_Surface* Create_Surface_Solid(int width, int height, SDL_Color fg, Uint32 *color) +{ + SDL_Surface *textbuf = AllocateAlignedPixels(width, height, SDL_PIXELFORMAT_INDEX8, 0); + if (textbuf == NULL) { + return NULL; + } + + // Underline/Strikethrough color style + *color = 1; + + // Fill the palette: 1 is foreground + { + SDL_Palette *palette = SDL_GetSurfacePalette(textbuf); + palette->colors[0].r = 255 - fg.r; + palette->colors[0].g = 255 - fg.g; + palette->colors[0].b = 255 - fg.b; + palette->colors[1].r = fg.r; + palette->colors[1].g = fg.g; + palette->colors[1].b = fg.b; + palette->colors[1].a = fg.a; + } + + SDL_SetSurfaceColorKey(textbuf, true, 0); + + return textbuf; +} + static SDL_Surface* Create_Surface_Shaded(int width, int height, SDL_Color fg, SDL_Color bg, Uint32 *color) { SDL_Surface *textbuf = AllocateAlignedPixels(width, height, SDL_PIXELFORMAT_INDEX8, 0); @@ -2180,6 +2221,7 @@ static void Flush_Glyph(c_glyph *glyph) glyph->stored = 0; glyph->index = 0; Flush_Glyph_Image(&glyph->pixmap); + Flush_Glyph_Image(&glyph->bitmap); } static void Flush_Cache(TTF_Font *font) @@ -2296,17 +2338,21 @@ static bool Load_Glyph(TTF_Font *font, c_glyph *cached, int want, int translatio cached->stored |= CACHED_METRICS; } - if (((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) || + if (((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || + ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) || ((want & CACHED_COLOR) && !(cached->stored & CACHED_COLOR)) || ((want & CACHED_LCD) && !(cached->stored & CACHED_LCD)) || (want & CACHED_SUBPIX) ) { - TTF_Image *dst = &cached->pixmap; + const int mono = (want & CACHED_BITMAP); + TTF_Image *dst = (mono ? &cached->bitmap : &cached->pixmap); FT_Glyph glyph = NULL; FT_Bitmap *src; FT_Render_Mode ft_render_mode; - { + if (mono) { + ft_render_mode = FT_RENDER_MODE_MONO; + } else { ft_render_mode = FT_RENDER_MODE_NORMAL; #if TTF_USE_SDF if ((want & CACHED_COLOR) && font->render_sdf) { @@ -2533,7 +2579,29 @@ static bool Load_Glyph(TTF_Font *font, c_glyph *cached, int want, int translatio } \ } - if (src->pixel_mode == FT_PIXEL_MODE_MONO) { + if (mono) { + if (src->pixel_mode == FT_PIXEL_MODE_MONO) { + while (quotient--) { + MONO_MONO(8); + } + MONO_MONO(remainder); + } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) { + while (quotient--) { + MONO_GRAY2(4); + } + MONO_GRAY2(remainder); + } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) { + while (quotient--) { + MONO_GRAY4(2); + } + MONO_GRAY4(remainder); + } else { + while (quotient--) { + unsigned char c = *srcp++; + *dstp++ = (c >= 0x80) ? 1 : 0; + } + } + } else if (src->pixel_mode == FT_PIXEL_MODE_MONO) { /* This special case wouldn't be here if the FT_Render_Glyph() * function wasn't buggy when it tried to render a .fon font with 256 * shades of gray. Instead, it returns a black and white surface @@ -2610,7 +2678,9 @@ static bool Load_Glyph(TTF_Font *font, c_glyph *cached, int want, int translatio // SDL_memset(pixmap + dst->width - font->glyph_overhang, 0, font->glyph_overhang); for (offset = 1; offset <= font->glyph_overhang; ++offset) { for (col = dst->width - 1; col > 0; --col) { - { + if (mono) { + pixmap[col] |= pixmap[col-1]; + } else { int pixel = (pixmap[col] + pixmap[col-1]); if (pixel > NUM_GRAYS - 1) { pixel = NUM_GRAYS - 1; @@ -2638,7 +2708,9 @@ static bool Load_Glyph(TTF_Font *font, c_glyph *cached, int want, int translatio #endif // Mark that we rendered this format - if (src->pixel_mode == FT_PIXEL_MODE_LCD) { + if (mono) { + cached->stored |= CACHED_BITMAP; + } else if (src->pixel_mode == FT_PIXEL_MODE_LCD) { cached->stored |= CACHED_LCD; } else { #if TTF_USE_COLOR @@ -2672,7 +2744,7 @@ static bool Load_Glyph(TTF_Font *font, c_glyph *cached, int want, int translatio } static bool Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, - int want_pixmap, int want_color, int want_lcd, int want_subpixel, + int want_bitmap, int want_pixmap, int want_color, int want_lcd, int want_subpixel, int translation, c_glyph **out_glyph, TTF_Image **out_image) { // cache size is 256, get key by masking @@ -2686,10 +2758,14 @@ static bool Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, *out_image = &glyph->pixmap; } + if (want_bitmap) { + *out_image = &glyph->bitmap; + } + if (want_subpixel) { /* Not a real cache, but if it always advances by integer pixels (eg translation 0 or same as previous), * this allows to render as fast as normal mode. */ - int want = CACHED_METRICS | want_pixmap | want_color | want_lcd | want_subpixel; + int want = CACHED_METRICS | want_bitmap | want_pixmap | want_color | want_lcd | want_subpixel; if (glyph->stored && glyph->index != idx) { Flush_Glyph(glyph); @@ -2712,13 +2788,17 @@ static bool Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, glyph->index = idx; return Load_Glyph(font, glyph, want, translation); } else { - const int want = CACHED_METRICS | want_pixmap | want_color | want_lcd; + const int want = CACHED_METRICS | want_bitmap | want_pixmap | want_color | want_lcd; // Faster check as it gets inlined if (want_pixmap) { if ((glyph->stored & CACHED_PIXMAP) && glyph->index == idx) { return true; } + } else if (want_bitmap) { + if ((glyph->stored & CACHED_BITMAP) && glyph->index == idx) { + return true; + } } else if (want_color) { if ((glyph->stored & CACHED_COLOR) && glyph->index == idx) { return true; @@ -2774,7 +2854,7 @@ static bool Find_GlyphMetrics(TTF_Font *font, Uint32 ch, c_glyph **out_glyph) TTF_CHECK_FONT(font, false); FT_UInt idx = get_char_index(font, ch); - return Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, out_glyph, NULL); + return Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, 0, out_glyph, NULL); } bool TTF_FontHasGlyph(TTF_Font *font, Uint32 ch) @@ -2808,7 +2888,7 @@ SDL_Surface *TTF_GetGlyphImageForIndex(TTF_Font *font, Uint32 glyph_index) TTF_CHECK_FONT(font, NULL); - if (!Find_GlyphByIndex(font, glyph_index, 1, 0, 0, 0, 0, NULL, &image)) { + if (!Find_GlyphByIndex(font, glyph_index, 0, CACHED_PIXMAP, 0, 0, 0, 0, NULL, &image)) { return NULL; } @@ -3036,7 +3116,7 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i continue; } #endif - if (!Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, &glyph, NULL)) { + if (!Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, 0, &glyph, NULL)) { goto failure; } @@ -3233,7 +3313,9 @@ static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, size_t if (fg.a == SDL_ALPHA_TRANSPARENT) { fg.a = SDL_ALPHA_OPAQUE; } - if (render_mode == RENDER_SHADED) { + if (render_mode == RENDER_SOLID) { + textbuf = Create_Surface_Solid(width, height, fg, &color); + } else if (render_mode == RENDER_SHADED) { textbuf = Create_Surface_Shaded(width, height, fg, bg, &color); } else if (render_mode == RENDER_BLENDED) { textbuf = Create_Surface_Blended(width, height, fg, &color); @@ -3267,6 +3349,20 @@ static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, size_t return NULL; } +SDL_Surface* TTF_RenderText_Solid(TTF_Font *font, const char *text, size_t length, SDL_Color fg) +{ + return TTF_Render_Internal(font, text, length, fg, fg /* unused */, RENDER_SOLID); +} + +SDL_Surface* TTF_RenderGlyph_Solid(TTF_Font *font, Uint32 ch, SDL_Color fg) +{ + char utf8[4], *end; + + end = SDL_UCS4ToUTF8(ch, utf8); + + return TTF_RenderText_Solid(font, utf8, (end - utf8), fg); +} + SDL_Surface* TTF_RenderText_Shaded(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg) { return TTF_Render_Internal(font, text, length, fg, bg, RENDER_SHADED); @@ -3564,7 +3660,9 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text if (fg.a == SDL_ALPHA_TRANSPARENT) { fg.a = SDL_ALPHA_OPAQUE; } - if (render_mode == RENDER_SHADED) { + if (render_mode == RENDER_SOLID) { + textbuf = Create_Surface_Solid(width, height, fg, &color); + } else if (render_mode == RENDER_SHADED) { textbuf = Create_Surface_Shaded(width, height, fg, bg, &color); } else if (render_mode == RENDER_BLENDED) { textbuf = Create_Surface_Blended(width, height, fg, &color); @@ -3628,6 +3726,11 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text return NULL; } +SDL_Surface* TTF_RenderText_Solid_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, int wrap_width) +{ + return TTF_Render_Wrapped_Internal(font, text, length, fg, fg /* unused */, wrap_width, RENDER_SOLID); +} + SDL_Surface* TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width) { return TTF_Render_Wrapped_Internal(font, text, length, fg, bg, wrap_width, RENDER_SHADED); diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym index a0e2d867..a91bf474 100644 --- a/src/SDL_ttf.sym +++ b/src/SDL_ttf.sym @@ -63,12 +63,15 @@ SDL3_ttf_0.0.0 { TTF_RenderGlyph_Blended; TTF_RenderGlyph_LCD; TTF_RenderGlyph_Shaded; + TTF_RenderGlyph_Solid; TTF_RenderText_Blended; TTF_RenderText_Blended_Wrapped; TTF_RenderText_LCD; TTF_RenderText_LCD_Wrapped; TTF_RenderText_Shaded; TTF_RenderText_Shaded_Wrapped; + TTF_RenderText_Solid; + TTF_RenderText_Solid_Wrapped; TTF_SetFontDirection; TTF_SetFontHinting; TTF_SetFontKerning; From 8a9167d36074905af08ab80cad37448715e1072a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 24 Oct 2024 09:10:07 -0700 Subject: [PATCH 14/14] Fixed caption placement with the surface text engine --- examples/showfont.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/showfont.c b/examples/showfont.c index a9284579..332bfc06 100644 --- a/examples/showfont.c +++ b/examples/showfont.c @@ -101,7 +101,7 @@ static void DrawScene(Scene *scene) case TextEngineSurface: /* Flush the renderer so we can draw directly to the window surface */ SDL_FlushRenderer(renderer); - TTF_DrawSurfaceText(scene->caption, scene->captionRect.w, scene->captionRect.h, scene->window_surface); + TTF_DrawSurfaceText(scene->caption, scene->captionRect.x, scene->captionRect.y, scene->window_surface); break; case TextEngineRenderer: TTF_DrawRendererText(scene->caption, (float)scene->captionRect.x, (float)scene->captionRect.y);