Skip to content

Commit

Permalink
Unify the MeasureString caching code
Browse files Browse the repository at this point in the history
  • Loading branch information
hrydgard committed Oct 16, 2024
1 parent 3def2dd commit e49d94a
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 203 deletions.
29 changes: 29 additions & 0 deletions Common/Render/Text/draw_text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,35 @@ void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, f
target.Flush(true);
}

void TextDrawer::MeasureString(std::string_view str, float *w, float *h) {
if (str.empty()) {
*w = 0.0;
*h = 0.0;
return;
}

CacheKey key{ std::string(str), fontHash_ };

TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);
if (iter != sizeCache_.end()) {
entry = iter->second.get();
} else {
entry = new TextMeasureEntry();
float extW, extH;
MeasureStringInternal(str, &extW, &extH);
entry->width = extW;
entry->height = extH;
// Hm, use the old calculation?
// int h = i == lines.size() - 1 ? entry->height : metrics.tmHeight + metrics.tmExternalLeading;
sizeCache_[key] = std::unique_ptr<TextMeasureEntry>(entry);
}

entry->lastUsedFrame = frameCount_;
*w = entry->width * fontScaleX_ * dpiScale_;
*h = entry->height * fontScaleY_ * dpiScale_;
}

void TextDrawer::MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align) {
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);

Expand Down
7 changes: 5 additions & 2 deletions Common/Render/Text/draw_text.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class TextDrawer {
virtual uint32_t SetFont(const char *fontName, int size, int flags) = 0;
virtual void SetFont(uint32_t fontHandle) = 0; // Shortcut once you've set the font once.
void SetFontScale(float xscale, float yscale);
virtual void MeasureString(std::string_view str, float *w, float *h) = 0;
virtual void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT);
void MeasureString(std::string_view str, float *w, float *h);
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT);

void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT);
void DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align);
Expand All @@ -69,6 +69,9 @@ class TextDrawer {

protected:
TextDrawer(Draw::DrawContext *draw);

virtual void MeasureStringInternal(std::string_view str, float *w, float *h) = 0;

void ClearCache();

virtual bool SupportsColorEmoji() const = 0;
Expand Down
48 changes: 15 additions & 33 deletions Common/Render/Text/draw_text_android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,41 +75,23 @@ void TextDrawerAndroid::SetFont(uint32_t fontHandle) {
}
}

void TextDrawerAndroid::MeasureString(std::string_view str, float *w, float *h) {
if (str.empty()) {
*w = 0.0;
*h = 0.0;
return;
}

CacheKey key{ std::string(str), fontHash_ };
TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);
if (iter != sizeCache_.end()) {
entry = iter->second.get();
void TextDrawerAndroid::MeasureStringInternal(std::string_view str, float *w, float *h) {
float scaledSize = 14;
auto iter = fontMap_.find(fontHash_);
if (iter != fontMap_.end()) {
scaledSize = iter->second.size;
} else {
float scaledSize = 14;
auto iter = fontMap_.find(fontHash_);
if (iter != fontMap_.end()) {
scaledSize = iter->second.size;
} else {
ERROR_LOG(Log::G3D, "Missing font");
}
std::string text(str);
auto env = getEnv();
// Unfortunate that we can't create a jstr from a std::string_view directly.
jstring jstr = env->NewStringUTF(text.c_str());
uint32_t size = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, scaledSize);
env->DeleteLocalRef(jstr);

entry = new TextMeasureEntry();
entry->width = (size >> 16);
entry->height = (size & 0xFFFF);
sizeCache_[key] = std::unique_ptr<TextMeasureEntry>(entry);
ERROR_LOG(Log::G3D, "Missing font");
}
entry->lastUsedFrame = frameCount_;
*w = entry->width * fontScaleX_ * dpiScale_;
*h = entry->height * fontScaleY_ * dpiScale_;
std::string text(str);
auto env = getEnv();
// Unfortunate that we can't create a jstr from a std::string_view directly.
jstring jstr = env->NewStringUTF(text.c_str());
uint32_t size = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, scaledSize);
env->DeleteLocalRef(jstr);

*w = (size >> 16);
*h = (size & 0xFFFF);
}

bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
Expand Down
2 changes: 1 addition & 1 deletion Common/Render/Text/draw_text_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ class TextDrawerAndroid : public TextDrawer {
bool IsReady() const override;
uint32_t SetFont(const char *fontName, int size, int flags) override;
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
void MeasureString(std::string_view str, float *w, float *h) override;
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;

protected:
void MeasureStringInternal(std::string_view str, float *w, float *h) override;
bool SupportsColorEmoji() const override { return true; }

void ClearFonts() override;
Expand Down
2 changes: 1 addition & 1 deletion Common/Render/Text/draw_text_cocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ class TextDrawerCocoa : public TextDrawer {

uint32_t SetFont(const char *fontName, int size, int flags) override;
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
void MeasureString(std::string_view str, float *w, float *h) override;
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;

protected:
void MeasureStringInternal(std::string_view str, float *w, float *h) override;
bool SupportsColorEmoji() const override { return true; }

void ClearFonts() override;
Expand Down
61 changes: 23 additions & 38 deletions Common/Render/Text/draw_text_cocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -117,50 +117,35 @@ void Destroy() {
fontMap_.clear();
}

void TextDrawerCocoa::MeasureString(std::string_view str, float *w, float *h) {
CacheKey key{ std::string(str), fontHash_ };

TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);
if (iter != sizeCache_.end()) {
entry = iter->second.get();
} else {
// INFO_LOG(Log::System, "Measuring %.*s", (int)str.length(), str.data());

auto iter = fontMap_.find(fontHash_);
NSDictionary *attributes = nil;
if (iter != fontMap_.end()) {
attributes = iter->second->attributes;
}
void TextDrawerCocoa::MeasureStringInternal(std::string_view str, float *w, float *h) {
// INFO_LOG(Log::System, "Measuring %.*s", (int)str.length(), str.data());
auto iter = fontMap_.find(fontHash_);
NSDictionary *attributes = nil;
if (iter != fontMap_.end()) {
attributes = iter->second->attributes;
}

std::vector<std::string_view> lines;
SplitString(str, '\n', lines);
std::vector<std::string_view> lines;
SplitString(str, '\n', lines);

int extW = 0, extH = 0;
for (auto &line : lines) {
NSString *string = [[NSString alloc] initWithBytes:line.data() length:line.size() encoding: NSUTF8StringEncoding];
NSAttributedString* as = [[NSAttributedString alloc] initWithString:string attributes:attributes];
CTLineRef ctline = CTLineCreateWithAttributedString((CFAttributedStringRef)as);
CGFloat ascent, descent, leading;
double fWidth = CTLineGetTypographicBounds(ctline, &ascent, &descent, &leading);
int extW = 0, extH = 0;
for (auto &line : lines) {
NSString *string = [[NSString alloc] initWithBytes:line.data() length:line.size() encoding: NSUTF8StringEncoding];
NSAttributedString* as = [[NSAttributedString alloc] initWithString:string attributes:attributes];
CTLineRef ctline = CTLineCreateWithAttributedString((CFAttributedStringRef)as);
CGFloat ascent, descent, leading;
double fWidth = CTLineGetTypographicBounds(ctline, &ascent, &descent, &leading);

size_t width = (size_t)ceilf(fWidth);
size_t height = (size_t)ceilf(ascent + descent);
size_t width = (size_t)ceilf(fWidth);
size_t height = (size_t)ceilf(ascent + descent);

if (width > extW)
extW = width;
extH += height;
}

entry = new TextMeasureEntry();
entry->width = extW;
entry->height = extH;
sizeCache_[key] = std::unique_ptr<TextMeasureEntry>(entry);
if (width > extW)
extW = width;
extH += height;
}

entry->lastUsedFrame = frameCount_;
*w = entry->width * fontScaleX_ * dpiScale_;
*h = entry->height * fontScaleY_ * dpiScale_;
*w = extW;
*h = extH;
}

bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
Expand Down
26 changes: 6 additions & 20 deletions Common/Render/Text/draw_text_qt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,13 @@ void TextDrawerQt::SetFont(uint32_t fontHandle) {
}
}

void TextDrawerQt::MeasureString(std::string_view str, float *w, float *h) {
CacheKey key{ std::string(str), fontHash_ };

TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);
if (iter != sizeCache_.end()) {
entry = iter->second.get();
} else {
QFont* font = fontMap_.find(fontHash_)->second;
QFontMetrics fm(*font);
QSize size = fm.size(0, QString::fromUtf8(str.data(), str.length()));

entry = new TextMeasureEntry();
entry->width = size.width();
entry->height = size.height();
sizeCache_[key] = std::unique_ptr<TextMeasureEntry>(entry);
}
void TextDrawerQt::MeasureStringInternal(std::string_view str, float *w, float *h) {
QFont* font = fontMap_.find(fontHash_)->second;
QFontMetrics fm(*font);
QSize size = fm.size(0, QString::fromUtf8(str.data(), str.length()));

entry->lastUsedFrame = frameCount_;
*w = entry->width * fontScaleX_ * dpiScale_;
*h = entry->height * fontScaleY_ * dpiScale_;
*w = size.width();
*h = size.height();
}

bool TextDrawerQt::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
Expand Down
2 changes: 1 addition & 1 deletion Common/Render/Text/draw_text_qt.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ class TextDrawerQt : public TextDrawer {

uint32_t SetFont(const char *fontName, int size, int flags) override;
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
void MeasureString(std::string_view str, float *w, float *h) override;
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;

protected:
void MeasureStringInternal(std::string_view str, float *w, float *h) override;
bool SupportsColorEmoji() const override { return false; }

void ClearFonts() override;
Expand Down
51 changes: 19 additions & 32 deletions Common/Render/Text/draw_text_sdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,13 @@ void TextDrawerSDL::PrepareFallbackFonts(std::string_view locale) {
#endif
}

uint32_t TextDrawerSDL::CheckMissingGlyph(const std::string& text) {
uint32_t TextDrawerSDL::CheckMissingGlyph(std::string_view text) {
TTF_Font *font = fontMap_.find(fontHash_)->second;
UTF8 utf8Decoded(text.c_str());
UTF8 utf8Decoded(text);

uint32_t missingGlyph = 0;
for (int i = 0; i < text.length(); ) {
while (!utf8Decoded.end()) {
uint32_t glyph = utf8Decoded.next();
i = utf8Decoded.byteIndex();

if (!TTF_GlyphIsProvided32(font, glyph)) {
missingGlyph = glyph;
break;
Expand Down Expand Up @@ -279,39 +277,28 @@ void TextDrawerSDL::SetFont(uint32_t fontHandle) {
}
}

void TextDrawerSDL::MeasureString(std::string_view str, float *w, float *h) {
CacheKey key{ std::string(str), fontHash_ };

TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);
if (iter != sizeCache_.end()) {
entry = iter->second.get();
} else {
TTF_Font *font = fontMap_.find(fontHash_)->second;
int ptSize = TTF_FontHeight(font) / 1.35;
void TextDrawerSDL::MeasureStringInternal(std::string_view str, float *w, float *h) {
TTF_Font *font = fontMap_.find(fontHash_)->second;
int ptSize = TTF_FontHeight(font) / 1.35;

uint32_t missingGlyph = CheckMissingGlyph(key.text);
uint32_t missingGlyph = CheckMissingGlyph(str);

if (missingGlyph) {
int fallbackFont = FindFallbackFonts(missingGlyph, ptSize);
if (fallbackFont >= 0) {
font = fallbackFonts_[fallbackFont];
}
if (missingGlyph) {
int fallbackFont = FindFallbackFonts(missingGlyph, ptSize);
if (fallbackFont >= 0) {
font = fallbackFonts_[fallbackFont];
}
}

int width = 0;
int height = 0;
TTF_SizeUTF8(font, key.text.c_str(), &width, &height);
int width = 0;
int height = 0;

entry = new TextMeasureEntry();
entry->width = width;
entry->height = height;
sizeCache_[key] = std::unique_ptr<TextMeasureEntry>(entry);
}
// Unfortunately we need to zero-terminate here.
std::string text(str);
TTF_SizeUTF8(font, text.c_str(), &width, &height);

entry->lastUsedFrame = frameCount_;
*w = entry->width * fontScaleX_ * dpiScale_;
*h = entry->height * fontScaleY_ * dpiScale_;
*w = width;
*h = height;
}

bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
Expand Down
5 changes: 3 additions & 2 deletions Common/Render/Text/draw_text_sdl.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ class TextDrawerSDL : public TextDrawer {

uint32_t SetFont(const char *fontName, int size, int flags) override;
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
void MeasureString(std::string_view str, float *w, float *h) override;
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;

protected:
void MeasureStringInternal(std::string_view str, float *w, float *h) override;

bool SupportsColorEmoji() const override { return false; }
void ClearFonts() override;

private:
void PrepareFallbackFonts(std::string_view locale);
uint32_t CheckMissingGlyph(const std::string& text);
uint32_t CheckMissingGlyph(std::string_view text);
int FindFallbackFonts(uint32_t missingGlyph, int ptSize);

std::map<uint32_t, _TTF_Font *> fontMap_;
Expand Down
Loading

0 comments on commit e49d94a

Please sign in to comment.