Merge pull request #21038 from mrmbernardi/opengl-text

Improve OpenGL TTF rendering
This commit is contained in:
mrmbernardi 2023-12-08 01:28:22 +01:00 committed by GitHub
commit 042d555d9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 225 additions and 237 deletions

View File

@ -5,6 +5,7 @@ uniform sampler2D uOpaqueDepth;
uniform usampler2D uTransparentTex;
uniform sampler2D uTransparentDepth;
uniform usampler2D uPaletteTex;
uniform usampler2D uBlendPaletteTex;
in vec2 fTextureCoordinate;
@ -21,6 +22,21 @@ void main()
{
transparent = 0u;
}
oColour = texture(uPaletteTex, vec2(opaque, transparent) / 256.f).r;
uint blendColour = (transparent & 0xff00u) >> 8;
if(blendColour > 0u)
{
if((transparent & 0x00ffu) != 0u)
{
oColour = blendColour;
}
else
{
oColour = texture(uBlendPaletteTex, vec2(opaque, blendColour) / 256.f).r;
}
}
else
{
oColour = texture(uPaletteTex, vec2(opaque, transparent) / 256.f).r;
}
}

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,28 @@ void main()
{
discard;
}
texel += fColour;
if ((fFlags & FLAG_TTF_TEXT) == 0)
{
texel += fColour;
}
else
{
uint hint_thresh = uint(fFlags & 0xff00) >> 8;
if(hint_thresh > 0u)
{
bool solidColor = texel > 180u;
texel = (texel > hint_thresh) ? fColour : 0u;
texel = texel << 8;
if(solidColor)
{
texel += 1u;
}
}
else
{
texel = fColour;
}
}
}
else
{

View File

@ -8,19 +8,21 @@
- Feature: [#20825] Made setting the game speed a game action.
- Feature: [#20853] [Plugin] Add “BaseTileElement.owner” which is saved in the park file.
- Change: [#20790] Default ride price set to free if park charges for entry.
- Fix: [#15293] TTF fonts dont format correctly with OpenGL.
- Fix: [#16453] Tile inspector invisibility shortcut does not use a game action.
- Fix: [#17774] Misplaced/missing land and construction rights tiles in RCT1 & RCT2 scenarios.
- Fix: [#18199] Dots in the game save's name no longer get truncated.
- Fix: [#19722] “Forbid tree removal” restriction doesn't forbid removal of large scenery tree items.
- Fix: [#20253] Crash when displaying a Lay-Down RCs half loop.
- Fix: [#20356] Cannot set tertiary colour on small scenery.
- Fix: [#20624] Scrolling text glitches after language is changed.
- Fix: [#20679] Android: game crashes at launch.
- Fix: [#20737] Spent money in player window underflows when getting refunds.
- Fix: [#20747] Staff speed cheat not applying to newly hired staff, UI not showing the current applied speed.
- Fix: [#20778] [Plugin] Incorrect target api when executing custom actions.
- Fix: [#20807] Tertiary colour not copied with small scenery.
- Fix: [#20964] Crash when player connects to server with a group assigned that no longer exists.
- Fix: [#20624] Scrolling text glitches after language is changed.
- Fix: [#20995] TTF fonts dont support hinting, outlines, or insets with OpenGL.
0.4.6 (2023-09-03)
------------------------------------------------------------------------

View File

@ -54,6 +54,7 @@ ApplyTransparencyShader::ApplyTransparencyShader()
glUniform1i(uTransparentTex, 2);
glUniform1i(uTransparentDepth, 3);
glUniform1i(uPaletteTex, 4);
glUniform1i(uBlendPaletteTex, 5);
}
ApplyTransparencyShader::~ApplyTransparencyShader()
@ -69,19 +70,22 @@ void ApplyTransparencyShader::GetLocations()
uTransparentTex = GetUniformLocation("uTransparentTex");
uTransparentDepth = GetUniformLocation("uTransparentDepth");
uPaletteTex = GetUniformLocation("uPaletteTex");
uBlendPaletteTex = GetUniformLocation("uBlendPaletteTex");
vPosition = GetAttributeLocation("vPosition");
vTextureCoordinate = GetAttributeLocation("vTextureCoordinate");
}
void ApplyTransparencyShader::SetTextures(
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex)
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex,
GLuint blendPaletteTex)
{
OpenGLAPI::SetTexture(0, GL_TEXTURE_2D, opaqueTex);
OpenGLAPI::SetTexture(1, GL_TEXTURE_2D, opaqueDepth);
OpenGLAPI::SetTexture(2, GL_TEXTURE_2D, transparentTex);
OpenGLAPI::SetTexture(3, GL_TEXTURE_2D, transparentDepth);
OpenGLAPI::SetTexture(4, GL_TEXTURE_2D, paletteTex);
OpenGLAPI::SetTexture(5, GL_TEXTURE_2D, blendPaletteTex);
}
void ApplyTransparencyShader::Draw()

View File

@ -20,6 +20,7 @@ private:
GLuint uTransparentTex;
GLuint uTransparentDepth;
GLuint uPaletteTex;
GLuint uBlendPaletteTex;
GLuint vPosition;
GLuint vTextureCoordinate;
@ -32,7 +33,8 @@ public:
~ApplyTransparencyShader() override;
static void SetTextures(
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex);
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex,
GLuint blendPaletteTex);
void Draw();
private:

View File

@ -118,6 +118,9 @@ struct DrawRectCommand
FLAG_NO_TEXTURE = (1u << 2u),
FLAG_MASK = (1u << 3u),
FLAG_CROSS_HATCH = (1u << 4u),
FLAG_TTF_TEXT = (1u << 5u),
// bits 8 to 16 used to store hinting threshold.
FLAG_TTF_HINTING_THRESHOLD_MASK = 0xff00
};
};

View File

@ -109,8 +109,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, uint8_t hinting_threshold) override;
void FlushCommandBuffers();
@ -906,8 +907,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, uint8_t hinting_threshold)
{
CalculcateClipping(dpi);
@ -937,16 +939,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++;
}
auto& cmdBuf = hinting_threshold > 0 ? _commandBuffers.transparent : _commandBuffers.rects;
DrawRectCommand& command = cmdBuf.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 | (hinting_threshold << 8);
command.colour = info->palette[1];
command.bounds = { left, top, right, bottom };
command.depth = _drawCount++;
}
@ -1021,7 +1060,8 @@ void OpenGLDrawingContext::HandleTransparency()
_drawRectShader->Use();
_drawRectShader->DrawInstances();
_swapFramebuffer->ApplyTransparency(*_applyTransparencyShader, _textureCache->GetPaletteTexture());
_swapFramebuffer->ApplyTransparency(
*_applyTransparencyShader, _textureCache->GetPaletteTexture(), _textureCache->GetBlendPaletteTexture());
}
_commandBuffers.transparent.clear();

View File

@ -26,7 +26,7 @@ OpenGLFramebuffer::OpenGLFramebuffer(SDL_Window* window)
SDL_GL_GetDrawableSize(window, &_width, &_height);
}
OpenGLFramebuffer::OpenGLFramebuffer(int32_t width, int32_t height, bool depth, bool integer)
OpenGLFramebuffer::OpenGLFramebuffer(int32_t width, int32_t height, bool depth, bool integer, bool word)
{
_width = width;
_height = height;
@ -35,7 +35,9 @@ OpenGLFramebuffer::OpenGLFramebuffer(int32_t width, int32_t height, bool depth,
glBindTexture(GL_TEXTURE_2D, _texture);
if (integer)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, nullptr);
int internalFormat = word ? GL_R16UI : GL_R8UI;
int type = word ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, GL_RED_INTEGER, type, nullptr);
}
else
{

View File

@ -28,7 +28,7 @@ private:
public:
explicit OpenGLFramebuffer(SDL_Window* window);
OpenGLFramebuffer(int32_t width, int32_t height, bool depth = true, bool integer = true);
OpenGLFramebuffer(int32_t width, int32_t height, bool depth = true, bool integer = true, bool word = false);
~OpenGLFramebuffer();
OpenGLFramebuffer(const OpenGLFramebuffer&) = delete;

View File

@ -19,7 +19,7 @@ constexpr GLuint indexValue[4] = { 0, 0, 0, 0 };
SwapFramebuffer::SwapFramebuffer(int32_t width, int32_t height)
: _opaqueFramebuffer(width, height)
, _transparentFramebuffer(width, height)
, _transparentFramebuffer(width, height, true, true, true)
, _mixFramebuffer(width, height, false)
, _backDepth(OpenGLFramebuffer::CreateDepthTexture(width, height))
{
@ -32,14 +32,14 @@ SwapFramebuffer::~SwapFramebuffer()
glDeleteTextures(1, &_backDepth);
}
void SwapFramebuffer::ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex)
void SwapFramebuffer::ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex, GLuint blendPaletteTex)
{
_mixFramebuffer.Bind();
glDisable(GL_DEPTH_TEST);
shader.Use();
shader.SetTextures(
_opaqueFramebuffer.GetTexture(), _opaqueFramebuffer.GetDepthTexture(), _transparentFramebuffer.GetTexture(),
_transparentFramebuffer.GetDepthTexture(), paletteTex);
_transparentFramebuffer.GetDepthTexture(), paletteTex, blendPaletteTex);
shader.Draw();
_backDepth = _transparentFramebuffer.SwapDepthTexture(_backDepth);

View File

@ -52,6 +52,6 @@ public:
_transparentFramebuffer.Bind();
}
void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex);
void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex, GLuint blendPaletteTex);
void Clear();
};

View File

@ -13,6 +13,7 @@
# include <algorithm>
# include <openrct2/drawing/Drawing.h>
# include <openrct2/interface/Colour.h>
# include <openrct2/util/Util.h>
# include <openrct2/world/Location.hpp>
# include <stdexcept>
@ -193,6 +194,18 @@ void TextureCache::CreateTextures()
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GeneratePaletteTexture();
auto blendArray = GetBlendColourMap();
if (blendArray != nullptr)
{
glGenTextures(1, &_blendPaletteTexture);
glBindTexture(GL_TEXTURE_2D, _blendPaletteTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_R8UI, PALETTE_SIZE, PALETTE_SIZE, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, blendArray);
}
_initialized = true;
_atlasesTextureIndices = 0;
_atlasesTextureCapacity = 0;
@ -414,6 +427,11 @@ GLuint TextureCache::GetPaletteTexture()
return _paletteTexture;
}
GLuint TextureCache::GetBlendPaletteTexture()
{
return _blendPaletteTexture;
}
GLint TextureCache::PaletteToY(FilterPaletteID palette)
{
return palette > FilterPaletteID::PaletteWater ? EnumValue(palette) + 5 : EnumValue(palette) + 1;

View File

@ -206,6 +206,7 @@ private:
std::array<uint32_t, SPR_IMAGE_LIST_END> _indexMap;
GLuint _paletteTexture = 0;
GLuint _blendPaletteTexture = 0;
#ifndef __MACOSX__
std::shared_mutex _mutex;
@ -227,6 +228,7 @@ public:
GLuint GetAtlasesTexture();
GLuint GetPaletteTexture();
GLuint GetBlendPaletteTexture();
static GLint PaletteToY(FilterPaletteID palette);
private:

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,35 +537,25 @@ 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;
}
}
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->DrawBitmap(&dpi, imageId, surface->pixels, surface->pitch, surface->h, drawX, drawY);
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;
}
@ -670,7 +634,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 +655,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, uint8_t hinting_threshold) 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, uint8_t hinting_threshold) override
{
}
};

