GUI2/Canvas: refactor surface blitting out of drawing routines

This entirely refactors surface blitting out of the canvas. Instead, each canvas owns a texture. This texture
is completely redrawn any time the canvas if marked dirty, else the result is cached and drawn to the screen
each draw cycle.

All windows are now redrawn every draw cycle. The use of the cached canvas textures means there's no noticeable
performance difference (likely a performance gain, actually) from using surface blitting.

There's still some code to clean up and a few things to fix.
This commit is contained in:
Charles Dang 2017-05-24 19:05:05 +11:00
parent 1a24fd5872
commit 41f921bd38
7 changed files with 282 additions and 185 deletions

View file

@ -30,8 +30,8 @@
[image]
x = 2
y = 2
w = "(width)"
h = "(height)"
w = "(width - 2)"
h = "(height - 2)"
name = "buttons/button_normal/{BACKGROUND_IMAGE}.png{IPF}"
[/image]

View file

@ -105,7 +105,8 @@ static void set_renderer_color(SDL_Renderer* renderer, color_t color)
* @param x2 The end x coordinate of the line to draw.
* @param y2 The end y coordinate of the line to draw.
*/
static void draw_line(surface& canvas,
static void draw_line(const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
color_t color,
unsigned x1,
@ -113,16 +114,14 @@ static void draw_line(surface& canvas,
const unsigned x2,
unsigned y2)
{
unsigned w = canvas->w;
DBG_GUI_D << "Shape: draw line from " << x1 << ',' << y1 << " to " << x2
<< ',' << y2 << " canvas width " << w << " canvas height "
<< canvas->h << ".\n";
<< ',' << y2 << " canvas width " << canvas_w << " canvas height "
<< canvas_h << ".\n";
assert(static_cast<int>(x1) < canvas->w);
assert(static_cast<int>(x2) < canvas->w);
assert(static_cast<int>(y1) < canvas->h);
assert(static_cast<int>(y2) < canvas->h);
assert(static_cast<int>(x1) < canvas_w);
assert(static_cast<int>(x2) < canvas_w);
assert(static_cast<int>(y1) < canvas_h);
assert(static_cast<int>(y2) < canvas_h);
set_renderer_color(renderer, color);
@ -149,22 +148,23 @@ static void draw_line(surface& canvas,
* @tparam octants A bitfield indicating which octants to draw, starting at twelve o'clock and moving clockwise.
*/
template<unsigned int octants = 0xff>
static void draw_circle(surface& canvas,
static void draw_circle(const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
color_t color,
const int x_center,
const int y_center,
const int radius)
{
unsigned w = canvas->w;
unsigned w = canvas_w;
DBG_GUI_D << "Shape: draw circle at " << x_center << ',' << y_center
<< " with radius " << radius << " canvas width " << w
<< " canvas height " << canvas->h << ".\n";
<< " canvas height " << canvas_h << ".\n";
if(octants & 0x0f) assert((x_center + radius) < canvas->w);
if(octants & 0x0f) assert((x_center + radius) < canvas_w);
if(octants & 0xf0) assert((x_center - radius) >= 0);
if(octants & 0x3c) assert((y_center + radius) < canvas->h);
if(octants & 0x3c) assert((y_center + radius) < canvas_h);
if(octants & 0xc3) assert((y_center - radius) >= 0);
set_renderer_color(renderer, color);
@ -215,22 +215,23 @@ static void draw_circle(surface& canvas,
* @tparam octants A bitfield indicating which octants to draw, starting at twelve o'clock and moving clockwise.
*/
template<unsigned int octants = 0xff>
static void fill_circle(surface& canvas,
static void fill_circle(const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
color_t color,
const int x_center,
const int y_center,
const int radius)
{
unsigned w = canvas->w;
unsigned w = canvas_w;
DBG_GUI_D << "Shape: draw filled circle at " << x_center << ',' << y_center
<< " with radius " << radius << " canvas width " << w
<< " canvas height " << canvas->h << ".\n";
<< " canvas height " << canvas_h << ".\n";
if(octants & 0x0f) assert((x_center + radius) < canvas->w);
if(octants & 0x0f) assert((x_center + radius) < canvas_w);
if(octants & 0xf0) assert((x_center - radius) >= 0);
if(octants & 0x3c) assert((y_center + radius) < canvas->h);
if(octants & 0x3c) assert((y_center + radius) < canvas_h);
if(octants & 0xc3) assert((y_center - radius) >= 0);
set_renderer_color(renderer, color);
@ -586,9 +587,11 @@ line_shape::line_shape(const config& cfg)
}
}
void line_shape::draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
void line_shape::draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
{
/**
* @todo formulas are now recalculated every draw cycle which is a bit silly
@ -602,21 +605,18 @@ void line_shape::draw(surface& canvas,
const unsigned y2 = y2_(variables);
DBG_GUI_D << "Line: draw from " << x1 << ',' << y1 << " to " << x2 << ','
<< y2 << " canvas size " << canvas->w << ',' << canvas->h
<< y2 << " canvas size " << canvas_w << ',' << canvas_h
<< ".\n";
VALIDATE(static_cast<int>(x1) < canvas->w
&& static_cast<int>(x2) < canvas->w
&& static_cast<int>(y1) < canvas->h
&& static_cast<int>(y2) < canvas->h,
VALIDATE(static_cast<int>(x1) < canvas_w
&& static_cast<int>(x2) < canvas_w
&& static_cast<int>(y1) < canvas_h
&& static_cast<int>(y2) < canvas_h,
_("Line doesn't fit on canvas."));
// @todo FIXME respect the thickness.
// lock the surface
surface_lock locker(canvas);
draw_line(canvas, renderer, color_(variables), x1, y1, x2, y2);
draw_line(canvas_w, canvas_h, renderer, color_(variables), x1, y1, x2, y2);
}
/***** ***** ***** ***** ***** Rectangle ***** ***** ***** ***** *****/
@ -675,9 +675,11 @@ rectangle_shape::rectangle_shape(const config& cfg)
}
}
void rectangle_shape::draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
void rectangle_shape::draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
{
/**
* @todo formulas are now recalculated every draw cycle which is a bit
@ -690,13 +692,13 @@ void rectangle_shape::draw(surface& canvas,
const int h = h_(variables);
DBG_GUI_D << "Rectangle: draw from " << x << ',' << y << " width " << w
<< " height " << h << " canvas size " << canvas->w << ','
<< canvas->h << ".\n";
<< " height " << h << " canvas size " << canvas_w << ','
<< canvas_h << ".\n";
VALIDATE(x < canvas->w
&& x + w <= canvas->w
&& y < canvas->h
&& y + h <= canvas->h, _("Rectangle doesn't fit on canvas."));
VALIDATE(x < canvas_w
&& x + w <= canvas_w
&& y < canvas_h
&& y + h <= canvas_h, _("Rectangle doesn't fit on canvas."));
surface_lock locker(canvas);
@ -789,9 +791,11 @@ round_rectangle_shape::round_rectangle_shape(const config& cfg)
}
}
void round_rectangle_shape::draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
void round_rectangle_shape::draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
{
/**
* @todo formulas are now recalculated every draw cycle which is a bit
@ -805,13 +809,13 @@ void round_rectangle_shape::draw(surface& canvas,
const int r = r_(variables);
DBG_GUI_D << "Rounded Rectangle: draw from " << x << ',' << y << " width " << w
<< " height " << h << " canvas size " << canvas->w << ','
<< canvas->h << ".\n";
<< " height " << h << " canvas size " << canvas_w << ','
<< canvas_h << ".\n";
VALIDATE(x < canvas->w
&& x + w <= canvas->w
&& y < canvas->h
&& y + h <= canvas->h, _("Rounded Rectangle doesn't fit on canvas."));
VALIDATE(x < canvas_w
&& x + w <= canvas_w
&& y < canvas_h
&& y + h <= canvas_h, _("Rounded Rectangle doesn't fit on canvas."));
surface_lock locker(canvas);
@ -829,10 +833,10 @@ void round_rectangle_shape::draw(surface& canvas,
SDL_RenderFillRects(renderer, area, count);
fill_circle<0xc0>(canvas, renderer, fill_color, x + r, y + r, r);
fill_circle<0x03>(canvas, renderer, fill_color, x + w - r, y + r, r);
fill_circle<0x30>(canvas, renderer, fill_color, x + r, y + h - r, r);
fill_circle<0x0c>(canvas, renderer, fill_color, x + w - r, y + h - r, r);
fill_circle<0xc0>(canvas_w, canvas_h, renderer, fill_color, x + r, y + r, r);
fill_circle<0x03>(canvas_w, canvas_h, renderer, fill_color, x + w - r, y + r, r);
fill_circle<0x30>(canvas_w, canvas_h, renderer, fill_color, x + r, y + h - r, r);
fill_circle<0x0c>(canvas_w, canvas_h, renderer, fill_color, x + w - r, y + h - r, r);
}
const color_t border_color = border_color_(variables);
@ -847,10 +851,10 @@ void round_rectangle_shape::draw(surface& canvas,
SDL_RenderDrawLine(renderer, x + i, y + r, x + i, y + h - r);
SDL_RenderDrawLine(renderer, x + w - i, y + r, x + w - i, y + h - r);
draw_circle<0xc0>(canvas, renderer, border_color, x + r, y + r, r - i);
draw_circle<0x03>(canvas, renderer, border_color, x + w - r, y + r, r - i);
draw_circle<0x30>(canvas, renderer, border_color, x + r, y + h - r, r - i);
draw_circle<0x0c>(canvas, renderer, border_color, x + w - r, y + h - r, r - i);
draw_circle<0xc0>(canvas_w, canvas_h, renderer, border_color, x + r, y + r, r - i);
draw_circle<0x03>(canvas_w, canvas_h, renderer, border_color, x + w - r, y + r, r - i);
draw_circle<0x30>(canvas_w, canvas_h, renderer, border_color, x + r, y + h - r, r - i);
draw_circle<0x0c>(canvas_w, canvas_h, renderer, border_color, x + w - r, y + h - r, r - i);
}
}
@ -901,9 +905,11 @@ circle_shape::circle_shape(const config& cfg)
}
}
void circle_shape::draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
void circle_shape::draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
{
/**
* @todo formulas are now recalculated every draw cycle which is a bit
@ -916,7 +922,7 @@ void circle_shape::draw(surface& canvas,
const unsigned radius = radius_(variables);
DBG_GUI_D << "Circle: drawn at " << x << ',' << y << " radius " << radius
<< " canvas size " << canvas->w << ',' << canvas->h << ".\n";
<< " canvas size " << canvas_w << ',' << canvas_h << ".\n";
VALIDATE_WITH_DEV_MESSAGE(
static_cast<int>(x - radius) >= 0,
@ -929,28 +935,28 @@ void circle_shape::draw(surface& canvas,
formatter() << "y = " << y << ", radius = " << radius);
VALIDATE_WITH_DEV_MESSAGE(
static_cast<int>(x + radius) < canvas->w,
static_cast<int>(x + radius) < canvas_w,
_("Circle doesn't fit on canvas."),
formatter() << "x = " << x << ", radius = " << radius
<< "', canvas width = " << canvas->w << ".");
<< "', canvas width = " << canvas_w << ".");
VALIDATE_WITH_DEV_MESSAGE(
static_cast<int>(y + radius) < canvas->h,
static_cast<int>(y + radius) < canvas_h,
_("Circle doesn't fit on canvas."),
formatter() << "y = " << y << ", radius = " << radius
<< "', canvas height = " << canvas->h << ".");
<< "', canvas height = " << canvas_h << ".");
// lock the surface
surface_lock locker(canvas);
const color_t fill_color = fill_color_(variables);
if(!fill_color.null() && radius) {
fill_circle(canvas, renderer, fill_color, x, y, radius);
fill_circle(canvas_w, canvas_h, renderer, fill_color, x, y, radius);
}
const color_t border_color = border_color_(variables);
for(unsigned int i = 0; i < border_thickness_; i++) {
draw_circle(canvas, renderer, border_color, x, y, radius - i);
draw_circle(canvas_w, canvas_h, renderer, border_color, x, y, radius - i);
}
}
@ -1041,9 +1047,11 @@ void image_shape::dimension_validation(unsigned value, const std::string& name,
);
}
void image_shape::draw(surface& canvas,
SDL_Renderer* /*renderer*/,
wfl::map_formula_callable& variables)
void image_shape::draw(
const int /*canvas_w*/,
const int /*canvas_h*/,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
{
DBG_GUI_D << "Image: draw.\n";
@ -1063,14 +1071,13 @@ void image_shape::draw(surface& canvas,
* The locator might return a different surface for every call so we can't
* cache the output, also not if no formula is used.
*/
surface tmp(image::get_image(image::locator(name)));
image_.assign(make_neutral_surface(image::get_image(image::locator(name))));
if(!tmp) {
if(!image_) {
ERR_GUI_D << "Image: '" << name << "' not found and won't be drawn." << std::endl;
return;
}
image_.assign(make_neutral_surface(tmp));
assert(image_);
src_clip_ = {0, 0, image_->w, image_->h};
@ -1100,9 +1107,8 @@ void image_shape::draw(surface& canvas,
wfl::variant(variables.fake_ptr()).execute_variant(actions_formula_.evaluate(local_variables));
// Copy the data to local variables to avoid overwriting the originals.
SDL_Rect src_clip = src_clip_;
SDL_Rect dst_clip = sdl::create_rect(clip_x, clip_y, 0, 0);
surface surf;
surface surf(nullptr);
// Test whether we need to scale and do the scaling if needed.
if ((w == 0) && (h == 0)) {
@ -1113,6 +1119,7 @@ void image_shape::draw(surface& canvas,
DBG_GUI_D << "Image: vertical stretch from " << image_->w << ','
<< image_->h << " to a height of " << h << ".\n";
// TODO: convert to texture handling.
surf = stretch_surface_vertical(image_, h);
w = image_->w;
}
@ -1121,6 +1128,7 @@ void image_shape::draw(surface& canvas,
<< ',' << image_->h << " to a width of " << w
<< ".\n";
// TODO: convert to texture handling.
surf = stretch_surface_horizontal(image_, w);
h = image_->h;
}
@ -1135,11 +1143,13 @@ void image_shape::draw(surface& canvas,
DBG_GUI_D << "Image: tiling from " << image_->w << ','
<< image_->h << " to " << w << ',' << h << ".\n";
// TODO: convert to texture handling.
surf = tile_surface(image_, w, h, false);
} else if(resize_mode_ == tile_center) {
DBG_GUI_D << "Image: tiling centrally from " << image_->w << ','
<< image_->h << " to " << w << ',' << h << ".\n";
// TODO: convert to texture handling.
surf = tile_surface(image_, w, h, true);
} else {
if(resize_mode_ == stretch) {
@ -1150,18 +1160,33 @@ void image_shape::draw(surface& canvas,
DBG_GUI_D << "Image: scaling from " << image_->w << ','
<< image_->h << " to " << w << ',' << h << ".\n";
surf = scale_surface_legacy(image_, w, h);
// Textures are automatically scaled to size.
}
}
src_clip.w = w;
src_clip.h = h;
}
if(!surf) {
surf = image_;
}
dst_clip.w = w ? w : surf->w;
dst_clip.h = h ? h : 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.
*/
SDL_Texture* txt = SDL_CreateTextureFromSurface(renderer, surf);
if(vertical_mirror_(local_variables)) {
surf = flip_surface(surf);
SDL_RenderCopyEx(renderer, txt, nullptr, &dst_clip, 0, nullptr, SDL_FLIP_VERTICAL);
} else {
SDL_RenderCopy(renderer, txt, nullptr, &dst_clip);
}
blit_surface(surf, &src_clip, canvas, &dst_clip);
SDL_DestroyTexture(txt);
}
image_shape::resize_mode image_shape::get_resize_mode(const std::string& resize_mode)
@ -1262,9 +1287,11 @@ text_shape::text_shape(const config& cfg)
}
}
void text_shape::draw(surface& canvas,
SDL_Renderer* /*renderer*/,
wfl::map_formula_callable& variables)
void text_shape::draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables)
{
assert(variables.has_key("text"));
@ -1325,9 +1352,9 @@ void text_shape::draw(surface& canvas,
DBG_GUI_D << "Text: drawing text '" << text << "' drawn from " << x << ','
<< y << " width " << w << " height " << h << " canvas size "
<< canvas->w << ',' << canvas->h << ".\n";
<< canvas_w << ',' << canvas_h << ".\n";
VALIDATE(static_cast<int>(x) < canvas->w && static_cast<int>(y) < canvas->h,
VALIDATE(static_cast<int>(x) < canvas_w && static_cast<int>(y) < canvas_h,
_("Text doesn't start on canvas."));
// A text might be to long and will be clipped.
@ -1341,8 +1368,18 @@ void text_shape::draw(surface& canvas,
"canvas and will be clipped.\n";
}
SDL_Rect dst = sdl::create_rect(x, y, canvas->w, canvas->h);
blit_surface(surf, nullptr, canvas, &dst);
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.
*/
SDL_Texture* txt = SDL_CreateTextureFromSurface(renderer, surf);
SDL_RenderCopy(renderer, txt, nullptr, &dst);
SDL_DestroyTexture(txt);
}
/***** ***** ***** ***** ***** CANVAS ***** ***** ***** ***** *****/
@ -1353,8 +1390,8 @@ canvas::canvas()
, blur_depth_(0)
, w_(0)
, h_(0)
, canvas_()
, renderer_(nullptr)
, texture_(nullptr)
, renderer_(*CVideo::get_singleton().get_window())
, variables_()
, functions_()
, is_dirty_(true)
@ -1379,7 +1416,8 @@ canvas::canvas(canvas&& c)
canvas::~canvas()
{
SDL_DestroyRenderer(renderer_);
SDL_SetRenderTarget(renderer_, nullptr);
SDL_DestroyTexture(texture_);
}
void canvas::draw(const bool force)
@ -1396,6 +1434,9 @@ void canvas::draw(const bool force)
variables_.add("height", wfl::variant(h_));
}
DBG_GUI_D << "Canvas: resetting canvas.\n";
// TODO: reenable
#if 0
if(!canvas_.null()) {
DBG_GUI_D << "Canvas: use cached canvas.\n";
} else {
@ -1403,32 +1444,50 @@ void canvas::draw(const bool force)
DBG_GUI_D << "Canvas: create new empty canvas.\n";
canvas_.assign(create_neutral_surface(w_, h_));
}
#endif
SDL_DestroyRenderer(renderer_);
// Recreate the texture.
if(texture_) {
SDL_DestroyTexture(texture_);
}
renderer_ = SDL_CreateSoftwareRenderer(canvas_);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w_, h_);
assert(texture_);
// draw items
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
SDL_SetRenderTarget(renderer_, texture_);
// Draw items.
for(auto& shape : shapes_) {
lg::scope_logger inner_scope_logging_object__(log_gui_draw, "Canvas: draw shape.");
shape->draw(canvas_, renderer_, variables_);
shape->draw(w_, h_, renderer_, variables_);
}
SDL_SetRenderTarget(renderer_, nullptr);
// TODO: re-enable
#if 0
// The shapes have been drawn and the draw result has been cached. Clear the list.
std::copy(shapes_.begin(), shapes_.end(), std::back_inserter(drawn_shapes_));
shapes_.clear();
SDL_RenderPresent(renderer_);
#endif
is_dirty_ = false;
}
void canvas::blit(surface& surf, SDL_Rect rect)
void canvas::blit(surface& /*surf*/, SDL_Rect rect)
{
SDL_RenderSetViewport(renderer_, &rect);
draw();
SDL_RenderCopy(renderer_, texture_, nullptr, nullptr);
SDL_RenderSetViewport(renderer_, nullptr);
// TODO: reenable
#if 0
if(blur_depth_) {
/*
* If the surf is the video surface the blurring seems to stack, this
@ -1445,8 +1504,7 @@ void canvas::blit(surface& surf, SDL_Rect rect)
sdl_blit(s, nullptr, surf, &r);
}
}
sdl_blit(canvas_, nullptr, surf, &rect);
#endif
}
void canvas::parse_cfg(const config& cfg)

View file

@ -61,14 +61,15 @@ public:
/**
* Draws the canvas.
*
* @param canvas The resulting image will be blitted upon this
* canvas.
* @param variables The canvas can have formulas in it's
* definition, this parameter contains the values
* for these formulas.
*/
virtual void draw(surface& canvas, SDL_Renderer* renderer,
wfl::map_formula_callable& variables) = 0;
virtual void draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) = 0;
bool immutable() const
{
@ -163,11 +164,6 @@ public:
return h_;
}
surface& surf()
{
return canvas_;
}
void set_variable(const std::string& key, const wfl::variant& value)
{
variables_.add(key, value);
@ -204,9 +200,10 @@ private:
/** Height of the canvas. */
unsigned h_;
/** The surface we draw all items on. */
surface canvas_;
/** The texture onto which items are drawn. */
SDL_Texture* texture_;
/** A pointer to the window renderer. */
SDL_Renderer* renderer_;
/** The variables of the canvas. */

View file

@ -30,9 +30,11 @@ public:
explicit line_shape(const config& cfg);
/** Implement shape::draw(). */
void draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
void draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
private:
typed_formula<unsigned> x1_, /**< The start x coordinate of the line. */
@ -66,9 +68,11 @@ public:
explicit rectangle_shape(const config& cfg);
/** Implement shape::draw(). */
void draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
void draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
private:
typed_formula<int> x_, /**< The x coordinate of the rectangle. */
@ -111,10 +115,11 @@ public:
explicit round_rectangle_shape(const config& cfg);
/** Implement shape::draw(). */
void draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
void draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
private:
typed_formula<int> x_, /**< The x coordinate of the rectangle. */
y_, /**< The y coordinate of the rectangle. */
@ -157,9 +162,11 @@ public:
explicit circle_shape(const config& cfg);
/** Implement shape::draw(). */
void draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
void draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
private:
typed_formula<unsigned> x_, /**< The center x coordinate of the circle. */
@ -186,10 +193,11 @@ public:
image_shape(const config& cfg, wfl::action_function_symbol_table& functions);
/** Implement shape::draw(). */
void draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
void draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
private:
typed_formula<unsigned> x_, /**< The x coordinate of the image. */
y_, /**< The y coordinate of the image. */
@ -252,9 +260,11 @@ public:
explicit text_shape(const config& cfg);
/** Implement shape::draw(). */
void draw(surface& canvas,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
void draw(
const int canvas_w,
const int canvas_h,
SDL_Renderer* renderer,
wfl::map_formula_callable& variables) override;
private:
typed_formula<unsigned> x_, /**< The x coordinate of the text. */

View file

@ -31,6 +31,8 @@
#include <boost/range/adaptor/reversed.hpp>
#include <SDL.h>
/**
* @todo The items below are not implemented yet.
*
@ -532,16 +534,36 @@ void sdl_event_handler::activate()
void sdl_event_handler::draw()
{
// Don't display this event since it floods the screen
// DBG_GUI_E << "Firing " << DRAW << ".\n";
// Don't draw anything if we have no dispatcher.
if(dispatchers_.empty()) {
return;
}
CVideo& video = dynamic_cast<window&>(*dispatchers_.back()).video();
/**
* Clear the renderer before beginning the draw cycle.
*/
SDL_Renderer* renderer = *video.get_window();
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
/**
* @todo Need to evaluate which windows really to redraw.
*
* For now we use a hack, but would be nice to rewrite it for 1.9/1.11.
*/
for(auto dispatcher : dispatchers_)
{
dispatcher->fire(DRAW, dynamic_cast<widget&>(*dispatcher));
}
if(!dispatchers_.empty()) {
CVideo& video = dynamic_cast<window&>(*dispatchers_.back()).video();
video.flip();
}
// Finally, render the screen.
video.flip();
}
void sdl_event_handler::draw_everything()

View file

@ -425,12 +425,12 @@ void widget::populate_dirty_list(window& caller,
}
call_stack.push_back(this);
if(is_dirty_) {
//if(is_dirty_) {
caller.add_to_dirty_list(call_stack);
} else {
//} else {
// virtual function which only does something for container items.
child_populate_dirty_list(caller, call_stack);
}
// child_populate_dirty_list(caller, call_stack);
//}
}
void

View file

@ -583,22 +583,22 @@ int window::show(const bool restore, const unsigned auto_close_timeout)
suspend_drawing_ = true;
// restore area
if(restore_) {
SDL_Rect rect = get_rectangle();
sdl_blit(restorer_, 0, video_.getSurface(), &rect);
font::undraw_floating_labels(video_.getSurface());
}
// if(restore_) {
// SDL_Rect rect = get_rectangle();
// sdl_blit(restorer_, 0, video_.getSurface(), &rect);
// font::undraw_floating_labels(video_.getSurface());
// }
throw;
}
suspend_drawing_ = true;
// restore area
if(restore_) {
SDL_Rect rect = get_rectangle();
sdl_blit(restorer_, 0, video_.getSurface(), &rect);
font::undraw_floating_labels(video_.getSurface());
}
//if(restore_) {
// SDL_Rect rect = get_rectangle();
// sdl_blit(restorer_, 0, video_.getSurface(), &rect);
// font::undraw_floating_labels(video_.getSurface());
//}
if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
tb->interrupt_composition();
@ -609,12 +609,15 @@ int window::show(const bool restore, const unsigned auto_close_timeout)
void window::draw()
{
//const size_t start = SDL_GetTicks();
/***** ***** ***** ***** Init ***** ***** ***** *****/
// Prohibited from drawing?
if(suspend_drawing_) {
return;
}
// TODO: remove
surface& frame_buffer = video_.getSurface();
/***** ***** Layout and get dirty list ***** *****/
@ -622,33 +625,35 @@ void window::draw()
// Restore old surface. In the future this phase will not be needed
// since all will be redrawn when needed with dirty rects. Since that
// doesn't work yet we need to undraw the window.
if(restore_ && restorer_) {
SDL_Rect rect = get_rectangle();
sdl_blit(restorer_, 0, frame_buffer, &rect);
}
//if(restore_ && restorer_) {
// SDL_Rect rect = get_rectangle();
// sdl_blit(restorer_, 0, frame_buffer, &rect);
//}
layout();
// Get new surface for restoring
SDL_Rect rect = get_rectangle();
//SDL_Rect rect = get_rectangle();
// We want the labels underneath the window so draw them and use them
// as restore point.
if(is_toplevel_) {
font::draw_floating_labels(frame_buffer);
//font::draw_floating_labels(frame_buffer);
}
if(restore_) {
restorer_ = get_surface_portion(frame_buffer, rect);
//restorer_ = get_surface_portion(frame_buffer, rect);
}
// Need full redraw so only set ourselves dirty.
dirty_list_.emplace_back(1, this);
//dirty_list_.emplace_back(1, this);
need_layout_ = false;
} else {
// Let widgets update themselves, which might dirty some things.
layout_children();
#if 0
// Now find the widgets that are dirty.
std::vector<widget*> call_stack;
if(!new_widgets) {
@ -658,34 +663,34 @@ void window::draw()
dirty_list_.clear();
dirty_list_.emplace_back(1, this);
}
#endif
}
if (dirty_list_.empty()) {
consecutive_changed_frames_ = 0u;
return;
}
//if (dirty_list_.empty()) {
// consecutive_changed_frames_ = 0u;
// return;
//}
++consecutive_changed_frames_;
if(consecutive_changed_frames_ >= 100u && id_ == "title_screen") {
/* The title screen has changed in 100 consecutive frames, i.e. every
frame for two seconds. It looks like the screen is constantly changing
or at least marking widgets as dirty.
That's a severe problem. Every time the title screen changes, all
other GUI windows need to be fully redrawn, with huge CPU usage cost.
For that reason, this situation is a hard error. */
throw std::logic_error("The title screen is constantly changing, "
"which has a huge CPU usage cost. See the code comment.");
}
//++consecutive_changed_frames_;
//if(consecutive_changed_frames_ >= 100u && id_ == "title_screen") {
// /* The title screen has changed in 100 consecutive frames, i.e. every
// frame for two seconds. It looks like the screen is constantly changing
// or at least marking widgets as dirty.
// That's a severe problem. Every time the title screen changes, all
// other GUI windows need to be fully redrawn, with huge CPU usage cost.
// For that reason, this situation is a hard error. */
/ throw std::logic_error("The title screen is constantly changing, "
// "which has a huge CPU usage cost. See the code comment.");
//}
for(auto & item : dirty_list_)
{
assert(!item.empty());
const SDL_Rect dirty_rect
= new_widgets ? video().screen_area()
: item.back()->get_dirty_rectangle();
//const SDL_Rect dirty_rect
// = new_widgets ? video().screen_area()
// : item.back()->get_dirty_rectangle();
// For testing we disable the clipping rect and force the entire screen to
// update. This way an item rendered at the wrong place is directly visible.
@ -693,7 +698,7 @@ void window::draw()
dirty_list_.clear();
dirty_list_.emplace_back(1, this);
#else
clip_rect_setter clip(frame_buffer, &dirty_rect);
//clip_rect_setter clip(frame_buffer, &dirty_rect);
#endif
/*
@ -739,8 +744,8 @@ void window::draw()
// Restore.
if(restore_) {
SDL_Rect rect = get_rectangle();
sdl_blit(restorer_, 0, frame_buffer, &rect);
//SDL_Rect rect = get_rectangle();
//sdl_blit(restorer_, 0, frame_buffer, &rect);
}
// Background.
@ -771,8 +776,10 @@ void window::draw()
redraw_windows_on_top();
std::vector<widget*> call_stack;
populate_dirty_list(*this, call_stack);
assert(dirty_list_.empty());
//std::cerr << "draw took, " << (SDL_GetTicks() - start) << " ms" << std::endl;
//populate_dirty_list(*this, call_stack);
//assert(dirty_list_.empty());
if(callback_next_draw_ != nullptr) {
callback_next_draw_();
@ -782,12 +789,14 @@ void window::draw()
void window::undraw()
{
#if 0
if(restore_ && restorer_) {
SDL_Rect rect = get_rectangle();
sdl_blit(restorer_, 0, video_.getSurface(), &rect);
// Since the old area might be bigger as the new one, invalidate
// it.
}
#endif // 0
}
window::invalidate_layout_blocker::invalidate_layout_blocker(window& window)
@ -882,6 +891,7 @@ void window::remove_linked_widget(const std::string& id, const widget* wgt)
void window::layout()
{
std::cerr << "calling layout" << std::endl;
/***** Initialize. *****/
const auto conf = cast_config_to<window_definition>();