Refactor TTF drawing (#21621)

This commit is contained in:
mrmbernardi 2024-03-28 23:28:54 +01:00 committed by GitHub
parent 4eacb21f57
commit d48b75fb86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 139 additions and 166 deletions

View File

@ -37,7 +37,6 @@
# include <openrct2/ui/UiContext.h>
# include <openrct2/util/Util.h>
# include <openrct2/world/Climate.h>
# include <unordered_map>
using namespace OpenRCT2;
using namespace OpenRCT2::Drawing;
@ -74,6 +73,8 @@ private:
int32_t _drawCount = 0;
uint32_t _ttfGlId = 0;
struct
{
LineCommandBatch lines;
@ -110,8 +111,7 @@ public:
void DrawSpriteSolid(DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, uint8_t colour) override;
void DrawGlyph(DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& palette) override;
void DrawTTFBitmap(
DrawPixelInfo& dpi, TextDrawInfo* info, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x,
int32_t y, uint8_t hinting_threshold) override;
DrawPixelInfo& dpi, TextDrawInfo* info, TTFSurface* surface, int32_t x, int32_t y, uint8_t hintingThreshold) override;
void FlushCommandBuffers();
@ -908,17 +908,25 @@ void OpenGLDrawingContext::DrawGlyph(DrawPixelInfo& dpi, const ImageId image, in
}
void OpenGLDrawingContext::DrawTTFBitmap(
DrawPixelInfo& dpi, TextDrawInfo* info, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x,
int32_t y, uint8_t hinting_threshold)
DrawPixelInfo& dpi, TextDrawInfo* info, TTFSurface* surface, int32_t x, int32_t y, uint8_t hintingThreshold)
{
# ifndef NO_TTF
CalculcateClipping(dpi);
const auto texture = _textureCache->GetOrLoadBitmapTexture(image, pixels, width, height);
auto baseId = uint32_t(0x7FFFF) - 1024;
auto imageId = baseId + _ttfGlId;
_engine.InvalidateImage(imageId);
const auto texture = _textureCache->GetOrLoadBitmapTexture(imageId, surface->pixels, surface->w, surface->h);
_ttfGlId++;
if (_ttfGlId >= 1023)
{
_ttfGlId = 0;
}
int32_t drawOffsetX = 0;
int32_t drawOffsetY = 0;
int32_t drawWidth = static_cast<uint16_t>(width);
int32_t drawHeight = static_cast<uint16_t>(height);
int32_t drawWidth = static_cast<uint16_t>(surface->w);
int32_t drawHeight = static_cast<uint16_t>(surface->h);
int32_t left = x + drawOffsetX;
int32_t top = y + drawOffsetY;
@ -976,7 +984,7 @@ void OpenGLDrawingContext::DrawTTFBitmap(
command.bounds = { left + 1, top + 1, right + 1, bottom + 1 };
command.depth = _drawCount++;
}
auto& cmdBuf = hinting_threshold > 0 ? _commandBuffers.transparent : _commandBuffers.rects;
auto& cmdBuf = hintingThreshold > 0 ? _commandBuffers.transparent : _commandBuffers.rects;
DrawRectCommand& command = cmdBuf.allocate();
command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom };
command.texColourAtlas = texture.index;
@ -984,10 +992,11 @@ void OpenGLDrawingContext::DrawTTFBitmap(
command.texMaskAtlas = 0;
command.texMaskBounds = { 0.0f, 0.0f, 0.0f, 0.0f };
command.palettes = { 0, 0, 0 };
command.flags = DrawRectCommand::FLAG_TTF_TEXT | (hinting_threshold << 8);
command.flags = DrawRectCommand::FLAG_TTF_TEXT | (hintingThreshold << 8);
command.colour = info->palette[1];
command.bounds = { left, top, right, bottom };
command.depth = _drawCount++;
# endif // NO_TTF
}
void OpenGLDrawingContext::FlushCommandBuffers()

View File

