Skip to content

Commit

Permalink
Refactored floating_label implementation
Browse files Browse the repository at this point in the history
* Store label as a texture instead of creating a texture from a surface every draw cycle
* Restored expired label removal and alpha fadeout (was accidentally removed earlier in my refactoring)
* Use alpha field of bg_color member for background color
* Draw tooltip backgrounds procedurally instead of with surfaces and part of the label texture itself.
  See included comment for small caveat.
  • Loading branch information
Vultraz committed Mar 18, 2018
1 parent 2c9d9ef commit f25a404
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 91 deletions.
195 changes: 111 additions & 84 deletions src/floating_label.cpp
Expand Up @@ -14,9 +14,14 @@

#include "floating_label.hpp"

#include "display.hpp"
#include "font/standard_colors.hpp"
#include "font/text.hpp"
#include "log.hpp"
#include "sdl/render_utils.hpp"
#include "sdl/surface.hpp"
#include "utils/general.hpp"

#include <boost/algorithm/string.hpp>

#include <map>
#include <set>
Expand All @@ -30,41 +35,37 @@ static lg::log_domain log_font("font");

namespace
{
typedef std::map<int, font::floating_label> label_map;
label_map labels;
std::map<int, font::floating_label> labels;

int label_id = 1;

std::stack<std::set<int>> label_contexts;
}

