4 #include "ttf_manager.hpp"
9 void report_error(const char*, const char*);
11 class renderer_2d_base
: public renderer
{
14 map
<texture_fullid
, SDL_Surface
*> tile_cache
;
15 int dispx
, dispy
, dimx
, dimy
;
16 // We may shrink or enlarge dispx/dispy in response to zoom requests. dispx/y_z are the
17 // size we actually display tiles at.
20 int origin_x
, origin_y
;
22 SDL_Surface
*tile_cache_lookup(texture_fullid
&id
, bool convert
=true) {
23 map
<texture_fullid
, SDL_Surface
*>::iterator it
= tile_cache
.find(id
);
24 if (it
!= tile_cache
.end()) {
27 // Create the colorized texture
28 SDL_Surface
*tex
= enabler
.textures
.get_texture_data(id
.texpos
);
30 color
= SDL_CreateRGBSurface(SDL_SWSURFACE
,
32 tex
->format
->BitsPerPixel
,
38 MessageBox (NULL
, "Unable to create texture!", "Fatal error", MB_OK
| MB_ICONEXCLAMATION
);
43 Uint32 color_fgi
= SDL_MapRGB(color
->format
, id
.r
*255, id
.g
*255, id
.b
*255);
44 Uint8
*color_fg
= (Uint8
*) &color_fgi
;
45 Uint32 color_bgi
= SDL_MapRGB(color
->format
, id
.br
*255, id
.bg
*255, id
.bb
*255);
46 Uint8
*color_bg
= (Uint8
*) &color_bgi
;
48 SDL_LockSurface(color
);
50 Uint8
*pixel_src
, *pixel_dst
;
51 for (int y
= 0; y
< tex
->h
; y
++) {
52 pixel_src
= ((Uint8
*)tex
->pixels
) + (y
* tex
->pitch
);
53 pixel_dst
= ((Uint8
*)color
->pixels
) + (y
* color
->pitch
);
54 for (int x
= 0; x
< tex
->w
; x
++, pixel_src
+=4, pixel_dst
+=4) {
55 float alpha
= pixel_src
[3] / 255.0;
56 for (int c
= 0; c
< 3; c
++) {
57 float fg
= color_fg
[c
] / 255.0, bg
= color_bg
[c
] / 255.0, tex
= pixel_src
[c
] / 255.0;
58 pixel_dst
[c
] = ((alpha
* (tex
* fg
)) + ((1 - alpha
) * bg
)) * 255;
63 SDL_UnlockSurface(color
);
64 SDL_UnlockSurface(tex
);
66 SDL_Surface
*disp
= convert
?
67 SDL_Resize(color
, dispx_z
, dispy_z
) : // Convert to display format; deletes color
68 color
; // color is not deleted, but we don't want it to be.
70 tile_cache
[id
] = disp
;
75 virtual bool init_video(int w
, int h
) {
76 // Get ourselves a 2D SDL window
77 Uint32 flags
= init
.display
.flag
.has_flag(INIT_DISPLAY_FLAG_2DHW
) ? SDL_HWSURFACE
: SDL_SWSURFACE
;
78 flags
|= init
.display
.flag
.has_flag(INIT_DISPLAY_FLAG_2DASYNC
) ? SDL_ASYNCBLIT
: 0;
80 // Set it up for windowed or fullscreen, depending.
81 if (enabler
.is_fullscreen()) {
82 flags
|= SDL_FULLSCREEN
;
84 if (!init
.display
.flag
.has_flag(INIT_DISPLAY_FLAG_NOT_RESIZABLE
))
85 flags
|= SDL_RESIZABLE
;
88 // (Re)create the window
89 screen
= SDL_SetVideoMode(w
, h
, 32, flags
);
90 if (screen
== NULL
) cout
<< "INIT FAILED!" << endl
;
92 return screen
!= NULL
;
96 list
<pair
<SDL_Surface
*,SDL_Rect
> > ttfs_to_render
;
98 void update_tile(int x
, int y
) {
99 // Figure out where to blit
101 dst
.x
= dispx_z
* x
+ origin_x
;
102 dst
.y
= dispy_z
* y
+ origin_y
;
103 // Read tiles from gps, create cached texture
104 Either
<texture_fullid
,texture_ttfid
> id
= screen_to_texid(x
, y
);
106 if (id
.isL
) { // Ordinary tile, cached here
107 tex
= tile_cache_lookup(id
.left
);
109 SDL_BlitSurface(tex
, NULL
, screen
, &dst
);
110 } else { // TTF, cached in ttf_manager so no point in also caching here
111 tex
= ttf_manager
.get_texture(id
.right
);
113 ttfs_to_render
.push_back(make_pair(tex
, dst
));
118 SDL_FillRect(screen
, NULL
, SDL_MapRGB(screen
->format
, 0, 0, 0));
119 for (int x
= 0; x
< gps
.dimx
; x
++)
120 for (int y
= 0; y
< gps
.dimy
; y
++)
124 virtual void render() {
125 // Render the TTFs, which we left for last
126 for (auto it
= ttfs_to_render
.begin(); it
!= ttfs_to_render
.end(); ++it
) {
127 SDL_BlitSurface(it
->first
, NULL
, screen
, &it
->second
);
129 ttfs_to_render
.clear();
134 virtual ~renderer_2d_base() {
135 for (auto it
= tile_cache
.cbegin(); it
!= tile_cache
.cend(); ++it
)
136 SDL_FreeSurface(it
->second
);
137 for (auto it
= ttfs_to_render
.cbegin(); it
!= ttfs_to_render
.cend(); ++it
)
138 SDL_FreeSurface(it
->first
);
141 void grid_resize(int w
, int h
) {
143 // Only reallocate the grid if it actually changes
144 if (init
.display
.grid_x
!= dimx
|| init
.display
.grid_y
!= dimy
)
145 gps_allocate(dimx
, dimy
);
146 // But always force a full display cycle
147 gps
.force_full_display_count
= 1;
148 enabler
.flag
|= ENABLERFLAG_RENDER
;
152 zoom_steps
= forced_steps
= 0;
155 int zoom_steps
, forced_steps
;
156 int natural_w
, natural_h
;
158 void compute_forced_zoom() {
160 pair
<int,int> zoomed
= compute_zoom();
161 while (zoomed
.first
< MIN_GRID_X
|| zoomed
.second
< MIN_GRID_Y
) {
163 zoomed
= compute_zoom();
165 while (zoomed
.first
> MAX_GRID_X
|| zoomed
.second
> MAX_GRID_Y
) {
167 zoomed
= compute_zoom();
171 pair
<int,int> compute_zoom(bool clamp
= false) {
172 const int dispx
= enabler
.is_fullscreen() ?
173 init
.font
.large_font_dispx
:
174 init
.font
.small_font_dispx
;
175 const int dispy
= enabler
.is_fullscreen() ?
176 init
.font
.large_font_dispy
:
177 init
.font
.small_font_dispy
;
180 w
= natural_w
+ zoom_steps
+ forced_steps
;
181 h
= double(natural_h
) * (double(w
) / double(natural_w
));
183 h
= natural_h
+ zoom_steps
+ forced_steps
;
184 w
= double(natural_w
) * (double(h
) / double(natural_h
));
187 w
= MIN(MAX(w
, MIN_GRID_X
), MAX_GRID_X
);
188 h
= MIN(MAX(h
, MIN_GRID_Y
), MAX_GRID_Y
);
190 return make_pair(w
,h
);
194 void resize(int w
, int h
) {
195 // We've gotten resized.. first step is to reinitialize video
196 cout
<< "New window size: " << w
<< "x" << h
<< endl
;
198 dispx
= enabler
.is_fullscreen() ?
199 init
.font
.large_font_dispx
:
200 init
.font
.small_font_dispx
;
201 dispy
= enabler
.is_fullscreen() ?
202 init
.font
.large_font_dispy
:
203 init
.font
.small_font_dispy
;
204 cout
<< "Font size: " << dispx
<< "x" << dispy
<< endl
;
205 // If grid size is currently overridden, we don't change it
206 if (enabler
.overridden_grid_sizes
.size() == 0) {
207 // (Re)calculate grid-size
208 dimx
= MIN(MAX(w
/ dispx
, MIN_GRID_X
), MAX_GRID_X
);
209 dimy
= MIN(MAX(h
/ dispy
, MIN_GRID_Y
), MAX_GRID_Y
);
210 cout
<< "Resizing grid to " << dimx
<< "x" << dimy
<< endl
;
211 grid_resize(dimx
, dimy
);
213 // Calculate zoomed tile size
214 natural_w
= MAX(w
/ dispx
,1);
215 natural_h
= MAX(h
/ dispy
,1);
216 compute_forced_zoom();
217 reshape(compute_zoom(true));
221 void reshape(pair
<int,int> max_grid
) {
222 int w
= max_grid
.first
,
224 // Compute the largest tile size that will fit this grid into the window, roughly maintaining aspect ratio
225 double try_x
= dispx
, try_y
= dispy
;
226 try_x
= screen
->w
/ w
;
227 try_y
= MIN(try_x
/ dispx
* dispy
, screen
->h
/ h
);
228 try_x
= MIN(try_x
, try_y
/ dispy
* dispx
);
229 dispx_z
= MAX(1,try_x
); dispy_z
= MAX(try_y
,1);
230 cout
<< "Resizing font to " << dispx_z
<< "x" << dispy_z
<< endl
;
231 // Remove now-obsolete tile catalog
232 for (map
<texture_fullid
, SDL_Surface
*>::iterator it
= tile_cache
.begin();
233 it
!= tile_cache
.end();
235 SDL_FreeSurface(it
->second
);
237 // Recompute grid based on the new tile size
238 w
= CLAMP(screen
->w
/ dispx_z
, MIN_GRID_X
, MAX_GRID_X
);
239 h
= CLAMP(screen
->h
/ dispy_z
, MIN_GRID_Y
, MAX_GRID_Y
);
242 cout
<< "Resizing grid to " << w
<< "x" << h
<< endl
;
246 gps
.force_full_display_count
= 1;
247 // Calculate viewport origin, for centering
248 origin_x
= (screen
->w
- dispx_z
* w
) / 2;
249 origin_y
= (screen
->h
- dispy_z
* h
) / 2;
250 // Reset TTF rendering
251 ttf_manager
.init(dispy_z
, dispx_z
);
256 void set_fullscreen() {
257 if (enabler
.is_fullscreen()) {
258 init
.display
.desired_windowed_width
= screen
->w
;
259 init
.display
.desired_windowed_height
= screen
->h
;
260 resize(init
.display
.desired_fullscreen_width
,
261 init
.display
.desired_fullscreen_height
);
263 resize(init
.display
.desired_windowed_width
, init
.display
.desired_windowed_height
);
267 bool get_mouse_coords(int &x
, int &y
) {
268 int mouse_x
, mouse_y
;
269 SDL_GetMouseState(&mouse_x
, &mouse_y
);
270 mouse_x
-= origin_x
; mouse_y
-= origin_y
;
271 if (mouse_x
< 0 || mouse_x
>= dispx_z
*dimx
||
272 mouse_y
< 0 || mouse_y
>= dispy_z
*dimy
)
274 x
= mouse_x
/ dispx_z
;
275 y
= mouse_y
/ dispy_z
;
279 void zoom(zoom_commands cmd
) {
280 pair
<int,int> before
= compute_zoom(true);
281 int before_steps
= zoom_steps
;
283 case zoom_in
: zoom_steps
-= init
.input
.zoom_speed
; break;
284 case zoom_out
: zoom_steps
+= init
.input
.zoom_speed
; break;
288 compute_forced_zoom();
291 pair
<int,int> after
= compute_zoom(true);
292 if (after
== before
&& (cmd
== zoom_in
|| cmd
== zoom_out
))
293 zoom_steps
= before_steps
;
300 class renderer_2d
: public renderer_2d_base
{
303 // Disable key repeat
304 SDL_EnableKeyRepeat(0, 0);
305 // Set window title/icon.
306 SDL_WM_SetCaption(GAME_TITLE_STRING
, NULL
);
307 SDL_Surface
*icon
= IMG_Load("data/art/icon.png");
309 SDL_WM_SetIcon(icon
, NULL
);
310 // The icon's surface doesn't get used past this point.
311 SDL_FreeSurface(icon
);
314 // Find the current desktop resolution if fullscreen resolution is auto
315 if (init
.display
.desired_fullscreen_width
== 0 ||
316 init
.display
.desired_fullscreen_height
== 0) {
317 const struct SDL_VideoInfo
*info
= SDL_GetVideoInfo();
318 init
.display
.desired_fullscreen_width
= info
->current_w
;
319 init
.display
.desired_fullscreen_height
= info
->current_h
;
322 // Initialize our window
323 bool worked
= init_video(enabler
.is_fullscreen() ?
324 init
.display
.desired_fullscreen_width
:
325 init
.display
.desired_windowed_width
,
326 enabler
.is_fullscreen() ?
327 init
.display
.desired_fullscreen_height
:
328 init
.display
.desired_windowed_height
);
330 // Fallback to windowed mode if fullscreen fails
331 if (!worked
&& enabler
.is_fullscreen()) {
332 enabler
.fullscreen
= false;
333 report_error("SDL initialization failure, trying windowed mode", SDL_GetError());
334 worked
= init_video(init
.display
.desired_windowed_width
,
335 init
.display
.desired_windowed_height
);
337 // Quit if windowed fails
339 report_error("SDL initialization failure", SDL_GetError());
345 class renderer_offscreen
: public renderer_2d_base
{
346 virtual bool init_video(int, int);
348 virtual ~renderer_offscreen();
349 renderer_offscreen(int, int);
350 void update_all(int, int);
351 void save_to_file(const string
&file
);