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:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user