From ea76b6988ccafaa6a4d4ed90f2489d0e49e1f180 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sat, 5 Sep 2015 09:32:30 -0400 Subject: Imported Upstream version 0.40.24 --- g_src/renderer_2d.hpp | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100755 g_src/renderer_2d.hpp (limited to 'g_src/renderer_2d.hpp') diff --git a/g_src/renderer_2d.hpp b/g_src/renderer_2d.hpp new file mode 100755 index 0000000..d088162 --- /dev/null +++ b/g_src/renderer_2d.hpp @@ -0,0 +1,352 @@ +#include "enabler.h" +#include "init.h" +#include "resize++.h" +#include "ttf_manager.hpp" + +#include +using namespace std; + +void report_error(const char*, const char*); + +class renderer_2d_base : public renderer { +protected: + SDL_Surface *screen; + map tile_cache; + int dispx, dispy, dimx, dimy; + // We may shrink or enlarge dispx/dispy in response to zoom requests. dispx/y_z are the + // size we actually display tiles at. + int dispx_z, dispy_z; + // Viewport origin + int origin_x, origin_y; + + SDL_Surface *tile_cache_lookup(texture_fullid &id, bool convert=true) { + map::iterator it = tile_cache.find(id); + if (it != tile_cache.end()) { + return it->second; + } else { + // Create the colorized texture + SDL_Surface *tex = enabler.textures.get_texture_data(id.texpos); + SDL_Surface *color; + color = SDL_CreateRGBSurface(SDL_SWSURFACE, + tex->w, tex->h, + tex->format->BitsPerPixel, + tex->format->Rmask, + tex->format->Gmask, + tex->format->Bmask, + 0); + if (!color) { + MessageBox (NULL, "Unable to create texture!", "Fatal error", MB_OK | MB_ICONEXCLAMATION); + abort(); + } + + // Fill it + Uint32 color_fgi = SDL_MapRGB(color->format, id.r*255, id.g*255, id.b*255); + Uint8 *color_fg = (Uint8*) &color_fgi; + Uint32 color_bgi = SDL_MapRGB(color->format, id.br*255, id.bg*255, id.bb*255); + Uint8 *color_bg = (Uint8*) &color_bgi; + SDL_LockSurface(tex); + SDL_LockSurface(color); + + Uint8 *pixel_src, *pixel_dst; + for (int y = 0; y < tex->h; y++) { + pixel_src = ((Uint8*)tex->pixels) + (y * tex->pitch); + pixel_dst = ((Uint8*)color->pixels) + (y * color->pitch); + for (int x = 0; x < tex->w; x++, pixel_src+=4, pixel_dst+=4) { + float alpha = pixel_src[3] / 255.0; + for (int c = 0; c < 3; c++) { + float fg = color_fg[c] / 255.0, bg = color_bg[c] / 255.0, tex = pixel_src[c] / 255.0; + pixel_dst[c] = ((alpha * (tex * fg)) + ((1 - alpha) * bg)) * 255; + } + } + } + + SDL_UnlockSurface(color); + SDL_UnlockSurface(tex); + + SDL_Surface *disp = convert ? + SDL_Resize(color, dispx_z, dispy_z) : // Convert to display format; deletes color + color; // color is not deleted, but we don't want it to be. + // Insert and return + tile_cache[id] = disp; + return disp; + } + } + + virtual bool init_video(int w, int h) { + // Get ourselves a 2D SDL window + Uint32 flags = init.display.flag.has_flag(INIT_DISPLAY_FLAG_2DHW) ? SDL_HWSURFACE : SDL_SWSURFACE; + flags |= init.display.flag.has_flag(INIT_DISPLAY_FLAG_2DASYNC) ? SDL_ASYNCBLIT : 0; + + // Set it up for windowed or fullscreen, depending. + if (enabler.is_fullscreen()) { + flags |= SDL_FULLSCREEN; + } else { + if (!init.display.flag.has_flag(INIT_DISPLAY_FLAG_NOT_RESIZABLE)) + flags |= SDL_RESIZABLE; + } + + // (Re)create the window + screen = SDL_SetVideoMode(w, h, 32, flags); + if (screen == NULL) cout << "INIT FAILED!" << endl; + + return screen != NULL; + } + +public: + list > ttfs_to_render; + + void update_tile(int x, int y) { + // Figure out where to blit + SDL_Rect dst; + dst.x = dispx_z * x + origin_x; + dst.y = dispy_z * y + origin_y; + // Read tiles from gps, create cached texture + Either id = screen_to_texid(x, y); + SDL_Surface *tex; + if (id.isL) { // Ordinary tile, cached here + tex = tile_cache_lookup(id.left); + // And blit. + SDL_BlitSurface(tex, NULL, screen, &dst); + } else { // TTF, cached in ttf_manager so no point in also caching here + tex = ttf_manager.get_texture(id.right); + // Blit later + ttfs_to_render.push_back(make_pair(tex, dst)); + } + } + + void update_all() { + SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0)); + for (int x = 0; x < gps.dimx; x++) + for (int y = 0; y < gps.dimy; y++) + update_tile(x, y); + } + + virtual void render() { + // Render the TTFs, which we left for last + for (auto it = ttfs_to_render.begin(); it != ttfs_to_render.end(); ++it) { + SDL_BlitSurface(it->first, NULL, screen, &it->second); + } + ttfs_to_render.clear(); + // And flip out. + SDL_Flip(screen); + } + + virtual ~renderer_2d_base() { + for (auto it = tile_cache.cbegin(); it != tile_cache.cend(); ++it) + SDL_FreeSurface(it->second); + for (auto it = ttfs_to_render.cbegin(); it != ttfs_to_render.cend(); ++it) + SDL_FreeSurface(it->first); + } + + void grid_resize(int w, int h) { + dimx = w; dimy = h; + // Only reallocate the grid if it actually changes + if (init.display.grid_x != dimx || init.display.grid_y != dimy) + gps_allocate(dimx, dimy); + // But always force a full display cycle + gps.force_full_display_count = 1; + enabler.flag |= ENABLERFLAG_RENDER; + } + + renderer_2d_base() { + zoom_steps = forced_steps = 0; + } + + int zoom_steps, forced_steps; + int natural_w, natural_h; + + void compute_forced_zoom() { + forced_steps = 0; + pair zoomed = compute_zoom(); + while (zoomed.first < MIN_GRID_X || zoomed.second < MIN_GRID_Y) { + forced_steps++; + zoomed = compute_zoom(); + } + while (zoomed.first > MAX_GRID_X || zoomed.second > MAX_GRID_Y) { + forced_steps--; + zoomed = compute_zoom(); + } + } + + pair compute_zoom(bool clamp = false) { + const int dispx = enabler.is_fullscreen() ? + init.font.large_font_dispx : + init.font.small_font_dispx; + const int dispy = enabler.is_fullscreen() ? + init.font.large_font_dispy : + init.font.small_font_dispy; + int w, h; + if (dispx < dispy) { + w = natural_w + zoom_steps + forced_steps; + h = double(natural_h) * (double(w) / double(natural_w)); + } else { + h = natural_h + zoom_steps + forced_steps; + w = double(natural_w) * (double(h) / double(natural_h)); + } + if (clamp) { + w = MIN(MAX(w, MIN_GRID_X), MAX_GRID_X); + h = MIN(MAX(h, MIN_GRID_Y), MAX_GRID_Y); + } + return make_pair(w,h); + } + + + void resize(int w, int h) { + // We've gotten resized.. first step is to reinitialize video + cout << "New window size: " << w << "x" << h << endl; + init_video(w, h); + dispx = enabler.is_fullscreen() ? + init.font.large_font_dispx : + init.font.small_font_dispx; + dispy = enabler.is_fullscreen() ? + init.font.large_font_dispy : + init.font.small_font_dispy; + cout << "Font size: " << dispx << "x" << dispy << endl; + // If grid size is currently overridden, we don't change it + if (enabler.overridden_grid_sizes.size() == 0) { + // (Re)calculate grid-size + dimx = MIN(MAX(w / dispx, MIN_GRID_X), MAX_GRID_X); + dimy = MIN(MAX(h / dispy, MIN_GRID_Y), MAX_GRID_Y); + cout << "Resizing grid to " << dimx << "x" << dimy << endl; + grid_resize(dimx, dimy); + } + // Calculate zoomed tile size + natural_w = MAX(w / dispx,1); + natural_h = MAX(h / dispy,1); + compute_forced_zoom(); + reshape(compute_zoom(true)); + cout << endl; + } + + void reshape(pair max_grid) { + int w = max_grid.first, + h = max_grid.second; + // Compute the largest tile size that will fit this grid into the window, roughly maintaining aspect ratio + double try_x = dispx, try_y = dispy; + try_x = screen->w / w; + try_y = MIN(try_x / dispx * dispy, screen->h / h); + try_x = MIN(try_x, try_y / dispy * dispx); + dispx_z = MAX(1,try_x); dispy_z = MAX(try_y,1); + cout << "Resizing font to " << dispx_z << "x" << dispy_z << endl; + // Remove now-obsolete tile catalog + for (map::iterator it = tile_cache.begin(); + it != tile_cache.end(); + ++it) + SDL_FreeSurface(it->second); + tile_cache.clear(); + // Recompute grid based on the new tile size + w = CLAMP(screen->w / dispx_z, MIN_GRID_X, MAX_GRID_X); + h = CLAMP(screen->h / dispy_z, MIN_GRID_Y, MAX_GRID_Y); + // Reset grid size +#ifdef DEBUG + cout << "Resizing grid to " << w << "x" << h << endl; +#endif + gps_allocate(w,h); + // Force redisplay + gps.force_full_display_count = 1; + // Calculate viewport origin, for centering + origin_x = (screen->w - dispx_z * w) / 2; + origin_y = (screen->h - dispy_z * h) / 2; + // Reset TTF rendering + ttf_manager.init(dispy_z, dispx_z); + } + +private: + + void set_fullscreen() { + if (enabler.is_fullscreen()) { + init.display.desired_windowed_width = screen->w; + init.display.desired_windowed_height = screen->h; + resize(init.display.desired_fullscreen_width, + init.display.desired_fullscreen_height); + } else { + resize(init.display.desired_windowed_width, init.display.desired_windowed_height); + } + } + + bool get_mouse_coords(int &x, int &y) { + int mouse_x, mouse_y; + SDL_GetMouseState(&mouse_x, &mouse_y); + mouse_x -= origin_x; mouse_y -= origin_y; + if (mouse_x < 0 || mouse_x >= dispx_z*dimx || + mouse_y < 0 || mouse_y >= dispy_z*dimy) + return false; + x = mouse_x / dispx_z; + y = mouse_y / dispy_z; + return true; + } + + void zoom(zoom_commands cmd) { + pair before = compute_zoom(true); + int before_steps = zoom_steps; + switch (cmd) { + case zoom_in: zoom_steps -= init.input.zoom_speed; break; + case zoom_out: zoom_steps += init.input.zoom_speed; break; + case zoom_reset: + zoom_steps = 0; + case zoom_resetgrid: + compute_forced_zoom(); + break; + } + pair after = compute_zoom(true); + if (after == before && (cmd == zoom_in || cmd == zoom_out)) + zoom_steps = before_steps; + else + reshape(after); + } + +}; + +class renderer_2d : public renderer_2d_base { +public: + renderer_2d() { + // Disable key repeat + SDL_EnableKeyRepeat(0, 0); + // Set window title/icon. + SDL_WM_SetCaption(GAME_TITLE_STRING, NULL); + SDL_Surface *icon = IMG_Load("data/art/icon.png"); + if (icon != NULL) { + SDL_WM_SetIcon(icon, NULL); + // The icon's surface doesn't get used past this point. + SDL_FreeSurface(icon); + } + + // Find the current desktop resolution if fullscreen resolution is auto + if (init.display.desired_fullscreen_width == 0 || + init.display.desired_fullscreen_height == 0) { + const struct SDL_VideoInfo *info = SDL_GetVideoInfo(); + init.display.desired_fullscreen_width = info->current_w; + init.display.desired_fullscreen_height = info->current_h; + } + + // Initialize our window + bool worked = init_video(enabler.is_fullscreen() ? + init.display.desired_fullscreen_width : + init.display.desired_windowed_width, + enabler.is_fullscreen() ? + init.display.desired_fullscreen_height : + init.display.desired_windowed_height); + + // Fallback to windowed mode if fullscreen fails + if (!worked && enabler.is_fullscreen()) { + enabler.fullscreen = false; + report_error("SDL initialization failure, trying windowed mode", SDL_GetError()); + worked = init_video(init.display.desired_windowed_width, + init.display.desired_windowed_height); + } + // Quit if windowed fails + if (!worked) { + report_error("SDL initialization failure", SDL_GetError()); + exit(EXIT_FAILURE); + } + } +}; + +class renderer_offscreen : public renderer_2d_base { + virtual bool init_video(int, int); +public: + virtual ~renderer_offscreen(); + renderer_offscreen(int, int); + void update_all(int, int); + void save_to_file(const string &file); +}; -- cgit v1.2.3