From e9b214282dc9159ea8e32c095fc47dd778829815 Mon Sep 17 00:00:00 2001 From: Ota <106656898+Opi-Txm@users.noreply.github.com> Date: Sat, 17 Feb 2024 23:31:07 +0100 Subject: [PATCH] Fix #21039: Text rendering bleeds pixels through windows Should fix [#21039 ](https://github.com/OpenRCT2/OpenRCT2/issues/21039). As it is explained in the issue, the outline and inset code would not respect the DPI bounds in the drawing process by accessing e.g. `*(dst + 1)` to draw an outline, in this case to the right. #### Approach I approached this issue by creating a function `GetPixel()` in `TTFSurface` that would return a value if the given x and y coordinates is a part of a TTF text, else 0. In the for loop, iterating through the TTF text, the if condition ``` if (surface->GetPixel(xx + srcX_start + 1, yy + srcY_start) || surface->GetPixel(xx + srcX_start - 1, yy + srcY_start) || surface->GetPixel(xx + srcX_start, yy + srcY_start + 1) || surface->GetPixel(xx + srcX_start, yy + srcY_start - 1)) ``` checks if the coordinates **(xx, yy)** is an outline of a text. the writing bounds. Adding `srcX_start`, which is 0 or `-skipX` if `skipX < 0`, when calling `GetPixel()`, would shift the pixel access to the correct position, where the text begins on the surface. The same is done for the Y axis. (Not having this would lead to the outlines/insets not moving with the text when the window with the TTF text moves) --- distribution/changelog.txt | 1 + src/openrct2/drawing/Drawing.String.cpp | 52 ++++++++++--------------- src/openrct2/drawing/TTF.cpp | 7 ++++ src/openrct2/drawing/TTF.h | 1 + 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 526e594604..13aa430a1c 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -33,6 +33,7 @@ - Fix: [#20628] Moving caret using Ctrl+left can move too far when using a multibyte grapheme. - Fix: [#20631] IME window not positioned correctly. - Fix: [#20845] Trying to save under a folder with no write permissions causes a crash. +- Fix: [#21039] Text rendering bleeds pixels through windows. - Fix: [#21054] “No entrance” style is selected by default in the track designer. - Fix: [#21145] [Plugin] setInterval/setTimeout handle conflict. - Fix: [#21157] [Plugin] Widgets do not redraw correctly when updating disabled or visibility state. diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index 3025fd54cc..f8e44f4503 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -572,16 +572,20 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD auto src = static_cast(surface->pixels); uint8_t* dst = dpi.bits; + int32_t srcXStart = 0; + int32_t srcYStart = 0; if (skipX < 0) { width += skipX; src += -skipX; + srcXStart += -skipX; skipX = 0; } if (skipY < 0) { height += skipY; src += (-skipY * surface->pitch); + srcYStart += -skipY; skipY = 0; } @@ -591,60 +595,44 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD int32_t srcScanSkip = surface->pitch - width; int32_t dstScanSkip = dpi.width + dpi.pitch - width; uint8_t* dst_orig = dst; - const uint8_t* src_orig = src; // Draw shadow/outline - if (info->flags & TEXT_DRAW_FLAG_OUTLINE) + if (info->flags & (TEXT_DRAW_FLAG_OUTLINE | TEXT_DRAW_FLAG_INSET)) { - for (int32_t yy = 0; yy < height - 0; yy++) + for (int32_t yy = 0; yy < height; yy++) { - for (int32_t xx = 0; xx < width - 0; xx++) + for (int32_t xx = 0; xx < width; xx++) { - if (*src != 0) + if (info->flags & TEXT_DRAW_FLAG_OUTLINE) { - // right - if (xx + skipX < dpi.width + dpi.pitch - 1) + if (GetPixel(*surface, xx + srcXStart + 1, yy + srcYStart) + || GetPixel(*surface, xx + srcXStart - 1, yy + srcYStart) + || GetPixel(*surface, xx + srcXStart, yy + srcYStart + 1) + || GetPixel(*surface, xx + srcXStart, yy + srcYStart - 1)) { - *(dst + 1) = info->palette[3]; - } - // left - if (xx + skipX > 1) - { - *(dst - 1) = info->palette[3]; - } - // top - if (yy + skipY > 1) - { - *(dst - width - dstScanSkip) = info->palette[3]; - } - // bottom - if (yy + skipY < dpi.height - 1) - { - *(dst + width + dstScanSkip) = info->palette[3]; + *dst = info->palette[3]; + } + } + if (info->flags & TEXT_DRAW_FLAG_INSET) + { + if (GetPixel(*surface, xx + srcXStart - 1, yy + srcYStart - 1)) + { + *dst = info->palette[3]; } } - src++; dst++; } // Skip any remaining bits - src += srcScanSkip; dst += dstScanSkip; } } - dst = dst_orig; - src = src_orig; for (int32_t yy = 0; yy < height; yy++) { for (int32_t xx = 0; xx < width; xx++) { if (*src != 0) { - if (info->flags & TEXT_DRAW_FLAG_INSET) - { - *(dst + width + dstScanSkip + 1) = info->palette[3]; - } - if (*src > 180 || !use_hinting) { // Centre of the glyph: use full colour. diff --git a/src/openrct2/drawing/TTF.cpp b/src/openrct2/drawing/TTF.cpp index 329ee34436..7c48db277a 100644 --- a/src/openrct2/drawing/TTF.cpp +++ b/src/openrct2/drawing/TTF.cpp @@ -374,6 +374,13 @@ void TTFFreeSurface(TTFSurface* surface) free(surface); } +uint8_t GetPixel(const TTFSurface& surface, int32_t x, int32_t y) +{ + if (x < 0 || y < 0 || x >= surface.w || y >= surface.h) + return 0; + return static_cast(surface.pixels)[y * surface.pitch + x]; +} + #else # include "TTF.h" diff --git a/src/openrct2/drawing/TTF.h b/src/openrct2/drawing/TTF.h index d3e497ff55..273fb48f90 100644 --- a/src/openrct2/drawing/TTF.h +++ b/src/openrct2/drawing/TTF.h @@ -43,5 +43,6 @@ void TTF_CloseFont(TTF_Font* font); void TTF_SetFontHinting(TTF_Font* font, int hinting); int TTF_GetFontHinting(const TTF_Font* font); void TTF_Quit(void); +uint8_t GetPixel(const TTFSurface& surface, int32_t x, int32_t y); #endif // NO_TTF