video: Add functions for immediate rendering of surfaces and textures.

This commit is contained in:
Tommy 2022-05-01 20:45:05 +12:00
parent 24f7d0d329
commit 581c6e6a93
3 changed files with 186 additions and 22 deletions

View file

@ -343,6 +343,7 @@ scripting/plugins/context.cpp
scripting/plugins/manager.cpp
sdl/point.cpp
sdl/input.cpp
sdl/texture.cpp
map_settings.cpp
side_filter.cpp
statistics.cpp

View file

@ -26,6 +26,7 @@
#include "sdl/utils.hpp"
#include "sdl/window.hpp"
#include "sdl/input.hpp"
#include "sdl/texture.hpp"
#ifdef TARGET_OS_OSX
#include "desktop/apple_video.hpp"
@ -164,13 +165,32 @@ void CVideo::video_event_handler::handle_window_event(const SDL_Event& event)
}
}
void CVideo::blit_surface(int x, int y, surface surf, SDL_Rect* srcrect, SDL_Rect* clip_rect)
void CVideo::blit_surface(const surface& surf, SDL_Rect* dst)
{
sdl_blit(surf, nullptr, getDrawingSurface(), dst);
render_low_res(dst);
}
void CVideo::blit_surface(int x, int y, const surface& surf)
{
SDL_Rect dst{x, y, 0, 0};
blit_surface(surf, &dst);
}
void CVideo::blit_surface(int x, int y, const surface& surf, const SDL_Rect* srcrect, const SDL_Rect* clip_rect)
{
surface& target(getDrawingSurface());
SDL_Rect dst{x, y, 0, 0};
const clip_rect_setter clip_setter(target, clip_rect, clip_rect != nullptr);
sdl_blit(surf, srcrect, target, &dst);
// dst gets updated by SDL_BlitSurface to reflect clipping etc.
render_low_res(&dst);
}
void CVideo::blit_texture(texture& tex, SDL_Rect* dst_rect, SDL_Rect* src_rect)
{
SDL_RenderCopy(*window.get(), tex, src_rect, dst_rect);
}
void CVideo::make_fake()
@ -276,6 +296,8 @@ void CVideo::update_framebuffer()
drawingSurface->w,
drawingSurface->h
);
// Always use alpha blending.
SDL_SetTextureBlendMode(drawing_texture_, SDL_BLENDMODE_BLEND);
}
// Update sizes for input conversion.
@ -402,6 +424,86 @@ void CVideo::delay(unsigned int milliseconds)
}
}
SDL_Rect CVideo::clip_to_draw_area(const SDL_Rect* r) const
{
if (r) {
return sdl::intersect_rects(*r, draw_area());
} else {
return draw_area();
}
}
SDL_Rect CVideo::to_output(const SDL_Rect& r) const
{
int s = output_size().x / get_width();
return {s*r.x, s*r.y, s*r.w, s*r.h};
}
void CVideo::render_low_res()
{
if (!drawingSurface) {
throw game::error("trying to render with no drawingSurface");
}
if (!drawing_texture_) {
throw game::error("trying to render with no drawing_texture_");
}
// Upload the drawing surface to the drawing texture.
void* pixels_out; // somewhere we can write raw pixel data to
int pitch; // the length of one row of pixels in bytes
SDL_LockTexture(drawing_texture_, nullptr, &pixels_out, &pitch);
if (pitch != drawingSurface->pitch) {
// If these don't match we are not gonna have a good time.
throw game::error("drawing surface and texture are incompatible");
}
// The pitch and pixel format should be the same,
// so we can just copy the whole chunk of memory directly.
size_t num_bytes = drawingSurface->h * pitch;
memcpy(pixels_out, drawingSurface->pixels, num_bytes);
SDL_UnlockTexture(drawing_texture_);
// Copy the whole drawing texture to the render target.
SDL_RenderCopy(*window.get(), drawing_texture_, nullptr, nullptr);
}
void CVideo::render_low_res(SDL_Rect* src_rect)
{
if (!drawingSurface) {
throw game::error("trying to render with no drawingSurface");
}
if (!drawing_texture_) {
throw game::error("trying to render with no drawing_texture_");
}
SDL_Rect r = clip_to_draw_area(src_rect);
uint8_t bytes_per_pixel = SDL_BYTESPERPIXEL(drawingSurface->format->format);
// Upload the drawing surface to the drawing texture.
void* pixels_out; // somewhere we can write raw pixel data to
int pitch; // the length of one row of pixels in bytes
SDL_LockTexture(drawing_texture_, &r, &pixels_out, &pitch);
if (pitch != drawingSurface->pitch) {
// If these don't match we are not gonna have a good time.
throw game::error("drawing surface and texture are incompatible");
}
char* pixels_in = static_cast<char*>(drawingSurface->pixels);
pixels_in += (r.x * bytes_per_pixel) + (r.y * pitch);
size_t row_bytes = r.w * bytes_per_pixel;
for (int y = 0; y < r.h; ++y) {
size_t row_offset = y * pitch;
memcpy(
static_cast<char*>(pixels_out) + row_offset,
pixels_in + row_offset,
row_bytes
);
}
SDL_UnlockTexture(drawing_texture_);
// Copy the drawing texture to the render target.
// SDL will adapt the destination rect coordinates automatically
// in high-dpi contexts, thanks to set_logical_size().
SDL_RenderCopy(*window.get(), drawing_texture_, &r, &r);
}
void CVideo::render_screen()
{
if(fake_screen_ || flip_locked_ > 0) {
@ -409,25 +511,11 @@ void CVideo::render_screen()
}
if (drawingSurface && drawing_texture_) {
// Upload the drawing surface to the drawing texture.
void* pixels_out; // somewhere we can write raw pixel data to
int pitch; // the length of one row of pixels in bytes
SDL_LockTexture(drawing_texture_, nullptr, &pixels_out, &pitch);
if (pitch != drawingSurface->pitch) {
// If these don't match we are not gonna have a good time.
throw game::error("drawing surface and texture are incompatible");
}
size_t num_bytes = drawingSurface->h * pitch;
memcpy(pixels_out, drawingSurface->pixels, num_bytes);
SDL_UnlockTexture(drawing_texture_);
//SDL_UpdateTexture(drawing_texture_, nullptr, drawingSurface->pixels, drawingSurface->pitch);
// Copy the drawing texture to the render target.
SDL_RenderCopy(*window.get(), drawing_texture_, nullptr, nullptr);
render_low_res();
}
if(window) {
// TODO: rename this to "present"
window->render();
}
}

