Add insetting and outlining to OpenGL TTF rendering

This commit is contained in:
Michael Bernardi 2023-12-03 03:15:46 +01:00
parent 6162ff9b00
commit f64706e1d9
10 changed files with 106 additions and 210 deletions

View File

@ -4,6 +4,7 @@ const int MASK_REMAP_COUNT = 3;
const int FLAG_NO_TEXTURE = (1 << 2);
const int FLAG_MASK = (1 << 3);
const int FLAG_CROSS_HATCH = (1 << 4);
const int FLAG_TTF_TEXT = (1 << 5);
uniform usampler2DArray uTexture;
uniform usampler2D uPaletteTex;
@ -41,7 +42,14 @@ void main()
{
discard;
}
texel += fColour;
if ((fFlags & FLAG_TTF_TEXT) == 0)
{
texel += fColour;
}
else
{
texel = fColour;
}
}
else
{

View File

@ -118,6 +118,7 @@ struct DrawRectCommand
FLAG_NO_TEXTURE = (1u << 2u),
FLAG_MASK = (1u << 3u),
FLAG_CROSS_HATCH = (1u << 4u),
FLAG_TTF_TEXT = (1u << 5u),
};
};

View File

@ -23,6 +23,7 @@
# include <SDL.h>
# include <algorithm>
# include <array>
# include <cmath>
# include <openrct2-ui/interface/Window.h>
# include <openrct2/Intro.h>
@ -109,8 +110,9 @@ public:
DrawPixelInfo* dpi, int32_t x, int32_t y, const ImageId maskImage, const ImageId colourImage) override;
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 DrawBitmap(
DrawPixelInfo* dpi, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x, int32_t y) override;
void DrawTTFBitmap(
DrawPixelInfo* dpi, TextDrawInfo* info, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x,
int32_t y) override;
void FlushCommandBuffers();
@ -906,8 +908,9 @@ void OpenGLDrawingContext::DrawGlyph(DrawPixelInfo* dpi, const ImageId image, in
command.depth = _drawCount++;
}
void OpenGLDrawingContext::DrawBitmap(
DrawPixelInfo* dpi, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x, int32_t y)
void OpenGLDrawingContext::DrawTTFBitmap(
DrawPixelInfo* dpi, TextDrawInfo* info, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x,
int32_t y)
{
CalculcateClipping(dpi);
@ -937,16 +940,53 @@ void OpenGLDrawingContext::DrawBitmap(
right += _offsetX;
bottom += _offsetY;
DrawRectCommand& command = _commandBuffers.rects.allocate();
if (info->flags & TEXT_DRAW_FLAG_OUTLINE)
{
std::array<ivec4, 4> boundsArr = { {
{ left + 1, top, right + 1, bottom },
{ left - 1, top, right - 1, bottom },
{ left, top + 1, right, bottom + 1 },
{ left, top - 1, right, bottom - 1 },
} };
for (auto b : boundsArr)
{
DrawRectCommand& command = _commandBuffers.rects.allocate();
command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom };
command.texColourAtlas = texture.index;
command.texColourBounds = texture.normalizedBounds;
command.texMaskAtlas = 0;
command.texMaskBounds = { 0.0f, 0.0f, 0.0f, 0.0f };
command.palettes = { 0, 0, 0 };
command.flags = DrawRectCommand::FLAG_TTF_TEXT;
command.colour = info->palette[3];
command.bounds = b;
command.depth = _drawCount++;
}
}
if (info->flags & TEXT_DRAW_FLAG_INSET)
{
DrawRectCommand& command = _commandBuffers.rects.allocate();
command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom };
command.texColourAtlas = texture.index;
command.texColourBounds = texture.normalizedBounds;
command.texMaskAtlas = 0;
command.texMaskBounds = { 0.0f, 0.0f, 0.0f, 0.0f };
command.palettes = { 0, 0, 0 };
command.flags = DrawRectCommand::FLAG_TTF_TEXT;
command.colour = info->palette[3];
command.bounds = { left + 1, top + 1, right + 1, bottom + 1 };
command.depth = _drawCount++;
}
DrawRectCommand& command = _commandBuffers.rects.allocate();
command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom };
command.texColourAtlas = texture.index;
command.texColourBounds = texture.normalizedBounds;
command.texMaskAtlas = 0;
command.texMaskBounds = { 0.0f, 0.0f, 0.0f, 0.0f };
command.palettes = { 0, 0, 0 };
command.flags = 0;
command.colour = 0;
command.flags = DrawRectCommand::FLAG_TTF_TEXT;
command.colour = info->palette[1];
command.bounds = { left, top, right, bottom };
command.depth = _drawCount++;
}

