refactor text_renderer

This commit is contained in:
Jinhao 2019-06-17 07:36:33 +08:00
parent 363953be20
commit a8c269d108

View File

@ -16,7 +16,7 @@ namespace nana
auto const end = str + len;
for(auto i = str; i != end; ++i)
{
if(*i == '\n')
if('\n' == *i)
{
top += static_cast<int>(f(top, str, i - str));
str = i + 1;
@ -26,166 +26,125 @@ namespace nana
f(top, str, end - str);
}
struct draw_string
{
drawable_type dw;
const int x, endpos;
align text_align;
draw_string(drawable_type dw, int x, int endpos, align ta)
: dw(dw), x(x), endpos(endpos), text_align(ta)
{}
class string_drawer
{
public:
string_drawer(graphics& graph, int left, int right, align ta, bool use_ellipsis):
graph_(graph),
left_(left),
right_(right),
text_align_(ta)
{
if (use_ellipsis)
{
#ifdef _nana_std_has_string_view
ellipsis_px_ = graph.text_extent_size(std::string_view{ "...", 3 }).width;
#else
ellipsis_px_ = graph.text_extent_size("...", 3).width;
#endif
}
}
unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize)
{
auto const drawable = graph_.handle();
auto const reordered = unicode_reorder(buf, bufsize);
if (reordered.empty())
return 0;
unsigned return_max_height = 0;
unsigned string_px = 0;
std::vector<nana::size> word_metrics;
for (auto & ent : reordered)
{
auto word_sz = detail::text_extent_size(drawable, ent.begin, ent.end - ent.begin);
word_metrics.push_back(word_sz);
nana::point pos{ x, top };
unsigned pixels = 0;
string_px += word_sz.width;
if (word_sz.height > return_max_height)
return_max_height = word_sz.height;
}
switch(text_align)
auto text_align = text_align_;
// Checks if ellipsis is enabled and the total pixels of string is larger than the space.
if (ellipsis_px_ && (string_px > static_cast<int>(right_ - left_)))
{
//The string should be drawn from left most point no matter what text align is.
text_align = align::left;
}
nana::point pos{ left_, top };
auto wdm = word_metrics.data();
switch (text_align)
{
case align::left:
for(auto & ent : reordered)
for (auto & ent : reordered)
{
std::size_t len = ent.end - ent.begin;
nana::size ts = detail::text_extent_size(dw, ent.begin, len);
if (pos.x + static_cast<int>(wdm->width) > 0)
{
if (pos.x + static_cast<int>(wdm->width) <= right_ - static_cast<int>(ellipsis_px_))
{
//This word can be fully painted.
detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin);
}
else
{
//This word is painted partially. Firstly, paints the word on a dummy graphics buffer.
if(ts.height > pixels) pixels = ts.height;
if(pos.x + static_cast<int>(ts.width) > 0)
detail::draw_string(dw, pos, ent.begin, len);
nana::rectangle r{ nana::size{ static_cast<unsigned>(right_ - ellipsis_px_) - pos.x, wdm->height } };
pos.x += static_cast<int>(ts.width);
if(pos.x >= endpos)
nana::paint::graphics dummy({ r.width, r.height });
dummy.typeface(graph_.typeface());
dummy.bitblt(r, graph_, pos);
#ifdef _nana_std_has_string_view
dummy.string({}, { ent.begin, ent.end - ent.begin }, graph_.palette(true));
#else
dummy.palette(true, graph_.palette(true));
dummy.string({}, ent.begin, ent.end - ent.begin);
#endif
r.x = pos.x;
r.y = top;
graph_.bitblt(r, dummy);
if (ellipsis_px_)
detail::draw_string(drawable, point{ right_ - static_cast<int>(ellipsis_px_), top }, L"...", 3);
break;
}
}
pos.x += static_cast<int>(wdm->width);
if (pos.x > right_ - static_cast<int>(ellipsis_px_))
break;
++wdm;
}
break;
case align::center:
pos.x = (right_ - left_ - string_px) / 2;
for (auto & ent : reordered)
{
unsigned lenpx = 0;
std::unique_ptr<unsigned[]> entity_pxs(new unsigned[reordered.size()]);
auto ent_px = entity_pxs.get();
for(auto & ent : reordered)
{
auto ts = detail::text_extent_size(dw, ent.begin, ent.end - ent.begin);
if(ts.height > pixels) pixels = ts.height;
lenpx += ts.width;
*ent_px++ = ts.width;
}
pos.x += (endpos - pos.x - static_cast<int>(lenpx))/2;
ent_px = entity_pxs.get();
for(auto & ent : reordered)
{
if (pos.x + static_cast<int>(*ent_px) > 0)
detail::draw_string(dw, pos, ent.begin, ent.end - ent.begin);
pos.x += static_cast<int>(*ent_px++);
if(pos.x >= endpos)
break;
}
detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin);
pos.x += (wdm++)->width;
}
break;
case align::right:
wdm = word_metrics.data() + word_metrics.size() - 1;
pos.x = right_;
for (auto i = reordered.crbegin(); i != reordered.crend(); ++i)
{
int xend = endpos;
std::swap(pos.x, xend);
for(auto i = reordered.crbegin(); i != reordered.crend(); ++i)
{
auto & ent = *i;
std::size_t len = ent.end - ent.begin;
nana::size ts = detail::text_extent_size(dw, ent.begin, len);
if(ts.height > pixels) pixels = ts.height;
if(pos.x > xend)
{
pos.x -= static_cast<int>(ts.width);
detail::draw_string(dw, pos, i->begin, len);
}
if(pos.x <= xend || pos.x <= 0)
break;
}
pos.x -= (wdm--)->width;
detail::draw_string(drawable, pos, i->begin, i->end - i->begin);
}
break;
}
return pixels;
}
};
struct draw_string_omitted
{
graphics & graph;
int x, endpos;
unsigned omitted_pixels;
draw_string_omitted(graphics& graph, int x, int endpos, bool omitted)
: graph(graph), x(x), endpos(endpos)
{
#ifdef _nana_std_has_string_view
omitted_pixels = (omitted ? graph.text_extent_size(std::string_view{ "...", 3 }).width : 0);
#else
omitted_pixels = (omitted ? graph.text_extent_size("...", 3).width : 0);
#endif
if (endpos - x > static_cast<int>(omitted_pixels))
this->endpos -= omitted_pixels;
else
this->endpos = x;
}
unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize)
{
drawable_type dw = graph.handle();
::nana::point pos{ x, top };
unsigned pixels = 0;
auto const reordered = unicode_reorder(buf, bufsize);
for(auto & i : reordered)
{
std::size_t len = i.end - i.begin;
nana::size ts = detail::text_extent_size(dw, i.begin, len);
if(ts.height > pixels) pixels = ts.height;
if(pos.x + static_cast<int>(ts.width) <= endpos)
{
detail::draw_string(dw, pos, i.begin, len);
pos.x += static_cast<int>(ts.width);
}
else
{
nana::rectangle r;
r.width = endpos - pos.x;
r.height = ts.height;
nana::paint::graphics dum_graph({ r.width, r.height });
dum_graph.bitblt(r, graph, pos);
#ifdef _nana_std_has_string_view
dum_graph.string({}, { i.begin, len }, graph.palette(true));
#else
dum_graph.palette(true, graph.palette(true));
dum_graph.string({}, i.begin, len);
#endif
r.x = pos.x;
r.y = top;
graph.bitblt(r, dum_graph);
if(omitted_pixels)
detail::draw_string(dw, point{ endpos, top }, L"...", 3);
break;
}
}
return pixels;
return return_max_height;
}
private:
graphics& graph_;
const int left_, right_; //the range of rendering area in x-axis
const align text_align_;
unsigned ellipsis_px_{ 0 };
};
@ -380,16 +339,10 @@ namespace nana
static std::size_t find_splitted(std::size_t begin, std::size_t end, int x, int endpos, unsigned * pxbuf)
{
unsigned acc_width = 0;
for(std::size_t i = begin; i < end; ++i)
for (std::size_t i = begin; i < end; ++i)
{
if(x + static_cast<int>(acc_width + pxbuf[i]) > endpos)
{
if(i == begin)
++i;
return i;
}
acc_width += pxbuf[i];
if ((x += static_cast<int>(pxbuf[i])) > endpos)
return (begin == i ? i + 1 : i);
}
return end;
}
@ -399,12 +352,11 @@ namespace nana
wchar_t ch = str[index];
if(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'))
{
wchar_t prch;
if(index)
{
prch = str[index - 1];
auto prch = str[index - 1];
if('0' <= ch && ch <= '9')
return !(('0' <= prch && prch <= '9') || (str[index - 1] == '-'));
return !(('0' <= prch && prch <= '9') || (prch == '-'));
return (('z' < prch || prch < 'a') && ('Z' < prch || prch < 'A'));
}
@ -481,6 +433,7 @@ namespace nana
{
for(std::size_t i = idx_head; i < len; ++i)
xpos += static_cast<int>(pxbuf[i]);
break;
}
//Check the word whether it is splittable.
@ -585,8 +538,8 @@ namespace nana
{
if (graph_)
{
helper::draw_string ds(graph_.handle(), pos.x, static_cast<int>(graph_.width()), text_align_);
helper::for_each_line(str, len, pos.y, ds);
helper::string_drawer sd{ graph_, pos.x, static_cast<int>(graph_.width()), text_align_, false };
helper::for_each_line(str, len, pos.y, sd);
}
}
@ -594,8 +547,8 @@ namespace nana
{
if (graph_)
{
helper::draw_string_omitted dso(graph_, pos.x, pos.x + static_cast<int>(restricted_pixels), omitted);
helper::for_each_line(str, len, pos.y, dso);
helper::string_drawer sd{ graph_, pos.x, pos.x + static_cast<int>(restricted_pixels), text_align_, omitted };
helper::for_each_line(str, len, pos.y, sd);
}
}