namespace font
{
floating_label::floating_label(const std::string& text, const surface& surf)
#if 0
: img_(),
#else
: surf_(surf)
, buf_(nullptr)
#endif
floating_label::floating_label(const std::string& text)
: texture_(nullptr)
, text_(text)
, font_size_(SIZE_NORMAL)
, color_(NORMAL_COLOR)
, bgcolor_()
, bgalpha_(0)
, current_alpha_(255)
, xpos_(0)
, ypos_(0)
, xmove_(0)
, ymove_(0)
, lifetime_(-1)
, width_(-1)
, height_(-1)
, clip_rect_(CVideo::get_singleton().screen_area())
, clip_rect_(screen_area())
, alpha_change_(0)
, visible_(true)
, align_(CENTER_ALIGN)
, border_(0)
, scroll_(ANCHOR_LABEL_SCREEN)
, use_markup_(true)
, fill_background_(false)
{
}

Expand All @@ -76,7 +77,8 @@ void floating_label::move(double xmove, double ymove)

int floating_label::xpos(size_t width) const
{
int xpos = int(xpos_);
int xpos = static_cast<int>(xpos_);

if(align_ == font::CENTER_ALIGN) {
xpos -= width / 2;
} else if(align_ == font::RIGHT_ALIGN) {
Expand All @@ -86,89 +88,103 @@ int floating_label::xpos(size_t width) const
return xpos;
}

surface floating_label::create_surface()
texture floating_label::create_texture()
{
if(surf_.null()) {
font::pango_text text;
text.set_foreground_color(color_);
text.set_font_size(font_size_);
text.set_maximum_width(width_ < 0 ? clip_rect_.w : width_);
text.set_maximum_height(height_ < 0 ? clip_rect_.h : height_, true);

// ignore last '\n'
if(!text_.empty() && *(text_.rbegin()) == '\n') {
text.set_text(std::string(text_.begin(), text_.end() - 1), use_markup_);
} else {
text.set_text(text_, use_markup_);
}
if(texture_.null()) {
//
// Render text
//

surface foreground = text.render();
// TODO: figure out why the global text renderer object gives too large a size.
font::pango_text renderer;

if(foreground == nullptr) {
ERR_FT << "could not create floating label's text" << std::endl;
return nullptr;
}
renderer.set_foreground_color(color_);
renderer.set_font_size(font_size_);
renderer.set_maximum_width(width_ < 0 ? clip_rect_.w : width_);
renderer.set_maximum_height(height_ < 0 ? clip_rect_.h : height_, true);

// combine foreground text with its background
if(bgalpha_ != 0) {
// background is a dark tooltip box
surface background = create_neutral_surface(foreground->w + border_ * 2, foreground->h + border_ * 2);
// Strip trailing newlines.
boost::trim_right(text_);

if(background == nullptr) {
ERR_FT << "could not create tooltip box" << std::endl;
return surf_ = foreground;
}
renderer.set_text(text_, use_markup_);

uint32_t color = SDL_MapRGBA(foreground->format, bgcolor_.r, bgcolor_.g, bgcolor_.b, bgalpha_);
sdl::fill_surface_rect(background, nullptr, color);
surface& foreground = renderer.render();

// we make the text less transparent, because the blitting on the
// dark background will darken the anti-aliased part.
// This 1.13 value seems to restore the brightness of version 1.4
// (where the text was blitted directly on screen)
adjust_surface_alpha(foreground, ftofxp(1.13));

SDL_Rect r{border_, border_, 0, 0};
adjust_surface_alpha(foreground, SDL_ALPHA_OPAQUE);
sdl_blit(foreground, nullptr, background, &r);
if(foreground == nullptr) {
ERR_FT << "could not create floating label's text" << std::endl;
return texture();
}

surf_ = background;
} else {
// background is blurred shadow of the text
//
// Add some text shadow if we're not drawing the background.
//
if(!fill_background_) {
surface background = create_neutral_surface(foreground->w + 4, foreground->h + 4);
sdl::fill_surface_rect(background, nullptr, 0);
SDL_Rect r{2, 2, 0, 0};

SDL_Rect r {2, 2, 0, 0};
sdl_blit(foreground, nullptr, background, &r);

background = shadow_image(background);

if(background == nullptr) {
ERR_FT << "could not create floating label's shadow" << std::endl;
return surf_ = foreground;
return texture_ = texture(foreground);
}

sdl_blit(foreground, nullptr, background, &r);
surf_ = background;

texture_ = texture(background);
} else {
texture_ = texture(foreground);
}
}

return surf_;
return texture_;
}

void floating_label::draw()
{
create_surface();
if(surf_ == nullptr) {
// No-op if texture is valid
create_texture();

if(texture_ == nullptr) {
return;
}

SDL_Rect rect = sdl::create_rect(xpos(surf_->w), ypos_, surf_->w, surf_->h);
const texture::info info = texture_.get_info();
SDL_Rect rect = sdl::create_rect(xpos(info.w), ypos_, info.w, info.h);

// TODO: cache?
texture tex(surf_);
move(xmove_, ymove_);

CVideo::get_singleton().render_copy(tex, nullptr, &rect);
}
// Fade out moving floating labels
if(lifetime_ > 0) {
--lifetime_;

if(alpha_change_ != 0 && (xmove_ != 0.0 || ymove_ != 0.0)) {
current_alpha_ = utils::clamp<unsigned int>(current_alpha_ + alpha_change_, 0, 255);
set_texture_alpha(texture_, current_alpha_);
}
}

// NOTE: there used to be a "fade out moving floating labels" effect here.
// Draw a semi-transparent background background alpha provided.
// NOTE: doing this this way instead of embedding it as part of label texture itself does
// have the side effect of removing the background from the fadeout effect. However, in
// practical use only the non-background versions are used with the fadeout effect. I can do
// some alpha fadeout on the background later too if relevant.
if(fill_background_) {
SDL_Rect bg_rect {
rect.x - border_,
rect.y - border_,
rect.w + (border_ * 2),
rect.h + (border_ * 2)
};

sdl::fill_rectangle(bg_rect, bgcolor_);
}

CVideo::get_singleton().render_copy(texture_, nullptr, &rect);
}

int add_floating_label(const floating_label& flabel)
{
Expand All @@ -184,24 +200,24 @@ int add_floating_label(const floating_label& flabel)

void move_floating_label(int handle, double xmove, double ymove)
{
const label_map::iterator i = labels.find(handle);
const auto i = labels.find(handle);
if(i != labels.end()) {
i->second.move(xmove, ymove);
}
}

void scroll_floating_labels(double xmove, double ymove)
{
for(label_map::iterator i = labels.begin(); i != labels.end(); ++i) {
if(i->second.scroll() == ANCHOR_LABEL_MAP) {
i->second.move(xmove, ymove);
for(auto& label : labels) {
if(label.second.scroll() == ANCHOR_LABEL_MAP) {
label.second.move(xmove, ymove);
}
}
}

void remove_floating_label(int handle)
{
const label_map::iterator i = labels.find(handle);
const auto i = labels.find(handle);
if(i != labels.end()) {
labels.erase(i);
}
Expand All @@ -213,33 +229,35 @@ void remove_floating_label(int handle)

void show_floating_label(int handle, bool value)
{
const label_map::iterator i = labels.find(handle);
const auto i = labels.find(handle);
if(i != labels.end()) {
i->second.show(value);
}
}

SDL_Rect get_floating_label_rect(int handle)
{
const label_map::iterator i = labels.find(handle);
const auto i = labels.find(handle);
if(i != labels.end()) {
const surface surf = i->second.create_surface();
if(surf != nullptr) {
return {0, 0, surf->w, surf->h};
const texture& tex = i->second.create_texture();
if(tex != nullptr) {
const texture::info info = tex.get_info();

return {0, 0, info.w, info.h};
}
}

return sdl::empty_rect;
}

floating_label_context::floating_label_context()
{
label_contexts.emplace();
label_contexts.push(std::set<int>());
}

floating_label_context::~floating_label_context()
{
const std::set<int>& context = label_contexts.top();

while(!context.empty()) {
// Remove_floating_label removes the passed label from the context.
// This loop removes a different label in every iteration.
Expand All @@ -255,15 +273,24 @@ void draw_floating_labels()
return;
}

const std::set<int>& context = label_contexts.top();
std::set<int>& context = label_contexts.top();

// draw the labels in the order they were added, so later added labels (likely to be tooltips)
// Remove expired labels.
for(auto itor = labels.begin(); itor != labels.end(); /* Handle increment in loop*/) {
if(context.count(itor->first) > 0 && itor->second.expired()) {
context.erase(itor->first);
labels.erase(itor++);
} else {
++itor;
}
}

// Draw the labels in the order they were added, so later added labels (likely to be tooltips)
// are displayed over earlier added labels.
for(label_map::iterator i = labels.begin(); i != labels.end(); ++i) {
if(context.count(i->first) > 0) {
i->second.draw();
for(auto& label : labels) {
if(context.count(label.first) > 0) {
label.second.draw();
}
}
}

}
16 changes: 10 additions & 6 deletions src/floating_label.hpp
Expand Up @@ -15,7 +15,8 @@
#pragma once

#include "color.hpp"
#include "sdl/surface.hpp"
#include "sdl/texture.hpp"

#include <string>

namespace font {
Expand All @@ -35,7 +36,7 @@ enum LABEL_SCROLL_MODE { ANCHOR_LABEL_SCREEN, ANCHOR_LABEL_MAP };
class floating_label
{
public:
floating_label(const std::string& text, const surface& surface = nullptr);
explicit floating_label(const std::string& text);

void set_font_size(int font_size) {font_size_ = font_size;}

Expand All @@ -57,7 +58,7 @@ class floating_label
void set_color(const color_t& color) {color_ = color;}
void set_bg_color(const color_t& bg_color) {
bgcolor_ = bg_color;
bgalpha_ = bg_color.a;
fill_background_ = bgcolor_.a != 255;
}
void set_border_size(int border) {border_ = border;}
// set width for word wrapping (use -1 to disable it)
Expand All @@ -71,7 +72,7 @@ class floating_label
void move(double xmove, double ymove);
void draw();

surface create_surface();
texture create_texture();

bool expired() const { return lifetime_ == 0; }

Expand All @@ -82,11 +83,12 @@ class floating_label
private:

int xpos(size_t width) const;
surface surf_, buf_;

texture texture_;
std::string text_;
int font_size_;
color_t color_, bgcolor_;
int bgalpha_;
unsigned int current_alpha_;
double xpos_, ypos_, xmove_, ymove_;
int lifetime_;
int width_, height_;
Expand All @@ -97,6 +99,8 @@ class floating_label
int border_;
LABEL_SCROLL_MODE scroll_;
bool use_markup_;

bool fill_background_;
};


Expand Down

0 comments on commit f25a404

Please sign in to comment.