1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-26 08:14:38 +01:00

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)
This commit is contained in:
Ota
2024-02-17 23:31:07 +01:00
committed by GitHub
parent ecbc523229
commit e9b214282d
4 changed files with 29 additions and 32 deletions

View File

@@ -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.

View File

@@ -572,16 +572,20 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD
auto src = static_cast<const uint8_t*>(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.

View File

@@ -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<const uint8_t*>(surface.pixels)[y * surface.pitch + x];
}
#else
# include "TTF.h"

View File

@@ -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