Change unit rendering to use IPF for image modification.

Previously it was doing all sorts of things using surface operations.

This is a stop-gap measure. If possible, these operations should
eventually moved into graphics shaders.
This commit is contained in:
Tommy 2022-06-05 00:31:06 +12:00
parent 3530d6f3ff
commit 8beb6ecfc9
4 changed files with 93 additions and 63 deletions

View file

@ -142,7 +142,7 @@ void arrow::draw_hex(const map_location& hex)
{
display* disp = display::get_singleton();
disp->render_image(disp->get_location_x(hex), disp->get_location_y(hex), layer_,
hex, image::get_image(symbols_map_[hex], image::SCALED_TO_ZOOM));
hex, symbols_map_[hex]);
}
}

View file

@ -1501,80 +1501,102 @@ void display::draw_text_in_hex(const map_location& loc,
drawing_buffer_add(layer, loc, {x, y, w, h}, text_surf);
}
//TODO: highdpi - convert this to use sdl::texture
void display::render_image(int x, int y, const display::drawing_layer drawing_layer,
const map_location& loc, surface image,
const map_location& loc, const image::locator& i_locator,
bool hreverse, bool greyscale, int32_t alpha,
color_t blendto, double blend_ratio, double submerged, bool vreverse)
{
if (image==nullptr)
// TODO: highdpi - w/h accessors
surface base_image_surface(image::get_surface(i_locator));
if (base_image_surface == nullptr) {
return;
SDL_Rect image_rect {x, y, image->w, image->h};
SDL_Rect clip_rect = map_area();
if (!sdl::rects_overlap(image_rect, clip_rect))
return;
surface surf(image);
if(hreverse) {
// TODO: highdpi - well this will get removed in due process anyway
surf = flip_surface(surf);
}
if(vreverse) {
surf = flop_surface(surf);
}
const int image_height = base_image_surface->h;
const int image_width = base_image_surface->w;
if(greyscale) {
surf = greyscale_image(surf);
}
if(blend_ratio != 0) {
surf = blend_surface(surf, blend_ratio, blendto);
}
if(alpha > floating_to_fixed_point(1.0)) {
surf = brighten_image(surf, alpha);
} else if(alpha != floating_to_fixed_point(1.0)) {
surf = surf.clone();
adjust_surface_alpha(surf, alpha);
}
if(surf == nullptr) {
ERR_DP << "surface lost..." << std::endl;
// TODO: highdpi - are x,y correct here?
SDL_Rect dest = scaled_to_zoom({x, y, image_width, image_height});
if (!sdl::rects_overlap(dest, map_area())) {
return;
}
// TODO: highdpi - fix. Probably move all this alpha stuff to image::, so it doesn't have to be recalculated.
texture tex(surf);
// For now, we add to the existing IPF modifications for the image.
std::string new_modifications;
if(submerged > 0.0) {
// divide the surface into 2 parts
const int submerge_height = std::max<int>(0, surf->h*(1.0-submerged));
const int depth = surf->h - submerge_height;
SDL_Rect srcrect {0, 0, surf->w, submerge_height};
SDL_Rect dest = {x, y, surf->w, submerge_height};
drawing_buffer_add(drawing_layer, loc, dest, tex, srcrect);
if (greyscale) {
new_modifications += "~GS()";
}
if(submerge_height != surf->h) {
//the lower part will be transparent
float alpha_base = 0.3f; // 30% alpha at surface of water
float alpha_delta = 0.015f; // lose 1.5% per pixel depth
alpha_delta *= zoom_ / DefaultZoom; // adjust with zoom
surf = submerge_alpha(surf, depth, alpha_base, alpha_delta);
srcrect.y = submerge_height;
srcrect.h = surf->h-submerge_height;
dest.y += submerge_height;
dest.h = srcrect.h;
drawing_buffer_add(drawing_layer, loc, dest, tex, srcrect);
if (blend_ratio > 0.0) {
new_modifications += "~BLEND(";
new_modifications += std::to_string(blendto.r);
new_modifications += ",";
new_modifications += std::to_string(blendto.g);
new_modifications += ",";
new_modifications += std::to_string(blendto.b);
new_modifications += ",";
// reduce blend_ratio precision to avoid caching too many options.
if (blend_ratio >= 1.0) {
new_modifications += "1.0";
} else {
new_modifications += "0.";
new_modifications += std::to_string(int(100*blend_ratio));
}
} else {
// simple blit
SDL_Rect dest = {x, y, surf->w, surf->h};
drawing_buffer_add(drawing_layer, loc, dest, tex);
new_modifications += ")";
}
// TODO: This is not what alpha means. Don't frivolously overload it.
// TODO: highdpi - perhaps this can be done by blitting twice, once in additive blend mode
// Note: this may not be identical to the original calculation,
// which was to multiply the RGB values by (alpha/255).
// But for now it looks close enough.
if(alpha > floating_to_fixed_point(1.0)) {
const std::string brighten_string = std::to_string((alpha - 255)/2);
new_modifications += "~CS(";
new_modifications += brighten_string;
new_modifications += ",";
new_modifications += brighten_string;
new_modifications += ",";
new_modifications += brighten_string;
new_modifications += ")";
}
// general formula for submerged alpha:
// if (y > WATERLINE)
// then min(max(alpha_mod, 0), 1) * alpha
// else alpha
// where alpha_mod = alpha_base - (y - WATERLINE) * alpha_delta
// formula variables: x, y, red, green, blue, alpha, width, height
// full WFL string:
// "~ADJUST_ALPHA(if(y>DL,clamp((AB-(y-DL)*AD),0,1)*alpha,alpha))"
// where DL = submersion line in pixels from top of image
// AB = base alpha proportion at submersion line (30%)
// AD = proportional alpha delta per pixel (1.5%)
if (submerged > 0.0) {
const int submersion_line = image_height * (1.0 - submerged);
const std::string sl_string = std::to_string(submersion_line);
new_modifications += "~ADJUST_ALPHA(if(y>";
new_modifications += sl_string;
new_modifications += ",clamp((0.3-(y-";
new_modifications += sl_string;
new_modifications += ")*0.015),0,1)*alpha,alpha))";
}
texture tex;
if (!new_modifications.empty()) {
const image::locator modified_locator(
i_locator.get_filename(),
i_locator.get_modifications() + new_modifications
);
tex = image::get_texture(modified_locator);
} else {
tex = image::get_texture(i_locator);
}
// TODO: highdpi - flipping and alpha modification need to be propagated from hreverse, vreverse, alpha
drawing_buffer_add(drawing_layer, loc, dest, tex);
}
void display::select_hex(map_location hex)
{
invalidate(selectedHex_);

View file

@ -267,6 +267,13 @@ public:
return static_cast<double>(zoom_) / static_cast<double>(game_config::tile_size);
}
/** Scale the width and height of a rect by the current zoom factor */
static SDL_Rect scaled_to_zoom(const SDL_Rect& r)
{
const double zf = get_zoom_factor();
return {r.x, r.y, int(r.w*zf), int(r.h*zf)};
}
/**
* given x,y co-ordinates of an onscreen pixel, will return the
* location of the hex that this pixel corresponds to.
@ -846,7 +853,7 @@ public:
* (presumably under water) and thus shouldn't be drawn
*/
void render_image(int x, int y, const display::drawing_layer drawing_layer,
const map_location& loc, surface image,
const map_location& loc, const image::locator& i_locator,
bool hreverse=false, bool greyscale=false,
int32_t alpha=floating_to_fixed_point(1.0), color_t blendto = {0,0,0},
double blend_ratio=0, double submerged=0.0,bool vreverse =false);

View file

@ -514,9 +514,10 @@ void unit_frame::redraw(const int frame_time, bool on_start_time, bool in_scope_
image_loc = image::locator(current_data.image, current_data.image_mod);
}
// TODO: highdpi - now only used for w/h. are these even necessary? If so add accessor to image:: to get only these without returning the whole surface.
surface image;
if(!image_loc.is_void() && !image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
image=image::get_image(image_loc, image::SCALED_TO_ZOOM);
image = image::get_image(image_loc, image::SCALED_TO_ZOOM);
}
const int d2 = display::get_singleton()->hex_size() / 2;
@ -555,7 +556,7 @@ void unit_frame::redraw(const int frame_time, bool on_start_time, bool in_scope_
display::get_singleton()->render_image(my_x, my_y,
static_cast<display::drawing_layer>(display::LAYER_UNIT_FIRST + current_data.drawing_layer),
src, image, facing_west, false,
src, image_loc, facing_west, false,
floating_to_fixed_point(current_data.highlight_ratio), current_data.blend_with ? *current_data.blend_with : color_t(),
current_data.blend_ratio, current_data.submerge, !facing_north);
}