View file

@ -24,6 +24,7 @@
#include <memory>
class surface;
class texture;
struct point;
struct SDL_Texture;
@ -181,6 +182,20 @@ public:
/** The current scale factor on High-DPI screens. */
std::pair<float, float> get_dpi_scale_factor() const;
/**
* Clip a rectangle to the drawing area.
*
* This does not change the original
* @param r The SDL_Rect to clip.
* @returns The new clipped SDL_Rect.
*/
SDL_Rect clip_to_draw_area(const SDL_Rect* r) const;
/**
* Convert coordinates in draw space to coordinates in render space.
*/
SDL_Rect to_output(const SDL_Rect& draw_space_rect) const;
/**
* Tests whether the given flags are currently set on the SDL window.
*
@ -210,17 +225,77 @@ public:
/***** ***** ***** ***** Drawing functions ***** ***** ****** *****/
/**
* Copies an area of a surface to the drawing surface.
* Draws a surface at the given location.
*
* The w and h members of dst are ignored, but will be updated
* to reflect the final draw extents including clipping.
*
* The surface will be rendered in game-native resolution,
* and all coordinates are given in this context.
*
* @param surf The surface to draw.
* @param dst Where to draw the surface. w and h are ignored, but will be updated to reflect the final draw extents including clipping.
*/
void blit_surface(const surface& surf, SDL_Rect* dst);
/**
* Draws a surface at the given coordinates.
*
* The surface will be rendered in game-native resolution,
* and all coordinates are given in this context.
*
* @param x The x coordinate at which to draw.
* @param y The y coordinate at which to draw.
* @param surf The surface to draw.
* @param srcrect The area of the surface to draw. This defaults to nullptr,
* which implies the entire thing.
* @param clip_rect The clipping rect. If not null, the surface will only be drawn
*/
void blit_surface(int x, int y, const surface& surf);
/**
* Draws an area of a surface at the given location.
*
* The surface will be rendered in game-native resolution,
* and all coordinates are given in this context.
*
* @param x The x coordinate at which to draw.
* @param y The y coordinate at which to draw.
* @param surf The surface to draw.
* @param srcrect The area of the surface to draw. If null, the entire surface is drawn.
* @param clip_rect The clipping area. If not null, the surface will only be drawn
* within the bounds of the given rectangle.
*/
void blit_surface(int x, int y, surface surf, SDL_Rect* srcrect = nullptr, SDL_Rect* clip_rect = nullptr);
void blit_surface(int x, int y, const surface& surf, const SDL_Rect* srcrect, const SDL_Rect* clip_rect);
/**
* Draws a texture, or part of a texture, at the given location.
*
* The portion of the texture to be drawn will be scaled to fill
* the target rectangle.
*
* This version takes coordinates in game-native resolution,
* which may be lower than the final output resolution in high-dpi
* contexts or if pixel scaling is used. The texture will be copied
* in high-resolution if possible.
*
* @param tex The texture to be copied / drawn.
* @param dstrect The target location to copy the texture to,
* in low-resolution game-native drawing coordinates.
* If null, this fills the entire render target.
* @param srcrect The portion of the texture to copy.
* If null, this copies the entire texture.
*/
void blit_texture(texture& tex, SDL_Rect* dstrect = nullptr, SDL_Rect* srcrect = nullptr);
/**
* Render a portion of the low-resolution drawing surface.
*
* @param src_rect The portion of the drawing surface to render, in draw-space coordinates. If null, the entire drawing surface is rendered.
*/
void render_low_res(SDL_Rect* src_rect);
/**
* Render the entire low-resolution drawing surface.
*/
void render_low_res();
/** Renders the screen. Should normally not be called directly! */
void render_screen();