1 //============================================================================
2 //
3 //   SSSS    tt          lll  lll
4 //  SS  SS   tt           ll   ll
5 //  SS     tttttt  eeee   ll   ll   aaaa
6 //   SSSS    tt   ee  ee  ll   ll      aa
7 //      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
8 //  SS  SS   tt   ee      ll   ll  aa  aa
9 //   SSSS     ttt  eeeee llll llll  aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17 
18 #ifndef FRAMEBUFFER_HXX
19 #define FRAMEBUFFER_HXX
20 
21 #include <list>
22 
23 class OSystem;
24 class Console;
25 class Settings;
26 class FBSurface;
27 class TIASurface;
28 
29 #ifdef GUI_SUPPORT
30   #include "Font.hxx"
31 #endif
32 
33 #include "Rect.hxx"
34 #include "Variant.hxx"
35 #include "TIAConstants.hxx"
36 #include "FBBackend.hxx"
37 #include "FrameBufferConstants.hxx"
38 #include "EventHandlerConstants.hxx"
39 #include "VideoModeHandler.hxx"
40 #include "bspf.hxx"
41 
42 /**
43   This class encapsulates all video buffers and is the basis for the video
44   display in Stella.  The FBBackend object contained in this class is
45   platform-specific, and most rendering tasks are delegated to it.
46 
47   The TIA is drawn here, and all GUI elements (ala ScummVM, which are drawn
48   into FBSurfaces), are in turn drawn here as well.
49 
50   @author  Stephen Anthony
51 */
52 class FrameBuffer
53 {
54   public:
55     // Zoom level step interval
56     static constexpr float ZOOM_STEPS = 0.25;
57 
58     enum UpdateMode {
59       NONE = 0,
60       REDRAW = 1,
61       RERENDER = 2
62     };
63 
64   public:
65     explicit FrameBuffer(OSystem& osystem);
66     ~FrameBuffer();
67 
68     /**
69       Initialize the framebuffer object (set up the underlying hardware).
70       Throws an exception upon encountering any errors.
71     */
72     void initialize();
73 
74     /**
75       (Re)creates the framebuffer display.  This must be called before any
76       calls are made to derived methods.
77 
78       @param title   The title of the application / window
79       @param size    The dimensions of the display
80       @param honourHiDPI  If true, consult the 'hidpi' setting and enlarge
81                           the display size accordingly; if false, use the
82                           exact dimensions as given
83 
84       @return  Status of initialization (see FBInitStatus 'enum')
85     */
86     FBInitStatus createDisplay(const string& title, BufferType type,
87                                Common::Size size, bool honourHiDPI = true);
88 
89     /**
90       Updates the display, which depending on the current mode could mean
91       drawing the TIA, any pending menus, etc.
92     */
93     void update(UpdateMode mode = UpdateMode::NONE);
94 
95     /**
96       There is a dedicated update method for emulation mode.
97     */
98     void updateInEmulationMode(float framesPerSecond);
99 
100     /**
101       Set pending rendering flag.
102     */
setPendingRender()103     void setPendingRender() { myPendingRender = true; }
104 
105     /**
106       Shows a text message onscreen.
107 
108       @param message  The message to be shown
109       @param position Onscreen position for the message
110       @param force    Force showing this message, even if messages are disabled
111     */
112     void showTextMessage(const string& message,
113                          MessagePosition position = MessagePosition::BottomCenter,
114                          bool force = false);
115     /**
116       Shows a message with a gauge bar onscreen.
117 
118       @param message    The message to be shown
119       @param valueText  The value of the gauge bar as text
120       @param value      The gauge bar percentage
121       @param minValue   The minimal value of the gauge bar
122       @param maxValue   The maximal value of the gauge bar
123     */
124     void showGaugeMessage(const string& message, const string& valueText,
125                           float value, float minValue = 0.F, float maxValue = 100.F);
126 
127     bool messageShown() const;
128 
129     /**
130       Toggles showing or hiding framerate statistics.
131     */
132     void toggleFrameStats(bool toggle = true);
133 
134     /**
135       Shows a message containing frame statistics for the current frame.
136     */
137     void showFrameStats(bool enable);
138 
139     /**
140       Enable/disable any pending messages.  Disabled messages aren't removed
141       from the message queue; they're just not redrawn into the framebuffer.
142     */
143     void enableMessages(bool enable);
144 
145     /**
146       Reset 'Paused' display delay counter
147     */
148     void setPauseDelay();
149 
150     /**
151       Allocate a new surface.  The FrameBuffer class takes all responsibility
152       for freeing this surface (ie, other classes must not delete it directly).
153 
154       @param w      The requested width of the new surface
155       @param h      The requested height of the new surface
156       @param inter  Interpolation mode
157       @param data   If non-null, use the given data values as a static surface
158 
159       @return  A pointer to a valid surface object, or nullptr
160     */
161     shared_ptr<FBSurface> allocateSurface(
162       int w,
163       int h,
164       ScalingInterpolation inter = ScalingInterpolation::none,
165       const uInt32* data = nullptr
166     );
167 
168     /**
169       Deallocate a previously allocated surface.  If no such surface exists,
170       this method does nothing.
171 
172       @param surface  The surface to remove/deallocate
173     */
174     void deallocateSurface(shared_ptr<FBSurface> surface);
175 
176     /**
177       Set up the TIA/emulation palette.  Due to the way the palette is stored,
178       a call to this method implicitly calls setUIPalette() too.
179 
180       @param rgb_palette  The array of colors in R/G/B format
181     */
182     void setTIAPalette(const PaletteArray& rgb_palette);
183 
184     /**
185       Set palette for user interface.
186     */
187     void setUIPalette();
188 
189     /**
190       Returns the current dimensions of the framebuffer image.
191       Note that this will take into account the current scaling (if any)
192       as well as image 'centering'.
193     */
imageRect() const194     const Common::Rect& imageRect() const { return myActiveVidMode.imageR; }
195 
196     /**
197       Returns the current dimensions of the framebuffer window.
198       This is the entire area containing the framebuffer image as well as any
199       'unusable' area.
200     */
screenSize() const201     const Common::Size& screenSize() const { return myActiveVidMode.screenS; }
screenRect() const202     const Common::Rect& screenRect() const { return myActiveVidMode.screenR; }
203 
204     /**
205       Returns the dimensions of the mode specific users' desktop.
206     */
desktopSize(BufferType bufferType) const207     const Common::Size& desktopSize(BufferType bufferType) const {
208       return myDesktopSize[displayId(bufferType)];
209     }
210 
211     /**
212       Get the supported renderers for the video hardware.
213 
214       @return  An array of supported renderers
215     */
supportedRenderers() const216     const VariantList& supportedRenderers() const { return myRenderers; }
217 
218     /**
219       Get the minimum/maximum supported TIA zoom level (windowed mode)
220       for the framebuffer.
221     */
supportedTIAMinZoom() const222     float supportedTIAMinZoom() const { return myTIAMinZoom * hidpiScaleFactor(); }
supportedTIAMaxZoom() const223     float supportedTIAMaxZoom() const { return maxWindowZoom(); }
224 
225     /**
226       Get the TIA surface associated with the framebuffer.
227     */
tiaSurface() const228     TIASurface& tiaSurface() const { return *myTIASurface; }
229 
230     /**
231       Toggles between fullscreen and window mode.
232     */
233     void toggleFullscreen(bool toggle = true);
234 
235   #ifdef ADAPTABLE_REFRESH_SUPPORT
236     /**
237       Toggles between adapt fullscreen refresh rate on and off.
238     */
239     void toggleAdaptRefresh(bool toggle = true);
240   #endif
241 
242     /**
243       Changes the fullscreen overscan.
244 
245       @param direction  +1 indicates increase, -1 indicates decrease
246     */
247     void changeOverscan(int direction = +1);
248 
249     /**
250       This method is called when the user wants to switch to the previous/next
251       available TIA video mode.  In windowed mode, this typically means going
252       to the next/previous zoom level.  In fullscreen mode, this typically
253       means switching between normal aspect and fully filling the screen.
254 
255       @param direction  +1 indicates next mode, -1 indicates previous mode
256     */
257     void switchVideoMode(int direction = +1);
258 
259     /**
260       Sets the state of the cursor (hidden or grabbed) based on the
261       current mode.
262     */
263     void setCursorState();
264 
265     /**
266       Checks if mouse grabbing is allowed.
267     */
268     bool grabMouseAllowed();
269 
270     /**
271       Sets the use of grabmouse.
272     */
273     void enableGrabMouse(bool enable);
274 
275     /**
276       Toggles the use of grabmouse (only has effect in emulation mode).
277     */
278     void toggleGrabMouse(bool toggle = true);
279 
280     /**
281       Query whether grabmouse is enabled.
282     */
grabMouseEnabled() const283     bool grabMouseEnabled() const { return myGrabMouse; }
284 
285     /**
286       Informs the Framebuffer of a change in EventHandler state.
287     */
288     void stateChanged(EventHandlerState state);
289 
290     /**
291       Answer whether hidpi mode is allowed.  In this mode, all FBSurfaces
292       are scaled to 2x normal size.
293     */
hidpiAllowed() const294     bool hidpiAllowed() const { return myHiDPIAllowed[displayId()]; }
295 
296     /**
297       Answer whether hidpi mode is enabled.  In this mode, all FBSurfaces
298       are scaled to 2x normal size.
299     */
hidpiEnabled() const300     bool hidpiEnabled() const { return myHiDPIEnabled[displayId()]; }
hidpiScaleFactor() const301     uInt32 hidpiScaleFactor() const { return myHiDPIEnabled[displayId()] ? 2 : 1; }
302 
303     /**
304       This method should be called to save the current settings of all
305       its subsystems.  Note that the this may be called when the class
306       hasn't been fully initialized, so we first need to check if the
307       subsytems actually exist.
308     */
309     void saveConfig(Settings& settings) const;
310 
311   #ifdef GUI_SUPPORT
312     /**
313       Get the font object(s) of the framebuffer
314     */
font() const315     const GUI::Font& font() const { return *myFont; }
infoFont() const316     const GUI::Font& infoFont() const { return *myInfoFont; }
smallFont() const317     const GUI::Font& smallFont() const { return *mySmallFont; }
launcherFont() const318     const GUI::Font& launcherFont() const { return *myLauncherFont; }
319 
320     /**
321       Get the font description from the font name
322 
323       @param name  The settings name of the font
324 
325       @return  The description of the font
326     */
327     FontDesc getFontDesc(const string& name) const;
328   #endif
329 
330     /**
331       Shows or hides the cursor based on the given boolean value.
332     */
showCursor(bool show)333     void showCursor(bool show) { myBackend->showCursor(show); }
334 
335     /**
336       Answers if the display is currently in fullscreen mode.
337     */
fullScreen() const338     bool fullScreen() const { return myBackend->fullScreen(); }
339 
340     /**
341       This method is called to retrieve the R/G/B data from the given pixel.
342 
343       @param pixel  The pixel containing R/G/B data
344       @param r      The red component of the color
345       @param g      The green component of the color
346       @param b      The blue component of the color
347     */
getRGB(uInt32 pixel,uInt8 * r,uInt8 * g,uInt8 * b) const348     void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const {
349       myBackend->getRGB(pixel, r, g, b);
350     }
351 
352     /**
353       This method is called to map a given R/G/B triple to the screen palette.
354 
355       @param r  The red component of the color.
356       @param g  The green component of the color.
357       @param b  The blue component of the color.
358     */
mapRGB(uInt8 r,uInt8 g,uInt8 b) const359     uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const {
360       return myBackend->mapRGB(r, g, b);
361     }
362 
363     /**
364       This method is called to get the specified ARGB data from the viewable
365       FrameBuffer area.  Note that this isn't the same as any internal
366       surfaces that may be in use; it should return the actual data as it
367       is currently seen onscreen.
368 
369       @param buffer  The actual pixel data in ARGB8888 format
370       @param pitch   The pitch (in bytes) for the pixel data
371       @param rect    The bounding rectangle for the buffer
372     */
readPixels(uInt8 * buffer,uInt32 pitch,const Common::Rect & rect) const373     void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const {
374       myBackend->readPixels(buffer, pitch, rect);
375     }
376 
377     /**
378       Clear the framebuffer.
379     */
clear()380     void clear() { myBackend->clear(); }
381 
382     /**
383       Transform from window to renderer coordinates, x/y direction
384      */
scaleX(int x) const385     int scaleX(int x) const { return myBackend->scaleX(x); }
scaleY(int y) const386     int scaleY(int y) const { return myBackend->scaleY(y); }
387 
388   private:
389     /**
390       These methods are used to load/save position and display of the
391       current window.
392     */
393     string getPositionKey() const;
394     string getDisplayKey(BufferType bufferType = BufferType::None) const;
395     void saveCurrentWindowPosition() const;
396 
397     /**
398       Frees and reloads all surfaces that the framebuffer knows about.
399     */
400     void resetSurfaces();
401 
402   #ifdef GUI_SUPPORT
403     /**
404       Helps to create a basic message onscreen.
405 
406       @param message  The message to be shown
407       @param position Onscreen position for the message
408       @param force    Force showing this message, even if messages are disabled
409     */
410     void createMessage(const string& message, MessagePosition position,
411                        bool force = false);
412   #endif
413 
414     /**
415       Draw pending messages.
416 
417       @return  Indicates whether any changes actually occurred.
418     */
419     bool drawMessage();
420 
421     /**
422       Hide pending messages.
423     */
424     void hideMessage();
425 
426     /**
427       Draws the frame stats overlay.
428     */
429     void drawFrameStats(float framesPerSecond);
430 
431 
432     /**
433       Get the display used for the current mode.
434     */
435     int displayId(BufferType bufferType = BufferType::None) const;
436 
437     /**
438       Build an applicable video mode based on the current settings in
439       effect, whether TIA mode is active, etc.  Then tell the backend
440       to actually use the new mode.
441 
442       @return  Whether the operation succeeded or failed
443     */
444     FBInitStatus applyVideoMode();
445 
446     /**
447       Calculate the maximum level by which the base window can be zoomed and
448       still fit in the desktop screen.
449     */
450     float maxWindowZoom() const;
451 
452     /**
453       Enables/disables fullscreen mode.
454     */
455     void setFullscreen(bool enable);
456 
457   #ifdef GUI_SUPPORT
458     /**
459       Setup the UI fonts
460     */
461     void setupFonts();
462   #endif
463 
464   private:
465     // The parent system for the framebuffer
466     OSystem& myOSystem;
467 
468     // Backend used for all platform-specific graphics operations
469     unique_ptr<FBBackend> myBackend;
470 
471     // Indicates the number of times the framebuffer was initialized
472     uInt32 myInitializedCount{0};
473 
474     // Used to set intervals between messages while in pause mode
475     Int32 myPausedCount{0};
476 
477     // Maximum dimensions of the desktop area
478     // Note that this takes 'hidpi' mode into account, so in some cases
479     // it will be less than the absolute desktop size
480     vector<Common::Size> myDesktopSize;
481 
482     // Maximum absolute dimensions of the desktop area
483     vector<Common::Size> myAbsDesktopSize;
484 
485     // The resolution of the attached displays in fullscreen mode
486     // The primary display is typically the first in the array
487     // Windowed modes use myDesktopSize directly
488     vector<Common::Size> myFullscreenDisplays;
489 
490     // The resolution of the attached displays in windowed mode
491     vector<Common::Size> myWindowedDisplays;
492 
493     // Supported renderers
494     VariantList myRenderers;
495 
496     // Flag for pending render
497     bool myPendingRender{false};
498 
499     // The VideoModeHandler class takes responsibility for all video
500     // mode functionality
501     VideoModeHandler myVidModeHandler;
502     VideoModeHandler::Mode myActiveVidMode;
503 
504     // Type of the frame buffer
505     BufferType myBufferType{BufferType::None};
506 
507   #ifdef GUI_SUPPORT
508     // The font object to use for the normal in-game GUI
509     unique_ptr<GUI::Font> myFont;
510 
511     // The info font object to use for the normal in-game GUI
512     unique_ptr<GUI::Font> myInfoFont;
513 
514     // The font object to use when space is very limited
515     unique_ptr<GUI::Font> mySmallFont;
516 
517     // The font object to use for the ROM launcher
518     unique_ptr<GUI::Font> myLauncherFont;
519   #endif
520 
521     // The TIASurface class takes responsibility for TIA rendering
522     unique_ptr<TIASurface> myTIASurface;
523 
524     // Used for onscreen messages and frame statistics
525     // (scanline count and framerate)
526     struct Message {
527       string text;
528       int counter{-1};
529       int x{0}, y{0}, w{0}, h{0};
530       MessagePosition position{MessagePosition::BottomCenter};
531       ColorId color{kNone};
532       shared_ptr<FBSurface> surface;
533       bool enabled{false};
534       bool dirty{false};
535       bool showGauge{false};
536       float value{0.0F};
537       string valueText;
538     };
539     Message myMsg;
540     Message myStatsMsg;
541     bool myStatsEnabled{false};
542     uInt32 myLastScanlines{0};
543 
544     bool myGrabMouse{false};
545     vector<bool> myHiDPIAllowed;
546     vector<bool> myHiDPIEnabled;
547 
548     // Minimum TIA zoom level that can be used for this framebuffer
549     float myTIAMinZoom{2.F};
550 
551     // Holds a reference to all the surfaces that have been created
552     std::list<shared_ptr<FBSurface>> mySurfaceList;
553 
554     // Maximum message width [chars]
555     static constexpr int MESSAGE_WIDTH = 56;
556     // Maximum gauge bar width [chars]
557     static constexpr int GAUGEBAR_WIDTH = 30;
558 
559     FullPaletteArray myFullPalette;
560     // Holds UI palette data (for each variation)
561     static UIPaletteArray ourStandardUIPalette, ourClassicUIPalette,
562                           ourLightUIPalette, ourDarkUIPalette;
563 
564   private:
565     // Following constructors and assignment operators not supported
566     FrameBuffer() = delete;
567     FrameBuffer(const FrameBuffer&) = delete;
568     FrameBuffer(FrameBuffer&&) = delete;
569     FrameBuffer& operator=(const FrameBuffer&) = delete;
570     FrameBuffer& operator=(FrameBuffer&&) = delete;
571 };
572 
573 #endif
574