View File

@ -130,7 +130,9 @@ namespace Colour
} // namespace Colour
#ifndef NO_TTF
static uint8_t BlendColourMap[PALETTE_COUNT][PALETTE_COUNT] = { 0 };
static BlendColourMapType BlendColourMap = { 0 };
static bool BlendColourMapInitialised = false;
static uint8_t FindClosestPaletteIndex(uint8_t red, uint8_t green, uint8_t blue)
{
@ -152,21 +154,44 @@ static uint8_t FindClosestPaletteIndex(uint8_t red, uint8_t green, uint8_t blue)
return closest;
}
static void InitBlendColourMap()
{
for (int i = 0; i < PALETTE_SIZE; i++)
{
for (int j = i; j < PALETTE_SIZE; j++)
{
uint8_t red = (gPalette[i].Red + gPalette[j].Red) / 2;
uint8_t green = (gPalette[i].Green + gPalette[j].Green) / 2;
uint8_t blue = (gPalette[i].Blue + gPalette[j].Blue) / 2;
auto colour = FindClosestPaletteIndex(red, green, blue);
BlendColourMap[i][j] = colour;
BlendColourMap[j][i] = colour;
}
}
BlendColourMapInitialised = true;
}
BlendColourMapType* GetBlendColourMap()
{
if (!BlendColourMapInitialised)
{
InitBlendColourMap();
}
return &BlendColourMap;
}
uint8_t BlendColours(const uint8_t paletteIndex1, const uint8_t paletteIndex2)
{
const uint8_t cMin = std::min(paletteIndex1, paletteIndex2);
const uint8_t cMax = std::max(paletteIndex1, paletteIndex2);
if (BlendColourMap[cMin][cMax] != 0)
if (!BlendColourMapInitialised)
{
return BlendColourMap[cMin][cMax];
InitBlendColourMap();
}
uint8_t red = (gPalette[cMin].Red + gPalette[cMax].Red) / 2;
uint8_t green = (gPalette[cMin].Green + gPalette[cMax].Green) / 2;
uint8_t blue = (gPalette[cMin].Blue + gPalette[cMax].Blue) / 2;
BlendColourMap[cMin][cMax] = FindClosestPaletteIndex(red, green, blue);
return BlendColourMap[cMin][cMax];
return BlendColourMap[paletteIndex1][paletteIndex2];
}
#else
BlendColourMapType* GetBlendColourMap()
{
return nullptr;
}
#endif

View File

@ -252,3 +252,6 @@ namespace Colour
#ifndef NO_TTF
uint8_t BlendColours(const uint8_t paletteIndex1, const uint8_t paletteIndex2);
#endif
typedef uint8_t BlendColourMapType[PALETTE_COUNT][PALETTE_COUNT];
BlendColourMapType* GetBlendColourMap();