@ -21,7 +21,6 @@
#include "../localisation/LocalisationService.h"
#include "../platform/Platform.h"
#include "../sprites.h"
#include "../util/Util.h"
#include "TTF.h"
#include <algorithm>
@ -509,7 +508,6 @@ static void TTFDrawStringRawSprite(DrawPixelInfo& dpi, std::string_view text, Te
#ifndef NO_TTF
static int _ttfGlId = 0;
static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextDrawInfo* info)
{
if (!TTFInitialise())
@ -528,135 +526,20 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD
return;
}
uint8_t colour = info->palette[1];
TTFSurface* surface = TTFSurfaceCacheGetOrAdd(fontDesc->font, text);
if (surface == nullptr)
return;
int32_t drawX = info->x + fontDesc->offset_x;
int32_t drawY = info->y + fontDesc->offset_y;
int32_t width = surface->w;
int32_t height = surface->h;
bool use_hinting = gConfigFonts.EnableHinting && fontDesc->hinting_threshold > 0;
if (OpenRCT2::GetContext()->GetDrawingEngineType() == DrawingEngine::OpenGL)
auto drawingEngine = dpi.DrawingEngine;
if (drawingEngine != nullptr)
{
auto baseId = uint32_t(0x7FFFF) - 1024;
auto imageId = baseId + _ttfGlId;
auto drawingEngine = dpi.DrawingEngine;
auto drawingContext = drawingEngine->GetDrawingContext();
uint8_t hint_thresh = use_hinting ? fontDesc->hinting_threshold : 0;
drawingEngine->InvalidateImage(imageId);
drawingContext->DrawTTFBitmap(
dpi, info, imageId, surface->pixels, surface->pitch, surface->h, drawX, drawY, hint_thresh);
_ttfGlId++;
if (_ttfGlId >= 1023)
{
_ttfGlId = 0;
}
info->x += width;
return;
}
int32_t overflowX = (dpi.x + dpi.width) - (drawX + width);
int32_t overflowY = (dpi.y + dpi.height) - (drawY + height);
if (overflowX < 0)
width += overflowX;
if (overflowY < 0)
height += overflowY;
int32_t skipX = drawX - dpi.x;
int32_t skipY = drawY - dpi.y;
info->x += width;
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;
}
dst += skipX;
dst += skipY * (dpi.width + dpi.pitch);
int32_t srcScanSkip = surface->pitch - width;
int32_t dstScanSkip = dpi.width + dpi.pitch - width;
uint8_t* dst_orig = dst;
// Draw shadow/outline
if (info->flags & (TEXT_DRAW_FLAG_OUTLINE | TEXT_DRAW_FLAG_INSET))
{
for (int32_t yy = 0; yy < height; yy++)
{
for (int32_t xx = 0; xx < width; xx++)
{
if (info->flags & TEXT_DRAW_FLAG_OUTLINE)
{
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 = info->palette[3];
}
}
if (info->flags & TEXT_DRAW_FLAG_INSET)
{
if (GetPixel(*surface, xx + srcXStart - 1, yy + srcYStart - 1))
{
*dst = info->palette[3];
}
}
dst++;
}
// Skip any remaining bits
dst += dstScanSkip;
}
}
dst = dst_orig;
for (int32_t yy = 0; yy < height; yy++)
{
for (int32_t xx = 0; xx < width; xx++)
{
if (*src != 0)
{
if (*src > 180 || !use_hinting)
{
// Centre of the glyph: use full colour.
*dst = colour;
}
else if (use_hinting && *src > fontDesc->hinting_threshold)
{
// Simulate font hinting by shading the background colour instead.
if (info->flags & TEXT_DRAW_FLAG_OUTLINE)
{
*dst = BlendColours(colour, info->palette[3]);
}
else
{
*dst = BlendColours(colour, *dst);
}
}
}
src++;
dst++;
}
src += srcScanSkip;
dst += dstScanSkip;
int32_t drawX = info->x + fontDesc->offset_x;
int32_t drawY = info->y + fontDesc->offset_y;
uint8_t hintThresh = gConfigFonts.EnableHinting ? fontDesc->hinting_threshold : 0;
OpenRCT2::Drawing::IDrawingContext* dc = drawingEngine->GetDrawingContext();
dc->DrawTTFBitmap(dpi, info, surface, drawX, drawY, hintThresh);
}
info->x += surface->w;
}
#endif // NO_TTF

View File

@ -11,6 +11,7 @@
#include "../common.h"
#include "Drawing.h"
#include "TTF.h"
namespace OpenRCT2::Drawing
{
@ -33,8 +34,8 @@ namespace OpenRCT2::Drawing
virtual void DrawGlyph(
DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& palette) abstract;
virtual void DrawTTFBitmap(
DrawPixelInfo& dpi, TextDrawInfo* info, ImageIndex image, const void* pixels, int32_t width, int32_t height,
int32_t x, int32_t y, uint8_t hinting_threshold) abstract;
DrawPixelInfo& dpi, TextDrawInfo* info, TTFSurface* surface, int32_t x, int32_t y,
uint8_t hintingThreshold) abstract;
};
} // namespace OpenRCT2::Drawing

