Use libraqm for text layout
This commit is contained in:
parent
43d322c634
commit
acb9054dcb
304
lib/irrlicht/include/GlyphLayout.h
Normal file
304
lib/irrlicht/include/GlyphLayout.h
Normal file
@ -0,0 +1,304 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#ifndef __GLYPH_LAYOUT_H_INCLUDED__
|
||||
#define __GLYPH_LAYOUT_H_INCLUDED__
|
||||
|
||||
#include "irrTypes.h"
|
||||
#include "dimension2d.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
enum GlyphLayoutFlag
|
||||
{
|
||||
GLF_RTL_LINE = 1, /* This line from this glyph is RTL. */
|
||||
GLF_RTL_CHAR = 2, /* This character(s) from this glyph is RTL. */
|
||||
GLF_BREAKABLE = 4, /* This glyph is breakable when line breaking. */
|
||||
GLF_QUICK_DRAW = 8, /* This glyph is not created by libraqm, which get x_advance_x directly from font. */
|
||||
GLF_NEWLINE = 16 /* This glyph will start a newline. */
|
||||
};
|
||||
|
||||
//! GlyphLayout copied from libraqm.
|
||||
struct GlyphLayout
|
||||
{
|
||||
u32 index;
|
||||
s32 x_advance;
|
||||
//s32 y_advance; we don't use y_advance atm
|
||||
s32 x_offset;
|
||||
s32 y_offset;
|
||||
/* Above variable is same for raqm_glyph_t */
|
||||
// If some characters share the same glyph
|
||||
std::vector<s32> cluster;
|
||||
//! used to sorting back the visual order after line breaking
|
||||
u32 original_index;
|
||||
u16 flags;
|
||||
//! this is the face_idx used in stk face ttf
|
||||
u16 face_idx;
|
||||
};
|
||||
|
||||
namespace Private
|
||||
{
|
||||
inline void breakLine(std::vector<GlyphLayout> gls, f32 max_line_width,
|
||||
f32 inverse_shaping, f32 scale, std::vector<std::vector<GlyphLayout> >& result);
|
||||
}
|
||||
|
||||
inline core::dimension2d<u32> getGlyphLayoutsDimension(
|
||||
const std::vector<GlyphLayout>& gls, s32 height_per_line, f32 inverse_shaping,
|
||||
f32 scale, s32 cluster = -1)
|
||||
{
|
||||
core::dimension2d<f32> dim(0.0f, 0.0f);
|
||||
core::dimension2d<f32> this_line(0.0f, height_per_line);
|
||||
|
||||
for (unsigned i = 0; i < gls.size(); i++)
|
||||
{
|
||||
const GlyphLayout& glyph = gls[i];
|
||||
if ((glyph.flags & GLF_NEWLINE) != 0)
|
||||
{
|
||||
dim.Height += this_line.Height;
|
||||
if (dim.Width < this_line.Width)
|
||||
dim.Width = this_line.Width;
|
||||
this_line.Width = 0;
|
||||
continue;
|
||||
}
|
||||
f32 cur_width = (s32)(glyph.x_advance * inverse_shaping) * scale;
|
||||
bool found_cluster = false;
|
||||
// Cursor positioning
|
||||
if (cluster != -1)
|
||||
{
|
||||
auto it = std::find(glyph.cluster.begin(), glyph.cluster.end(), cluster);
|
||||
if (it != glyph.cluster.end() &&
|
||||
(i == gls.size() - 1 || cluster != gls[i + 1].cluster.front()))
|
||||
{
|
||||
found_cluster = true;
|
||||
// Get cluster ratio to total glyph width, so for example
|
||||
// cluster 0 in ffi glyph will be 0.333
|
||||
f32 ratio = f32(it - glyph.cluster.begin() + 1) /
|
||||
(f32)glyph.cluster.size();
|
||||
// Show cursor in left side, so no need to add width
|
||||
if ((glyph.flags & GLF_RTL_CHAR) != 0)
|
||||
cur_width = 0;
|
||||
else
|
||||
cur_width *= ratio;
|
||||
}
|
||||
}
|
||||
this_line.Width += cur_width;
|
||||
if (found_cluster)
|
||||
break;
|
||||
}
|
||||
|
||||
dim.Height += this_line.Height;
|
||||
if (dim.Width < this_line.Width)
|
||||
dim.Width = this_line.Width;
|
||||
|
||||
core::dimension2d<u32> ret_dim(0, 0);
|
||||
ret_dim.Width = (u32)(dim.Width + 0.9f); // round up
|
||||
ret_dim.Height = (u32)(dim.Height + 0.9f);
|
||||
|
||||
return ret_dim;
|
||||
}
|
||||
|
||||
inline s32 getCurosrFromDimension(f32 x, f32 y,
|
||||
const std::vector<GlyphLayout>& gls, s32 height_per_line,
|
||||
f32 inverse_shaping, f32 scale)
|
||||
{
|
||||
if (gls.empty())
|
||||
return 0;
|
||||
f32 total_width = 0.0f;
|
||||
for (unsigned i = 0; i < gls.size(); i++)
|
||||
{
|
||||
const GlyphLayout& glyph = gls[i];
|
||||
if ((glyph.flags & GLF_NEWLINE) != 0)
|
||||
{
|
||||
// TODO: handling newline
|
||||
break;
|
||||
}
|
||||
f32 cur_width = (s32)(glyph.x_advance * inverse_shaping) * scale;
|
||||
if (glyph.cluster.size() == 1)
|
||||
{
|
||||
if (i == 0 && cur_width * 0.5 > x)
|
||||
return 0;
|
||||
if (total_width + cur_width * 0.5 > x)
|
||||
return glyph.cluster.front();
|
||||
}
|
||||
else if (total_width + cur_width > x)
|
||||
{
|
||||
// Handle glyph like 'ffi'
|
||||
f32 each_cluster_width = cur_width / (f32)glyph.cluster.size();
|
||||
f32 remain_width = x - total_width;
|
||||
total_width = 0.0f;
|
||||
for (unsigned j = 0; j < glyph.cluster.size(); j++)
|
||||
{
|
||||
if (total_width + each_cluster_width * 0.5 > remain_width)
|
||||
return glyph.cluster[j];
|
||||
total_width += each_cluster_width;
|
||||
}
|
||||
return glyph.cluster.back();
|
||||
}
|
||||
total_width += cur_width;
|
||||
}
|
||||
return gls.back().cluster.back() + 1;
|
||||
}
|
||||
|
||||
inline std::vector<f32> getGlyphLayoutsWidthPerLine(
|
||||
const std::vector<GlyphLayout>& gls, f32 inverse_shaping, f32 scale)
|
||||
{
|
||||
std::vector<f32> result;
|
||||
f32 cur_width = 0.0f;
|
||||
for (auto& glyph : gls)
|
||||
{
|
||||
if ((glyph.flags & GLF_NEWLINE) != 0)
|
||||
{
|
||||
result.push_back(cur_width);
|
||||
cur_width = 0;
|
||||
continue;
|
||||
}
|
||||
cur_width += (s32)(glyph.x_advance * inverse_shaping) * scale;
|
||||
}
|
||||
|
||||
result.push_back(cur_width);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void breakGlyphLayouts(std::vector<GlyphLayout>& gls, f32 max_line_width,
|
||||
f32 inverse_shaping, f32 scale)
|
||||
{
|
||||
if (gls.size() < 2)
|
||||
return;
|
||||
std::vector<std::vector<GlyphLayout> > broken_line;
|
||||
u32 start = 0;
|
||||
for (u32 i = 0; i < gls.size(); i++)
|
||||
{
|
||||
GlyphLayout& glyph = gls[i];
|
||||
if ((glyph.flags & GLF_NEWLINE) != 0)
|
||||
{
|
||||
Private::breakLine({ gls.begin() + start, gls.begin() + i},
|
||||
max_line_width, inverse_shaping, scale, broken_line);
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start - gls.size() - 1 > 0)
|
||||
{
|
||||
Private::breakLine({ gls.begin() + start, gls.begin() + gls.size() },
|
||||
max_line_width, inverse_shaping, scale, broken_line);
|
||||
}
|
||||
|
||||
gls.clear();
|
||||
// Sort glyphs in original order
|
||||
for (u32 i = 0; i < broken_line.size(); i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
gui::GlyphLayout gl = { 0 };
|
||||
gl.flags = gui::GLF_NEWLINE;
|
||||
gls.push_back(gl);
|
||||
}
|
||||
auto& line = broken_line[i];
|
||||
std::sort(line.begin(), line.end(), []
|
||||
(const irr::gui::GlyphLayout& a_gi,
|
||||
const irr::gui::GlyphLayout& b_gi)
|
||||
{
|
||||
return a_gi.original_index < b_gi.original_index;
|
||||
});
|
||||
for (auto& glyph : line)
|
||||
gls.push_back(glyph);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Private
|
||||
{
|
||||
/** Used it only for single line (ie without line breaking mark). */
|
||||
inline f32 getGlyphLayoutsWidth(const std::vector<GlyphLayout>& gls,
|
||||
f32 inverse_shaping, f32 scale)
|
||||
{
|
||||
return std::accumulate(gls.begin(), gls.end(), 0,
|
||||
[inverse_shaping, scale] (const f32 previous,
|
||||
const irr::gui::GlyphLayout& cur_gi)
|
||||
{
|
||||
return previous + (s32)(cur_gi.x_advance * inverse_shaping) * scale;
|
||||
});
|
||||
}
|
||||
|
||||
inline void breakLine(std::vector<GlyphLayout> gls, f32 max_line_width,
|
||||
f32 inverse_shaping, f32 scale,
|
||||
std::vector<std::vector<GlyphLayout> >& result)
|
||||
{
|
||||
const f32 line_size = getGlyphLayoutsWidth(gls, inverse_shaping, scale);
|
||||
if (line_size <= max_line_width)
|
||||
{
|
||||
result.emplace_back(std::move(gls));
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort glyphs in logical order
|
||||
std::sort(gls.begin(), gls.end(), []
|
||||
(const GlyphLayout& a_gi, const GlyphLayout& b_gi)
|
||||
{
|
||||
return a_gi.cluster.front() < b_gi.cluster.front();
|
||||
});
|
||||
|
||||
u32 end = 0;
|
||||
s32 start = 0;
|
||||
f32 total_width = 0.0f;
|
||||
|
||||
for (; end < gls.size(); end++)
|
||||
{
|
||||
f32 cur_width = (s32)(gls[end].x_advance * inverse_shaping) * scale;
|
||||
if (cur_width > max_line_width)
|
||||
{
|
||||
// Very large glyph
|
||||
result.push_back({gls[end]});
|
||||
start = end;
|
||||
}
|
||||
else if (cur_width + total_width <= max_line_width)
|
||||
{
|
||||
total_width += cur_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
int break_point = end - 1;
|
||||
do
|
||||
{
|
||||
if (break_point <= 0 || break_point == start)
|
||||
{
|
||||
// Forcely break at line ending position if no break
|
||||
// mark, this fix text without space of out network
|
||||
// lobby
|
||||
result.push_back(
|
||||
{gls.begin() + start, gls.begin() + end});
|
||||
start = end;
|
||||
total_width = (s32)(gls[end].x_advance * inverse_shaping) * scale;
|
||||
break;
|
||||
}
|
||||
if ((gls[break_point].flags & GLF_BREAKABLE) != 0)
|
||||
{
|
||||
result.push_back(
|
||||
{gls.begin() + start, gls.begin() + break_point + 1});
|
||||
end = start = break_point + 1;
|
||||
total_width = (s32)(gls[end].x_advance * inverse_shaping) * scale;
|
||||
break;
|
||||
}
|
||||
break_point--;
|
||||
}
|
||||
while (break_point >= start);
|
||||
}
|
||||
}
|
||||
if (gls.begin() + start != gls.end())
|
||||
{
|
||||
result.push_back({gls.begin() + start, gls.end()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
|
||||
#endif
|
||||
|
@ -10,10 +10,14 @@
|
||||
#include "rect.h"
|
||||
#include "irrString.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
struct GlyphLayout;
|
||||
|
||||
//! An enum for the different types of GUI font.
|
||||
enum EGUI_FONT_TYPE
|
||||
@ -52,6 +56,23 @@ public:
|
||||
video::SColor color, bool hcenter=false, bool vcenter=false,
|
||||
const core::rect<s32>* clip=0) = 0;
|
||||
|
||||
//! Draws glyphs and clips it to the specified rectangle if wanted.
|
||||
/** \param gls: List of GlyphLaout
|
||||
\param position: Rectangle specifying position where to draw the text.
|
||||
\param color: Color of the text
|
||||
\param hcenter: Specifies if the text should be centered horizontally into the rectangle.
|
||||
\param vcenter: Specifies if the text should be centered vertically into the rectangle.
|
||||
\param clip: Optional pointer to a rectangle against which the text will be clipped.
|
||||
If the pointer is null, no clipping will be done. */
|
||||
virtual void draw(const std::vector<GlyphLayout>& gls, const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter=false, bool vcenter=false,
|
||||
const core::rect<s32>* clip=0) = 0;
|
||||
|
||||
//! Draws some text and clips it to the specified rectangle if wanted (without text shaping)
|
||||
virtual void drawQuick(const core::stringw& text, const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter=false,
|
||||
bool vcenter=false, const core::rect<s32>* clip=0) = 0;
|
||||
|
||||
//! Calculates the width and height of a given string of text.
|
||||
/** \return Returns width and height of the area covered by the text if
|
||||
it would be drawn. */
|
||||
@ -89,12 +110,25 @@ public:
|
||||
//! Returns the distance between letters
|
||||
virtual s32 getKerningHeight() const = 0;
|
||||
|
||||
//! Returns the height per line (including padding between 2 lines)
|
||||
virtual s32 getHeightPerLine() const = 0;
|
||||
|
||||
//! Define which characters should not be drawn by the font.
|
||||
/** For example " " would not draw any space which is usually blank in
|
||||
most fonts.
|
||||
\param s String of symbols which are not send down to the videodriver
|
||||
*/
|
||||
virtual void setInvisibleCharacters( const wchar_t *s ) = 0;
|
||||
|
||||
//! Convert text to glyph layouts for fast rendering with caching enabled
|
||||
/* If line_data is not null, each broken line u32string will be saved and
|
||||
can be used for advanced glyph and text mapping, and cache will be disabled. */
|
||||
virtual void initGlyphLayouts(const core::stringw& text,
|
||||
std::vector<GlyphLayout>& gls, std::vector<std::u32string>* line_data = NULL) = 0;
|
||||
|
||||
virtual f32 getInverseShaping() const = 0;
|
||||
virtual f32 getScale() const = 0;
|
||||
virtual void setScale(f32 scale) = 0;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
|
@ -8,11 +8,14 @@
|
||||
#include "IGUIElement.h"
|
||||
#include "SColor.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
class IGUIFont;
|
||||
struct GlyphLayout;
|
||||
|
||||
//! Multi or single line text label.
|
||||
class IGUIStaticText : public IGUIElement
|
||||
@ -125,6 +128,12 @@ namespace gui
|
||||
|
||||
//! Checks whether the text in this element should be interpreted as right-to-left
|
||||
virtual bool isRightToLeft() const = 0;
|
||||
|
||||
virtual const std::vector<GlyphLayout>& getGlyphLayouts() const = 0;
|
||||
virtual void setGlyphLayouts(std::vector<GlyphLayout>& gls) = 0;
|
||||
virtual void clearGlyphLayouts() = 0;
|
||||
virtual void setUseGlyphLayoutsOnly(bool gls_only) = 0;
|
||||
virtual bool useGlyphLayoutsOnly() const = 0;
|
||||
};
|
||||
|
||||
|
||||
|
@ -53,6 +53,20 @@ public:
|
||||
video::SColor color, bool hcenter=false,
|
||||
bool vcenter=false, const core::rect<s32>* clip=0);
|
||||
|
||||
virtual void drawQuick(const core::stringw& text, const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter=false,
|
||||
bool vcenter=false, const core::rect<s32>* clip=0)
|
||||
{
|
||||
draw(text, position, color, hcenter, vcenter, clip);
|
||||
}
|
||||
|
||||
virtual void draw(const std::vector<GlyphLayout>& gls, const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter=false,
|
||||
bool vcenter=false, const core::rect<s32>* clip=0) {}
|
||||
|
||||
virtual void initGlyphLayouts(const core::stringw& text,
|
||||
std::vector<GlyphLayout>& gls, std::vector<std::u32string>* line_data = NULL) {}
|
||||
|
||||
//! returns the dimension of a text
|
||||
virtual core::dimension2d<u32> getDimension(const wchar_t* text) const;
|
||||
|
||||
@ -66,6 +80,8 @@ public:
|
||||
virtual void setKerningWidth (s32 kerning);
|
||||
virtual void setKerningHeight (s32 kerning);
|
||||
|
||||
virtual s32 getHeightPerLine() const { return (s32)getDimension(L"A").Height + getKerningHeight(); }
|
||||
|
||||
//! set an Pixel Offset on Drawing ( scale position on width )
|
||||
virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const;
|
||||
virtual s32 getKerningHeight() const;
|
||||
@ -77,6 +93,9 @@ public:
|
||||
virtual u32 getSpriteNoFromChar(const wchar_t *c) const;
|
||||
|
||||
virtual void setInvisibleCharacters( const wchar_t *s );
|
||||
virtual void setScale(f32 scale) {}
|
||||
virtual f32 getInverseShaping() const { return 1.0f; }
|
||||
virtual f32 getScale() const { return 1.0f; }
|
||||
|
||||
private:
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "IGUIFont.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "rect.h"
|
||||
#include "utfwrapping.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -27,7 +26,7 @@ CGUIStaticText::CGUIStaticText(const wchar_t* text, bool border,
|
||||
Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background),
|
||||
RestrainTextInside(true), RightToLeft(false),
|
||||
OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)),
|
||||
OverrideFont(0), LastBreakFont(0)
|
||||
OverrideFont(0), LastBreakFont(0), m_use_glyph_layouts_only(false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIStaticText");
|
||||
@ -80,63 +79,39 @@ void CGUIStaticText::draw()
|
||||
frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
|
||||
}
|
||||
|
||||
// draw the text
|
||||
if (Text.size())
|
||||
// draw the text layout
|
||||
if (!m_glyph_layouts.empty())
|
||||
{
|
||||
IGUIFont* font = getActiveFont();
|
||||
|
||||
if (font)
|
||||
{
|
||||
if (!WordWrap)
|
||||
if (font != LastBreakFont)
|
||||
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)
|
||||
{
|
||||
if (VAlign == EGUIA_LOWERRIGHT)
|
||||
{
|
||||
frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y -
|
||||
font->getDimension(L"A").Height - font->getKerningHeight();
|
||||
}
|
||||
if (HAlign == EGUIA_LOWERRIGHT)
|
||||
{
|
||||
frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
|
||||
font->getDimension(Text.c_str()).Width;
|
||||
}
|
||||
|
||||
font->draw(Text.c_str(), frameRect,
|
||||
OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
|
||||
HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
|
||||
r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
|
||||
}
|
||||
else
|
||||
else if (VAlign == EGUIA_LOWERRIGHT)
|
||||
{
|
||||
if (font != LastBreakFont)
|
||||
breakText();
|
||||
|
||||
core::rect<s32> r = frameRect;
|
||||
s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
|
||||
s32 totalHeight = height * BrokenText.size();
|
||||
if (VAlign == EGUIA_CENTER)
|
||||
{
|
||||
r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
|
||||
}
|
||||
else if (VAlign == EGUIA_LOWERRIGHT)
|
||||
{
|
||||
r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
|
||||
}
|
||||
|
||||
for (u32 i=0; i<BrokenText.size(); ++i)
|
||||
{
|
||||
if (HAlign == EGUIA_LOWERRIGHT)
|
||||
{
|
||||
r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
|
||||
font->getDimension(BrokenText[i].c_str()).Width;
|
||||
}
|
||||
|
||||
font->draw(BrokenText[i].c_str(), r,
|
||||
OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
|
||||
HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
|
||||
|
||||
r.LowerRightCorner.Y += height;
|
||||
r.UpperLeftCorner.Y += height;
|
||||
}
|
||||
r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
|
||||
}
|
||||
|
||||
|
||||
if (HAlign == EGUIA_LOWERRIGHT)
|
||||
{
|
||||
r.UpperLeftCorner.X = frameRect.LowerRightCorner.X - dim.Width;
|
||||
}
|
||||
|
||||
font->draw(m_glyph_layouts, r,
|
||||
OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
|
||||
HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,10 +282,7 @@ bool CGUIStaticText::isRightToLeft() const
|
||||
//! Breaks the single text line.
|
||||
void CGUIStaticText::breakText()
|
||||
{
|
||||
if (!WordWrap)
|
||||
return;
|
||||
|
||||
BrokenText.clear();
|
||||
m_glyph_layouts.clear();
|
||||
|
||||
IGUISkin* skin = Environment->getSkin();
|
||||
IGUIFont* font = getActiveFont();
|
||||
@ -319,194 +291,13 @@ void CGUIStaticText::breakText()
|
||||
|
||||
LastBreakFont = font;
|
||||
|
||||
core::stringw line;
|
||||
core::stringw word;
|
||||
core::stringw whitespace;
|
||||
s32 size = Text.size();
|
||||
s32 length = 0;
|
||||
s32 elWidth = RelativeRect.getWidth();
|
||||
f32 elWidth = RelativeRect.getWidth();
|
||||
if (Border)
|
||||
elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X);
|
||||
wchar_t c;
|
||||
|
||||
// We have to deal with right-to-left and left-to-right differently
|
||||
// However, most parts of the following code is the same, it's just
|
||||
// some order and boundaries which change.
|
||||
if (!RightToLeft)
|
||||
{
|
||||
// regular (left-to-right)
|
||||
for (s32 i=0; i<size; ++i)
|
||||
{
|
||||
c = Text[i];
|
||||
bool lineBreak = false;
|
||||
|
||||
if (c == L'\r') // Mac or Windows breaks
|
||||
{
|
||||
lineBreak = true;
|
||||
if (Text[i+1] == L'\n') // Windows breaks
|
||||
{
|
||||
Text.erase(i+1);
|
||||
--size;
|
||||
}
|
||||
c = '\0';
|
||||
}
|
||||
else if (c == L'\n') // Unix breaks
|
||||
{
|
||||
lineBreak = true;
|
||||
c = '\0';
|
||||
}
|
||||
word += c;
|
||||
|
||||
if (word.size())
|
||||
{
|
||||
const s32 wordlgth = font->getDimension(word.c_str()).Width;
|
||||
|
||||
if (length && (length + wordlgth > elWidth))
|
||||
{ // too long to fit inside
|
||||
// break to next line
|
||||
unsigned int where = 1;
|
||||
while (where != line.size()) //Find the first breakable position
|
||||
{
|
||||
if (UtfNoEnding(Text[i - where]) || //Prevent unsuitable character from displaying
|
||||
UtfNoStarting(Text[i - where]) || //at the position of starting or ending of a line
|
||||
UtfNoStarting(Text[i + 1 - where])) //Handle case which more than one non-newline-starting characters are together
|
||||
{
|
||||
where++;
|
||||
continue;
|
||||
}
|
||||
if (breakable(Text[i - where]))
|
||||
break;
|
||||
else
|
||||
where++;
|
||||
}
|
||||
if (where != line.size())
|
||||
{
|
||||
core::stringw first = line.subString(0, line.size() + 1 - where);
|
||||
core::stringw second = line.subString(line.size() + 1 - where , where - 1);
|
||||
if (first.lastChar() == wchar_t(0x00AD))
|
||||
BrokenText.push_back(first + L"-"); //Print the Unicode Soft HYphen (SHY / 00AD) character
|
||||
else
|
||||
BrokenText.push_back(first);
|
||||
const s32 secondLength = font->getDimension(second.c_str()).Width;
|
||||
|
||||
length = secondLength + wordlgth;
|
||||
line = second + word;
|
||||
}
|
||||
else if (breakable(c) || UtfNoEnding(c) || UtfNoStarting(c)) //Unusual case
|
||||
{
|
||||
BrokenText.push_back(line); //Force breaking to next line too if last word is breakable,
|
||||
line = word; //it happens when someone writes too many non-newline-starting
|
||||
length = wordlgth; //chars in the first line, so we ignore the rules.
|
||||
}
|
||||
// No suitable place to break words, so there's nothing more we can do
|
||||
// break to next line
|
||||
else
|
||||
{
|
||||
line += word;
|
||||
length += wordlgth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
line += word;
|
||||
length += wordlgth;
|
||||
}
|
||||
|
||||
word = L"";
|
||||
|
||||
}
|
||||
// compute line break
|
||||
if (lineBreak)
|
||||
{
|
||||
line += word;
|
||||
BrokenText.push_back(line);
|
||||
line = L"";
|
||||
word = L"";
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
line += word;
|
||||
BrokenText.push_back(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
// right-to-left
|
||||
for (s32 i=size; i>=0; --i)
|
||||
{
|
||||
c = Text[i];
|
||||
bool lineBreak = false;
|
||||
|
||||
if (c == L'\r') // Mac or Windows breaks
|
||||
{
|
||||
lineBreak = true;
|
||||
if ((i>0) && Text[i-1] == L'\n') // Windows breaks
|
||||
{
|
||||
Text.erase(i-1);
|
||||
--size;
|
||||
}
|
||||
c = '\0';
|
||||
}
|
||||
else if (c == L'\n') // Unix breaks
|
||||
{
|
||||
lineBreak = true;
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
if (c==L' ' || c==0 || i==0)
|
||||
{
|
||||
if (word.size())
|
||||
{
|
||||
// here comes the next whitespace, look if
|
||||
// we must break the last word to the next line.
|
||||
const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
|
||||
const s32 wordlgth = font->getDimension(word.c_str()).Width;
|
||||
|
||||
if (length && (length + wordlgth + whitelgth > elWidth))
|
||||
{
|
||||
// break to next line
|
||||
BrokenText.push_back(line);
|
||||
length = wordlgth;
|
||||
line = word;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add word to line
|
||||
line = whitespace + line;
|
||||
line = word + line;
|
||||
length += whitelgth + wordlgth;
|
||||
}
|
||||
|
||||
word = L"";
|
||||
whitespace = L"";
|
||||
}
|
||||
|
||||
if (c != 0)
|
||||
whitespace = core::stringw(&c, 1) + whitespace;
|
||||
|
||||
// compute line break
|
||||
if (lineBreak)
|
||||
{
|
||||
line = whitespace + line;
|
||||
line = word + line;
|
||||
BrokenText.push_back(line);
|
||||
line = L"";
|
||||
word = L"";
|
||||
whitespace = L"";
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// yippee this is a word..
|
||||
word = core::stringw(&c, 1) + word;
|
||||
}
|
||||
}
|
||||
|
||||
line = whitespace + line;
|
||||
line = word + line;
|
||||
BrokenText.push_back(line);
|
||||
}
|
||||
if (!m_use_glyph_layouts_only)
|
||||
font->initGlyphLayouts(Text, m_glyph_layouts);
|
||||
if (WordWrap)
|
||||
breakGlyphLayouts(m_glyph_layouts, elWidth, font->getInverseShaping(), font->getScale());
|
||||
}
|
||||
|
||||
|
||||
@ -532,39 +323,23 @@ s32 CGUIStaticText::getTextHeight() const
|
||||
if (!font)
|
||||
return 0;
|
||||
|
||||
s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
|
||||
auto dim = getGlyphLayoutsDimension(m_glyph_layouts,
|
||||
font->getHeightPerLine(), font->getInverseShaping(), font->getScale());
|
||||
|
||||
if (WordWrap)
|
||||
height *= BrokenText.size();
|
||||
|
||||
return height;
|
||||
return dim.Height;
|
||||
}
|
||||
|
||||
|
||||
s32 CGUIStaticText::getTextWidth() const
|
||||
{
|
||||
IGUIFont * font = getActiveFont();
|
||||
if(!font)
|
||||
if (!font)
|
||||
return 0;
|
||||
|
||||
if(WordWrap)
|
||||
{
|
||||
s32 widest = 0;
|
||||
auto dim = getGlyphLayoutsDimension(m_glyph_layouts,
|
||||
font->getHeightPerLine(), font->getInverseShaping(), font->getScale());
|
||||
|
||||
for(u32 line = 0; line < BrokenText.size(); ++line)
|
||||
{
|
||||
s32 width = font->getDimension(BrokenText[line].c_str()).Width;
|
||||
|
||||
if(width > widest)
|
||||
widest = width;
|
||||
}
|
||||
|
||||
return widest;
|
||||
}
|
||||
else
|
||||
{
|
||||
return font->getDimension(Text.c_str()).Width;
|
||||
}
|
||||
return dim.Width;
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "IGUIStaticText.h"
|
||||
#include "irrArray.h"
|
||||
#include "GlyphLayout.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -115,6 +116,12 @@ namespace gui
|
||||
//! Reads attributes of the element
|
||||
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
|
||||
|
||||
virtual const std::vector<GlyphLayout>& getGlyphLayouts() const { return m_glyph_layouts; }
|
||||
virtual void setGlyphLayouts(std::vector<GlyphLayout>& gls) { m_glyph_layouts = gls; }
|
||||
virtual void clearGlyphLayouts() { m_glyph_layouts.clear(); }
|
||||
virtual void setUseGlyphLayoutsOnly(bool gls_only) { m_use_glyph_layouts_only = gls_only; }
|
||||
virtual bool useGlyphLayoutsOnly() const { return m_use_glyph_layouts_only; }
|
||||
|
||||
private:
|
||||
|
||||
//! Breaks the single text line.
|
||||
@ -133,7 +140,9 @@ namespace gui
|
||||
gui::IGUIFont* OverrideFont;
|
||||
gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated.
|
||||
|
||||
core::array< core::stringw > BrokenText;
|
||||
//! If true, setText / updating this element will not reshape with Text object
|
||||
bool m_use_glyph_layouts_only;
|
||||
std::vector<GlyphLayout> m_glyph_layouts;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#include <IWriteFile.h>
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "graphics/glwrap.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "online/http_request.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
@ -38,6 +38,7 @@ static std::vector<UserConfigParam*> all_params;
|
||||
#include "io/utf_writer.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
|
@ -30,7 +30,7 @@ class FaceTTF;
|
||||
class BoldFace : public FontWithFace
|
||||
{
|
||||
private:
|
||||
virtual unsigned int getGlyphPageSize() const OVERRIDE { return 1024; }
|
||||
virtual unsigned int getGlyphPageSize() const OVERRIDE { return 512; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getScalingFactorOne() const OVERRIDE { return 0.3f; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -45,7 +45,8 @@ public:
|
||||
virtual void init() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void reset() OVERRIDE;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool disableTextShaping() const OVERRIDE { return true; }
|
||||
}; // DigitFace
|
||||
|
||||
#endif
|
||||
|
@ -19,7 +19,6 @@
|
||||
#ifndef HEADER_FACE_TTF_HPP
|
||||
#define HEADER_FACE_TTF_HPP
|
||||
|
||||
#include "utils/leak_check.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
#include <cassert>
|
||||
@ -51,7 +50,7 @@ struct FontArea
|
||||
};
|
||||
|
||||
/** This class will load a list of TTF files from \ref FontManager, and save
|
||||
* them inside \ref m_faces for \ref FontWithFace to load glyph.
|
||||
* them inside \ref m_ft_faces for \ref FontWithFace to load glyph.
|
||||
* \ingroup font
|
||||
*/
|
||||
class FaceTTF : public NoCopy
|
||||
@ -60,7 +59,7 @@ class FaceTTF : public NoCopy
|
||||
private:
|
||||
/** Contains all FT_Face with a list of loaded glyph index with the
|
||||
* \ref FontArea. */
|
||||
std::vector<std::pair<FT_Face, std::map<unsigned, FontArea> > > m_faces;
|
||||
std::vector<std::pair<FT_Face, std::map<unsigned, FontArea> > > m_ft_faces;
|
||||
#endif
|
||||
public:
|
||||
LEAK_CHECK()
|
||||
@ -72,55 +71,44 @@ public:
|
||||
void reset()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
for (unsigned int i = 0; i < m_faces.size(); i++)
|
||||
m_faces[i].second.clear();
|
||||
#endif
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/* Return a white-space font area, which is the first glyph in ttf. */
|
||||
const FontArea* getFirstFontArea() const
|
||||
{
|
||||
#ifdef SERVER_ONLY
|
||||
static FontArea area;
|
||||
return &area;
|
||||
#else
|
||||
if (m_faces.empty())
|
||||
return NULL;
|
||||
if (m_faces.front().second.empty())
|
||||
return NULL;
|
||||
return &(m_faces.front().second.begin()->second);
|
||||
for (unsigned int i = 0; i < m_ft_faces.size(); i++)
|
||||
m_ft_faces[i].second.clear();
|
||||
#endif
|
||||
}
|
||||
#ifndef SERVER_ONLY
|
||||
// ------------------------------------------------------------------------
|
||||
void loadFaces(std::vector<FT_Face> faces)
|
||||
void loadTTF(std::vector<FT_Face> faces)
|
||||
{
|
||||
for (unsigned int i = 0; i < faces.size(); i++)
|
||||
m_faces.emplace_back(faces[i], std::map<unsigned, FontArea>());
|
||||
m_ft_faces.emplace_back(faces[i], std::map<unsigned, FontArea>());
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return a TTF in \ref m_faces.
|
||||
* \param i index of TTF file in \ref m_faces.
|
||||
/** Return a TTF in \ref m_ft_faces.
|
||||
* \param i index of TTF file in \ref m_ft_faces.
|
||||
*/
|
||||
FT_Face getFace(unsigned int i) const
|
||||
{
|
||||
assert(i < m_faces.size());
|
||||
return m_faces[i].first;
|
||||
assert(i < m_ft_faces.size());
|
||||
return m_ft_faces[i].first;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the total TTF files loaded. */
|
||||
unsigned int getTotalFaces() const { return (unsigned int)m_faces.size(); }
|
||||
unsigned int getTotalFaces() const
|
||||
{ return (unsigned int)m_ft_faces.size(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void insertFontArea(const FontArea& a, unsigned font_index,
|
||||
unsigned glyph_index)
|
||||
{
|
||||
auto& ttf = m_faces.at(font_index).second;
|
||||
auto& ttf = m_ft_faces.at(font_index).second;
|
||||
ttf[glyph_index] = a;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
bool enabledForFont(unsigned idx) const
|
||||
{ return m_ft_faces.size() > idx && m_ft_faces[idx].first != NULL; }
|
||||
// ------------------------------------------------------------------------
|
||||
const FontArea* getFontArea(unsigned font_index, unsigned glyph_index)
|
||||
{
|
||||
auto& ttf = m_faces.at(font_index).second;
|
||||
auto& ttf = m_ft_faces.at(font_index).second;
|
||||
auto it = ttf.find(glyph_index);
|
||||
if (it != ttf.end())
|
||||
return &it->second;
|
||||
@ -129,9 +117,9 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
bool getFontAndGlyphFromChar(uint32_t c, unsigned* font, unsigned* glyph)
|
||||
{
|
||||
for (unsigned i = 0; i < m_faces.size(); i++)
|
||||
for (unsigned i = 0; i < m_ft_faces.size(); i++)
|
||||
{
|
||||
unsigned glyph_index = FT_Get_Char_Index(m_faces[i].first, c);
|
||||
unsigned glyph_index = FT_Get_Char_Index(m_ft_faces[i].first, c);
|
||||
if (glyph_index > 0)
|
||||
{
|
||||
*font = i;
|
||||
|
@ -28,6 +28,11 @@
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
#include <fribidi/fribidi.h>
|
||||
#include <raqm.h>
|
||||
#endif
|
||||
|
||||
FontManager *font_manager = NULL;
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor. It will initialize the \ref m_ft_library.
|
||||
@ -36,6 +41,8 @@ FontManager::FontManager()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
m_ft_library = NULL;
|
||||
m_digit_face = NULL;
|
||||
m_shaping_dpi = 128;
|
||||
if (ProfileWorld::isNoGraphics())
|
||||
return;
|
||||
|
||||
@ -57,7 +64,9 @@ FontManager::~FontManager()
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < m_faces.size(); i++)
|
||||
checkFTError(FT_Done_Face(m_faces[i]), "removing faces");
|
||||
checkFTError(FT_Done_Face(m_faces[i]), "removing faces for shaping");
|
||||
if (m_digit_face != NULL)
|
||||
checkFTError(FT_Done_Face(m_digit_face), "removing digit face");
|
||||
checkFTError(FT_Done_FreeType(m_ft_library), "removing freetype library");
|
||||
#endif
|
||||
} // ~FontManager
|
||||
@ -85,6 +94,399 @@ std::vector<FT_Face>
|
||||
}
|
||||
return ret;
|
||||
} // loadTTF
|
||||
|
||||
// ============================================================================
|
||||
namespace LineBreakingRules
|
||||
{
|
||||
// Here a list of characters that don't start or end a line for
|
||||
// chinese/japanese/korean. Only commonly use and full width characters are
|
||||
// included. You should use full width characters when writing CJK,
|
||||
// like using "。"instead of a ".", you can add more characters if needed.
|
||||
// For full list please visit:
|
||||
// http://webapp.docx4java.org/OnlineDemo/ecma376/WordML/kinsoku.html
|
||||
bool noStartingLine(char32_t c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
// ’
|
||||
case 8217:
|
||||
return true;
|
||||
// ”
|
||||
case 8221:
|
||||
return true;
|
||||
// 々
|
||||
case 12293:
|
||||
return true;
|
||||
// 〉
|
||||
case 12297:
|
||||
return true;
|
||||
// 》
|
||||
case 12299:
|
||||
return true;
|
||||
// 」
|
||||
case 12301:
|
||||
return true;
|
||||
// }
|
||||
case 65373:
|
||||
return true;
|
||||
// 〕
|
||||
case 12309:
|
||||
return true;
|
||||
// )
|
||||
case 65289:
|
||||
return true;
|
||||
// 』
|
||||
case 12303:
|
||||
return true;
|
||||
// 】
|
||||
case 12305:
|
||||
return true;
|
||||
// 〗
|
||||
case 12311:
|
||||
return true;
|
||||
// !
|
||||
case 65281:
|
||||
return true;
|
||||
// %
|
||||
case 65285:
|
||||
return true;
|
||||
// ?
|
||||
case 65311:
|
||||
return true;
|
||||
// `
|
||||
case 65344:
|
||||
return true;
|
||||
// ,
|
||||
case 65292:
|
||||
return true;
|
||||
// :
|
||||
case 65306:
|
||||
return true;
|
||||
// ;
|
||||
case 65307:
|
||||
return true;
|
||||
// .
|
||||
case 65294:
|
||||
return true;
|
||||
// 。
|
||||
case 12290:
|
||||
return true;
|
||||
// 、
|
||||
case 12289:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} // noStartingLine
|
||||
//-------------------------------------------------------------------------
|
||||
bool noEndingLine(char32_t c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
// ‘
|
||||
case 8216:
|
||||
return true;
|
||||
// “
|
||||
case 8220:
|
||||
return true;
|
||||
// 〈
|
||||
case 12296:
|
||||
return true;
|
||||
// 《
|
||||
case 12298:
|
||||
return true;
|
||||
// 「
|
||||
case 12300:
|
||||
return true;
|
||||
// {
|
||||
case 65371:
|
||||
return true;
|
||||
// 〔
|
||||
case 12308:
|
||||
return true;
|
||||
// (
|
||||
case 65288:
|
||||
return true;
|
||||
// 『
|
||||
case 12302:
|
||||
return true;
|
||||
// 【
|
||||
case 12304:
|
||||
return true;
|
||||
// 〖
|
||||
case 12310:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} // noEndingLine
|
||||
//-------------------------------------------------------------------------
|
||||
// Helper function
|
||||
bool breakable(char32_t c)
|
||||
{
|
||||
if ((c > 12287 && c < 40960) || // Common CJK words
|
||||
(c > 44031 && c < 55204) || // Hangul
|
||||
(c > 63743 && c < 64256) || // More Chinese
|
||||
c == 173 || c == 32 || // Soft hyphen and white space
|
||||
c == 47 || c == 92) // Slash and blackslash
|
||||
return true;
|
||||
return false;
|
||||
} // breakable
|
||||
//-------------------------------------------------------------------------
|
||||
void insertBreakMark(const std::u32string& str, std::vector<bool>& result)
|
||||
{
|
||||
assert(str.size() == result.size());
|
||||
for (unsigned i = 0; i < result.size(); i++)
|
||||
{
|
||||
char32_t c = str[i];
|
||||
char32_t nextline_char = 20;
|
||||
if (i < result.size() - 1)
|
||||
nextline_char = str[i + 1];
|
||||
if (breakable(c) && !noEndingLine(c) &&
|
||||
!noStartingLine(nextline_char))
|
||||
{
|
||||
result[i] = true;
|
||||
}
|
||||
}
|
||||
} // insertBreakMark
|
||||
} // namespace LineBreakingRules
|
||||
|
||||
// ============================================================================
|
||||
namespace RTLRules
|
||||
{
|
||||
//-------------------------------------------------------------------------
|
||||
void insertRTLMark(const std::u32string& str, std::vector<bool>& line,
|
||||
std::vector<bool>& char_bool)
|
||||
{
|
||||
// Check if first character has strong RTL, then consider this line is
|
||||
// RTL
|
||||
std::vector<FriBidiCharType> types;
|
||||
std::vector<FriBidiLevel> levels;
|
||||
types.resize(str.size(), 0);
|
||||
levels.resize(str.size(), 0);
|
||||
FriBidiParType par_type = FRIBIDI_PAR_ON;
|
||||
const FriBidiChar* fribidi_str = (const FriBidiChar*)str.c_str();
|
||||
fribidi_get_bidi_types(fribidi_str, str.size(), types.data());
|
||||
#if FRIBIDI_MAJOR_VERSION >= 1
|
||||
std::vector<FriBidiBracketType> btypes;
|
||||
btypes.resize(str.size(), 0);
|
||||
fribidi_get_bracket_types(fribidi_str, str.size(), types.data(),
|
||||
btypes.data());
|
||||
int max_level = fribidi_get_par_embedding_levels_ex(types.data(),
|
||||
btypes.data(), str.size(), &par_type, levels.data());
|
||||
#else
|
||||
int max_level = fribidi_get_par_embedding_levels(types.data(),
|
||||
str.size(), &par_type, levels.data());
|
||||
#endif
|
||||
(void)max_level;
|
||||
bool cur_rtl = par_type == FRIBIDI_PAR_RTL;
|
||||
if (cur_rtl)
|
||||
{
|
||||
for (unsigned i = 0; i < line.size(); i++)
|
||||
line[i] = true;
|
||||
}
|
||||
int cur_level = levels[0];
|
||||
for (unsigned i = 0; i < char_bool.size(); i++)
|
||||
{
|
||||
if (levels[i] != cur_level)
|
||||
{
|
||||
cur_rtl = !cur_rtl;
|
||||
cur_level = levels[i];
|
||||
}
|
||||
char_bool[i] = cur_rtl;
|
||||
}
|
||||
} // insertRTLMark
|
||||
} // namespace RTLRules
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/* Turn text into glyph layout for rendering by libraqm. */
|
||||
void FontManager::shape(const std::u32string& text,
|
||||
std::vector<irr::gui::GlyphLayout>& gls,
|
||||
std::vector<std::u32string>* line_data)
|
||||
|
||||
{
|
||||
if (text.empty())
|
||||
return;
|
||||
|
||||
auto lines = StringUtils::split(text, U'\n');
|
||||
// If the text end with and newline, it will miss a newline height, so we
|
||||
// it back here
|
||||
if (text.back() == U'\n')
|
||||
lines.push_back(U"");
|
||||
|
||||
for (unsigned l = 0; l < lines.size(); l++)
|
||||
{
|
||||
if (l != 0)
|
||||
{
|
||||
gui::GlyphLayout gl = { 0 };
|
||||
gl.flags = gui::GLF_NEWLINE;
|
||||
gls.push_back(gl);
|
||||
}
|
||||
|
||||
std::u32string& str = lines[l];
|
||||
str.erase(std::remove(str.begin(), str.end(), U'\r'), str.end());
|
||||
str.erase(std::remove(str.begin(), str.end(), U'\t'), str.end());
|
||||
if (str.empty())
|
||||
{
|
||||
if (line_data)
|
||||
line_data->push_back(str);
|
||||
continue;
|
||||
}
|
||||
|
||||
raqm_t* rq = raqm_create();
|
||||
if (!rq)
|
||||
{
|
||||
Log::error("FontManager", "Failed to raqm_create.");
|
||||
gls.clear();
|
||||
if (line_data)
|
||||
line_data->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const int length = (int)str.size();
|
||||
const uint32_t* string_array = (const uint32_t*)str.c_str();
|
||||
|
||||
if (!raqm_set_text(rq, string_array, length))
|
||||
{
|
||||
Log::error("FontManager", "Failed to raqm_set_text.");
|
||||
raqm_destroy(rq);
|
||||
gls.clear();
|
||||
if (line_data)
|
||||
line_data->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
FT_Face cur_face = m_faces.front();
|
||||
for (unsigned j = 0; j < m_faces.size(); j++)
|
||||
{
|
||||
unsigned glyph_index = FT_Get_Char_Index(m_faces[j], str[i]);
|
||||
if (glyph_index > 0)
|
||||
{
|
||||
cur_face = m_faces[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkFTError(FT_Set_Pixel_Sizes(cur_face, 0,
|
||||
m_shaping_dpi), "setting DPI");
|
||||
if (!raqm_set_freetype_face_range(rq, cur_face, i, 1))
|
||||
{
|
||||
Log::error("FontManager",
|
||||
"Failed to raqm_set_freetype_face_range.");
|
||||
raqm_destroy(rq);
|
||||
gls.clear();
|
||||
if (line_data)
|
||||
line_data->clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (raqm_layout(rq))
|
||||
{
|
||||
std::vector<gui::GlyphLayout> cur_line;
|
||||
std::vector<bool> rtl_line, rtl_char, breakable;
|
||||
rtl_line.resize(str.size(), false);
|
||||
rtl_char.resize(str.size(), false);
|
||||
breakable.resize(str.size(), false);
|
||||
LineBreakingRules::insertBreakMark(str, breakable);
|
||||
RTLRules::insertRTLMark(str, rtl_line, rtl_char);
|
||||
size_t count = 0;
|
||||
raqm_glyph_t* glyphs = raqm_get_glyphs(rq, &count);
|
||||
for (unsigned idx = 0; idx < (unsigned)count; idx++)
|
||||
{
|
||||
gui::GlyphLayout gl = { 0 };
|
||||
gl.index = glyphs[idx].index;
|
||||
gl.cluster.push_back(glyphs[idx].cluster);
|
||||
gl.x_advance = glyphs[idx].x_advance / BEARING;
|
||||
gl.x_offset = glyphs[idx].x_offset / BEARING;
|
||||
gl.y_offset = glyphs[idx].y_offset / BEARING;
|
||||
gl.face_idx = m_ft_faces_to_index.at(glyphs[idx].ftface);
|
||||
gl.original_index = idx;
|
||||
if (rtl_line[glyphs[idx].cluster])
|
||||
gl.flags |= gui::GLF_RTL_LINE;
|
||||
if (rtl_char[glyphs[idx].cluster])
|
||||
gl.flags |= gui::GLF_RTL_CHAR;
|
||||
if (breakable[glyphs[idx].cluster])
|
||||
gl.flags |= gui::GLF_BREAKABLE;
|
||||
cur_line.push_back(gl);
|
||||
}
|
||||
// Sort glyphs in logical order
|
||||
std::sort(cur_line.begin(), cur_line.end(), []
|
||||
(const gui::GlyphLayout& a_gi, const gui::GlyphLayout& b_gi)
|
||||
{
|
||||
return a_gi.cluster.front() < b_gi.cluster.front();
|
||||
});
|
||||
for (unsigned l = 0; l < cur_line.size(); l++)
|
||||
{
|
||||
const int cur_cluster = cur_line[l].cluster.front();
|
||||
// Last cluster handling, add the remaining clusters if missing
|
||||
if (l == cur_line.size() - 1)
|
||||
{
|
||||
for (int extra_cluster = cur_cluster + 1;
|
||||
extra_cluster <= (int)str.size() - 1; extra_cluster++)
|
||||
cur_line[l].cluster.push_back(extra_cluster);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure there is every cluster values appear in the
|
||||
// list at least once, it will be used for cursor
|
||||
// positioning later
|
||||
const int next_cluster = cur_line[l + 1].cluster.front();
|
||||
for (int extra_cluster = cur_cluster + 1;
|
||||
extra_cluster <= next_cluster - 1; extra_cluster++)
|
||||
cur_line[l].cluster.push_back(extra_cluster);
|
||||
}
|
||||
}
|
||||
// Sort glyphs in visual order
|
||||
std::sort(cur_line.begin(), cur_line.end(), []
|
||||
(const gui::GlyphLayout& a_gi,
|
||||
const gui::GlyphLayout& b_gi)
|
||||
{
|
||||
return a_gi.original_index < b_gi.original_index;
|
||||
});
|
||||
gls.insert(gls.end(), cur_line.begin(), cur_line.end());
|
||||
raqm_destroy(rq);
|
||||
if (line_data)
|
||||
line_data->push_back(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("FontManager", "Failed to raqm_layout.");
|
||||
raqm_destroy(rq);
|
||||
gls.clear();
|
||||
if (line_data)
|
||||
line_data->clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // shape
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Convert text to glyph layouts for fast rendering with caching enabled
|
||||
* If line_data is not null, each broken line u32string will be saved and
|
||||
* can be used for advanced glyph and text mapping, and cache will be
|
||||
* disabled, no newline characters are allowed in text if line_data is not
|
||||
* NULL.
|
||||
*/
|
||||
void FontManager::initGlyphLayouts(const core::stringw& text,
|
||||
std::vector<irr::gui::GlyphLayout>& gls,
|
||||
std::vector<std::u32string>* line_data)
|
||||
{
|
||||
if (ProfileWorld::isNoGraphics() || text.empty())
|
||||
return;
|
||||
|
||||
if (line_data != NULL)
|
||||
{
|
||||
shape(StringUtils::wideToUtf32(text), gls, line_data);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& cached_gls = m_cached_gls[text];
|
||||
if (cached_gls.empty())
|
||||
shape(StringUtils::wideToUtf32(text), cached_gls);
|
||||
gls = cached_gls;
|
||||
} // initGlyphLayouts
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -95,14 +497,22 @@ void FontManager::loadFonts()
|
||||
#ifndef SERVER_ONLY
|
||||
// First load the TTF files required by each font
|
||||
std::vector<FT_Face> normal_ttf = loadTTF(stk_config->m_normal_ttf);
|
||||
// We use 16bit face idx in GlyphLayout class
|
||||
if (normal_ttf.size() > 65535)
|
||||
normal_ttf.resize(65535);
|
||||
for (uint16_t i = 0; i < normal_ttf.size(); i++)
|
||||
m_ft_faces_to_index[normal_ttf[i]] = i;
|
||||
|
||||
std::vector<FT_Face> digit_ttf = loadTTF(stk_config->m_digit_ttf);
|
||||
if (!digit_ttf.empty())
|
||||
m_digit_face = digit_ttf.front();
|
||||
#endif
|
||||
|
||||
// Now load fonts with settings of ttf file
|
||||
unsigned int font_loaded = 0;
|
||||
RegularFace* regular = new RegularFace();
|
||||
#ifndef SERVER_ONLY
|
||||
regular->getFaceTTF()->loadFaces(normal_ttf);
|
||||
regular->getFaceTTF()->loadTTF(normal_ttf);
|
||||
#endif
|
||||
regular->init();
|
||||
m_fonts.push_back(regular);
|
||||
@ -110,7 +520,7 @@ void FontManager::loadFonts()
|
||||
|
||||
BoldFace* bold = new BoldFace();
|
||||
#ifndef SERVER_ONLY
|
||||
bold->getFaceTTF()->loadFaces(normal_ttf);
|
||||
bold->getFaceTTF()->loadTTF(normal_ttf);
|
||||
#endif
|
||||
bold->init();
|
||||
m_fonts.push_back(bold);
|
||||
@ -118,7 +528,7 @@ void FontManager::loadFonts()
|
||||
|
||||
DigitFace* digit = new DigitFace();
|
||||
#ifndef SERVER_ONLY
|
||||
digit->getFaceTTF()->loadFaces(digit_ttf);
|
||||
digit->getFaceTTF()->loadTTF(digit_ttf);
|
||||
#endif
|
||||
digit->init();
|
||||
m_fonts.push_back(digit);
|
||||
@ -126,7 +536,6 @@ void FontManager::loadFonts()
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
m_faces.insert(m_faces.end(), normal_ttf.begin(), normal_ttf.end());
|
||||
m_faces.insert(m_faces.end(), digit_ttf.begin(), digit_ttf.end());
|
||||
#endif
|
||||
} // loadFonts
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@ -35,6 +36,9 @@
|
||||
#ifndef SERVER_ONLY
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "irrString.h"
|
||||
#include "GlyphLayout.h"
|
||||
#endif
|
||||
|
||||
class FontWithFace;
|
||||
@ -52,14 +56,28 @@ private:
|
||||
/** A FreeType library, it holds the FT_Face internally inside freetype. */
|
||||
FT_Library m_ft_library;
|
||||
|
||||
/** List of ttf files loaded. */
|
||||
/** List of TTF files for complex text shaping. */
|
||||
std::vector<FT_Face> m_faces;
|
||||
|
||||
/** TTF file for digit font in STK. */
|
||||
FT_Face m_digit_face;
|
||||
|
||||
/** DPI used when shaping, each face will apply an inverse of this and the
|
||||
* dpi of itself when rendering, so all faces can share the same glyph
|
||||
* layout cache. */
|
||||
unsigned m_shaping_dpi;
|
||||
|
||||
/** Map FT_Face to index for quicker layout. */
|
||||
std::map<FT_Face, uint16_t> m_ft_faces_to_index;
|
||||
|
||||
/** Text drawn to glyph layouts cache. */
|
||||
std::map<irr::core::stringw,
|
||||
std::vector<irr::gui::GlyphLayout> > m_cached_gls;
|
||||
#endif
|
||||
|
||||
/** Map type for each \ref FontWithFace with a index, save getting time in
|
||||
* \ref getFont. */
|
||||
std::unordered_map<std::type_index, int> m_font_type_map;
|
||||
|
||||
public:
|
||||
LEAK_CHECK()
|
||||
// ------------------------------------------------------------------------
|
||||
@ -99,13 +117,25 @@ public:
|
||||
FT_Library getFTLibrary() const { return m_ft_library; }
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<FT_Face> loadTTF(const std::vector<std::string>& ttf_list);
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned getShapingDPI() const { return m_shaping_dpi; }
|
||||
// ------------------------------------------------------------------------
|
||||
void shape(const std::u32string& text,
|
||||
std::vector<irr::gui::GlyphLayout>& gls,
|
||||
std::vector<std::u32string>* line_data = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<irr::gui::GlyphLayout>& getCachedLayouts
|
||||
(const irr::core::stringw& str) { return m_cached_gls[str]; }
|
||||
// ------------------------------------------------------------------------
|
||||
void initGlyphLayouts(const irr::core::stringw& text,
|
||||
std::vector<irr::gui::GlyphLayout>& gls,
|
||||
std::vector<std::u32string>* line_data = NULL);
|
||||
#endif
|
||||
// ------------------------------------------------------------------------
|
||||
void loadFonts();
|
||||
// ------------------------------------------------------------------------
|
||||
void unitTesting();
|
||||
|
||||
|
||||
}; // FontManager
|
||||
|
||||
extern FontManager *font_manager;
|
||||
|
@ -31,7 +31,9 @@
|
||||
#include "guiengine/skin.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/utf8.h"
|
||||
|
||||
#include "GlyphLayout.h"
|
||||
#include <array>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -50,7 +52,8 @@ FontWithFace::FontWithFace(const std::string& name)
|
||||
m_fallback_font_scale = 1.0f;
|
||||
m_glyph_max_height = 0;
|
||||
m_face_ttf = new FaceTTF();
|
||||
|
||||
m_face_dpi = 40;
|
||||
m_inverse_shaping = 1.0f;
|
||||
} // FontWithFace
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Destructor. Clears the glyph page and sprite bank.
|
||||
@ -172,17 +175,18 @@ void FontWithFace::createNewGlyphPage()
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Render a glyph for a character into bitmap and save it into the glyph page.
|
||||
* \param c \ref GlyphInfo for the character.
|
||||
* \param font_number Font number in \ref FaceTTF ttf list
|
||||
* \param glyph_index Glyph index in ttf
|
||||
*/
|
||||
void FontWithFace::insertGlyph(const GlyphInfo& gi)
|
||||
void FontWithFace::insertGlyph(unsigned font_number, unsigned glyph_index)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (ProfileWorld::isNoGraphics())
|
||||
return;
|
||||
|
||||
assert(gi.glyph_index > 0);
|
||||
assert(gi.font_number < m_face_ttf->getTotalFaces());
|
||||
FT_Face cur_face = m_face_ttf->getFace(gi.font_number);
|
||||
assert(glyph_index > 0);
|
||||
assert(font_number < m_face_ttf->getTotalFaces());
|
||||
FT_Face cur_face = m_face_ttf->getFace(font_number);
|
||||
FT_GlyphSlot slot = cur_face->glyph;
|
||||
|
||||
// Same face may be shared across the different FontWithFace,
|
||||
@ -190,7 +194,7 @@ void FontWithFace::insertGlyph(const GlyphInfo& gi)
|
||||
font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()),
|
||||
"setting DPI");
|
||||
|
||||
font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index,
|
||||
font_manager->checkFTError(FT_Load_Glyph(cur_face, glyph_index,
|
||||
FT_LOAD_DEFAULT), "loading a glyph");
|
||||
|
||||
font_manager->checkFTError(shapeOutline(&(slot->outline)),
|
||||
@ -272,7 +276,7 @@ void FontWithFace::insertGlyph(const GlyphInfo& gi)
|
||||
a.offset_y = m_glyph_max_height - cur_height + cur_offset_y;
|
||||
a.offset_y_bt = -cur_offset_y;
|
||||
a.spriteno = f.rectNumber;
|
||||
m_face_ttf->insertFontArea(a, gi.font_number, gi.glyph_index);
|
||||
m_face_ttf->insertFontArea(a, font_number, glyph_index);
|
||||
|
||||
// Store used area
|
||||
m_used_width += texture_size.Width;
|
||||
@ -293,7 +297,7 @@ void FontWithFace::updateCharactersList()
|
||||
for (const wchar_t& c : m_new_char_holder)
|
||||
{
|
||||
const GlyphInfo& gi = getGlyphInfo(c);
|
||||
insertGlyph(gi);
|
||||
insertGlyph(gi.font_number, gi.glyph_index);
|
||||
}
|
||||
m_new_char_holder.clear();
|
||||
|
||||
@ -357,9 +361,33 @@ void FontWithFace::setDPI()
|
||||
|
||||
factorTwo += UserConfigParams::m_fonts_size * 5 - 10;
|
||||
m_face_dpi = int(factorTwo * getScalingFactorOne() * scale);
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
if (!disableTextShaping())
|
||||
{
|
||||
m_inverse_shaping = (1.0f / (float)font_manager->getShapingDPI()) *
|
||||
float(m_face_dpi);
|
||||
}
|
||||
#endif
|
||||
} // setDPI
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/* Get the question mark glyph to show unsupported charaters. */
|
||||
const FontArea* FontWithFace::getUnknownFontArea() const
|
||||
{
|
||||
#ifdef SERVER_ONLY
|
||||
static FontArea area;
|
||||
return &area;
|
||||
#else
|
||||
std::map<wchar_t, GlyphInfo>::const_iterator n =
|
||||
m_character_glyph_info_map.find(L'?');
|
||||
assert(n != m_character_glyph_info_map.end());
|
||||
const FontArea* area = m_face_ttf->getFontArea(n->second.font_number,
|
||||
n->second.glyph_index);
|
||||
assert(area != NULL);
|
||||
return area;
|
||||
#endif
|
||||
} // getUnknownFontArea
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Return the \ref FontArea about a character.
|
||||
* \param c The character to get.
|
||||
@ -372,7 +400,7 @@ const FontArea& FontWithFace::getAreaFromCharacter(const wchar_t c,
|
||||
m_character_glyph_info_map.find(c);
|
||||
// Not found, return the first font area, which is a white-space
|
||||
if (n == m_character_glyph_info_map.end())
|
||||
return *(m_face_ttf->getFirstFontArea());
|
||||
return *getUnknownFontArea();
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
const FontArea* area = m_face_ttf->getFontArea(n->second.font_number,
|
||||
@ -394,7 +422,7 @@ const FontArea& FontWithFace::getAreaFromCharacter(const wchar_t c,
|
||||
*fallback_font = false;
|
||||
#endif
|
||||
|
||||
return *(m_face_ttf->getFirstFontArea());
|
||||
return *getUnknownFontArea();
|
||||
} // getAreaFromCharacter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -404,8 +432,8 @@ const FontArea& FontWithFace::getAreaFromCharacter(const wchar_t c,
|
||||
* \param font_settings \ref FontSettings to use.
|
||||
* \return The dimension of text
|
||||
*/
|
||||
core::dimension2d<u32> FontWithFace::getDimension(const wchar_t* text,
|
||||
FontSettings* font_settings)
|
||||
core::dimension2d<u32> FontWithFace::getDimension(const core::stringw& text,
|
||||
FontSettings* font_settings)
|
||||
{
|
||||
#ifdef SERVER_ONLY
|
||||
return core::dimension2d<u32>(1, 1);
|
||||
@ -414,42 +442,18 @@ core::dimension2d<u32> FontWithFace::getDimension(const wchar_t* text,
|
||||
return core::dimension2d<u32>(1, 1);
|
||||
|
||||
const float scale = font_settings ? font_settings->getScale() : 1.0f;
|
||||
// Test if lazy load char is needed
|
||||
insertCharacters(text);
|
||||
updateCharactersList();
|
||||
|
||||
core::dimension2d<float> dim(0.0f, 0.0f);
|
||||
core::dimension2d<float> this_line(0.0f, m_font_max_height * scale);
|
||||
|
||||
for (const wchar_t* p = text; *p; ++p)
|
||||
if (disableTextShaping())
|
||||
{
|
||||
if (*p == L'\r' || // Windows breaks
|
||||
*p == L'\n' ) // Unix breaks
|
||||
{
|
||||
if (*p==L'\r' && p[1] == L'\n') // Windows breaks
|
||||
++p;
|
||||
dim.Height += this_line.Height;
|
||||
if (dim.Width < this_line.Width)
|
||||
dim.Width = this_line.Width;
|
||||
this_line.Width = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool fallback = false;
|
||||
const FontArea &area = getAreaFromCharacter(*p, &fallback);
|
||||
|
||||
this_line.Width += getCharWidth(area, fallback, scale);
|
||||
return gui::getGlyphLayoutsDimension(text2GlyphsWithoutShaping(text),
|
||||
m_font_max_height, 1.0f/*inverse shaping*/, scale);
|
||||
}
|
||||
|
||||
dim.Height += this_line.Height;
|
||||
if (dim.Width < this_line.Width)
|
||||
dim.Width = this_line.Width;
|
||||
auto& gls = font_manager->getCachedLayouts(text);
|
||||
if (gls.empty() && !text.empty())
|
||||
font_manager->shape(StringUtils::wideToUtf32(text), gls);
|
||||
|
||||
core::dimension2d<u32> ret_dim(0, 0);
|
||||
ret_dim.Width = (u32)(dim.Width + 0.9f); // round up
|
||||
ret_dim.Height = (u32)(dim.Height + 0.9f);
|
||||
|
||||
return ret_dim;
|
||||
return gui::getGlyphLayoutsDimension(gls,
|
||||
m_font_max_height * scale, m_inverse_shaping, scale);
|
||||
#endif
|
||||
} // getDimension
|
||||
|
||||
@ -488,7 +492,7 @@ int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x,
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Render text and clip it to the specified rectangle if wanted, it will also
|
||||
* do checking for missing characters in font and lazy load them.
|
||||
* \param text The text to be rendering.
|
||||
* \param gl GlyphLayout rendered by libraqm to be rendering.
|
||||
* \param position The position to be rendering.
|
||||
* \param color The color used when rendering.
|
||||
* \param hcenter If rendered horizontally center.
|
||||
@ -497,7 +501,7 @@ int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x,
|
||||
* \param font_settings \ref FontSettings to use.
|
||||
* \param char_collector \ref FontCharCollector to render billboard text.
|
||||
*/
|
||||
void FontWithFace::render(const core::stringw& text,
|
||||
void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
|
||||
const core::rect<s32>& position,
|
||||
const video::SColor& color, bool hcenter,
|
||||
bool vcenter, const core::rect<s32>* clip,
|
||||
@ -505,14 +509,13 @@ void FontWithFace::render(const core::stringw& text,
|
||||
FontCharCollector* char_collector)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (ProfileWorld::isNoGraphics())
|
||||
if (ProfileWorld::isNoGraphics() || gl.empty())
|
||||
return;
|
||||
|
||||
const bool black_border = font_settings ?
|
||||
font_settings->useBlackBorder() : false;
|
||||
const bool colored_border = font_settings ?
|
||||
font_settings->useColoredBorder() : false;
|
||||
const bool rtl = font_settings ? font_settings->isRTL() : false;
|
||||
const float scale = font_settings ? font_settings->getScale() : 1.0f;
|
||||
const float shadow = font_settings ? font_settings->useShadow() : false;
|
||||
|
||||
@ -525,7 +528,7 @@ void FontWithFace::render(const core::stringw& text,
|
||||
core::rect<s32> shadowpos = position;
|
||||
shadowpos.LowerRightCorner.X += 2;
|
||||
shadowpos.LowerRightCorner.Y += 2;
|
||||
render(text, shadowpos, font_settings->getShadowColor(), hcenter,
|
||||
render(gl, shadowpos, font_settings->getShadowColor(), hcenter,
|
||||
vcenter, clip, font_settings);
|
||||
|
||||
// Set back
|
||||
@ -535,18 +538,28 @@ void FontWithFace::render(const core::stringw& text,
|
||||
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())
|
||||
return;
|
||||
|
||||
if (rtl || hcenter || vcenter || clip)
|
||||
// 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 = getDimension(text.c_str(), font_settings);
|
||||
text_dimension = gui::getGlyphLayoutsDimension(
|
||||
gl, m_font_max_height * scale, m_inverse_shaping, scale);
|
||||
|
||||
if (hcenter)
|
||||
offset.X += (position.getWidth() - text_dimension.Width) / 2;
|
||||
else if (rtl)
|
||||
offset.X += (position.getWidth() - text_dimension.Width);
|
||||
|
||||
{
|
||||
offset.X += (s32)(
|
||||
(position.getWidth() - width_per_line[0]) / 2.0f);
|
||||
}
|
||||
if (vcenter)
|
||||
offset.Y += (position.getHeight() - text_dimension.Height) / 2;
|
||||
{
|
||||
offset.Y += (s32)(
|
||||
(position.getHeight() - text_dimension.Height) / 2.0f);
|
||||
}
|
||||
if (clip)
|
||||
{
|
||||
core::rect<s32> clippedRect(core::position2d<s32>
|
||||
@ -557,40 +570,86 @@ void FontWithFace::render(const core::stringw& text,
|
||||
}
|
||||
|
||||
// Collect character locations
|
||||
const unsigned int text_size = text.size();
|
||||
const unsigned int text_size = gl.size();
|
||||
core::array<s32> indices(text_size);
|
||||
core::array<core::position2d<float>> offsets(text_size);
|
||||
std::vector<bool> fallback(text_size);
|
||||
|
||||
// Test again if lazy load char is needed,
|
||||
// as some text isn't drawn with getDimension
|
||||
insertCharacters(text.c_str());
|
||||
updateCharactersList();
|
||||
// Check if the line is RTL
|
||||
bool rtl = (gl[0].flags & gui::GLF_RTL_LINE) != 0;
|
||||
if (!hcenter && rtl)
|
||||
offset.X += (s32)(position.getWidth() - width_per_line[0]);
|
||||
|
||||
for (u32 i = 0; i < text_size; i++)
|
||||
unsigned cur_line = 0;
|
||||
bool line_changed = false;
|
||||
for (unsigned i = 0; i < gl.size(); i++)
|
||||
{
|
||||
wchar_t c = text[i];
|
||||
|
||||
if (c == L'\r' || // Windows breaks
|
||||
c == L'\n' ) // Unix breaks
|
||||
const gui::GlyphLayout& glyph_layout = gl[i];
|
||||
if ((glyph_layout.flags & gui::GLF_NEWLINE) != 0)
|
||||
{
|
||||
if (c==L'\r' && text[i+1]==L'\n')
|
||||
c = text[++i];
|
||||
// Y doesn't matter because we don't use advance y in harfbuzz
|
||||
offset.Y += m_font_max_height * scale;
|
||||
offset.X = float(position.UpperLeftCorner.X);
|
||||
if (hcenter)
|
||||
offset.X += (position.getWidth() - text_dimension.Width) >> 1;
|
||||
cur_line++;
|
||||
line_changed = true;
|
||||
continue;
|
||||
} // if lineBreak
|
||||
}
|
||||
if (line_changed)
|
||||
{
|
||||
line_changed = false;
|
||||
rtl = (gl[i].flags & gui::GLF_RTL_LINE) != 0;
|
||||
offset.X = float(position.UpperLeftCorner.X);
|
||||
if (hcenter)
|
||||
{
|
||||
offset.X += (s32)(
|
||||
(position.getWidth() - width_per_line.at(cur_line)) / 2.f);
|
||||
}
|
||||
else if (rtl)
|
||||
{
|
||||
offset.X +=
|
||||
(s32)(position.getWidth() - width_per_line.at(cur_line));
|
||||
}
|
||||
}
|
||||
|
||||
bool use_fallback_font = false;
|
||||
const FontArea &area = getAreaFromCharacter(c, &use_fallback_font);
|
||||
const FontArea* area = NULL;
|
||||
if (glyph_layout.index == 0)
|
||||
area = getUnknownFontArea();
|
||||
if (area == NULL)
|
||||
{
|
||||
if (m_face_ttf->enabledForFont(glyph_layout.face_idx))
|
||||
{
|
||||
area = m_face_ttf->getFontArea(
|
||||
glyph_layout.face_idx, glyph_layout.index);
|
||||
if (area == NULL)
|
||||
{
|
||||
insertGlyph(glyph_layout.face_idx, glyph_layout.index);
|
||||
area = m_face_ttf->getFontArea(
|
||||
glyph_layout.face_idx, glyph_layout.index);
|
||||
}
|
||||
}
|
||||
else if (m_fallback_font && m_fallback_font
|
||||
->m_face_ttf->enabledForFont(glyph_layout.face_idx))
|
||||
{
|
||||
use_fallback_font = true;
|
||||
area = m_fallback_font->m_face_ttf->getFontArea(
|
||||
glyph_layout.face_idx, glyph_layout.index);
|
||||
if (area == NULL)
|
||||
{
|
||||
m_fallback_font->insertGlyph(glyph_layout.face_idx,
|
||||
glyph_layout.index);
|
||||
area = m_fallback_font->m_face_ttf->getFontArea(
|
||||
glyph_layout.face_idx, glyph_layout.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
fallback[i] = use_fallback_font;
|
||||
if (char_collector == NULL)
|
||||
{
|
||||
float glyph_offset_x = area.bearing_x *
|
||||
float glyph_offset_x = (int)(area->bearing_x +
|
||||
glyph_layout.x_offset * m_inverse_shaping) *
|
||||
(fallback[i] ? m_fallback_font_scale : scale);
|
||||
float glyph_offset_y = area.offset_y *
|
||||
float glyph_offset_y = (int)(area->offset_y -
|
||||
glyph_layout.y_offset * m_inverse_shaping) *
|
||||
(fallback[i] ? m_fallback_font_scale : scale);
|
||||
offset.X += glyph_offset_x;
|
||||
offset.Y += glyph_offset_y;
|
||||
@ -601,9 +660,11 @@ void FontWithFace::render(const core::stringw& text,
|
||||
else
|
||||
{
|
||||
// Billboard text specific, use offset_y_bt instead
|
||||
float glyph_offset_x = area.bearing_x *
|
||||
float glyph_offset_x = (int)(area->bearing_x +
|
||||
glyph_layout.x_offset * m_inverse_shaping) *
|
||||
(fallback[i] ? m_fallback_font_scale : scale);
|
||||
float glyph_offset_y = area.offset_y_bt *
|
||||
float glyph_offset_y = (int)(area->offset_y_bt +
|
||||
glyph_layout.y_offset * m_inverse_shaping) *
|
||||
(fallback[i] ? m_fallback_font_scale : scale);
|
||||
offset.X += glyph_offset_x;
|
||||
offset.Y += glyph_offset_y;
|
||||
@ -612,8 +673,16 @@ void FontWithFace::render(const core::stringw& text,
|
||||
offset.Y -= glyph_offset_y;
|
||||
}
|
||||
|
||||
indices.push_back(area.spriteno);
|
||||
offset.X += getCharWidth(area, fallback[i], scale);
|
||||
indices.push_back(area->spriteno);
|
||||
if ((glyph_layout.flags & gui::GLF_QUICK_DRAW) != 0)
|
||||
{
|
||||
offset.X += glyph_layout.x_advance * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset.X +=
|
||||
(int)(glyph_layout.x_advance * m_inverse_shaping) * scale;
|
||||
}
|
||||
} // for i < text_size
|
||||
|
||||
// Do the actual rendering
|
||||
@ -773,3 +842,89 @@ float FontWithFace::getCharWidth(const FontArea& area, bool fallback,
|
||||
else
|
||||
return area.advance_x * scale;
|
||||
} // getCharWidth
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/* Cached version of render to make drawing as fast as possible. */
|
||||
void FontWithFace::drawText(const core::stringw& text,
|
||||
const core::rect<s32>& position,
|
||||
const video::SColor& color, bool hcenter,
|
||||
bool vcenter, const core::rect<s32>* clip,
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector)
|
||||
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (text.empty() || ProfileWorld::isNoGraphics())
|
||||
return;
|
||||
|
||||
if (disableTextShaping())
|
||||
{
|
||||
render(text2GlyphsWithoutShaping(text), position, color, hcenter,
|
||||
vcenter, clip, font_settings, char_collector);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& gls = font_manager->getCachedLayouts(text);
|
||||
if (gls.empty() && !text.empty())
|
||||
font_manager->shape(StringUtils::wideToUtf32(text), gls);
|
||||
|
||||
render(gls, position, color, hcenter, vcenter, clip,
|
||||
font_settings, char_collector);
|
||||
#endif
|
||||
} // drawText
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/* No text shaping and bidi operation of drawText. */
|
||||
void FontWithFace::drawTextQuick(const core::stringw& text,
|
||||
const core::rect<s32>& position,
|
||||
const video::SColor& color, bool hcenter,
|
||||
bool vcenter, const core::rect<s32>* clip,
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (text.empty() || ProfileWorld::isNoGraphics())
|
||||
return;
|
||||
|
||||
render(text2GlyphsWithoutShaping(text), position, color, hcenter,
|
||||
vcenter, clip, font_settings, char_collector);
|
||||
#endif
|
||||
} // drawTextQuick
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Convert text to drawable GlyphLayout without text shaping, used in digit
|
||||
* font or debugging message, it will only use the preloaded characters. */
|
||||
std::vector<gui::GlyphLayout> FontWithFace::
|
||||
text2GlyphsWithoutShaping(const core::stringw& t)
|
||||
{
|
||||
std::vector<gui::GlyphLayout> layouts;
|
||||
#ifndef SERVER_ONLY
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
{
|
||||
wchar_t c = t[i];
|
||||
gui::GlyphLayout gl = { 0 };
|
||||
if (c == L'\r' || // Windows breaks
|
||||
c == L'\n' ) // Unix breaks
|
||||
{
|
||||
if (c == L'\r' && i != t.size() - 1 && t[i + 1] == L'\n')
|
||||
i++;
|
||||
gl.flags = gui::GLF_NEWLINE;
|
||||
layouts.push_back(gl);
|
||||
continue;
|
||||
}
|
||||
auto ret = m_character_glyph_info_map.find(c);
|
||||
if (ret == m_character_glyph_info_map.end())
|
||||
continue;
|
||||
const FontArea* area = m_face_ttf->getFontArea
|
||||
(ret->second.font_number, ret->second.glyph_index);
|
||||
if (area == NULL)
|
||||
continue;
|
||||
gl.index = ret->second.glyph_index;
|
||||
gl.x_advance = area->advance_x;
|
||||
gl.face_idx = ret->second.font_number;
|
||||
gl.flags = gui::GLF_QUICK_DRAW;
|
||||
layouts.push_back(gl);
|
||||
}
|
||||
#endif
|
||||
return layouts;
|
||||
} // text2GlyphsWithoutShaping
|
||||
|
@ -160,6 +160,9 @@ private:
|
||||
/** The dpi of this font. */
|
||||
unsigned int m_face_dpi;
|
||||
|
||||
/** Used to undo the scale on text shaping, only need to take care of
|
||||
* width. */
|
||||
float m_inverse_shaping;
|
||||
/** Store a list of loaded and tested character to a \ref GlyphInfo. */
|
||||
std::map<wchar_t, GlyphInfo> m_character_glyph_info_map;
|
||||
|
||||
@ -213,8 +216,6 @@ private:
|
||||
/** Add a character into \ref m_new_char_holder for lazy loading later. */
|
||||
void addLazyLoadChar(wchar_t c) { m_new_char_holder.insert(c); }
|
||||
// ------------------------------------------------------------------------
|
||||
void insertGlyph(const GlyphInfo& gi);
|
||||
// ------------------------------------------------------------------------
|
||||
void setDPI();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Override it if sub-class should not do lazy loading characters. */
|
||||
@ -233,6 +234,11 @@ private:
|
||||
/** Override it if sub-class has bold outline. */
|
||||
virtual bool isBold() const { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
const FontArea* getUnknownFontArea() const;
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<gui::GlyphLayout> text2GlyphsWithoutShaping(
|
||||
const core::stringw& t);
|
||||
// ------------------------------------------------------------------------
|
||||
#ifndef SERVER_ONLY
|
||||
/** Override it if any outline shaping is needed to be done before
|
||||
* rendering the glyph into bitmap.
|
||||
@ -251,18 +257,32 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void reset();
|
||||
// ------------------------------------------------------------------------
|
||||
core::dimension2d<u32> getDimension(const wchar_t* text,
|
||||
FontSettings* font_settings = NULL);
|
||||
virtual core::dimension2d<u32> getDimension(const core::stringw& text,
|
||||
FontSettings* font_settings = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
int getCharacterFromPos(const wchar_t* text, int pixel_x,
|
||||
FontSettings* font_settings = NULL) const;
|
||||
// ------------------------------------------------------------------------
|
||||
void render(const core::stringw& text, const core::rect<s32>& position,
|
||||
const video::SColor& color, bool hcenter, bool vcenter,
|
||||
const core::rect<s32>* clip,
|
||||
void render(const std::vector<gui::GlyphLayout>& gl,
|
||||
const core::rect<s32>& position, const video::SColor& color,
|
||||
bool hcenter, bool vcenter, const core::rect<s32>* clip,
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void drawText(const core::stringw& text,
|
||||
const core::rect<s32>& position,
|
||||
const video::SColor& color, bool hcenter,
|
||||
bool vcenter, const core::rect<s32>* clip,
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
void drawTextQuick(const core::stringw& text,
|
||||
const core::rect<s32>& position,
|
||||
const video::SColor& color, bool hcenter, bool vcenter,
|
||||
const core::rect<s32>* clip,
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
void dumpGlyphPage(const std::string& name);
|
||||
// ------------------------------------------------------------------------
|
||||
void dumpGlyphPage();
|
||||
@ -277,6 +297,14 @@ public:
|
||||
unsigned int getDPI() const { return m_face_dpi; }
|
||||
// ------------------------------------------------------------------------
|
||||
FaceTTF* getFaceTTF() const { return m_face_ttf; }
|
||||
// ------------------------------------------------------------------------
|
||||
void insertGlyph(unsigned font_number, unsigned glyph_index);
|
||||
// ------------------------------------------------------------------------
|
||||
int getFontMaxHeight() const { return m_font_max_height; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool disableTextShaping() const { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
float getInverseShaping() const { return m_inverse_shaping; }
|
||||
}; // FontWithFace
|
||||
|
||||
#endif
|
||||
|
@ -1756,7 +1756,7 @@ void IrrDriver::displayFPS()
|
||||
gui::IGUIFont* font = GUIEngine::getSmallFont();
|
||||
core::rect<s32> position;
|
||||
|
||||
const int fheight = font->getDimension(L"X").Height;
|
||||
const int fheight = font->getHeightPerLine();
|
||||
if (UserConfigParams::m_artist_debug_mode)
|
||||
position = core::rect<s32>(51, 0, 30*fheight+51, 2*fheight + fheight / 3);
|
||||
else
|
||||
@ -1795,7 +1795,7 @@ void IrrDriver::displayFPS()
|
||||
no_trust--;
|
||||
|
||||
static video::SColor fpsColor = video::SColor(255, 0, 0, 0);
|
||||
font->draw(StringUtils::insertValues (L"FPS: ... Ping: %dms", ping),
|
||||
font->drawQuick(StringUtils::insertValues (L"FPS: ... Ping: %dms", ping),
|
||||
core::rect< s32 >(100,0,400,50), fpsColor, false);
|
||||
return;
|
||||
}
|
||||
@ -1839,7 +1839,7 @@ void IrrDriver::displayFPS()
|
||||
|
||||
static video::SColor fpsColor = video::SColor(255, 0, 0, 0);
|
||||
|
||||
font->draw( fps_string.c_str(), position, fpsColor, false );
|
||||
font->drawQuick( fps_string.c_str(), position, fpsColor, false );
|
||||
#endif
|
||||
} // updateFPS
|
||||
|
||||
@ -2030,16 +2030,16 @@ void IrrDriver::renderNetworkDebug()
|
||||
(int)d, (int)h, (int)m, (int)s, (int)f);
|
||||
|
||||
gui::IGUIFont* font = GUIEngine::getFont();
|
||||
unsigned height = font->getDimension(L"X").Height + 2;
|
||||
unsigned height = font->getHeightPerLine() + 2;
|
||||
background_rect.UpperLeftCorner.X += 5;
|
||||
static video::SColor black = video::SColor(255, 0, 0, 0);
|
||||
font->draw(StringUtils::insertValues(
|
||||
font->drawQuick(StringUtils::insertValues(
|
||||
L"Server time: %s Server state frequency: %d",
|
||||
str, NetworkConfig::get()->getStateFrequency()),
|
||||
background_rect, black, false);
|
||||
|
||||
background_rect.UpperLeftCorner.Y += height;
|
||||
font->draw(StringUtils::insertValues(
|
||||
font->drawQuick(StringUtils::insertValues(
|
||||
L"Upload speed (KBps): %f Download speed (KBps): %f",
|
||||
(float)STKHost::get()->getUploadSpeed() / 1024.0f,
|
||||
(float)STKHost::get()->getDownloadSpeed() / 1024.0f,
|
||||
@ -2047,7 +2047,7 @@ void IrrDriver::renderNetworkDebug()
|
||||
false);
|
||||
|
||||
background_rect.UpperLeftCorner.Y += height;
|
||||
font->draw(StringUtils::insertValues(
|
||||
font->drawQuick(StringUtils::insertValues(
|
||||
L"Packet loss: %d Packet loss variance: %d",
|
||||
peer->getENetPeer()->packetLoss,
|
||||
peer->getENetPeer()->packetLossVariance,
|
||||
|
@ -88,8 +88,8 @@ void STKTextBillboard::updateAbsolutePosition()
|
||||
void STKTextBillboard::init(core::stringw text, FontWithFace* face)
|
||||
{
|
||||
m_chars = new std::vector<STKTextBillboardChar>();
|
||||
core::dimension2du size = face->getDimension(text.c_str());
|
||||
face->render(text, core::rect<s32>(0, 0, size.Width, size.Height),
|
||||
core::dimension2du size = face->getDimension(text);
|
||||
face->drawText(text, core::rect<s32>(0, 0, size.Width, size.Height),
|
||||
video::SColor(255,255,255,255), false, false, NULL, NULL, this);
|
||||
|
||||
const float scale = 0.02f;
|
||||
@ -258,8 +258,8 @@ void STKTextBillboard::init(core::stringw text, FontWithFace* face)
|
||||
void STKTextBillboard::initLegacy(core::stringw text, FontWithFace* face)
|
||||
{
|
||||
m_chars = new std::vector<STKTextBillboardChar>();
|
||||
core::dimension2du size = face->getDimension(text.c_str());
|
||||
face->render(text, core::rect<s32>(0, 0, size.Width, size.Height),
|
||||
core::dimension2du size = face->getDimension(text);
|
||||
face->drawText(text, core::rect<s32>(0, 0, size.Width, size.Height),
|
||||
video::SColor(255,255,255,255), false, false, NULL, NULL, this);
|
||||
|
||||
const float scale = 0.02f;
|
||||
|
@ -18,10 +18,9 @@
|
||||
|
||||
#include "guiengine/scalable_font.hpp"
|
||||
|
||||
#include "font/face_ttf.hpp"
|
||||
#include "font/font_manager.hpp"
|
||||
#include "font/font_settings.hpp"
|
||||
#include "font/font_with_face.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -31,8 +30,7 @@ namespace gui
|
||||
ScalableFont::ScalableFont(FontWithFace* face)
|
||||
{
|
||||
m_face = face;
|
||||
m_font_settings = new FontSettings(false/*black_border*/,
|
||||
translations->isRTLLanguage());
|
||||
m_font_settings = new FontSettings();
|
||||
} // ScalableFont
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -44,7 +42,6 @@ ScalableFont::~ScalableFont()
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::updateRTL()
|
||||
{
|
||||
m_font_settings->setRTL(translations->isRTLLanguage());
|
||||
} // updateRTL
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -53,27 +50,32 @@ void ScalableFont::setShadow(const irr::video::SColor &col)
|
||||
m_font_settings->setShadow(true);
|
||||
m_font_settings->setShadowColor(col);
|
||||
} // setShadow
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::disableShadow()
|
||||
{
|
||||
m_font_settings->setShadow(false);
|
||||
} // disableShadow
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::setBlackBorder(bool enabled)
|
||||
{
|
||||
m_font_settings->setBlackBorder(enabled);
|
||||
} // setBlackBorder
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::setColoredBorder(const irr::video::SColor &col)
|
||||
{
|
||||
m_font_settings->setColoredBorder(true);
|
||||
m_font_settings->setBorderColor(col);
|
||||
} // setColoredBorder
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::setThinBorder(bool thin)
|
||||
{
|
||||
m_font_settings->setThinBorder(thin);
|
||||
} // setThinBorder
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::disableColoredBorder()
|
||||
{
|
||||
@ -104,10 +106,18 @@ void ScalableFont::draw(const core::stringw& text,
|
||||
bool hcenter, bool vcenter,
|
||||
const core::rect<s32>* clip)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
m_face->render(text, position, color, hcenter, vcenter, clip,
|
||||
m_face->drawText(text, position, color, hcenter, vcenter, clip,
|
||||
m_font_settings);
|
||||
} // draw
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::draw(const std::vector<GlyphLayout>& gls,
|
||||
const core::rect<s32>& position, video::SColor color,
|
||||
bool hcenter, bool vcenter,
|
||||
const core::rect<s32>* clip)
|
||||
{
|
||||
m_face->render(gls, position, color, hcenter, vcenter, clip,
|
||||
m_font_settings);
|
||||
#endif
|
||||
} // draw
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -116,17 +126,18 @@ void ScalableFont::draw(const core::stringw& text,
|
||||
const video::SColor& color, bool hcenter, bool vcenter,
|
||||
const core::rect<s32>* clip, bool ignoreRTL)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
bool previousRTL = m_font_settings->isRTL();
|
||||
if (ignoreRTL)
|
||||
m_font_settings->setRTL(false);
|
||||
|
||||
m_face->render(text, position, color, hcenter, vcenter, clip,
|
||||
m_face->drawText(text, position, color, hcenter, vcenter, clip,
|
||||
m_font_settings);
|
||||
} // draw
|
||||
|
||||
if (ignoreRTL)
|
||||
m_font_settings->setRTL(previousRTL);
|
||||
#endif
|
||||
// ----------------------------------------------------------------------------
|
||||
void ScalableFont::drawQuick(const core::stringw& text,
|
||||
const core::rect<s32>& position,
|
||||
const video::SColor color, bool hcenter,
|
||||
bool vcenter, const core::rect<s32>* clip)
|
||||
{
|
||||
m_face->drawTextQuick(text, position, color, hcenter, vcenter, clip,
|
||||
m_font_settings);
|
||||
} // draw
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -141,13 +152,36 @@ IGUISpriteBank* ScalableFont::getSpriteBank() const
|
||||
return m_face->getSpriteBank();
|
||||
} // getSpriteBank
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
u32 ScalableFont::getSpriteNoFromChar(const wchar_t *c) const
|
||||
// ----------------------------------------------------------------------------
|
||||
s32 ScalableFont::getHeightPerLine() const
|
||||
{
|
||||
const FontArea& area =
|
||||
m_face->getAreaFromCharacter(*c, NULL/*fallback_font*/);
|
||||
return area.spriteno;
|
||||
} // getSpriteNoFromChar
|
||||
return m_face->getFontMaxHeight() * m_font_settings->getScale();
|
||||
} // getHeightPerLine
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Convert text to glyph layouts for fast rendering with caching enabled
|
||||
* If line_data is not null, each broken line u32string will be saved and
|
||||
* can be used for advanced glyph and text mapping, and cache will be
|
||||
* disabled.
|
||||
*/
|
||||
void ScalableFont::initGlyphLayouts(const core::stringw& text,
|
||||
std::vector<GlyphLayout>& gls,
|
||||
std::vector<std::u32string>* line_data)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
font_manager->initGlyphLayouts(text, gls, line_data);
|
||||
#endif
|
||||
} // initGlyphLayouts
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
f32 ScalableFont::getInverseShaping() const
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
return m_face->getInverseShaping();
|
||||
#else
|
||||
return 1.0f;
|
||||
#endif
|
||||
} // getShapingScale
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
|
@ -49,9 +49,9 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
const FontSettings* getFontSettings() const { return m_font_settings; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setScale(float scale);
|
||||
virtual void setScale(float scale);
|
||||
// ------------------------------------------------------------------------
|
||||
float getScale() const;
|
||||
virtual float getScale() const;
|
||||
// ------------------------------------------------------------------------
|
||||
void setShadow(const irr::video::SColor &col);
|
||||
// ------------------------------------------------------------------------
|
||||
@ -78,9 +78,26 @@ public:
|
||||
video::SColor color, bool hcenter = false,
|
||||
bool vcenter = false, const core::rect<s32>* clip = 0);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void drawQuick(const core::stringw& text,
|
||||
const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter = false,
|
||||
bool vcenter = false,
|
||||
const core::rect<s32>* clip = 0);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void draw(const std::vector<GlyphLayout>& gls,
|
||||
const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter = false,
|
||||
bool vcenter = false, const core::rect<s32>* clip = 0);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void initGlyphLayouts(const core::stringw& text,
|
||||
std::vector<GlyphLayout>& gls,
|
||||
std::vector<std::u32string>* line_data = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
/** returns the dimension of a text */
|
||||
virtual core::dimension2d<u32> getDimension(const wchar_t* text) const;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual s32 getHeightPerLine() const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Calculates the index of the character in the text which is on a
|
||||
* specific position. */
|
||||
virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const;
|
||||
@ -91,8 +108,8 @@ public:
|
||||
/** gets the sprite bank */
|
||||
virtual IGUISpriteBank* getSpriteBank() const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** returns the sprite number from a given character */
|
||||
virtual u32 getSpriteNoFromChar(const wchar_t *c) const;
|
||||
/** returns the sprite number from a given character, unused in STK */
|
||||
virtual u32 getSpriteNoFromChar(const wchar_t *c) const { return 0; }
|
||||
// ------------------------------------------------------------------------
|
||||
// Below is not used:
|
||||
/** set an Pixel Offset on Drawing ( scale position on width ) */
|
||||
@ -108,6 +125,8 @@ public:
|
||||
virtual s32 getKerningHeight() const { return 0; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void setInvisibleCharacters( const wchar_t *s ) {}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual f32 getInverseShaping() const;
|
||||
|
||||
};
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "guiengine/widgets/spinner_widget.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
#include <IGUIElement.h>
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "utils/interpolation_array.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/transport_address.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/time.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "network/rewinder.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "items/projectile_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
/** Constructor for a state: it only takes the size, and allocates a buffer
|
||||
* for all state info.
|
||||
|
@ -16,6 +16,8 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// The order here is important. If all_params is declared later (e.g. after
|
||||
|
@ -17,6 +17,7 @@
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "network/transport_address.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#ifdef WIN32
|
||||
# include <iphlpapi.h>
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "io/utf_writer.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "io/utf_writer.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
|
@ -36,6 +36,7 @@ using namespace irr;
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
#include "utils/aligned_array.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
#include "utils/ptr_vector.hpp"
|
||||
|
@ -21,9 +21,12 @@
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#include "config/stk_config.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/types.hpp"
|
||||
#include "utils/utf8.h"
|
||||
#include "irrArray.h"
|
||||
|
||||
#include "coreutil.h"
|
||||
|
||||
@ -214,6 +217,67 @@ namespace StringUtils
|
||||
}
|
||||
} // split
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/** Splits a string into substrings separated by a certain character, and
|
||||
* returns a std::vector of all those substring. E.g.:
|
||||
* split("a b=c d=e",' ') --> ["a", "b=c", "d=e"]
|
||||
* \param s The string to split.
|
||||
* \param c The character by which the string is split.
|
||||
*/
|
||||
std::vector<std::u32string> split(const std::u32string& s, char32_t c,
|
||||
bool keepSplitChar)
|
||||
{
|
||||
std::vector<std::u32string> result;
|
||||
|
||||
try
|
||||
{
|
||||
std::u32string::size_type start=0;
|
||||
while(start < (unsigned int) s.size())
|
||||
{
|
||||
std::u32string::size_type i=s.find(c, start);
|
||||
if (i!=std::u32string::npos)
|
||||
{
|
||||
if (keepSplitChar)
|
||||
{
|
||||
int from = (int)start-1;
|
||||
if (from < 0) from = 0;
|
||||
|
||||
result.push_back(std::u32string(s, from, i-from));
|
||||
}
|
||||
else result.push_back(std::u32string(s,start, i-start));
|
||||
|
||||
start=i+1;
|
||||
}
|
||||
else // end of string reached
|
||||
{
|
||||
if (keepSplitChar && start != 0)
|
||||
result.push_back(std::u32string(s,start-1));
|
||||
else
|
||||
result.push_back(std::u32string(s,start));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log::error("StringUtils",
|
||||
"Error in split(std::string) : %s @ line %i : %s.",
|
||||
__FILE__, __LINE__, e.what());
|
||||
Log::error("StringUtils", "Splitting '%s'.",
|
||||
wideToUtf8(utf32ToWide(s)).c_str());
|
||||
|
||||
for (int n=0; n<(int)result.size(); n++)
|
||||
{
|
||||
Log::error("StringUtils", "Split : %s",
|
||||
wideToUtf8(utf32ToWide(result[n])).c_str());
|
||||
}
|
||||
|
||||
assert(false); // in debug mode, trigger debugger
|
||||
exit(1);
|
||||
}
|
||||
} // split
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/** Splits a string into substrings separated by a certain character, and
|
||||
* returns a std::vector of all those substring. E.g.:
|
||||
@ -791,7 +855,16 @@ namespace StringUtils
|
||||
std::string wideToUtf8(const wchar_t* input)
|
||||
{
|
||||
std::vector<char> utf8line;
|
||||
utf8::utf16to8(input, input + wcslen(input), back_inserter(utf8line));
|
||||
if (sizeof(wchar_t) == 2)
|
||||
{
|
||||
utf8::utf16to8(input, input + wcslen(input),
|
||||
back_inserter(utf8line));
|
||||
}
|
||||
else if (sizeof(wchar_t) == 4)
|
||||
{
|
||||
utf8::utf32to8(input, input + wcslen(input),
|
||||
back_inserter(utf8line));
|
||||
}
|
||||
utf8line.push_back(0);
|
||||
return std::string(&utf8line[0]);
|
||||
} // wideToUtf8
|
||||
@ -808,10 +881,19 @@ namespace StringUtils
|
||||
/** Converts the irrlicht wide string to an utf8-encoded std::string. */
|
||||
irr::core::stringw utf8ToWide(const char* input)
|
||||
{
|
||||
std::vector<wchar_t> utf16line;
|
||||
utf8::utf8to16(input, input + strlen(input), back_inserter(utf16line));
|
||||
utf16line.push_back(0);
|
||||
return irr::core::stringw(&utf16line[0]);
|
||||
std::vector<wchar_t> wchar_line;
|
||||
if (sizeof(wchar_t) == 2)
|
||||
{
|
||||
utf8::utf8to16(input, input + strlen(input),
|
||||
back_inserter(wchar_line));
|
||||
}
|
||||
else if (sizeof(wchar_t) == 4)
|
||||
{
|
||||
utf8::utf8to32(input, input + strlen(input),
|
||||
back_inserter(wchar_line));
|
||||
}
|
||||
wchar_line.push_back(0);
|
||||
return irr::core::stringw(&wchar_line[0]);
|
||||
} // utf8ToWide
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -1162,6 +1244,59 @@ namespace StringUtils
|
||||
#endif
|
||||
} // partOfLongUnicodeChar
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
irr::core::stringw utf32ToWide(const std::u32string& input)
|
||||
{
|
||||
std::vector<wchar_t> wchar_line;
|
||||
if (sizeof(wchar_t) == 2)
|
||||
{
|
||||
const uint32_t* chars = (const uint32_t*)input.c_str();
|
||||
utf8::utf16to32(chars, chars + input.size(),
|
||||
back_inserter(wchar_line));
|
||||
}
|
||||
else if (sizeof(wchar_t) == sizeof(char32_t))
|
||||
{
|
||||
wchar_line.resize(input.size());
|
||||
memcpy(wchar_line.data(), input.c_str(),
|
||||
input.size() * sizeof(char32_t));
|
||||
}
|
||||
wchar_line.push_back(0);
|
||||
return irr::core::stringw(&wchar_line[0]);
|
||||
} // utf32ToWide
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
std::u32string utf8ToUtf32(const std::string &input)
|
||||
{
|
||||
std::u32string result;
|
||||
utf8::utf8to32(input.c_str(), input.c_str() + input.size(),
|
||||
back_inserter(result));
|
||||
return result;
|
||||
} // utf8ToUtf32
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
std::string utf32ToUtf8(const std::u32string& input)
|
||||
{
|
||||
std::string result;
|
||||
utf8::utf32to8(input.c_str(), input.c_str() + input.size(),
|
||||
back_inserter(result));
|
||||
return result;
|
||||
} // utf32ToUtf8
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
std::u32string wideToUtf32(const irr::core::stringw& input)
|
||||
{
|
||||
std::u32string utf32_line;
|
||||
if (sizeof(wchar_t) != sizeof(char32_t))
|
||||
{
|
||||
const uint16_t* chars = (const uint16_t*)input.c_str();
|
||||
utf8::utf16to32(chars, chars + input.size(),
|
||||
back_inserter(utf32_line));
|
||||
}
|
||||
else if (sizeof(wchar_t) == sizeof(char32_t))
|
||||
utf32_line = (const char32_t*)input.c_str();
|
||||
return utf32_line;
|
||||
} // wideToUtf32
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** At the moment only versionToInt is tested.
|
||||
*/
|
||||
@ -1181,8 +1316,26 @@ namespace StringUtils
|
||||
assert(versionToInt("1-rc9" ) == 10000029);
|
||||
assert(versionToInt("1.0-rc1" ) == 10000021); // same as 1-rc1
|
||||
} // unitTesting
|
||||
// ------------------------------------------------------------------------
|
||||
std::string getUserAgentString()
|
||||
{
|
||||
std::string uagent(std::string("SuperTuxKart/") + STK_VERSION);
|
||||
#ifdef WIN32
|
||||
uagent += (std::string)" (Windows)";
|
||||
#elif defined(__APPLE__)
|
||||
uagent += (std::string)" (Macintosh)";
|
||||
#elif defined(__FreeBSD__)
|
||||
uagent += (std::string)" (FreeBSD)";
|
||||
#elif defined(ANDROID)
|
||||
uagent += (std::string)" (Android)";
|
||||
#elif defined(linux)
|
||||
uagent += (std::string)" (Linux)";
|
||||
#else
|
||||
// Unknown system type
|
||||
#endif
|
||||
return uagent;
|
||||
} // getUserAgentString
|
||||
|
||||
} // namespace StringUtils
|
||||
|
||||
|
||||
/* EOF */
|
||||
|
@ -27,9 +27,7 @@
|
||||
#include <sstream>
|
||||
#include <irrString.h>
|
||||
#include <IGUIFont.h>
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/types.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include <irrTypes.h>
|
||||
|
||||
namespace StringUtils
|
||||
{
|
||||
@ -58,6 +56,8 @@ namespace StringUtils
|
||||
std::string toLowerCase(const std::string&);
|
||||
std::vector<std::string> split(const std::string& s, char c,
|
||||
bool keepSplitChar=false);
|
||||
std::vector<std::u32string> split(const std::u32string& s, char32_t c,
|
||||
bool keepSplitChar=false);
|
||||
std::vector<irr::core::stringw> split(const irr::core::stringw& s,
|
||||
char c, bool keepSplitChar=false);
|
||||
std::vector<uint32_t> splitToUInt(const std::string& s, char c,
|
||||
@ -244,34 +244,20 @@ namespace StringUtils
|
||||
|
||||
irr::core::stringw utf8ToWide(const char* input);
|
||||
irr::core::stringw utf8ToWide(const std::string &input);
|
||||
std::u32string utf8ToUtf32(const std::string &input);
|
||||
std::string wideToUtf8(const wchar_t* input);
|
||||
std::string wideToUtf8(const irr::core::stringw& input);
|
||||
std::string utf32ToUtf8(const std::u32string& input);
|
||||
std::string findAndReplace(const std::string& source, const std::string& find, const std::string& replace);
|
||||
std::string removeWhitespaces(const std::string& input);
|
||||
void breakText(const std::wstring& input, std::vector<std::wstring> &output,
|
||||
unsigned int max_width, irr::gui::IGUIFont* font, bool right_to_left=false);
|
||||
bool breakable (wchar_t c);
|
||||
bool partOfLongUnicodeChar (wchar_t c);
|
||||
irr::core::stringw utf32ToWide(const std::u32string& input);
|
||||
std::u32string wideToUtf32(const irr::core::stringw& input);
|
||||
|
||||
inline std::string getUserAgentString()
|
||||
{
|
||||
std::string uagent(std::string("SuperTuxKart/") + STK_VERSION);
|
||||
#ifdef WIN32
|
||||
uagent += (std::string)" (Windows)";
|
||||
#elif defined(__APPLE__)
|
||||
uagent += (std::string)" (Macintosh)";
|
||||
#elif defined(__FreeBSD__)
|
||||
uagent += (std::string)" (FreeBSD)";
|
||||
#elif defined(ANDROID)
|
||||
uagent += (std::string)" (Android)";
|
||||
#elif defined(linux)
|
||||
uagent += (std::string)" (Linux)";
|
||||
#else
|
||||
// Unknown system type
|
||||
#endif
|
||||
return uagent;
|
||||
}
|
||||
|
||||
std::string getUserAgentString();
|
||||
/**
|
||||
* Returns the hostname part of an url (if any)
|
||||
*
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "utils/time.hpp"
|
||||
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
#include <ctime>
|
||||
|
@ -230,6 +230,36 @@ namespace utf8
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf16to32 (u16bit_iterator start, u16bit_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
{
|
||||
uint32_t cp = internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (internal::is_lead_surrogate(cp))
|
||||
{
|
||||
if (start != end)
|
||||
{
|
||||
uint32_t trail_surrogate = internal::mask16(*start++);
|
||||
if (internal::is_trail_surrogate(trail_surrogate))
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
|
||||
}
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
}
|
||||
// Lone trail surrogate
|
||||
else if (internal::is_trail_surrogate(cp))
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
(*result++) = cp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
@ -254,6 +284,24 @@ namespace utf8
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename u32bit_iterator>
|
||||
u16bit_iterator utf32to16 (u32bit_iterator start, u32bit_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
{
|
||||
uint32_t cp = start++;
|
||||
if (cp > 0xffff)
|
||||
{
|
||||
//make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
|
@ -141,6 +141,23 @@ namespace utf8
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf16to32 (u16bit_iterator start, u16bit_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
{
|
||||
uint32_t cp = internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (internal::is_lead_surrogate(cp))
|
||||
{
|
||||
uint32_t trail_surrogate = internal::mask16(*start++);
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
}
|
||||
(*result++) = cp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
@ -165,6 +182,24 @@ namespace utf8
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename u32bit_iterator>
|
||||
u16bit_iterator utf32to16 (u32bit_iterator start, u32bit_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
{
|
||||
uint32_t cp = start++;
|
||||
if (cp > 0xffff)
|
||||
{
|
||||
//make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user