Share FontWithFace render code with getCluster for more functionality

This commit is contained in:
Benau 2021-09-05 08:41:46 +08:00
parent 4deeac7dc8
commit 8f41b721ed
10 changed files with 182 additions and 100 deletions

View File

@ -7,6 +7,7 @@
#include "irrTypes.h"
#include "dimension2d.h"
#include "rect.h"
#include <algorithm>
#include <memory>
@ -275,6 +276,71 @@ inline void breakGlyphLayouts(std::vector<GlyphLayout>& gls, f32 max_line_width,
}
}
inline bool getDrawOffset(const core::rect<s32>& position, bool hcenter,
bool vcenter, const std::vector<GlyphLayout>& gls,
f32 inverse_shaping, s32 font_max_height,
s32 glyph_max_height, f32 scale,
const core::rect<s32>* clip,
core::position2d<f32>* out_offset,
f32* out_next_line_height,
std::vector<f32>* out_width_per_line)
{
core::position2d<f32> offset(f32(position.UpperLeftCorner.X),
f32(position.UpperLeftCorner.Y));
core::dimension2d<s32> text_dimension;
std::vector<f32> width_per_line = gui::getGlyphLayoutsWidthPerLine(gls,
inverse_shaping, scale);
if (width_per_line.empty())
return false;
bool too_long_broken_text = false;
f32 next_line_height = font_max_height * scale;
if (width_per_line.size() > 1 &&
width_per_line.size() * next_line_height > position.getHeight())
{
// Make too long broken text draw as fit as possible
next_line_height = (f32)position.getHeight() / width_per_line.size();
too_long_broken_text = true;
}
// The offset must be round to integer when setting the offests
// or * inverse_shaping, so the glyph is drawn without blurring effects
if (hcenter || vcenter || clip)
{
text_dimension = gui::getGlyphLayoutsDimension(
gls, next_line_height, inverse_shaping, scale);
if (hcenter)
{
offset.X += (s32)(
(position.getWidth() - width_per_line[0]) / 2.0f);
}
if (vcenter)
{
if (too_long_broken_text)
offset.Y -= (s32)
((font_max_height - glyph_max_height) * scale);
else
{
offset.Y += (s32)(
(position.getHeight() - text_dimension.Height) / 2.0f);
}
}
if (clip)
{
core::rect<s32> clippedRect(core::position2d<s32>
(s32(offset.X), s32(offset.Y)), text_dimension);
clippedRect.clipAgainst(*clip);
if (!clippedRect.isValid())
return false;
}
}
*out_offset = offset;
*out_next_line_height = next_line_height;
*out_width_per_line = width_per_line;
return true;
}
namespace Private
{
/** Used it only for single line (ie without line breaking mark). */

View File

@ -127,6 +127,9 @@ public:
virtual f32 getInverseShaping() const = 0;
virtual f32 getScale() const = 0;
virtual void setScale(f32 scale) = 0;
//! Return FontWithFace properties from stk-code, override later in scalable font. */
virtual s32 getFaceFontMaxHeight() const { return getDimension(L"X").Height; }
virtual s32 getFaceGlyphMaxHeight() const { return getDimension(L"X").Height; }
};
} // end namespace gui

View File

@ -10,6 +10,7 @@
#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace irr

View File