View File

@ -1577,12 +1577,11 @@ static void ScrollingTextSetBitmapForTTF(
return;
}
int32_t pitch = surface->pitch;
int32_t width = surface->w;
auto src = static_cast<const uint8_t*>(surface->pixels);
// Pitch offset
src += 2 * pitch;
src += 2 * width;
// Line height offset
int32_t min_vpos = -fontDesc->offset_y;
@ -1608,7 +1607,7 @@ static void ScrollingTextSetBitmapForTTF(
for (int32_t y = min_vpos; y < max_vpos; y++)
{
uint8_t src_pixel = src[y * pitch + x];
uint8_t src_pixel = src[y * width + x];
if ((!use_hinting && src_pixel != 0) || src_pixel > 140)
{
// Centre of the glyph: use full colour.

View File

@ -9,7 +9,6 @@
#ifndef NO_TTF
# include <atomic>
# include <mutex>
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdocumentation"
@ -21,7 +20,6 @@
# include "../config/Config.h"
# include "../core/Numerics.hpp"
# include "../core/String.hpp"
# include "../localisation/Localisation.h"
# include "../localisation/LocalisationService.h"
# include "../platform/Platform.h"
# include "TTF.h"
@ -374,13 +372,6 @@ 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

@ -15,6 +15,7 @@
bool TTFInitialise();
void TTFDispose();
struct TTFSurface;
#ifndef NO_TTF
@ -23,7 +24,6 @@ struct TTFSurface
const void* pixels;
int32_t w;
int32_t h;
int32_t pitch;
};
TTFFontDescriptor* TTFGetFontFromSpriteBase(FontStyle fontStyle);
@ -43,6 +43,5 @@ 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

View File

@ -216,7 +216,7 @@ static void TTF_initLineMectrics(const TTF_Font* font, const TTFSurface* textbuf
dst = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels));
if (row > 0)
{
dst += row * textbuf->pitch;
dst += row * textbuf->w;
}
height = font->underline_height;
@ -236,7 +236,7 @@ outline into account.
static void TTF_drawLine_Solid(const TTF_Font* font, const TTFSurface* textbuf, const int row)
{
int line;
uint8_t* dst_check = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + textbuf->pitch * textbuf->h;
uint8_t* dst_check = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + textbuf->w * textbuf->h;
uint8_t* dst;
int height;
@ -247,7 +247,7 @@ static void TTF_drawLine_Solid(const TTF_Font* font, const TTFSurface* textbuf,
{
/* 1 because 0 is the bg color */
std::fill_n(dst, textbuf->w, 0x01);
dst += textbuf->pitch;
dst += textbuf->w;
}
}
@ -258,7 +258,7 @@ static void TTF_drawLine_Solid(const TTF_Font* font, const TTFSurface* textbuf,
static void TTF_drawLine_Shaded(const TTF_Font* font, const TTFSurface* textbuf, const int row)
{
int line;
uint8_t* dst_check = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + textbuf->pitch * textbuf->h;
uint8_t* dst_check = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + textbuf->w * textbuf->h;
uint8_t* dst;
int height;
@ -268,7 +268,7 @@ static void TTF_drawLine_Shaded(const TTF_Font* font, const TTFSurface* textbuf,
for (line = height; line > 0 && dst < dst_check; --line)
{
std::fill_n(dst, textbuf->w, NUM_GRAYS - 1);
dst += textbuf->pitch;
dst += textbuf->w;
}
}
@ -1299,12 +1299,11 @@ TTFSurface* TTF_RenderUTF8(TTF_Font* font, const char* text, bool shaded)
}
textbuf->w = width;
textbuf->h = height;
textbuf->pitch = width;
textbuf->pixels = calloc(1, width * height);
/* Adding bound checking to avoid all kinds of memory corruption errors
that may occur. */
dst_check = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + textbuf->pitch * textbuf->h;
dst_check = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + textbuf->w * textbuf->h;
/* check kerning */
use_kerning = FT_HAS_KERNING(font->face) && font->kerning;
@ -1363,7 +1362,7 @@ TTFSurface* TTF_RenderUTF8(TTF_Font* font, const char* text, bool shaded)
{
continue;
}
dst = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + (row + glyph->yoffset) * textbuf->pitch
dst = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + (row + glyph->yoffset) * textbuf->w
+ xstart + glyph->minx;
src = current->buffer + row * current->pitch;

View File

@ -10,7 +10,6 @@
#include "X8DrawingEngine.h"
#include "../Context.h"
#include "../Game.h"
#include "../Intro.h"
#include "../config/Config.h"
#include "../core/Numerics.hpp"
@ -19,7 +18,6 @@
#include "../interface/Window.h"
#include "../ui/UiContext.h"
#include "../util/Util.h"
#include "../world/Climate.h"
#include "Drawing.h"
#include "IDrawingContext.h"
#include "IDrawingEngine.h"
@ -743,3 +741,100 @@ void X8DrawingContext::DrawGlyph(DrawPixelInfo& dpi, const ImageId image, int32_
{
GfxDrawSpritePaletteSetSoftware(dpi, image, { x, y }, paletteMap);
}
#ifndef NO_TTF
template<bool TUseHinting>
static void DrawTTFBitmapInternal(
DrawPixelInfo& dpi, uint8_t colour, TTFSurface* surface, int32_t x, int32_t y, uint8_t hintingThreshold)
{
const int32_t surfaceWidth = surface->w;
int32_t width = surfaceWidth;
int32_t height = surface->h;
const int32_t overflowX = (dpi.x + dpi.width) - (x + width);
const int32_t overflowY = (dpi.y + dpi.height) - (y + height);
if (overflowX < 0)
width += overflowX;
if (overflowY < 0)
height += overflowY;
int32_t skipX = x - dpi.x;
int32_t skipY = y - dpi.y;
auto src = static_cast<const uint8_t*>(surface->pixels);
uint8_t* dst = dpi.bits;
if (skipX < 0)
{
width += skipX;
src += -skipX;
skipX = 0;
}
if (skipY < 0)
{
height += skipY;
src += (-skipY * surfaceWidth);
skipY = 0;
}
dst += skipX;
dst += skipY * (dpi.width + dpi.pitch);
const int32_t srcScanSkip = surfaceWidth - width;
const int32_t dstScanSkip = dpi.width + dpi.pitch - width;
for (int32_t yy = 0; yy < height; yy++)
{
for (int32_t xx = 0; xx < width; xx++)
{
if (*src != 0)
{
if constexpr (TUseHinting)
{
if (*src > 180)
{
// Centre of the glyph: use full colour.
*dst = colour;
}
else if (*src > hintingThreshold)
{
*dst = BlendColours(colour, *dst);
}
}
else
{
*dst = colour;
}
}
src++;
dst++;
}
src += srcScanSkip;
dst += dstScanSkip;
}
}
#endif // NO_TTF
void X8DrawingContext::DrawTTFBitmap(
DrawPixelInfo& dpi, TextDrawInfo* info, TTFSurface* surface, int32_t x, int32_t y, uint8_t hintingThreshold)
{
#ifndef NO_TTF
const uint8_t fgColor = info->palette[1];
const uint8_t bgColor = info->palette[3];
if (info->flags & TEXT_DRAW_FLAG_OUTLINE)
{
DrawTTFBitmapInternal<false>(dpi, bgColor, surface, x + 1, y, 0);
DrawTTFBitmapInternal<false>(dpi, bgColor, surface, x - 1, y, 0);
DrawTTFBitmapInternal<false>(dpi, bgColor, surface, x, y + 1, 0);
DrawTTFBitmapInternal<false>(dpi, bgColor, surface, x, y - 1, 0);
}
if (info->flags & TEXT_DRAW_FLAG_INSET)
{
DrawTTFBitmapInternal<false>(dpi, bgColor, surface, x + 1, y + 1, 0);
}
if (hintingThreshold > 0)
DrawTTFBitmapInternal<true>(dpi, fgColor, surface, x, y, hintingThreshold);
else
DrawTTFBitmapInternal<false>(dpi, fgColor, surface, x, y, 0);
#endif // NO_TTF
}

View File

@ -9,7 +9,6 @@
#pragma once
#include "../common.h"
#include "IDrawingContext.h"
#include "IDrawingEngine.h"
@ -148,10 +147,8 @@ namespace OpenRCT2
void DrawGlyph(
DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& paletteMap) override;
void DrawTTFBitmap(
DrawPixelInfo& dpi, TextDrawInfo* info, uint32_t image, const void* pixels, int32_t width, int32_t height,
int32_t x, int32_t y, uint8_t hinting_threshold) override
{
}
DrawPixelInfo& dpi, TextDrawInfo* info, TTFSurface* surface, int32_t x, int32_t y,
uint8_t hintingThreshold) override;
};
} // namespace Drawing
} // namespace OpenRCT2