Skip to content

Commit

Permalink
Merge pull request #19178 from hrydgard/text-draw-refactor
Browse files Browse the repository at this point in the history
Text draw refactor part #1
  • Loading branch information
hrydgard authored May 24, 2024
2 parents bef6456 + 8112e51 commit a04ad1c
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 83 deletions.
14 changes: 7 additions & 7 deletions Common/Data/Text/WrapText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void WordWrapper::AppendWord(int endIndex, int lastChar, bool addNewline) {

// This will include the newline.
if (x_ <= maxW_) {
out_.append(str_ + lastWordStartIndex, str_ + endIndex);
out_.append(str_.data() + lastWordStartIndex, str_.data() + endIndex);
} else {
scanForNewline_ = true;
}
Expand All @@ -167,7 +167,7 @@ void WordWrapper::AppendWord(int endIndex, int lastChar, bool addNewline) {

if (lastLineStart_ != out_.size()) {
// To account for kerning around spaces, we recalculate the entire line width.
x_ = MeasureWidth(out_.c_str() + lastLineStart_, out_.size() - lastLineStart_);
x_ = MeasureWidth(std::string_view(out_.c_str() + lastLineStart_, out_.size() - lastLineStart_));
} else {
x_ = 0.0f;
}
Expand All @@ -178,10 +178,10 @@ void WordWrapper::AppendWord(int endIndex, int lastChar, bool addNewline) {

void WordWrapper::Wrap() {
// First, let's check if it fits as-is.
size_t len = strlen(str_);
if (MeasureWidth(str_, len) <= maxW_) {
size_t len = str_.length();
if (MeasureWidth(str_) <= maxW_) {
// If it fits, we don't need to go through each character.
out_ = str_;
out_ = std::string(str_);
return;
}

Expand All @@ -190,7 +190,7 @@ void WordWrapper::Wrap() {
out_.reserve(len + len / 16);

if (flags_ & FLAG_ELLIPSIZE_TEXT) {
ellipsisWidth_ = MeasureWidth("...", 3);
ellipsisWidth_ = MeasureWidth("...");
}

for (UTF8 utf(str_); !utf.end(); ) {
Expand Down Expand Up @@ -219,7 +219,7 @@ void WordWrapper::Wrap() {
}

// Measure the entire word for kerning purposes. May not be 100% perfect.
float newWordWidth = MeasureWidth(str_ + lastIndex_, afterIndex - lastIndex_);
float newWordWidth = MeasureWidth(str_.substr(lastIndex_, afterIndex - lastIndex_));

// Is this the end of a word (space)? We'll also output up to a soft hyphen.
if (wordWidth_ > 0.0f && IsSpaceOrShy(c)) {
Expand Down
9 changes: 6 additions & 3 deletions Common/Data/Text/WrapText.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
#pragma once

#include <string>
#include <string_view>

class WordWrapper {
public:
WordWrapper(const char *str, float maxW, int flags)
WordWrapper(std::string_view str, float maxW, int flags)
: str_(str), maxW_(maxW), flags_(flags) {
}
virtual ~WordWrapper() {}

// TODO: This should return a vector of std::string_view for the lines, instead of building up a new string.
std::string Wrapped();

protected:
virtual float MeasureWidth(const char *str, size_t bytes) = 0;
virtual float MeasureWidth(std::string_view str) = 0;

void Wrap();
bool WrapBeforeWord();
void AppendWord(int endIndex, int lastChar, bool addNewline);
Expand All @@ -26,7 +29,7 @@ class WordWrapper {
return IsSpace(c) || IsShy(c);
}

const char *const str_;
const std::string_view str_;
const float maxW_;
const int flags_;
std::string out_;
Expand Down
45 changes: 21 additions & 24 deletions Common/Render/DrawBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,19 +446,20 @@ void DrawBuffer::DrawImage2GridH(ImageID atlas_image, float x1, float y1, float
class AtlasWordWrapper : public WordWrapper {
public:
// Note: maxW may be height if rotated.
AtlasWordWrapper(const AtlasFont &atlasfont, float scale, const char *str, float maxW, int flags) : WordWrapper(str, maxW, flags), atlasfont_(atlasfont), scale_(scale) {
AtlasWordWrapper(const AtlasFont &atlasfont, float scale, std::string_view str, float maxW, int flags)
: WordWrapper(str, maxW, flags), atlasfont_(atlasfont), scale_(scale) {
}

protected:
float MeasureWidth(const char *str, size_t bytes) override;
float MeasureWidth(std::string_view str) override;

const AtlasFont &atlasfont_;
const float scale_;
};

float AtlasWordWrapper::MeasureWidth(const char *str, size_t bytes) {
float AtlasWordWrapper::MeasureWidth(std::string_view str) {
float w = 0.0f;
for (UTF8 utf(str); utf.byteIndex() < (int)bytes; ) {
for (UTF8 utf(str); !utf.end(); ) {
uint32_t c = utf.next();
if (c == '&') {
// Skip ampersand prefixes ("&&" is an ampersand.)
Expand All @@ -473,7 +474,7 @@ float AtlasWordWrapper::MeasureWidth(const char *str, size_t bytes) {
return w;
}

void DrawBuffer::MeasureTextCount(FontID font, const char *text, int count, float *w, float *h) {
void DrawBuffer::MeasureText(FontID font, std::string_view text, float *w, float *h) {
const AtlasFont *atlasfont = fontAtlas_->getFont(font);
if (!atlasfont)
atlasfont = atlas->getFont(font);
Expand All @@ -491,7 +492,7 @@ void DrawBuffer::MeasureTextCount(FontID font, const char *text, int count, floa
while (true) {
if (utf.end())
break;
if (utf.byteIndex() >= count)
if (utf.byteIndex() >= text.length())
break;
cval = utf.next();
// Translate non-breaking space to space.
Expand All @@ -517,14 +518,14 @@ void DrawBuffer::MeasureTextCount(FontID font, const char *text, int count, floa
if (h) *h = atlasfont->height * fontscaley * lines;
}

void DrawBuffer::MeasureTextRect(FontID font_id, const char *text, int count, const Bounds &bounds, float *w, float *h, int align) {
if (!text || font_id.isInvalid()) {
void DrawBuffer::MeasureTextRect(FontID font_id, std::string_view text, const Bounds &bounds, float *w, float *h, int align) {
if (text.empty() || font_id.isInvalid()) {
*w = 0.0f;
*h = 0.0f;
return;
}

std::string toMeasure = std::string(text, count);
std::string toMeasure = std::string(text);
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
const AtlasFont *font = fontAtlas_->getFont(font_id);
Expand All @@ -535,17 +536,13 @@ void DrawBuffer::MeasureTextRect(FontID font_id, const char *text, int count, co
*h = 0.0f;
return;
}
AtlasWordWrapper wrapper(*font, fontscalex, toMeasure.c_str(), bounds.w, wrap);
AtlasWordWrapper wrapper(*font, fontscalex, toMeasure, bounds.w, wrap);
toMeasure = wrapper.Wrapped();
}
MeasureTextCount(font_id, toMeasure.c_str(), (int)toMeasure.length(), w, h);
MeasureText(font_id, toMeasure, w, h);
}

void DrawBuffer::MeasureText(FontID font, const char *text, float *w, float *h) {
return MeasureTextCount(font, text, (int)strlen(text), w, h);
}

void DrawBuffer::DrawTextShadow(FontID font, const char *text, float x, float y, Color color, int flags) {
void DrawBuffer::DrawTextShadow(FontID font, std::string_view text, float x, float y, Color color, int flags) {
uint32_t alpha = (color >> 1) & 0xFF000000;
DrawText(font, text, x + 2, y + 2, alpha, flags);
DrawText(font, text, x, y, color, flags);
Expand All @@ -562,9 +559,8 @@ void DrawBuffer::DoAlign(int flags, float *x, float *y, float *w, float *h) {
}
}


// TODO: Actually use the rect properly, take bounds.
void DrawBuffer::DrawTextRect(FontID font, const char *text, float x, float y, float w, float h, Color color, int align) {
void DrawBuffer::DrawTextRect(FontID font, std::string_view text, float x, float y, float w, float h, Color color, int align) {
if (align & ALIGN_HCENTER) {
x += w / 2;
} else if (align & ALIGN_RIGHT) {
Expand All @@ -576,18 +572,18 @@ void DrawBuffer::DrawTextRect(FontID font, const char *text, float x, float y, f
y += h;
}

std::string toDraw = text;
std::string toDraw(text);
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
const AtlasFont *atlasfont = fontAtlas_->getFont(font);
if (!atlasfont)
atlasfont = atlas->getFont(font);
if (wrap && atlasfont) {
AtlasWordWrapper wrapper(*atlasfont, fontscalex, toDraw.c_str(), w, wrap);
AtlasWordWrapper wrapper(*atlasfont, fontscalex, toDraw, w, wrap);
toDraw = wrapper.Wrapped();
}

float totalWidth, totalHeight;
MeasureTextRect(font, toDraw.c_str(), (int)toDraw.size(), Bounds(x, y, w, h), &totalWidth, &totalHeight, align);
MeasureTextRect(font, toDraw, Bounds(x, y, w, h), &totalWidth, &totalHeight, align);

std::vector<std::string> lines;
SplitString(toDraw, '\n', lines);
Expand All @@ -606,21 +602,22 @@ void DrawBuffer::DrawTextRect(FontID font, const char *text, float x, float y, f
DrawText(font, line.c_str(), x, baseY, color, align);

float tw, th;
MeasureText(font, line.c_str(), &tw, &th);
MeasureText(font, line, &tw, &th);
baseY += th;
}
}

// ROTATE_* doesn't yet work right.
void DrawBuffer::DrawText(FontID font, const char *text, float x, float y, Color color, int align) {
void DrawBuffer::DrawText(FontID font, std::string_view text, float x, float y, Color color, int align) {
// rough estimate
size_t textLen = strlen(text);
int textLen = (int)text.length();
if (count_ + textLen * 6 > MAX_VERTS) {
Flush(true);
if (textLen * 6 >= MAX_VERTS) {
textLen = std::min(MAX_VERTS / 6 - 10, (int)textLen);
}
}
text = text.substr(0, textLen);

const AtlasFont *atlasfont = fontAtlas_->getFont(font);
if (!atlasfont)
Expand Down
12 changes: 5 additions & 7 deletions Common/Render/DrawBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,13 @@ class DrawBuffer {
// This is only 6 triangles, much cheaper.
void DrawImage2GridH(ImageID atlas_image, float x1, float y1, float x2, Color color = COLOR(0xFFFFFF), float scale = 1.0);

void MeasureText(FontID font, const char *text, float *w, float *h);
void MeasureText(FontID font, std::string_view text, float *w, float *h);

// NOTE: Count is in plain chars not utf-8 chars!
void MeasureTextCount(FontID font, const char *text, int count, float *w, float *h);
void MeasureTextRect(FontID font, const char *text, int count, const Bounds &bounds, float *w, float *h, int align = 0);
void MeasureTextRect(FontID font, std::string_view text, const Bounds &bounds, float *w, float *h, int align = 0);

void DrawTextRect(FontID font, const char *text, float x, float y, float w, float h, Color color = 0xFFFFFFFF, int align = 0);
void DrawText(FontID font, const char *text, float x, float y, Color color = 0xFFFFFFFF, int align = 0);
void DrawTextShadow(FontID font, const char *text, float x, float y, Color color = 0xFFFFFFFF, int align = 0);
void DrawTextRect(FontID font, std::string_view text, float x, float y, float w, float h, Color color = 0xFFFFFFFF, int align = 0);
void DrawText(FontID font, std::string_view text, float x, float y, Color color = 0xFFFFFFFF, int align = 0);
void DrawTextShadow(FontID font, std::string_view text, float x, float y, Color color = 0xFFFFFFFF, int align = 0);

void SetFontScale(float xs, float ys) {
fontscalex = xs;
Expand Down
6 changes: 3 additions & 3 deletions Common/Render/Text/draw_text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ TextDrawer::TextDrawer(Draw::DrawContext *draw) : draw_(draw) {
TextDrawer::~TextDrawer() {
}

float TextDrawerWordWrapper::MeasureWidth(const char *str, size_t bytes) {
float TextDrawerWordWrapper::MeasureWidth(std::string_view str) {
float w, h;
drawer_->MeasureString(str, bytes, &w, &h);
drawer_->MeasureString(str.data(), str.length(), &w, &h);
return w;
}

void TextDrawer::WrapString(std::string &out, const char *str, float maxW, int flags) {
void TextDrawer::WrapString(std::string &out, std::string_view str, float maxW, int flags) {
TextDrawerWordWrapper wrapper(this, str, maxW, flags);
out = wrapper.Wrapped();
}
Expand Down
6 changes: 3 additions & 3 deletions Common/Render/Text/draw_text.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class TextDrawer {

Draw::DrawContext *draw_;
virtual void ClearCache() = 0;
void WrapString(std::string &out, const char *str, float maxWidth, int flags);
void WrapString(std::string &out, std::string_view str, float maxWidth, int flags);

struct CacheKey {
bool operator < (const CacheKey &other) const {
Expand All @@ -90,11 +90,11 @@ class TextDrawer {

class TextDrawerWordWrapper : public WordWrapper {
public:
TextDrawerWordWrapper(TextDrawer *drawer, const char *str, float maxW, int flags)
TextDrawerWordWrapper(TextDrawer *drawer, std::string_view str, float maxW, int flags)
: WordWrapper(str, maxW, flags), drawer_(drawer) {}

protected:
float MeasureWidth(const char *str, size_t bytes) override;
float MeasureWidth(std::string_view str) override;

TextDrawer *drawer_;
};
25 changes: 10 additions & 15 deletions Common/UI/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,35 +206,30 @@ void UIContext::SetFontStyle(const UI::FontStyle &fontStyle) {
}
}

void UIContext::MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, float *x, float *y, int align) const {
_dbg_assert_(str != nullptr);
MeasureTextCount(style, scaleX, scaleY, str, (int)strlen(str), x, y, align);
}

void UIContext::MeasureTextCount(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, float *x, float *y, int align) const {
_dbg_assert_(str != nullptr);
void UIContext::MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align) const {
_dbg_assert_(str.data() != nullptr);
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
float sizeFactor = (float)style.sizePts / 24.0f;
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
Draw()->MeasureTextCount(style.atlasFont, str, count, x, y);
Draw()->MeasureText(style.atlasFont, str, x, y);
} else {
textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags);
textDrawer_->SetFontScale(scaleX, scaleY);
textDrawer_->MeasureString(str, count, x, y);
textDrawer_->MeasureString(str.data(), str.length(), x, y);
textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags);
}
}

void UIContext::MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, const Bounds &bounds, float *x, float *y, int align) const {
_dbg_assert_(str != nullptr);
void UIContext::MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, float *x, float *y, int align) const {
_dbg_assert_(str.data() != nullptr);
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
float sizeFactor = (float)style.sizePts / 24.0f;
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
Draw()->MeasureTextRect(style.atlasFont, str, count, bounds, x, y, align);
Draw()->MeasureTextRect(style.atlasFont, str, bounds, x, y, align);
} else {
textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags);
textDrawer_->SetFontScale(scaleX, scaleY);
textDrawer_->MeasureStringRect(str, count, bounds, x, y, align);
textDrawer_->MeasureStringRect(str.data(), str.length(), bounds, x, y, align);
textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags);
}
}
Expand Down Expand Up @@ -291,10 +286,10 @@ void UIContext::DrawTextRect(const char *str, const Bounds &bounds, uint32_t col

static constexpr float MIN_TEXT_SCALE = 0.7f;

float UIContext::CalculateTextScale(const char *text, float availWidth, float availHeight) const {
float UIContext::CalculateTextScale(std::string_view str, float availWidth, float availHeight) const {
float actualWidth, actualHeight;
Bounds availBounds(0, 0, availWidth, availHeight);
MeasureTextRect(theme->uiFont, 1.0f, 1.0f, text, (int)strlen(text), availBounds, &actualWidth, &actualHeight, ALIGN_VCENTER);
MeasureTextRect(theme->uiFont, 1.0f, 1.0f, str, availBounds, &actualWidth, &actualHeight, ALIGN_VCENTER);
if (actualWidth > availWidth) {
return std::max(MIN_TEXT_SCALE, availWidth / actualWidth);
}
Expand Down
7 changes: 3 additions & 4 deletions Common/UI/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,16 @@ class UIContext {
void SetFontStyle(const UI::FontStyle &style);
const UI::FontStyle &GetFontStyle() { return *fontStyle_; }
void SetFontScale(float scaleX, float scaleY);
void MeasureTextCount(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, float *x, float *y, int align = 0) const;
void MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, float *x, float *y, int align = 0) const;
void MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, const char *str, int count, const Bounds &bounds, float *x, float *y, int align = 0) const;
void MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align = 0) const;
void MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, float *x, float *y, int align = 0) const;
void DrawText(const char *str, float x, float y, uint32_t color, int align = 0);
void DrawTextShadow(const char *str, float x, float y, uint32_t color, int align = 0);
void DrawTextRect(const char *str, const Bounds &bounds, uint32_t color, int align = 0);
void DrawTextShadowRect(const char *str, const Bounds &bounds, uint32_t color, int align = 0);
// Will squeeze the text into the bounds if needed.
void DrawTextRectSqueeze(const char *str, const Bounds &bounds, uint32_t color, int align = 0);

float CalculateTextScale(const char *text, float availWidth, float availHeight) const;
float CalculateTextScale(std::string_view str, float availWidth, float availHeight) const;

void FillRect(const UI::Drawable &drawable, const Bounds &bounds);
void DrawRectDropShadow(const Bounds &bounds, float radius, float alpha, uint32_t color = 0);
Expand Down
8 changes: 4 additions & 4 deletions Common/UI/PopupScreens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ void AbstractChoiceWithValueDisplay::GetContentDimensionsBySpec(const UIContext
Bounds availBounds(0, 0, availWidth, vert.size);

float valueW, valueH;
dc.MeasureTextRect(dc.theme->uiFont, scale, scale, valueText.c_str(), (int)valueText.size(), availBounds, &valueW, &valueH, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
dc.MeasureTextRect(dc.theme->uiFont, scale, scale, valueText, availBounds, &valueW, &valueH, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
valueW += paddingX;

// Give the choice itself less space to grow in, so it shrinks if needed.
Expand Down Expand Up @@ -641,7 +641,7 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) {

float w, h;
Bounds availBounds(0, 0, availWidth, bounds_.h);
dc.MeasureTextRect(dc.theme->uiFont, scale, scale, valueText.c_str(), (int)valueText.size(), availBounds, &w, &h, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
dc.MeasureTextRect(dc.theme->uiFont, scale, scale, valueText, availBounds, &w, &h, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
textPadding_.right = w + paddingX;

Choice::Draw(dc);
Expand All @@ -662,10 +662,10 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) {
}
}

float AbstractChoiceWithValueDisplay::CalculateValueScale(const UIContext &dc, const std::string &valueText, float availWidth) const {
float AbstractChoiceWithValueDisplay::CalculateValueScale(const UIContext &dc, std::string_view valueText, float availWidth) const {
float actualWidth, actualHeight;
Bounds availBounds(0, 0, availWidth, bounds_.h);
dc.MeasureTextRect(dc.theme->uiFont, 1.0f, 1.0f, valueText.c_str(), (int)valueText.size(), availBounds, &actualWidth, &actualHeight);
dc.MeasureTextRect(dc.theme->uiFont, 1.0f, 1.0f, valueText, availBounds, &actualWidth, &actualHeight);
if (actualWidth > availWidth) {
return std::max(0.8f, availWidth / actualWidth);
}
Expand Down
Loading

0 comments on commit a04ad1c

Please sign in to comment.