@ -90,28 +90,13 @@ void CGUIStaticText::draw()
breakText();
core::rect<s32> r = frameRect;
auto dim = getGlyphLayoutsDimension(m_glyph_layouts,
font->getHeightPerLine(), font->getInverseShaping(), font->getScale());
s32 totalHeight = dim.Height;
if (VAlign == EGUIA_CENTER)
{
r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
}
else if (VAlign == EGUIA_LOWERRIGHT)
{
r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
}
if (HAlign == EGUIA_LOWERRIGHT)
{
r.UpperLeftCorner.X = frameRect.LowerRightCorner.X - dim.Width;
}
bool hcenter;
const core::rect<s32>* clip;
getDrawPosition(&r, &hcenter, &clip);
font->draw(m_glyph_layouts, r,
OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
hcenter, false, clip);
}
}
@ -405,40 +390,49 @@ s32 CGUIStaticText::getCluster(int x, int y, std::shared_ptr<std::u32string>* ou
if (m_glyph_layouts.empty())
return -1;
core::rect<s32> draw_pos = AbsoluteRect;
IGUISkin* skin = Environment->getSkin();
if (!skin)
return -1;
if (Border)
draw_pos.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
IGUIFont* font = getActiveFont();
std::vector<f32> width_per_line = gui::getGlyphLayoutsWidthPerLine(
m_glyph_layouts, font->getInverseShaping(), font->getScale());
if (width_per_line.empty())
bool hcenter;
const core::rect<s32>* clip;
getDrawPosition(&draw_pos, &hcenter, &clip);
core::position2d<float> offset;
f32 next_line_height = 0.0f;
std::vector<f32> width_per_line;
if (!gui::getDrawOffset(draw_pos, hcenter, false, m_glyph_layouts,
font->getInverseShaping(), font->getFaceFontMaxHeight(),
font->getFaceGlyphMaxHeight(), font->getScale(), clip, &offset,
&next_line_height, &width_per_line))
return -1;
// Check if the line is RTL
core::rect<s32> position = getAbsolutePosition();
bool rtl = (m_glyph_layouts[0].flags & gui::GLF_RTL_LINE) != 0;
int offset = 0;
int cur_line = 0;
if (rtl)
offset = (s32)(position.getWidth() - width_per_line[cur_line]);
if (!hcenter && rtl)
offset.X += (s32)(draw_pos.getWidth() - width_per_line[0]);
float next_line_height = font->getHeightPerLine();
if (width_per_line.size() > 1 &&
width_per_line.size() * next_line_height > position.getHeight())
{
next_line_height = (float)position.getHeight() /
(float)width_per_line.size();
}
unsigned cur_line = 0;
bool line_changed = false;
int idx = -1;
core::recti r;
r.UpperLeftCorner.X = r.LowerRightCorner.X = offset;
r.LowerRightCorner.Y = (int)next_line_height;
bool line_changed = false;
core::recti test_rect;
test_rect.UpperLeftCorner.X = test_rect.LowerRightCorner.X = (s32)offset.X;
test_rect.UpperLeftCorner.Y = (s32)offset.Y;
test_rect.LowerRightCorner.Y = (s32)offset.Y + (s32)next_line_height;
for (unsigned i = 0; i < m_glyph_layouts.size(); i++)
{
const GlyphLayout& glyph_layout = m_glyph_layouts[i];
// Newline handling (from font with face render)
if ((glyph_layout.flags & GLF_NEWLINE) != 0)
{
r.UpperLeftCorner.Y += (int)next_line_height;
r.LowerRightCorner.Y += (int)next_line_height;
test_rect.UpperLeftCorner.Y += (int)next_line_height;
test_rect.LowerRightCorner.Y += (int)next_line_height;
cur_line++;
line_changed = true;
continue;
@ -447,18 +441,25 @@ s32 CGUIStaticText::getCluster(int x, int y, std::shared_ptr<std::u32string>* ou
{
line_changed = false;
rtl = (glyph_layout.flags & gui::GLF_RTL_LINE) != 0;
offset = 0;
if (rtl)
offset.X = float(draw_pos.UpperLeftCorner.X);
if (hcenter)
{
offset = (s32)
(position.getWidth() - width_per_line[cur_line]);
offset.X += (s32)(
(draw_pos.getWidth() - width_per_line.at(cur_line)) / 2.f);
}
r.UpperLeftCorner.X = r.LowerRightCorner.X = offset;
else if (rtl)
{
offset.X +=
(s32)(draw_pos.getWidth() - width_per_line.at(cur_line));
}
test_rect.UpperLeftCorner.X = test_rect.LowerRightCorner.X =
(s32)offset.X;
}
r.LowerRightCorner.X += int(
(float)glyph_layout.x_advance * font->getInverseShaping() *
test_rect.LowerRightCorner.X += s32(
(f32)glyph_layout.x_advance * font->getInverseShaping() *
font->getScale());
if (r.isPointInside(p))
if (test_rect.isPointInside(p))
{
idx = i;
break;
@ -476,6 +477,35 @@ s32 CGUIStaticText::getCluster(int x, int y, std::shared_ptr<std::u32string>* ou
}
void CGUIStaticText::getDrawPosition(core::rect<s32>* draw_pos, bool* hcenter, const core::rect<s32>** clip)
{
core::rect<s32> r = *draw_pos;
IGUIFont* font = getActiveFont();
auto dim = getGlyphLayoutsDimension(m_glyph_layouts,
font->getHeightPerLine(), font->getInverseShaping(), font->getScale());
s32 totalHeight = dim.Height;
if (VAlign == EGUIA_CENTER)
{
r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
}
else if (VAlign == EGUIA_LOWERRIGHT)
{
r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
}
if (HAlign == EGUIA_LOWERRIGHT)
{
r.UpperLeftCorner.X = AbsoluteRect.LowerRightCorner.X - dim.Width;
}
*draw_pos = r;
*hcenter = (HAlign == EGUIA_CENTER);
*clip = (RestrainTextInside ? &AbsoluteClippingRect : NULL);
}
} // end namespace gui
} // end namespace irr

View File

@ -118,6 +118,7 @@ namespace gui
//! Breaks the single text line.
void breakText();
void getDrawPosition(core::rect<s32>* draw_pos, bool* hcenter, const core::rect<s32>** clip);
EGUI_ALIGNMENT HAlign, VAlign;
bool Border;

View File

@ -591,56 +591,14 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
font_settings->setShadow(true);
}
core::position2d<float> offset(float(position.UpperLeftCorner.X),
float(position.UpperLeftCorner.Y));
core::dimension2d<s32> text_dimension;
auto width_per_line = gui::getGlyphLayoutsWidthPerLine(gl,
m_inverse_shaping, scale);
if (width_per_line.empty())
core::position2d<float> offset;
f32 next_line_height = 0.0f;
std::vector<f32> width_per_line;
if (!gui::getDrawOffset(position, hcenter, vcenter, gl, m_inverse_shaping,
m_font_max_height, m_glyph_max_height, scale, clip, &offset,
&next_line_height, &width_per_line))
return;
bool too_long_broken_text = false;
float next_line_height = m_font_max_height * scale;
if (width_per_line.size() > 1 &&
width_per_line.size() * next_line_height > position.getHeight())
{
// Make too long broken text draw as fit as possible
next_line_height = (float)position.getHeight() / width_per_line.size();
too_long_broken_text = true;
}
// The offset must be round to integer when setting the offests
// or * m_inverse_shaping, so the glyph is drawn without blurring effects
if (hcenter || vcenter || clip)
{
text_dimension = gui::getGlyphLayoutsDimension(
gl, next_line_height, m_inverse_shaping, scale);
if (hcenter)
{
offset.X += (s32)(
(position.getWidth() - width_per_line[0]) / 2.0f);
}
if (vcenter)
{
if (too_long_broken_text)
offset.Y -= (s32)
((m_font_max_height - m_glyph_max_height) * scale);
else
{
offset.Y += (s32)(
(position.getHeight() - text_dimension.Height) / 2.0f);
}
}
if (clip)
{
core::rect<s32> clippedRect(core::position2d<s32>
(s32(offset.X), s32(offset.Y)), text_dimension);
clippedRect.clipAgainst(*clip);
if (!clippedRect.isValid()) return;
}
}
// Collect character locations
const unsigned int text_size = gl.size();
std::vector<std::pair<s32, bool> > indices;

View File

@ -300,6 +300,8 @@ public:
// ------------------------------------------------------------------------
int getFontMaxHeight() const { return m_font_max_height; }
// ------------------------------------------------------------------------
int getGlyphMaxHeight() const { return m_glyph_max_height; }
// ------------------------------------------------------------------------
virtual bool disableTextShaping() const { return false; }
// ------------------------------------------------------------------------
float getInverseShaping() const { return m_inverse_shaping; }

View File

@ -176,5 +176,25 @@ f32 ScalableFont::getInverseShaping() const
#endif
} // getShapingScale
// ----------------------------------------------------------------------------
s32 ScalableFont::getFaceFontMaxHeight() const
{
#ifndef SERVER_ONLY
return m_face->getFontMaxHeight();
#else
return 1;
#endif
} // getFaceFontMaxHeight
// ----------------------------------------------------------------------------
s32 ScalableFont::getFaceGlyphMaxHeight() const
{
#ifndef SERVER_ONLY
return m_face->getGlyphMaxHeight();
#else
return 1;
#endif
} // getFaceGlyphMaxHeight
} // end namespace gui
} // end namespace irr

View File

@ -125,7 +125,10 @@ public:
virtual void setInvisibleCharacters( const wchar_t *s ) {}
// ------------------------------------------------------------------------
virtual f32 getInverseShaping() const;
// ------------------------------------------------------------------------
virtual s32 getFaceFontMaxHeight() const;
// ------------------------------------------------------------------------
virtual s32 getFaceGlyphMaxHeight() const;
};
} // end namespace gui

View File

@ -265,9 +265,7 @@ void NetworkingLobby::init()
if (mouse.Event == EMIE_LMOUSE_PRESSED_DOWN)
{
std::shared_ptr<std::u32string> s;
int cluster = text->getCluster(
mouse.X - text->getAbsolutePosition().UpperLeftCorner.X,
mouse.Y - text->getAbsolutePosition().UpperLeftCorner.Y, &s);
int cluster = text->getCluster(mouse.X, mouse.Y, &s);
if (cluster == -1 || (unsigned)cluster > s->size())
return false;