View File

@ -28,18 +28,6 @@
using namespace OpenRCT2;
enum : uint32_t
{
TEXT_DRAW_FLAG_INSET = 1 << 0,
TEXT_DRAW_FLAG_OUTLINE = 1 << 1,
TEXT_DRAW_FLAG_DARK = 1 << 2,
TEXT_DRAW_FLAG_EXTRA_DARK = 1 << 3,
TEXT_DRAW_FLAG_NO_FORMATTING = 1 << 28,
TEXT_DRAW_FLAG_Y_OFFSET_EFFECT = 1 << 29,
TEXT_DRAW_FLAG_TTF = 1 << 30,
TEXT_DRAW_FLAG_NO_DRAW = 1u << 31
};
static int32_t TTFGetStringWidth(std::string_view text, FontStyle fontStyle, bool noFormatting);
/**
@ -490,20 +478,6 @@ void DrawNewsTicker(
}
}
struct TextDrawInfo
{
int32_t startX;
int32_t startY;
int32_t x;
int32_t y;
int32_t maxX;
int32_t maxY;
int32_t flags;
uint8_t palette[8];
::FontStyle FontStyle;
const int8_t* y_offset;
};
static void TTFDrawCharacterSprite(DrawPixelInfo& dpi, int32_t codepoint, TextDrawInfo* info)
{
int32_t characterWidth = FontSpriteGetCodepointWidth(info->FontStyle, codepoint);
@ -563,29 +537,17 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD
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 pixels = reinterpret_cast<uint8_t*>(const_cast<void*>(surface->pixels));
auto pixelsLen = static_cast<size_t>(surface->pitch) * surface->h;
for (size_t pp = 0; pp < pixelsLen; pp++)
{
if (pixels[pp] != 0)
{
pixels[pp] = colour;
}
else
{
pixels[pp] = PALETTE_INDEX_0;
}
}
// if(use_hinting) return; // Not implemented yet.
auto baseId = uint32_t(0x7FFFF) - 1024;
auto imageId = baseId + _ttfGlId;
auto drawingEngine = dpi.DrawingEngine;
auto drawingContext = drawingEngine->GetDrawingContext();
drawingEngine->InvalidateImage(imageId);
drawingContext->DrawBitmap(&dpi, imageId, surface->pixels, surface->pitch, surface->h, drawX, drawY);
drawingContext->DrawTTFBitmap(&dpi, info, imageId, surface->pixels, surface->pitch, surface->h, drawX, drawY);
_ttfGlId++;
if (_ttfGlId >= 1023)
@ -670,7 +632,6 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD
dst = dst_orig;
src = src_orig;
bool use_hinting = gConfigFonts.EnableHinting && fontDesc->hinting_threshold > 0;
for (int32_t yy = 0; yy < height; yy++)
{
for (int32_t xx = 0; xx < width; xx++)
@ -692,9 +653,7 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD
// Simulate font hinting by shading the background colour instead.
if (info->flags & TEXT_DRAW_FLAG_OUTLINE)
{
// As outlines are black, these texts should always use a darker shade
// of the foreground colour for font hinting.
*dst = BlendColours(colour, PALETTE_INDEX_0);
*dst = BlendColours(colour, info->palette[3]);
}
else
{

View File

@ -132,6 +132,32 @@ struct DrawPixelInfo
DrawPixelInfo Crop(const ScreenCoordsXY& pos, const ScreenSize& size) const;
};
struct TextDrawInfo
{
int32_t startX;
int32_t startY;
int32_t x;
int32_t y;
int32_t maxX;
int32_t maxY;
int32_t flags;
uint8_t palette[8];
::FontStyle FontStyle;
const int8_t* y_offset;
};
enum : uint32_t
{
TEXT_DRAW_FLAG_INSET = 1 << 0,
TEXT_DRAW_FLAG_OUTLINE = 1 << 1,
TEXT_DRAW_FLAG_DARK = 1 << 2,
TEXT_DRAW_FLAG_EXTRA_DARK = 1 << 3,
TEXT_DRAW_FLAG_NO_FORMATTING = 1 << 28,
TEXT_DRAW_FLAG_Y_OFFSET_EFFECT = 1 << 29,
TEXT_DRAW_FLAG_TTF = 1 << 30,
TEXT_DRAW_FLAG_NO_DRAW = 1u << 31
};
struct RCTG1Element
{
uint32_t offset; // 0x00 note: uint32_t always!

View File

@ -32,9 +32,9 @@ namespace OpenRCT2::Drawing
virtual void DrawSpriteSolid(DrawPixelInfo* dpi, const ImageId image, int32_t x, int32_t y, uint8_t colour) abstract;
virtual void DrawGlyph(
DrawPixelInfo* dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& palette) abstract;
virtual void DrawBitmap(
DrawPixelInfo* dpi, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x,
int32_t y) 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) abstract;
};
} // namespace OpenRCT2::Drawing

View File

@ -365,12 +365,7 @@ static TTFSurface* TTFRender(TTF_Font* font, std::string_view text)
{
thread_local std::string buffer;
buffer.assign(text);
if (TTF_GetFontHinting(font) != 0)
{
return TTF_RenderUTF8_Shaded(font, buffer.c_str(), 0x000000FF, 0x000000FF);
}
return TTF_RenderUTF8_Solid(font, buffer.c_str(), 0x000000FF);
return TTF_RenderUTF8(font, buffer.c_str(), TTF_GetFontHinting(font) != 0);
}
void TTFFreeSurface(TTFSurface* surface)

View File

@ -38,8 +38,7 @@ int TTF_Init(void);
TTF_Font* TTF_OpenFont(const char* file, int ptsize);
int TTF_GlyphIsProvided(const TTF_Font* font, codepoint_t ch);
int TTF_SizeUTF8(TTF_Font* font, const char* text, int* w, int* h);
TTFSurface* TTF_RenderUTF8_Solid(TTF_Font* font, const char* text, uint32_t colour);
TTFSurface* TTF_RenderUTF8_Shaded(TTF_Font* font, const char* text, uint32_t fg, uint32_t bg);
TTFSurface* TTF_RenderUTF8(TTF_Font* font, const char* text, bool shaded);
void TTF_CloseFont(TTF_Font* font);
void TTF_SetFontHinting(TTF_Font* font, int hinting);
int TTF_GetFontHinting(const TTF_Font* font);

View File

@ -1263,7 +1263,7 @@ int TTF_SizeUTF8(TTF_Font* font, const char* text, int* w, int* h)
return status;
}
TTFSurface* TTF_RenderUTF8_Solid(TTF_Font* font, const char* text, [[maybe_unused]] uint32_t colour)
TTFSurface* TTF_RenderUTF8(TTF_Font* font, const char* text, bool shaded)
{
bool first;
int xstart;
@ -1321,7 +1321,7 @@ TTFSurface* TTF_RenderUTF8_Solid(TTF_Font* font, const char* text, [[maybe_unuse
continue;
}
error = Find_Glyph(font, c, CACHED_METRICS | CACHED_BITMAP);
error = Find_Glyph(font, c, CACHED_METRICS | (shaded ? CACHED_PIXMAP : CACHED_BITMAP));
if (error)
{
TTF_SetFTError("Couldn't find glyph", error);
@ -1329,7 +1329,7 @@ TTFSurface* TTF_RenderUTF8_Solid(TTF_Font* font, const char* text, [[maybe_unuse
return NULL;
}
glyph = font->current;
current = &glyph->bitmap;
current = shaded ? &glyph->pixmap : &glyph->bitmap;
/* Ensure the width of the pixmap is correct. On some cases,
* freetype may report a larger pixmap than possible.*/
width = current->width;
@ -1385,152 +1385,20 @@ TTFSurface* TTF_RenderUTF8_Solid(TTF_Font* font, const char* text, [[maybe_unuse
if (TTF_HANDLE_STYLE_UNDERLINE(font))
{
row = TTF_underline_top_row(font);
TTF_drawLine_Solid(font, textbuf, row);
if (shaded)
TTF_drawLine_Shaded(font, textbuf, row);
else
TTF_drawLine_Solid(font, textbuf, row);
}
/* Handle the strikethrough style */
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font))
{
row = TTF_strikethrough_top_row(font);
TTF_drawLine_Solid(font, textbuf, row);
}
return textbuf;
}
TTFSurface* TTF_RenderUTF8_Shaded(TTF_Font* font, const char* text, [[maybe_unused]] uint32_t fg, [[maybe_unused]] uint32_t bg)
{
bool first;
int xstart;
int width;
int height;
TTFSurface* textbuf;
uint8_t* src;
uint8_t* dst;
uint8_t* dst_check;
unsigned int row, col;
FT_Bitmap* current;
c_glyph* glyph;
FT_Error error;
FT_Long use_kerning;
FT_UInt prev_index = 0;
size_t textlen;
TTF_CHECKPOINTER(text, NULL);
/* Get the dimensions of the text surface */
if ((TTF_SizeUTF8(font, text, &width, &height) < 0) || !width)
{
TTF_SetError("Text has zero width");
return NULL;
}
/* Create the target surface */
textbuf = static_cast<TTFSurface*>(calloc(1, sizeof(TTFSurface)));
if (textbuf == NULL)
{
return NULL;
}
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;
/* check kerning */
use_kerning = FT_HAS_KERNING(font->face) && font->kerning;
/* Load and render each character */
textlen = strlen(text);
first = true;
xstart = 0;
while (textlen > 0)
{
uint16_t c = UTF8_getch(&text, &textlen);
if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED)
{
continue;
}
error = Find_Glyph(font, c, CACHED_METRICS | CACHED_PIXMAP);
if (error)
{
TTF_SetFTError("Couldn't find glyph", error);
TTFFreeSurface(textbuf);
return NULL;
}
glyph = font->current;
/* Ensure the width of the pixmap is correct. On some cases,
* freetype may report a larger pixmap than possible.*/
width = glyph->pixmap.width;
if (font->outline <= 0 && width > glyph->maxx - glyph->minx)
{
width = glyph->maxx - glyph->minx;
}
/* do kerning, if possible AC-Patch */
if (use_kerning && prev_index && glyph->index)
{
FT_Vector delta;
FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta);
xstart += delta.x >> 6;
}
/* Compensate for the wrap around with negative minx's */
if (first && (glyph->minx < 0))
{
xstart -= glyph->minx;
}
first = false;
current = &glyph->pixmap;
for (row = 0; row < current->rows; ++row)
{
/* Make sure we don't go either over, or under the
* limit */
if (static_cast<signed>(row) + glyph->yoffset < 0)
{
continue;
}
if (static_cast<signed>(row) + glyph->yoffset >= textbuf->h)
{
continue;
}
dst = const_cast<uint8_t*>(static_cast<const uint8_t*>(textbuf->pixels)) + (row + glyph->yoffset) * textbuf->pitch
+ xstart + glyph->minx;
src = current->buffer + row * current->pitch;
for (col = width; col > 0 && dst < dst_check; --col)
{
*dst++ |= *src++;
}
}
xstart += glyph->advance;
if (TTF_HANDLE_STYLE_BOLD(font))
{
xstart += font->glyph_overhang;
}
prev_index = glyph->index;
}
/* Handle the underline style */
if (TTF_HANDLE_STYLE_UNDERLINE(font))
{
row = TTF_underline_top_row(font);
TTF_drawLine_Shaded(font, textbuf, row);
}
/* Handle the strikethrough style */
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font))
{
row = TTF_strikethrough_top_row(font);
TTF_drawLine_Shaded(font, textbuf, row);
if (shaded)
TTF_drawLine_Shaded(font, textbuf, row);
else
TTF_drawLine_Solid(font, textbuf, row);
}
return textbuf;
}

View File

@ -147,9 +147,9 @@ namespace OpenRCT2
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& paletteMap) override;
void DrawBitmap(
DrawPixelInfo* dpi, uint32_t image, const void* pixels, int32_t width, int32_t height, int32_t x,
int32_t y) 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) override
{
}
};