1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 
15 //
16 // Graphics initialization
17 //
18 
19 #include <algorithm>
20 #include "ac/draw.h"
21 #include "debug/debugger.h"
22 #include "debug/out.h"
23 #include "gfx/ali3dexception.h"
24 #include "gfx/bitmap.h"
25 #include "gfx/gfxdriverfactory.h"
26 #include "gfx/gfxfilter.h"
27 #include "gfx/graphicsdriver.h"
28 #include "main/config.h"
29 #include "main/engine_setup.h"
30 #include "main/graphics_mode.h"
31 #include "main/main_allegro.h"
32 #include "platform/base/agsplatformdriver.h"
33 
34 // Don't try to figure out the window size on the mac because the port resizes itself.
35 #if defined(MAC_VERSION) || defined(ALLEGRO_SDL2) || defined(IOS_VERSION) || defined(PSP_VERSION) || defined(ANDROID_VERSION)
36 #define USE_SIMPLE_GFX_INIT
37 #endif
38 
39 using namespace AGS::Common;
40 using namespace AGS::Engine;
41 
42 extern int proper_exit;
43 extern AGSPlatformDriver *platform;
44 extern IGraphicsDriver *gfxDriver;
45 extern volatile int timerloop;
46 
47 
48 IGfxDriverFactory *GfxFactory = NULL;
49 
50 // Last saved fullscreen and windowed configs; they are used when switching
51 // between between fullscreen and windowed modes at runtime.
52 // If particular mode is modified, e.g. by script command, related config should be overwritten.
53 ActiveDisplaySetting SavedFullscreenSetting;
54 ActiveDisplaySetting SavedWindowedSetting;
55 // Current frame scaling setup
56 GameFrameSetup     CurFrameSetup;
57 // The game-to-screen transformation
58 PlaneScaling       GameScaling;
59 
60 
GameFrameSetup()61 GameFrameSetup::GameFrameSetup()
62     : ScaleDef(kFrame_IntScale)
63     , ScaleFactor(1)
64 {
65 }
66 
GameFrameSetup(FrameScaleDefinition def,int factor)67 GameFrameSetup::GameFrameSetup(FrameScaleDefinition def, int factor)
68     : ScaleDef(def)
69     , ScaleFactor(factor)
70 {
71 }
72 
IsValid() const73 bool GameFrameSetup::IsValid() const
74 {
75     return ScaleDef != kFrame_IntScale || ScaleFactor > 0;
76 }
77 
DisplayModeSetup()78 DisplayModeSetup::DisplayModeSetup()
79     : RefreshRate(0)
80     , VSync(false)
81     , Windowed(false)
82 {
83 }
84 
85 
get_desktop_size()86 Size get_desktop_size()
87 {
88     Size sz;
89     get_desktop_resolution(&sz.Width, &sz.Height);
90     return sz;
91 }
92 
get_max_display_size(bool windowed)93 Size get_max_display_size(bool windowed)
94 {
95     Size device_size = get_desktop_size();
96     if (windowed)
97         platform->ValidateWindowSize(device_size.Width, device_size.Height, false);
98     return device_size;
99 }
100 
create_gfx_driver(const String & gfx_driver_id)101 bool create_gfx_driver(const String &gfx_driver_id)
102 {
103     GfxFactory = GetGfxDriverFactory(gfx_driver_id);
104     if (!GfxFactory)
105     {
106         Debug::Printf(kDbgMsg_Error, "Failed to initialize %s graphics factory. Error: %s", gfx_driver_id.GetCStr(), get_allegro_error());
107         return false;
108     }
109     Debug::Printf("Using graphics factory: %s", gfx_driver_id.GetCStr());
110     gfxDriver = GfxFactory->GetDriver();
111     if (!gfxDriver)
112     {
113         Debug::Printf(kDbgMsg_Error, "Failed to create graphics driver. Error: %s", get_allegro_error());
114         return false;
115     }
116     Debug::Printf("Created graphics driver: %s", gfxDriver->GetDriverName());
117     return true;
118 }
119 
120 // Set requested graphics filter, or default filter if the requested one failed
graphics_mode_set_filter_any(const GfxFilterSetup & setup)121 bool graphics_mode_set_filter_any(const GfxFilterSetup &setup)
122 {
123     Debug::Printf("Requested gfx filter: %s", setup.UserRequest.GetCStr());
124     if (!graphics_mode_set_filter(setup.ID))
125     {
126         String def_filter = GfxFactory->GetDefaultFilterID();
127         if (def_filter.CompareNoCase(setup.ID) == 0)
128             return false;
129         Debug::Printf(kDbgMsg_Error, "Failed to apply gfx filter: %s; will try to use factory default filter '%s' instead",
130                 setup.UserRequest.GetCStr(), def_filter.GetCStr());
131         if (!graphics_mode_set_filter(def_filter))
132             return false;
133     }
134     Debug::Printf("Using gfx filter: %s", GfxFactory->GetDriver()->GetGraphicsFilter()->GetInfo().Id.GetCStr());
135     return true;
136 }
137 
find_nearest_supported_mode(const IGfxModeList & modes,const Size & wanted_size,const int color_depth,const Size * ratio_reference,const Size * upper_bound,DisplayMode & dm,int * mode_index)138 bool find_nearest_supported_mode(const IGfxModeList &modes, const Size &wanted_size, const int color_depth,
139                                  const Size *ratio_reference, const Size *upper_bound, DisplayMode &dm, int *mode_index)
140 {
141     uint32_t wanted_ratio = 0;
142     if (ratio_reference && !ratio_reference->IsNull())
143     {
144         wanted_ratio = (ratio_reference->Height << kShift) / ratio_reference->Width;
145     }
146 
147     int nearest_width = 0;
148     int nearest_height = 0;
149     int nearest_width_diff = 0;
150     int nearest_height_diff = 0;
151     int nearest_mode_index = -1;
152     int mode_count = modes.GetModeCount();
153     DisplayMode mode;
154     for (int i = 0; i < mode_count; ++i)
155     {
156         if (!modes.GetMode(i, mode))
157         {
158             continue;
159         }
160         if (mode.ColorDepth != color_depth)
161         {
162             continue;
163         }
164         if (wanted_ratio > 0)
165         {
166             uint32_t mode_ratio = (mode.Height << kShift) / mode.Width;
167             if (mode_ratio != wanted_ratio)
168             {
169                 continue;
170             }
171         }
172         if (upper_bound && (mode.Width > upper_bound->Width || mode.Height > upper_bound->Height))
173             continue;
174         if (mode.Width == wanted_size.Width && mode.Height == wanted_size.Height)
175         {
176             nearest_width = mode.Width;
177             nearest_height = mode.Height;
178             nearest_mode_index = i;
179             break;
180         }
181 
182         int diff_w = abs(wanted_size.Width - mode.Width);
183         int diff_h = abs(wanted_size.Height - mode.Height);
184         bool same_diff_w_higher = (diff_w == nearest_width_diff && nearest_width < wanted_size.Width);
185         bool same_diff_h_higher = (diff_h == nearest_height_diff && nearest_height < wanted_size.Height);
186 
187         if (nearest_width == 0 ||
188             (diff_w < nearest_width_diff || same_diff_w_higher) && diff_h <= nearest_height_diff ||
189             (diff_h < nearest_height_diff || same_diff_h_higher) && diff_w <= nearest_width_diff)
190         {
191             nearest_width = mode.Width;
192             nearest_width_diff = diff_w;
193             nearest_height = mode.Height;
194             nearest_height_diff = diff_h;
195             nearest_mode_index = i;
196         }
197     }
198 
199     if (nearest_width > 0 && nearest_height > 0)
200     {
201         dm = mode;
202         if (mode_index)
203             *mode_index = nearest_mode_index;
204         return true;
205     }
206     return false;
207 }
208 
set_game_frame_after_screen_size(const Size & game_size,const Size screen_size,const GameFrameSetup & setup)209 Size set_game_frame_after_screen_size(const Size &game_size, const Size screen_size, const GameFrameSetup &setup)
210 {
211     // Set game frame as native game resolution scaled by particular method
212     Size frame_size;
213     if (setup.ScaleDef == kFrame_MaxStretch)
214     {
215         frame_size = screen_size;
216     }
217     else if (setup.ScaleDef == kFrame_MaxProportional)
218     {
219         frame_size = ProportionalStretch(screen_size, game_size);
220     }
221     else
222     {
223         int scale;
224         if (setup.ScaleDef == kFrame_MaxRound)
225             scale = Math::Min((screen_size.Width / game_size.Width) << kShift,
226                               (screen_size.Height / game_size.Height) << kShift);
227         else
228             scale = convert_scaling_to_fp(setup.ScaleFactor);
229 
230         // Ensure scaling factors are sane
231         if (scale <= 0)
232             scale = kUnit;
233 
234         frame_size = Size((game_size.Width * scale) >> kShift, (game_size.Height * scale) >> kShift);
235         // If the scaled game size appear larger than the screen,
236         // use "proportional stretch" method instead
237         if (frame_size.ExceedsByAny(screen_size))
238             frame_size = ProportionalStretch(screen_size, game_size);
239     }
240     return frame_size;
241 }
242 
precalc_screen_size(const Size & game_size,const DisplayModeSetup & dm_setup,const GameFrameSetup & frame_setup)243 Size precalc_screen_size(const Size &game_size, const DisplayModeSetup &dm_setup, const GameFrameSetup &frame_setup)
244 {
245     Size screen_size, frame_size;
246     Size device_size = get_max_display_size(dm_setup.Windowed);
247 
248     // Set requested screen (window) size, depending on screen definition option
249     ScreenSizeSetup scsz = dm_setup.ScreenSize;
250     switch (scsz.SizeDef)
251     {
252     case kScreenDef_Explicit:
253         // Use resolution from user config
254         screen_size = scsz.Size;
255         if (screen_size.IsNull())
256         {
257             // If the configuration did not define proper screen size,
258             // use the scaled game size instead
259             frame_size = set_game_frame_after_screen_size(game_size, device_size, frame_setup);
260             if (screen_size.Width <= 0)
261                 screen_size.Width = frame_size.Width;
262             if (screen_size.Height <= 0)
263                 screen_size.Height = frame_size.Height;
264         }
265         break;
266     case kScreenDef_ByGameScaling:
267         // Use game frame (scaled game) size
268         frame_size = set_game_frame_after_screen_size(game_size, device_size, frame_setup);
269         screen_size = frame_size;
270         break;
271     case kScreenDef_MaxDisplay:
272         // Set as big as current device size
273         screen_size = device_size;
274         break;
275     }
276     return screen_size;
277 }
278 
279 // Find closest possible compatible display mode and initialize it
try_init_compatible_mode(const DisplayMode & dm,const bool match_device_ratio)280 bool try_init_compatible_mode(const DisplayMode &dm, const bool match_device_ratio)
281 {
282     const Size &screen_size = Size(dm.Width, dm.Height);
283     // Find nearest compatible mode and init that
284     Debug::Printf("Attempting to find nearest supported resolution for screen size %d x %d (%d-bit) %s",
285         dm.Width, dm.Height, dm.ColorDepth, dm.Windowed ? "windowed" : "fullscreen");
286     const Size device_size = get_max_display_size(dm.Windowed);
287     if (dm.Windowed)
288         Debug::Printf("Maximal allowed window size: %d x %d", device_size.Width, device_size.Height);
289     DisplayMode dm_compat = dm;
290 
291     std::auto_ptr<IGfxModeList> modes(gfxDriver->GetSupportedModeList(dm.ColorDepth));  // TODO: use unique_ptr when available
292 
293     // Windowed mode
294     if (dm.Windowed)
295     {
296         // If windowed mode, make the resolution stay in the generally supported limits
297         if (Size(dm.Width, dm.Height).ExceedsByAny(device_size))
298         {
299             dm_compat.Width = device_size.Width;
300             dm_compat.Height = device_size.Height;
301         }
302     }
303     // Fullscreen mode
304     else
305     {
306         // If told to find mode with aspect ratio matching current desktop resolution, then first
307         // try find matching one, and if failed then try any compatible one
308         bool mode_found = false;
309         if (modes.get())
310         {
311             if (match_device_ratio)
312                 mode_found = find_nearest_supported_mode(*modes.get(), screen_size, dm.ColorDepth, &device_size, NULL, dm_compat);
313             if (!mode_found)
314                 mode_found = find_nearest_supported_mode(*modes.get(), screen_size, dm.ColorDepth, NULL, NULL, dm_compat);
315         }
316         if (!mode_found)
317             Debug::Printf("Could not find compatible fullscreen mode. Will try to force-set mode requested by user and fallback to windowed mode if that fails.");
318         dm_compat.Vsync = dm.Vsync;
319         dm_compat.Windowed = false;
320     }
321 
322     bool result = graphics_mode_set_dm(dm_compat);
323     if (!result && dm.Windowed)
324     {
325         // When initializing windowed mode we could start with any random window size;
326         // if that did not work, try to find nearest supported mode, as with fullscreen mode,
327         // except refering to max window size as an upper bound
328         if (find_nearest_supported_mode(*modes.get(), screen_size, dm.ColorDepth, NULL, &device_size, dm_compat))
329         {
330             dm_compat.Vsync = dm.Vsync;
331             dm_compat.Windowed = true;
332             result = graphics_mode_set_dm(dm_compat);
333         }
334     }
335     return result;
336 }
337 
338 // Try to find and initialize compatible display mode as close to given setup as possible
try_init_mode_using_setup(const Size & game_size,const DisplayModeSetup & dm_setup,const int col_depth,const GameFrameSetup & frame_setup,const GfxFilterSetup & filter_setup)339 bool try_init_mode_using_setup(const Size &game_size, const DisplayModeSetup &dm_setup,
340                                const int col_depth, const GameFrameSetup &frame_setup,
341                                const GfxFilterSetup &filter_setup)
342 {
343     // We determine the requested size of the screen using setup options
344     const Size screen_size = precalc_screen_size(game_size, dm_setup, frame_setup);
345     DisplayMode dm(GraphicResolution(screen_size.Width, screen_size.Height, col_depth),
346                    dm_setup.Windowed, dm_setup.RefreshRate, dm_setup.VSync);
347     if (!try_init_compatible_mode(dm, dm_setup.ScreenSize.SizeDef == kScreenDef_Explicit ? false : dm_setup.ScreenSize.MatchDeviceRatio))
348         return false;
349 
350     // Set up native size and render frame
351     if (!graphics_mode_set_native_size(game_size) || !graphics_mode_set_render_frame(frame_setup))
352         return false;
353 
354     // Set up graphics filter
355     if (!graphics_mode_set_filter_any(filter_setup))
356         return false;
357     return true;
358 }
359 
log_out_driver_modes(const int color_depth)360 void log_out_driver_modes(const int color_depth)
361 {
362     IGfxModeList *modes = gfxDriver->GetSupportedModeList(color_depth);
363     if (!modes)
364     {
365         Debug::Printf(kDbgMsg_Error, "Couldn't get a list of supported resolutions for color depth = %d", color_depth);
366         return;
367     }
368     const int mode_count = modes->GetModeCount();
369     DisplayMode mode;
370     String mode_str;
371     for (int i = 0, in_str = 0; i < mode_count; ++i)
372     {
373         if (!modes->GetMode(i, mode) || mode.ColorDepth != color_depth)
374             continue;
375         mode_str.Append(String::FromFormat("%dx%d;", mode.Width, mode.Height));
376         if (++in_str % 8 == 0)
377             mode_str.Append("\n\t");
378     }
379     delete modes;
380 
381     String out_str = String::FromFormat("Supported gfx modes (%d-bit): ", color_depth);
382     if (!mode_str.IsEmpty())
383     {
384         out_str.Append("\n\t");
385         out_str.Append(mode_str);
386     }
387     else
388         out_str.Append("none");
389     Debug::Printf(out_str);
390 }
391 
392 // Create requested graphics driver and try to find and initialize compatible display mode as close to user setup as possible;
393 // if the given setup fails, gets default setup for the opposite type of mode (fullscreen/windowed) and tries that instead.
create_gfx_driver_and_init_mode_any(const String & gfx_driver_id,const Size & game_size,const DisplayModeSetup & dm_setup,const ColorDepthOption & color_depth,const GameFrameSetup & frame_setup,const GfxFilterSetup & filter_setup)394 bool create_gfx_driver_and_init_mode_any(const String &gfx_driver_id, const Size &game_size, const DisplayModeSetup &dm_setup,
395                                          const ColorDepthOption &color_depth, const GameFrameSetup &frame_setup, const GfxFilterSetup &filter_setup)
396 {
397     if (!graphics_mode_create_renderer(gfx_driver_id))
398         return false;
399 
400     const int use_col_depth =
401         color_depth.Forced ? color_depth.Bits : gfxDriver->GetDisplayDepthForNativeDepth(color_depth.Bits);
402     // Log out supported driver modes
403     log_out_driver_modes(use_col_depth);
404 
405     bool result = try_init_mode_using_setup(game_size, dm_setup, use_col_depth, frame_setup, filter_setup);
406     // Try windowed mode if fullscreen failed, and vice versa
407     if (!result && editor_debugging_enabled == 0)
408     {
409         // we need to clone from initial config, because not every parameter is set by graphics_mode_get_defaults()
410         DisplayModeSetup dm_setup_alt = dm_setup;
411         dm_setup_alt.Windowed = !dm_setup.Windowed;
412         GameFrameSetup frame_setup_alt;
413         graphics_mode_get_defaults(dm_setup_alt.Windowed, dm_setup_alt.ScreenSize, frame_setup_alt);
414         result = try_init_mode_using_setup(game_size, dm_setup_alt, use_col_depth, frame_setup_alt, filter_setup);
415     }
416     return result;
417 }
418 
simple_create_gfx_driver_and_init_mode(const String & gfx_driver_id,const Size & game_size,const DisplayModeSetup & dm_setup,const ColorDepthOption & color_depth,const GameFrameSetup & frame_setup,const GfxFilterSetup & filter_setup)419 bool simple_create_gfx_driver_and_init_mode(const String &gfx_driver_id,
420                                             const Size &game_size,
421                                             const DisplayModeSetup &dm_setup,
422                                             const ColorDepthOption &color_depth,
423                                             const GameFrameSetup &frame_setup,
424                                             const GfxFilterSetup &filter_setup)
425 {
426     if (!graphics_mode_create_renderer(gfx_driver_id)) { return false; }
427 
428     const int col_depth = gfxDriver->GetDisplayDepthForNativeDepth(color_depth.Bits);
429 
430     DisplayMode dm(GraphicResolution(game_size.Width, game_size.Height, col_depth),
431                    dm_setup.Windowed, dm_setup.RefreshRate, dm_setup.VSync);
432 
433     if (!graphics_mode_set_dm(dm)) { return false; }
434     if (!graphics_mode_set_native_size(game_size)) { return false; }
435     if (!graphics_mode_set_render_frame(frame_setup)) { return false; }
436     if (!graphics_mode_set_filter_any(filter_setup)) { return false; }
437 
438     return true;
439 }
440 
441 
display_gfx_mode_error(const Size & game_size,const ScreenSetup & setup,const int color_depth)442 void display_gfx_mode_error(const Size &game_size, const ScreenSetup &setup, const int color_depth)
443 {
444     proper_exit=1;
445     platform->FinishedUsingGraphicsMode();
446 
447     String main_error;
448     ScreenSizeSetup scsz = setup.DisplayMode.ScreenSize;
449     PGfxFilter filter = gfxDriver ? gfxDriver->GetGraphicsFilter() : PGfxFilter();
450     Size wanted_screen;
451     if (scsz.SizeDef == kScreenDef_Explicit)
452         main_error.Format("There was a problem initializing graphics mode %d x %d (%d-bit), or finding nearest compatible mode, with game size %d x %d and filter '%s'.",
453             scsz.Size.Width, scsz.Size.Height, color_depth, game_size.Width, game_size.Height, filter ? filter->GetInfo().Id.GetCStr() : "Undefined");
454     else
455         main_error.Format("There was a problem finding and/or creating valid graphics mode for game size %d x %d (%d-bit) and requested filter '%s'.",
456             game_size.Width, game_size.Height, color_depth, setup.Filter.UserRequest.IsEmpty() ? "Undefined" : setup.Filter.UserRequest.GetCStr());
457 
458     platform->DisplayAlert("%s\n"
459             "(Problem: '%s')\n"
460             "Try to correct the problem, or seek help from the AGS homepage."
461             "%s",
462             main_error.GetCStr(), get_allegro_error(), platform->GetGraphicsTroubleshootingText());
463 }
464 
graphics_mode_init_any(const Size game_size,const ScreenSetup & setup,const ColorDepthOption & color_depth)465 bool graphics_mode_init_any(const Size game_size, const ScreenSetup &setup, const ColorDepthOption &color_depth)
466 {
467     // Log out display information
468     Size device_size;
469     if (get_desktop_resolution(&device_size.Width, &device_size.Height) == 0)
470         Debug::Printf("Device display resolution: %d x %d", device_size.Width, device_size.Height);
471     else
472         Debug::Printf(kDbgMsg_Error, "Unable to obtain device resolution");
473 
474     const char *screen_sz_def_options[kNumScreenDef] = { "explicit", "scaling", "max" };
475     ScreenSizeSetup scsz = setup.DisplayMode.ScreenSize;
476     const bool ignore_device_ratio = setup.DisplayMode.Windowed || scsz.SizeDef == kScreenDef_Explicit;
477     GameFrameSetup gameframe = setup.DisplayMode.Windowed ? setup.WinGameFrame : setup.FsGameFrame;
478     const String scale_option = make_scaling_option(gameframe);
479     Debug::Printf(kDbgMsg_Init, "Graphic settings: driver: %s, windowed: %s, screen def: %s, screen size: %d x %d, match device ratio: %s, game scale: %s",
480         setup.DriverID.GetCStr(),
481         setup.DisplayMode.Windowed ? "yes" : "no", screen_sz_def_options[scsz.SizeDef],
482         scsz.Size.Width, scsz.Size.Height,
483         ignore_device_ratio ? "ignore" : (scsz.MatchDeviceRatio ? "yes" : "no"), scale_option.GetCStr());
484 
485     // Prepare the list of available gfx factories, having the one requested by user at first place
486     // TODO: make factory & driver IDs case-insensitive!
487     StringV ids;
488     GetGfxDriverFactoryNames(ids);
489     StringV::iterator it = ids.begin();
490     for (; it != ids.end(); ++it)
491     {
492         if (it->CompareNoCase(setup.DriverID) == 0) break;
493     }
494     if (it != ids.end())
495         std::rotate(ids.begin(), it, ids.end());
496     else
497         Debug::Printf(kDbgMsg_Error, "Requested graphics driver '%s' not found, will try existing drivers instead", setup.DriverID.GetCStr());
498 
499     // Try to create renderer and init gfx mode, choosing one factory at a time
500     bool result = false;
501     for (StringV::const_iterator it = ids.begin(); it != ids.end(); ++it)
502     {
503         result =
504 #ifdef USE_SIMPLE_GFX_INIT
505             simple_create_gfx_driver_and_init_mode
506 #else
507             create_gfx_driver_and_init_mode_any
508 #endif
509                 (*it, game_size, setup.DisplayMode, color_depth, gameframe, setup.Filter);
510 
511         if (result)
512             break;
513         graphics_mode_shutdown();
514     }
515     // If all possibilities failed, display error message and quit
516     if (!result)
517     {
518         display_gfx_mode_error(game_size, setup, color_depth.Bits);
519         return false;
520     }
521     return true;
522 }
523 
graphics_mode_get_last_setting(bool windowed)524 ActiveDisplaySetting graphics_mode_get_last_setting(bool windowed)
525 {
526     return windowed ? SavedWindowedSetting : SavedFullscreenSetting;
527 }
528 
529 bool graphics_mode_update_render_frame();
GfxDriverOnSurfaceUpdate()530 void GfxDriverOnSurfaceUpdate()
531 {
532     // Resize render frame using current scaling settings
533     graphics_mode_update_render_frame();
534     on_coordinates_scaling_changed();
535 }
536 
graphics_mode_create_renderer(const String & driver_id)537 bool graphics_mode_create_renderer(const String &driver_id)
538 {
539     if (!create_gfx_driver(driver_id))
540         return false;
541 
542     gfxDriver->SetCallbackOnInit(GfxDriverOnInitCallback);
543     gfxDriver->SetCallbackOnSurfaceUpdate(GfxDriverOnSurfaceUpdate);
544     // TODO: this is remains of the old code; find out if this is really
545     // the best time and place to set the tint method
546     gfxDriver->SetTintMethod(TintReColourise);
547     return true;
548 }
549 
graphics_mode_set_dm_any(const Size & game_size,const DisplayModeSetup & dm_setup,const ColorDepthOption & color_depth,const GameFrameSetup & frame_setup)550 bool graphics_mode_set_dm_any(const Size &game_size, const DisplayModeSetup &dm_setup,
551                               const ColorDepthOption &color_depth, const GameFrameSetup &frame_setup)
552 {
553     // We determine the requested size of the screen using setup options
554     const Size screen_size = precalc_screen_size(game_size, dm_setup, frame_setup);
555     DisplayMode dm(GraphicResolution(screen_size.Width, screen_size.Height, color_depth.Bits),
556                    dm_setup.Windowed, dm_setup.RefreshRate, dm_setup.VSync);
557     return try_init_compatible_mode(dm, dm_setup.ScreenSize.MatchDeviceRatio);
558 }
559 
graphics_mode_set_dm(const DisplayMode & dm)560 bool graphics_mode_set_dm(const DisplayMode &dm)
561 {
562     Debug::Printf("Attempt to switch gfx mode to %d x %d (%d-bit) %s",
563         dm.Width, dm.Height, dm.ColorDepth, dm.Windowed ? "windowed" : "fullscreen");
564 
565     // Tell Allegro new default bitmap color depth (must be done before set_gfx_mode)
566     // TODO: this is also done inside ALSoftwareGraphicsDriver implementation; can remove one?
567     set_color_depth(dm.ColorDepth);
568     // TODO: this is remains of the old code; find out what it means and do we
569     // need this if we are not using allegro software driver?
570     if (dm.RefreshRate >= 50)
571         request_refresh_rate(dm.RefreshRate);
572 
573     if (!gfxDriver->SetDisplayMode(dm, &timerloop))
574     {
575         Debug::Printf(kDbgMsg_Error, "Failed to init gfx mode. Error: %s", get_allegro_error());
576         return false;
577     }
578 
579     DisplayMode rdm = gfxDriver->GetDisplayMode();
580     if (rdm.Windowed)
581         SavedWindowedSetting.Dm = rdm;
582     else
583         SavedFullscreenSetting.Dm = rdm;
584     Debug::Printf("Succeeded. Using gfx mode %d x %d (%d-bit) %s",
585         rdm.Width, rdm.Height, rdm.ColorDepth, rdm.Windowed ? "windowed" : "fullscreen");
586     return true;
587 }
588 
graphics_mode_update_render_frame()589 bool graphics_mode_update_render_frame()
590 {
591     if (!gfxDriver || !gfxDriver->IsModeSet() || !gfxDriver->IsNativeSizeValid())
592         return false;
593 
594     DisplayMode dm = gfxDriver->GetDisplayMode();
595     Size screen_size = Size(dm.Width, dm.Height);
596     Size native_size = gfxDriver->GetNativeSize();
597     Size frame_size = set_game_frame_after_screen_size(native_size, screen_size, CurFrameSetup);
598     Rect render_frame = CenterInRect(RectWH(screen_size), RectWH(frame_size));
599 
600     if (!gfxDriver->SetRenderFrame(render_frame))
601     {
602         Debug::Printf(kDbgMsg_Error, "Failed to set render frame (%d, %d, %d, %d : %d x %d). Error: %s",
603             render_frame.Left, render_frame.Top, render_frame.Right, render_frame.Bottom,
604             render_frame.GetWidth(), render_frame.GetHeight(), get_allegro_error());
605         return false;
606     }
607 
608     Rect dst_rect = gfxDriver->GetRenderDestination();
609     Debug::Printf("Render frame set, render dest (%d, %d, %d, %d : %d x %d)",
610         dst_rect.Left, dst_rect.Top, dst_rect.Right, dst_rect.Bottom, dst_rect.GetWidth(), dst_rect.GetHeight());
611     // init game scaling transformation
612     GameScaling.Init(native_size, gfxDriver->GetRenderDestination());
613     return true;
614 }
615 
graphics_mode_set_native_size(const Size & native_size)616 bool graphics_mode_set_native_size(const Size &native_size)
617 {
618     if (!gfxDriver || native_size.IsNull())
619         return false;
620     if (!gfxDriver->SetNativeSize(native_size))
621         return false;
622     // if render frame translation was already set, then update it with new native size
623     if (gfxDriver->IsRenderFrameValid())
624         graphics_mode_update_render_frame();
625     return true;
626 }
627 
graphics_mode_get_render_frame()628 GameFrameSetup graphics_mode_get_render_frame()
629 {
630     return CurFrameSetup;
631 }
632 
graphics_mode_set_render_frame(const GameFrameSetup & frame_setup)633 bool graphics_mode_set_render_frame(const GameFrameSetup &frame_setup)
634 {
635     if (!frame_setup.IsValid())
636         return false;
637     CurFrameSetup = frame_setup;
638     if (gfxDriver->GetDisplayMode().Windowed)
639         SavedWindowedSetting.FrameSetup = frame_setup;
640     else
641         SavedFullscreenSetting.FrameSetup = frame_setup;
642     graphics_mode_update_render_frame();
643     return true;
644 }
645 
graphics_mode_set_filter(const String & filter_id)646 bool graphics_mode_set_filter(const String &filter_id)
647 {
648     if (!GfxFactory)
649         return false;
650 
651     String filter_error;
652     PGfxFilter filter = GfxFactory->SetFilter(filter_id, filter_error);
653     if (!filter)
654     {
655         Debug::Printf(kDbgMsg_Error, "Unable to set graphics filter '%s'. Error: %s", filter_id.GetCStr(), filter_error.GetCStr());
656         return false;
657     }
658     Rect filter_rect  = filter->GetDestination();
659     Debug::Printf("Graphics filter set: '%s', filter dest (%d, %d, %d, %d : %d x %d)", filter->GetInfo().Id.GetCStr(),
660         filter_rect.Left, filter_rect.Top, filter_rect.Right, filter_rect.Bottom, filter_rect.GetWidth(), filter_rect.GetHeight());
661     return true;
662 }
663 
graphics_mode_shutdown()664 void graphics_mode_shutdown()
665 {
666     if (GfxFactory)
667         GfxFactory->Shutdown();
668     GfxFactory = NULL;
669     gfxDriver = NULL;
670 
671     // Tell Allegro that we are no longer in graphics mode
672     set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
673 }
674