Skip to content

Commit

Permalink
Add strokePercentage parameter to Font in order to print outlined text
Browse files Browse the repository at this point in the history
  • Loading branch information
jhasse committed Apr 25, 2024
1 parent b5cbe36 commit 84b7692
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 14 deletions.
25 changes: 20 additions & 5 deletions src/freetype.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2007-2022 Jan Niklas Hasse <jhasse@bixense.com>
// Copyright 2007-2024 Jan Niklas Hasse <jhasse@bixense.com>
// For conditions of distribution and use, see copyright notice in LICENSE.txt

#define _LIBCPP_DISABLE_DEPRECATION_WARNINGS
Expand All @@ -24,8 +24,9 @@

namespace jngl {

Character::Character(const char32_t ch, const unsigned int fontHeight, FT_Face face) {
const auto flags = FT_LOAD_TARGET_LIGHT | FT_LOAD_RENDER;
Character::Character(const char32_t ch, const unsigned int fontHeight, FT_Face face,
FT_Stroker stroker) {
const auto flags = FT_LOAD_TARGET_LIGHT | FT_LOAD_DEFAULT;
if (FT_Load_Char(face, ch, flags)) {
const std::string msg =
std::string("FT_Load_Glyph failed. Character: ") + std::to_string(ch);
Expand All @@ -39,6 +40,10 @@ Character::Character(const char32_t ch, const unsigned int fontHeight, FT_Face f
if (FT_Get_Glyph(face->glyph, &glyph)) {
throw std::runtime_error("FT_Get_Glyph failed");
}
if (stroker) {
FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
}
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, 1);
Finally freeGlyph([&]() { FT_Done_Glyph(glyph); });
const auto bitmap_glyph = reinterpret_cast<FT_BitmapGlyph>(glyph); // NOLINT
const FT_Bitmap& bitmap = bitmap_glyph->bitmap;
Expand Down Expand Up @@ -134,12 +139,12 @@ Character& FontImpl::GetCharacter(std::string::iterator& it, const std::string::
}
if (characters_[unicodeCharacter] == nullptr) {
characters_[unicodeCharacter] =
std::make_shared<Character>(unicodeCharacter, height_, face);
std::make_shared<Character>(unicodeCharacter, height_, face, stroker);
}
return *(characters_[unicodeCharacter]);
}

FontImpl::FontImpl(const std::string& relativeFilename, unsigned int height)
FontImpl::FontImpl(const std::string& relativeFilename, unsigned int height, float strokePercentage)
: height_(static_cast<unsigned int>(height * getScaleFactor())),
lineHeight(static_cast<int>(height_ * LINE_HEIGHT_FACOTR)) {
auto filename = pathPrefix + relativeFilename;
Expand Down Expand Up @@ -191,10 +196,20 @@ FontImpl::FontImpl(const std::string& relativeFilename, unsigned int height)
// in terms of 1/64ths of pixels. Thus, to make a font
// h pixels high, we need to request a size of h*64.
FT_Set_Char_Size(face, height_ * 64, height_ * 64, 96, 96);

FT_Fixed strokeWidth = std::lround(strokePercentage * static_cast<float>(height) * 0.64);
if (strokeWidth != 0) {
FT_Stroker_New(library, &stroker);
FT_Stroker_Set(stroker, strokeWidth, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND,
0);
}
}

FontImpl::~FontImpl() {
freeFace.reset(); // free face_ with FT_Done_Face
if (stroker) {
FT_Stroker_Done(stroker);
}
if (--instanceCounter == 0) {
FT_Done_FreeType(library);
}
Expand Down
8 changes: 5 additions & 3 deletions src/freetype.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2007-2022 Jan Niklas Hasse <jhasse@bixense.com>
// Copyright 2007-2024 Jan Niklas Hasse <jhasse@bixense.com>
// For conditions of distribution and use, see copyright notice in LICENSE.txt

#pragma once
Expand All @@ -8,6 +8,7 @@

#include <ft2build.h> // NOLINT
#include FT_FREETYPE_H
#include FT_STROKER_H

#include <map>

Expand All @@ -18,7 +19,7 @@ extern unsigned char fontColorRed, fontColorGreen, fontColorBlue, fontColorAlpha

class Character {
public:
Character(char32_t ch, unsigned int fontHeight, FT_Face);
Character(char32_t ch, unsigned int fontHeight, FT_Face, FT_Stroker);
Character(const Character&) = delete;
Character& operator=(const Character&) = delete;
Character(Character&&) = delete;
Expand All @@ -36,7 +37,7 @@ class Character {

class FontImpl {
public:
FontImpl(const std::string& relativeFilename, unsigned int height);
FontImpl(const std::string& relativeFilename, unsigned int height, float strokePercentage);
FontImpl(const FontImpl&) = delete;
FontImpl& operator=(const FontImpl&) = delete;
FontImpl(FontImpl&&) = delete;
Expand All @@ -54,6 +55,7 @@ class FontImpl {
static int instanceCounter;
static FT_Library library;
FT_Face face = nullptr;
FT_Stroker stroker = nullptr;
std::unique_ptr<Finally> freeFace; // Frees face_ if necessary
unsigned int height_;
int lineHeight;
Expand Down
3 changes: 2 additions & 1 deletion src/jngl/font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ void popFontColor() {
rgbs.pop();
}

Font::Font(const std::string& filename, unsigned int size) : impl(new FontImpl(filename, size)) {
Font::Font(const std::string& filename, unsigned int size, float strokePercentage)
: impl(new FontImpl(filename, size, strokePercentage)) {
}

void Font::print(const std::string& text, int x, int y) {
Expand Down
9 changes: 8 additions & 1 deletion src/jngl/font.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ class Mat3;
class Font {
public:
/// Creates a font from \a filename in \a size px
Font(const std::string& filename, unsigned int size);
///
/// You may set \a strokePercentage to a positive or negative % value to increase or decrease
/// the font size, without actually changing the position/size of the individual characters.
/// This can be used to draw an outlined text by e.g. first drawing with a 5% stroke and then
/// printing the same text with 0% (or even a negative stroke) over it.
/// Example: To increase the size of each character by 2 pixel for a Font with a \a size of 20
/// pixel, you would pass 10.f for \a strokePercentage.
Font(const std::string& filename, unsigned int size, float strokePercentage = 0);

/// Uses the font to print something at \a x \a y. The color can be specified using setFontColor.
void print(const std::string&, int x, int y);
Expand Down
16 changes: 13 additions & 3 deletions src/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ class Test : public jngl::Work {
if (jngl::keyPressed('g')) {
jngl::setWork<AsyncLoad>();
}
if (jngl::keyPressed('e')) {
jngl::errorMessage("Hello World!");
}
}
void drawBackground() const;
void draw() const override {
Expand Down Expand Up @@ -168,9 +171,14 @@ class Test : public jngl::Work {
jngl::print(" $", static_cast<int>(jngl::getTextWidth("UTF-8: ä ö ü ß Ĉ Ψ ≈") + 5), 105);
jngl::setFontSize(12);
jngl::print("Press 1-9 to test the performance\nPress E to show a error box.", 5, 135);
if (jngl::keyPressed('e')) {
jngl::errorMessage("Hello World!");
}

auto mv = jngl::modelview().translate({ 5, 175 });
jngl::setFontColor(static_cast<unsigned char>(255 * (1 - factor)),
static_cast<unsigned char>(255 * factor), 255);
fontStroke.print(mv, "Text with outline.");
jngl::setFontColor(0x000000_rgb);
fontNormal.print(mv, "Text with outline.");

jngl::print("Press S to use the blur shader.", 5, 390);
jngl::print("Press F to turn drawing on a FBO " + std::string(drawOnFrameBuffer ? "off" : "on") + ".", 5, 410);
jngl::print("Press V to toggle V-SYNC.", 5, 430);
Expand Down Expand Up @@ -251,6 +259,8 @@ class Test : public jngl::Work {
jngl::Finally soundLoader;
std::optional<jngl::Finally> paused;
std::unique_ptr<jngl::Channel> music;
jngl::Font fontNormal{ "Arial.ttf", 12 };
jngl::Font fontStroke{ "Arial.ttf", 12, 5 };
};

jngl::AppParameters jnglInit() {
Expand Down
2 changes: 1 addition & 1 deletion src/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void Window::print(const std::string& text, const int xposition, const int yposi

void Window::setFont(const std::string& filename) {
if (fonts_[fontSize_].find(filename) == fonts_[fontSize_].end()) {
auto font = std::make_shared<FontImpl>(filename, fontSize_);
auto font = std::make_shared<FontImpl>(filename, fontSize_, 0);
fonts_[fontSize_][filename] = font;
}
fontName_ = filename;
Expand Down

0 comments on commit 84b7692

Please sign in to comment.