Added a texture cache for pango_text render output
This commit is contained in:
parent
a0f8ad67a5
commit
8647ff63c0
5 changed files with 124 additions and 29 deletions
|
@ -2201,7 +2201,7 @@ void display::refresh_report(const std::string& report_name, const config * new_
|
|||
text.set_text(t, true);
|
||||
text.set_maximum_width(area.w);
|
||||
text.set_maximum_height(area.h, false);
|
||||
surface s = text.render();
|
||||
surface s = text.render_and_get_surface();
|
||||
|
||||
// check if next element is text with almost no space to show it
|
||||
const int minimal_text = 12; // width in pixels
|
||||
|
@ -2214,7 +2214,7 @@ void display::refresh_report(const std::string& report_name, const config * new_
|
|||
//NOTE this space should be longer than minimal_text pixels
|
||||
t = t + " ";
|
||||
text.set_text(t, true);
|
||||
s = text.render();
|
||||
s = text.render_and_get_surface();
|
||||
// use the area of this element for next tooltips
|
||||
used_ellipsis = true;
|
||||
ellipsis_area.x = x;
|
||||
|
|
|
@ -108,7 +108,7 @@ texture floating_label::create_texture()
|
|||
|
||||
renderer.set_text(text_, use_markup_);
|
||||
|
||||
surface& foreground = renderer.render();
|
||||
surface& foreground = renderer.render_and_get_surface();
|
||||
|
||||
if(foreground == nullptr) {
|
||||
ERR_FT << "could not create floating label's text" << std::endl;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "preferences/general.hpp"
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
@ -40,6 +41,9 @@
|
|||
|
||||
namespace font {
|
||||
|
||||
// Cache
|
||||
//pango_text_cache_t rendered_text_cache {};
|
||||
|
||||
pango_text::pango_text()
|
||||
: context_(pango_font_map_create_context(pango_cairo_font_map_get_default()), g_object_unref)
|
||||
, layout_(pango_layout_new(context_.get()), g_object_unref)
|
||||
|
@ -65,6 +69,7 @@ pango_text::pango_text()
|
|||
, length_(0)
|
||||
, surface_dirty_(true)
|
||||
, surface_buffer_()
|
||||
, hash_(0)
|
||||
{
|
||||
// With 72 dpi the sizes are the same as with SDL_TTF so hardcoded.
|
||||
pango_cairo_context_set_resolution(context_.get(), 72.0);
|
||||
|
@ -88,13 +93,18 @@ pango_text::pango_text()
|
|||
cairo_font_options_destroy(fo);
|
||||
}
|
||||
|
||||
surface& pango_text::render()
|
||||
texture& pango_text::render_and_get_texture()
|
||||
{
|
||||
this->rerender();
|
||||
return rendered_text_cache[hash_];
|
||||
}
|
||||
|
||||
surface& pango_text::render_and_get_surface()
|
||||
{
|
||||
this->rerender();
|
||||
return surface_;
|
||||
}
|
||||
|
||||
|
||||
int pango_text::get_width() const
|
||||
{
|
||||
return this->get_size().x;
|
||||
|
@ -694,6 +704,16 @@ void pango_text::rerender(const bool force)
|
|||
this->recalculate(force);
|
||||
surface_dirty_ = false;
|
||||
|
||||
// Update hash
|
||||
hash_ = std::hash<pango_text>()(*this);
|
||||
|
||||
// If we already have the appropriate texture in-cache, exit.
|
||||
auto iter = rendered_text_cache.find(hash_);
|
||||
if(iter != rendered_text_cache.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Else, render the updated text...
|
||||
int width = rect_.x + rect_.width;
|
||||
int height = rect_.y + rect_.height;
|
||||
if(maximum_width_ > 0) { width = std::min(width, maximum_width_); }
|
||||
|
@ -729,6 +749,9 @@ void pango_text::rerender(const bool force)
|
|||
surface_.assign(SDL_CreateRGBSurfaceFrom(
|
||||
&surface_buffer_[0], width, height, 32, stride, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000));
|
||||
#endif
|
||||
|
||||
// ...and add it to the cache.
|
||||
rendered_text_cache.emplace(hash_, texture(surface_));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -873,3 +896,37 @@ pango_text& get_text_renderer()
|
|||
}
|
||||
|
||||
} // namespace font
|
||||
|
||||
namespace std
|
||||
{
|
||||
size_t hash<font::pango_text>::operator()(const font::pango_text& t) const
|
||||
{
|
||||
using boost::hash_value;
|
||||
using boost::hash_combine;
|
||||
|
||||
//
|
||||
// Text hashing uses 32-bit FNV-1a.
|
||||
// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a
|
||||
//
|
||||
|
||||
size_t hash = 2166136261;
|
||||
for(const char& c : t.text_) {
|
||||
hash |= c;
|
||||
hash *= 16777619;
|
||||
}
|
||||
|
||||
hash_combine(hash, t.font_class_);
|
||||
hash_combine(hash, t.font_size_);
|
||||
hash_combine(hash, t.font_style_);
|
||||
hash_combine(hash, t.foreground_color_.to_rgba_bytes());
|
||||
hash_combine(hash, t.get_width());
|
||||
hash_combine(hash, t.get_height());
|
||||
hash_combine(hash, t.maximum_width_);
|
||||
hash_combine(hash, t.maximum_height_);
|
||||
hash_combine(hash, t.alignment_);
|
||||
hash_combine(hash, t.ellipse_mode_);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "font/font_options.hpp"
|
||||
#include "color.hpp"
|
||||
#include "sdl/surface.hpp"
|
||||
#include "sdl/texture.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "serialization/unicode_types.hpp"
|
||||
|
||||
|
@ -24,19 +25,20 @@
|
|||
#include <pango/pangocairo.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/***
|
||||
/**
|
||||
* Note: This is the cairo-pango code path, not the SDL_TTF code path.
|
||||
*/
|
||||
|
||||
struct language_def;
|
||||
struct point;
|
||||
|
||||
namespace font {
|
||||
|
||||
namespace font
|
||||
{
|
||||
// add background color and also font markup.
|
||||
|
||||
/**
|
||||
|
@ -82,12 +84,20 @@ public:
|
|||
pango_text & operator = (const pango_text &) = delete;
|
||||
|
||||
/**
|
||||
* Returns the rendered text.
|
||||
* Returns the rendered text texture from the cache.
|
||||
*
|
||||
* Before rendering it tests whether a redraw is needed and if so it first
|
||||
* redraws the surface before returning it.
|
||||
* If the surface is flagged dirty it will first be re-rendered and a new
|
||||
* texture added to the cache upon redraw.
|
||||
*/
|
||||
surface& render();
|
||||
texture& render_and_get_texture();
|
||||
|
||||
/**
|
||||
* Returns the rendered text surface directly.
|
||||
*
|
||||
* If the surface is flagged dirty it will first be re-rendered and a new
|
||||
* texture added to the cache upon redraw.
|
||||
*/
|
||||
surface& render_and_get_surface();
|
||||
|
||||
/** Returns the width needed for the text. */
|
||||
int get_width() const;
|
||||
|
@ -445,6 +455,12 @@ private:
|
|||
|
||||
std::vector<std::string> find_links(utils::string_view text) const;
|
||||
void format_links(std::string& text, const std::vector<std::string>& links) const;
|
||||
|
||||
/** Hash for the current settings (text, size, etc) configuration. */
|
||||
size_t hash_;
|
||||
|
||||
// Allow specialization of std::hash for pango_text
|
||||
friend struct std::hash<pango_text>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -456,4 +472,29 @@ private:
|
|||
*/
|
||||
pango_text& get_text_renderer();
|
||||
|
||||
using pango_text_cache_t = std::map<size_t, texture>;
|
||||
|
||||
/**
|
||||
* The text texture cache.
|
||||
*
|
||||
* Each time a specific bit of text is rendered, a corresponding texture is created and
|
||||
* added to the cache. We don't store the surface since there isn't really any use for
|
||||
* it. If we need texture size that can be easily queried.
|
||||
*
|
||||
* @todo Figure out how this can be optimized with a texture atlas. It should be possible
|
||||
* to store smaller bits of text in the atlas and construct new textures from them.
|
||||
*/
|
||||
static pango_text_cache_t rendered_text_cache;
|
||||
|
||||
} // namespace font
|
||||
|
||||
// Specialize std::hash for pango_text
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<font::pango_text>
|
||||
{
|
||||
size_t operator()(const font::pango_text& t) const;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
|
|
@ -1293,22 +1293,27 @@ void text_shape::draw(
|
|||
: PANGO_ELLIPSIZE_END)
|
||||
.set_characters_per_line(characters_per_line_);
|
||||
|
||||
surface& surf = text_renderer.render();
|
||||
if(surf->w == 0) {
|
||||
// Get the resulting texture.
|
||||
texture& txt = text_renderer.render_and_get_texture();
|
||||
|
||||
// TODO: should use pango_text::get_size but the dimensions are inaccurate. Investigate.
|
||||
texture::info info = txt.get_info();
|
||||
|
||||
if(info.w == 0) {
|
||||
DBG_GUI_D << "Text: Rendering '" << text
|
||||
<< "' resulted in an empty canvas, leave.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
wfl::map_formula_callable local_variables(variables);
|
||||
local_variables.add("text_width", wfl::variant(surf->w));
|
||||
local_variables.add("text_height", wfl::variant(surf->h));
|
||||
local_variables.add("text_width", wfl::variant(info.w));
|
||||
local_variables.add("text_height", wfl::variant(info.h));
|
||||
/*
|
||||
std::cerr << "Text: drawing text '" << text
|
||||
<< " maximum width " << maximum_width_(variables)
|
||||
<< " maximum height " << maximum_height_(variables)
|
||||
<< " text width " << surf->w
|
||||
<< " text height " << surf->h;
|
||||
<< " text width " << info.w
|
||||
<< " text height " << info.h;
|
||||
*/
|
||||
///@todo formulas are now recalculated every draw cycle which is a
|
||||
// bit silly unless there has been a resize. So to optimize we should
|
||||
|
@ -1327,25 +1332,17 @@ void text_shape::draw(
|
|||
_("Text doesn't start on canvas."));
|
||||
|
||||
// A text might be to long and will be clipped.
|
||||
if(surf->w > static_cast<int>(w)) {
|
||||
if(info.w > static_cast<int>(w)) {
|
||||
WRN_GUI_D << "Text: text is too wide for the "
|
||||
"canvas and will be clipped.\n";
|
||||
}
|
||||
|
||||
if(surf->h > static_cast<int>(h)) {
|
||||
if(info.h > static_cast<int>(h)) {
|
||||
WRN_GUI_D << "Text: text is too high for the "
|
||||
"canvas and will be clipped.\n";
|
||||
}
|
||||
|
||||
SDL_Rect dst = sdl::create_rect(x, y, surf->w, surf->h);
|
||||
|
||||
/* NOTE: we cannot use SDL_UpdateTexture to copy the surface pixel data directly to the canvas texture
|
||||
* since no alpha blending occurs; values (even pure alpha) totally overwrite the underlying pixel data.
|
||||
*
|
||||
* To work around that, we create a texture from the surface and copy it to the renderer. This cleanly
|
||||
* copies the surface to the canvas texture with the appropriate alpha blending.
|
||||
*/
|
||||
texture txt(surf);
|
||||
SDL_Rect dst = sdl::create_rect(x, y, info.w, info.h);
|
||||
|
||||
CVideo::get_singleton().render_copy(txt, nullptr, &dst);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue