Skip to content

Commit

Permalink
GUI2/Canvas: refactor surface use out of image rendering
Browse files Browse the repository at this point in the history
The only real reason to keep using a surface was in order to have resize mode manipulations. I've now added
texture-only handling methods (the tiling one was borrowed from display.cpp's draw_background function).
  • Loading branch information
Vultraz committed Jul 31, 2017
1 parent a90bd36 commit 3403e4f
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 81 deletions.
123 changes: 47 additions & 76 deletions src/gui/core/canvas.cpp
Expand Up @@ -1027,7 +1027,6 @@ image_shape::image_shape(const config& cfg, wfl::action_function_symbol_table& f
, y_(cfg["y"])
, w_(cfg["w"])
, h_(cfg["h"])
, src_clip_()
, image_()
, image_name_(cfg["name"])
, resize_mode_(get_resize_mode(cfg["resize_mode"]))
Expand Down Expand Up @@ -1069,32 +1068,30 @@ void image_shape::draw(
return;
}

/*
* The locator might return a different surface for every call so we can't
* cache the output, also not if no formula is used.
*/
image_.assign(make_neutral_surface(image::get_image(image::locator(name))));
image_ = image::get_texture(name);

if(!image_) {
ERR_GUI_D << "Image: '" << name << "' not found and won't be drawn." << std::endl;
return;
}

assert(image_);
src_clip_ = {0, 0, image_->w, image_->h};
//
// Calculate dimensions and set WFL variables.
//
const texture::info info = image_.get_info();

wfl::map_formula_callable local_variables(variables);
local_variables.add("image_original_width", wfl::variant(image_->w));
local_variables.add("image_original_height", wfl::variant(image_->h));
local_variables.add("image_original_width", wfl::variant(info.w));
local_variables.add("image_original_height", wfl::variant(info.h));

unsigned w = w_(local_variables);
dimension_validation(w, name, "w");

unsigned h = h_(local_variables);
dimension_validation(h, name, "h");

local_variables.add("image_width", wfl::variant(w ? w : image_->w));
local_variables.add("image_height", wfl::variant(h ? h : image_->h));
local_variables.add("image_width", wfl::variant(w ? w : info.w));
local_variables.add("image_height", wfl::variant(h ? h : info.h));

const unsigned clip_x = x_(local_variables);
dimension_validation(clip_x, name, "x");
Expand All @@ -1108,84 +1105,58 @@ void image_shape::draw(
// Execute the provided actions for this context.
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 dst_clip = sdl::create_rect(clip_x, clip_y, 0, 0);
surface surf(nullptr);

// Test whether we need to scale and do the scaling if needed.
if(w || h) {
bool done = false;
bool stretch_image = (resize_mode_ == stretch) && (!!w ^ !!h);
if(!w) {
if(stretch_image) {
DBG_GUI_D << "Image: vertical stretch from " << image_->w << ','
<< image_->h << " to a height of " << h << ".\n";

// Textures are automatically scaled to size.
done = true;
}
w = image_->w;
}
//
// Copy image texture to canvas texture (which should be the current rendering target)/
//
CVideo& video = CVideo::get_singleton();

// Flip on the vertical axis - ie, a horizontal flip.
const bool mirror = vertical_mirror_(local_variables);

if(!h) {
if(stretch_image) {
DBG_GUI_D << "Image: horizontal stretch from " << image_->w
<< ',' << image_->h << " to a width of " << w
<< ".\n";
SDL_Rect dst_clip = sdl::create_rect(clip_x, clip_y, 0, 0);

// Textures are automatically scaled to size.
done = true;
}
h = image_->h;
}
const unsigned int dst_w = w ? w : info.w;
const unsigned int dst_h = h ? h : info.h;

if(!done) {
// TODO: remove stretch mode
switch(resize_mode_) {
case scale:
case stretch: {
DBG_GUI_D << "Image: scaling from " << info.w << ',' << info.h << " to " << w << ',' << h << std::endl;

if(resize_mode_ == tile) {
DBG_GUI_D << "Image: tiling from " << image_->w << ','
<< image_->h << " to " << w << ',' << h << ".\n";
dst_clip.w = dst_w;
dst_clip.h = dst_h;

// 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";
video.render_copy(image_, nullptr, &dst_clip, mirror, false);
break;
}

// TODO: convert to texture handling.
surf = tile_surface(image_, w, h, true);
} else {
if(resize_mode_ == stretch) {
ERR_GUI_D << "Image: failed to stretch image, "
"fall back to scaling.\n";
}
// TODO: move this to a more general place.
case tile: {
DBG_GUI_D << "Image: tiling from " << info.w << ',' << info.h << " to " << w << ',' << h << std::endl;

DBG_GUI_D << "Image: scaling from " << image_->w << ','
<< image_->h << " to " << w << ',' << h << ".\n";
const unsigned int w_count = static_cast<int>(std::ceil(static_cast<double>(dst_w) / static_cast<double>(info.w)));
const unsigned int h_count = static_cast<int>(std::ceil(static_cast<double>(dst_h) / static_cast<double>(info.h)));

// Textures are automatically scaled to size.
for(unsigned int xi = 0, current_x = dst_clip.x; xi < w_count; ++xi, current_x += info.w) {
for(unsigned int iy = 0, current_y = dst_clip.y; iy < h_count; ++iy, current_y += info.h) {
SDL_Rect area = sdl::create_rect(current_x, current_y, info.w, info.h);
video.render_copy(image_, nullptr, &area, mirror, false);
}
}
}

if(!surf) {
surf = image_;
break;
}

// Flip on the vertical axis - ie, a horizontal flip.
const bool mirror = vertical_mirror_(local_variables);

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.
*/
texture txt(surf);
case tile_center: {
DBG_GUI_D << "Image: tiling centrally from " << info.w << ',' << info.h << " to " << w << ',' << h << std::endl;
break;
}

CVideo::get_singleton().render_copy(txt, nullptr, &dst_clip, mirror, false);
default:
// TODO: text description
ERR_GUI_D << "Unknown resize mode option: " << resize_mode_ << std::endl;
}
}

image_shape::resize_mode image_shape::get_resize_mode(const std::string& resize_mode)
Expand Down
10 changes: 5 additions & 5 deletions src/gui/core/canvas_private.hpp
Expand Up @@ -204,11 +204,11 @@ class image_shape : public canvas::shape {
w_, /**< The width of the image. */
h_; /**< The height of the image. */

/** Contains the size of the image. */
SDL_Rect src_clip_;

/** The image is cached in this surface. */
surface image_;
/**
* The image texture. Since formulas may return different values each draw cycle, this is reassigned
* each time, so this is mostly here to avoid constantly allocating a new textures.
*/
texture image_;

/**
* Name of the image.
Expand Down

0 comments on commit 3403e4f

Please sign in to comment.