From f64706e1d9760f4cd17e8645295fa821bf6d5c20 Mon Sep 17 00:00:00 2001 From: Michael Bernardi Date: Sun, 3 Dec 2023 03:15:46 +0100 Subject: [PATCH 1/2] Add insetting and outlining to OpenGL TTF rendering --- data/shaders/drawrect.frag | 10 +- .../drawing/engines/opengl/DrawCommands.h | 1 + .../engines/opengl/OpenGLDrawingEngine.cpp | 54 +++++- src/openrct2/drawing/Drawing.String.cpp | 49 +----- src/openrct2/drawing/Drawing.h | 26 +++ src/openrct2/drawing/IDrawingContext.h | 6 +- src/openrct2/drawing/TTF.cpp | 7 +- src/openrct2/drawing/TTF.h | 3 +- src/openrct2/drawing/TTFSDLPort.cpp | 154 ++---------------- src/openrct2/drawing/X8DrawingEngine.h | 6 +- 10 files changed, 106 insertions(+), 210 deletions(-) diff --git a/data/shaders/drawrect.frag b/data/shaders/drawrect.frag index be6bed1a80..f5a26749bd 100644 --- a/data/shaders/drawrect.frag +++ b/data/shaders/drawrect.frag @@ -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 { diff --git a/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h b/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h index ee4802fcf8..2becc8f8bb 100644 --- a/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h +++ b/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h @@ -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), }; }; diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp index 2372e3dff9..b18a8f4e42 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -23,6 +23,7 @@ # include # include +# include # include # include # include @@ -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 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++; } diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index 45ad5dab29..86cee0a5ee 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -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(const_cast(surface->pixels)); - auto pixelsLen = static_cast(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 { diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index de2196b1d2..ebacfe38fc 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -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! diff --git a/src/openrct2/drawing/IDrawingContext.h b/src/openrct2/drawing/IDrawingContext.h index 273b72ca89..096fdb0f69 100644 --- a/src/openrct2/drawing/IDrawingContext.h +++ b/src/openrct2/drawing/IDrawingContext.h @@ -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 diff --git a/src/openrct2/drawing/TTF.cpp b/src/openrct2/drawing/TTF.cpp index e1749579a7..780d248de1 100644 --- a/src/openrct2/drawing/TTF.cpp +++ b/src/openrct2/drawing/TTF.cpp @@ -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) diff --git a/src/openrct2/drawing/TTF.h b/src/openrct2/drawing/TTF.h index 9cdf83fc94..b793c3ce96 100644 --- a/src/openrct2/drawing/TTF.h +++ b/src/openrct2/drawing/TTF.h @@ -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); diff --git a/src/openrct2/drawing/TTFSDLPort.cpp b/src/openrct2/drawing/TTFSDLPort.cpp index e27207919f..f26b35b86c 100644 --- a/src/openrct2/drawing/TTFSDLPort.cpp +++ b/src/openrct2/drawing/TTFSDLPort.cpp @@ -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(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(static_cast(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(row) + glyph->yoffset < 0) - { - continue; - } - if (static_cast(row) + glyph->yoffset >= textbuf->h) - { - continue; - } - - dst = const_cast(static_cast(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; } diff --git a/src/openrct2/drawing/X8DrawingEngine.h b/src/openrct2/drawing/X8DrawingEngine.h index 15af40cc0a..a11aa9d8c8 100644 --- a/src/openrct2/drawing/X8DrawingEngine.h +++ b/src/openrct2/drawing/X8DrawingEngine.h @@ -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 { } }; From 8c7d1d149aec8dd6f997b51ddbeb33dcd6a300bc Mon Sep 17 00:00:00 2001 From: Michael Bernardi Date: Sun, 3 Dec 2023 18:58:28 +0100 Subject: [PATCH 2/2] Added OpenGL TTF hinting --- data/shaders/applytransparency.frag | 20 +++++++- data/shaders/drawrect.frag | 16 +++++- distribution/changelog.txt | 4 +- .../opengl/ApplyTransparencyShader.cpp | 6 ++- .../engines/opengl/ApplyTransparencyShader.h | 4 +- .../drawing/engines/opengl/DrawCommands.h | 2 + .../engines/opengl/OpenGLDrawingEngine.cpp | 14 ++--- .../engines/opengl/OpenGLFramebuffer.cpp | 6 ++- .../engines/opengl/OpenGLFramebuffer.h | 2 +- .../engines/opengl/SwapFramebuffer.cpp | 6 +-- .../drawing/engines/opengl/SwapFramebuffer.h | 2 +- .../drawing/engines/opengl/TextureCache.cpp | 18 +++++++ .../drawing/engines/opengl/TextureCache.h | 2 + src/openrct2/drawing/Drawing.String.cpp | 6 ++- src/openrct2/drawing/IDrawingContext.h | 2 +- src/openrct2/drawing/X8DrawingEngine.h | 2 +- src/openrct2/interface/Colour.cpp | 51 ++++++++++++++----- src/openrct2/interface/Colour.h | 3 ++ 18 files changed, 129 insertions(+), 37 deletions(-) diff --git a/data/shaders/applytransparency.frag b/data/shaders/applytransparency.frag index faa5f770d1..77656804fe 100644 --- a/data/shaders/applytransparency.frag +++ b/data/shaders/applytransparency.frag @@ -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; + } } diff --git a/data/shaders/drawrect.frag b/data/shaders/drawrect.frag index f5a26749bd..b09983ba85 100644 --- a/data/shaders/drawrect.frag +++ b/data/shaders/drawrect.frag @@ -48,7 +48,21 @@ void main() } else { - texel = fColour; + 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 diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 788b574062..96fe242970 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -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 don’t 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 RC’s 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 don’t support hinting, outlines, or insets with OpenGL. 0.4.6 (2023-09-03) ------------------------------------------------------------------------ diff --git a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp index 6a06d3eeb6..0d5a926afd 100644 --- a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp @@ -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() diff --git a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h index b655324f29..14d545c990 100644 --- a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h +++ b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h @@ -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: diff --git a/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h b/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h index 2becc8f8bb..ab2b3915bf 100644 --- a/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h +++ b/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h @@ -119,6 +119,8 @@ struct DrawRectCommand 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 }; }; diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp index b18a8f4e42..155e6fa8fe 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -23,7 +23,6 @@ # include # include -# include # include # include # include @@ -112,7 +111,7 @@ public: 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) override; + int32_t y, uint8_t hinting_threshold) override; void FlushCommandBuffers(); @@ -910,7 +909,7 @@ 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) + int32_t y, uint8_t hinting_threshold) { CalculcateClipping(dpi); @@ -977,15 +976,15 @@ void OpenGLDrawingContext::DrawTTFBitmap( command.bounds = { left + 1, top + 1, right + 1, bottom + 1 }; command.depth = _drawCount++; } - - DrawRectCommand& command = _commandBuffers.rects.allocate(); + 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 = DrawRectCommand::FLAG_TTF_TEXT; + command.flags = DrawRectCommand::FLAG_TTF_TEXT | (hinting_threshold << 8); command.colour = info->palette[1]; command.bounds = { left, top, right, bottom }; command.depth = _drawCount++; @@ -1061,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(); diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp index 6fb950c7df..f9129d4321 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp @@ -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 { diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h index 7be132d526..0a1f07fec5 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h @@ -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; diff --git a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp index 4a49bc6350..307b547791 100644 --- a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp @@ -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); diff --git a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h index db1e0f031a..97facb813b 100644 --- a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h +++ b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h @@ -52,6 +52,6 @@ public: _transparentFramebuffer.Bind(); } - void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex); + void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex, GLuint blendPaletteTex); void Clear(); }; diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp index 5791d2ae23..a7a24e7388 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp @@ -13,6 +13,7 @@ # include # include +# include # include # include # include @@ -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; diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h index a16a03b4fc..26c8c9bc54 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h @@ -206,6 +206,7 @@ private: std::array _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: diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index 86cee0a5ee..303283877f 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -541,19 +541,21 @@ static void TTFDrawStringRawTTF(DrawPixelInfo& dpi, std::string_view text, TextD if (OpenRCT2::GetContext()->GetDrawingEngineType() == DrawingEngine::OpenGL) { - // 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(); + 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); + 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; } diff --git a/src/openrct2/drawing/IDrawingContext.h b/src/openrct2/drawing/IDrawingContext.h index 096fdb0f69..a7966565db 100644 --- a/src/openrct2/drawing/IDrawingContext.h +++ b/src/openrct2/drawing/IDrawingContext.h @@ -34,7 +34,7 @@ namespace OpenRCT2::Drawing 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) abstract; + int32_t x, int32_t y, uint8_t hinting_threshold) abstract; }; } // namespace OpenRCT2::Drawing diff --git a/src/openrct2/drawing/X8DrawingEngine.h b/src/openrct2/drawing/X8DrawingEngine.h index a11aa9d8c8..df545fa2a2 100644 --- a/src/openrct2/drawing/X8DrawingEngine.h +++ b/src/openrct2/drawing/X8DrawingEngine.h @@ -149,7 +149,7 @@ namespace OpenRCT2 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) override + int32_t x, int32_t y, uint8_t hinting_threshold) override { } }; diff --git a/src/openrct2/interface/Colour.cpp b/src/openrct2/interface/Colour.cpp index db80e464de..f3e2e7b49a 100644 --- a/src/openrct2/interface/Colour.cpp +++ b/src/openrct2/interface/Colour.cpp @@ -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 diff --git a/src/openrct2/interface/Colour.h b/src/openrct2/interface/Colour.h index 57eb414a70..083b07fd16 100644 --- a/src/openrct2/interface/Colour.h +++ b/src/openrct2/interface/Colour.h @@ -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(); \ No newline at end of file