#include "engine.h" engine_internal_state_t *internal_state = NULL; game_state_t *game_state = NULL; void mark_dirty_rect(int x, int y, int w, int h) { if (w <= 0 || h <= 0) { return; } // clip rectangle to the framebuffer if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (x + w > game_state->width) { w = game_state->width - x; } if (y + h > game_state->height) { h = game_state->height - y; } if (w <= 0 || h <= 0) { return; } SDL_Rect rect = { .x = x, .y = y, .w = w, .h = h }; if (!game_state->framebuffer_dirty) { game_state->dirty_rect = rect; game_state->framebuffer_dirty = true; return; } int left = game_state->dirty_rect.x < rect.x ? game_state->dirty_rect.x : rect.x; int top = game_state->dirty_rect.y < rect.y ? game_state->dirty_rect.y : rect.y; int old_right = game_state->dirty_rect.x + game_state->dirty_rect.w; int new_right = rect.x + rect.w; int old_bottom = game_state->dirty_rect.y + game_state->dirty_rect.h; int new_bottom = rect.y + rect.h; int right = old_right > new_right ? old_right : new_right; int bottom = old_bottom > new_bottom ? old_bottom : new_bottom; game_state->dirty_rect = (SDL_Rect) { .x = left, .y = top, .w = right - left, .h = bottom - top }; } bool upload_dirty_texture(void) { if (!game_state->framebuffer_dirty) { return true; } SDL_Texture *texture = internal_state->sdl_state->texture; SDL_Rect rect = game_state->dirty_rect; void *locked_pixels = NULL; int locked_pitch = 0; if (!SDL_LockTexture( texture, &rect, &locked_pixels, &locked_pitch )) { SDL_Log( "SDL_LockTexture failed: %s", SDL_GetError() ); return false; } const size_t source_pitch = (size_t)game_state->width * sizeof(uint32_t); const size_t row_bytes = (size_t)rect.w * sizeof(uint32_t); const uint8_t *source = (const uint8_t *)game_state->framebuffer + (size_t)rect.y * source_pitch + (size_t)rect.x * sizeof(uint32_t); uint8_t *destination = (uint8_t *)locked_pixels; for (int y = 0; y < rect.h; ++y) { memcpy( destination + (size_t)y * locked_pitch, source + (size_t)y * source_pitch, row_bytes ); } SDL_UnlockTexture(texture); game_state->framebuffer_dirty = false; return true; } void engine_start() { // main game loop while(game_state->is_running) { // poll events while (SDL_PollEvent(&game_state->event)) { internal_state->engine_poll_events(); } // call the user-defined framebuffer manipulation function internal_state->engine_update_framebuffer(); // update the texture with the modified framebuffer upload_dirty_texture(); SDL_RenderClear( internal_state->sdl_state->renderer ); SDL_RenderTexture( internal_state->sdl_state->renderer, internal_state->sdl_state->texture, NULL, NULL ); SDL_RenderPresent( internal_state->sdl_state->renderer ); } } void engine_stop() { engine_sdl_destroy(internal_state->sdl_state); free(internal_state); free(game_state); }