1 // 2 // Overlay.h 3 // Audacity 4 // 5 // Created by Paul Licameli on 5/7/16. 6 // 7 // 8 9 #ifndef __AUDACITY_OVERLAY__ 10 #define __AUDACITY_OVERLAY__ 11 12 13 14 #include <utility> 15 16 class OverlayPanel; 17 class wxDC; 18 class wxRect; 19 class wxSize; 20 21 /* 22 <b>How Audacity Redisplay Works \n 23 Roger Dannenberg</b> \n 24 Oct 2010 \n 25 26 This is a brief guide to Audacity redisplay -- it may not be complete. It 27 is my attempt to understand the complicated graphics strategy. 28 29 One basic idea is that redrawing waveforms is rather slow, so Audacity 30 saves waveform images in bitmaps to make redrawing faster. In particular, 31 during audio playback (and recording), the vertical time indicator is 32 drawn over the waveform about 20 times per second. To avoid unnecessary 33 computation, the indicator is erased by copying a column of pixels from 34 a bitmap image of the waveform. Notice that this implies a two-stage 35 process: first, waveforms are drawn to the bitmap; then, the bitmap 36 (or pieces of it) are copied to the screen, perhaps along with other 37 graphics. 38 39 The bitmap is for the entire track panel, i.e. multiple tracks, and 40 includes things like the Gain and Pan slders to the left of the 41 waveform images. 42 43 The screen update uses a mixture of direct drawing and indirect paint 44 events. The "normal" way to update a graphical display is to call 45 the Refresh() method when something invalidates the screen. Later, the 46 system calls OnPaint(), which the application overrides to (re)draw the 47 screen. In wxWidgets, you can also draw directly to the screen without 48 calling Refresh() and without waiting for OnPaint() to be called. 49 50 I would expect there to be a 2-level invalidation scheme: Some changes 51 invalidate the bitmap, forcing a bitmap redraw *and* a screen redraw. 52 Other changes merely update the screen using pre-existing bitmaps. In 53 Audacity, the "2-level" invalidation works like this: Anything 54 that invalidates the bitmap calls TrackPanel::Refresh(), which 55 has an eraseBackground parameter. This flag says to redraw the 56 bitmap when OnPaint() is called. If eraseBackground is false, the 57 existing bitmap can be used for waveform images. Audacity also 58 draws directly to the screen to update the time indicator during 59 playback. To move the indicator, one column of pixels is drawn to 60 the screen to remove the indicator. Then the indicator is drawn at 61 a NEW time location. 62 63 Notice that the zoom guidelines, the focused track highlight, 64 and snap guidelines could be drawn directly to the screen rather than to 65 the bitmap, generally eliminating redraw work. 66 67 One problem is slider updates. Sliders are in the left area of the track 68 panel. They are not wxWindows like wxSliders, but instead are just drawn 69 on the TrackPanel. When slider state changes, *all* tracks do a full 70 refresh, including recomputing the backing store. It would make more sense 71 to just invalidate the region containing the slider. However, doing that 72 would require either incrementally updating the bitmap (not currently done), 73 or maintaining the sliders and other track info on the screen and not in 74 the bitmap. 75 76 */ 77 78 /* 79 PRL: above explanation was formerly in TrackArtist.cpp. 80 81 What it says is correct but also applies to other things than the play 82 indicator, such as the point editing cursor and the quick-play indicator 83 line. I abstracted out class Overlay to describe these drawables, and 84 cooperating class OverlayPanel that can manage multiple Overlays, figuring 85 out when the invalidation of one of them necessitates invalidation of other 86 overlapping ones. 87 88 The base class OverlayPanel, of TrackPanel, was also reused by the 89 AdornedRulerPanel. 90 91 */ 92 93 class AUDACITY_DLL_API Overlay 94 { 95 public: 96 Overlay() = default; 97 Overlay( const Overlay & ) PROHIBITED; 98 Overlay &operator=( const Overlay & ) PROHIBITED; 99 virtual ~Overlay() = 0; 100 101 ///\brief This number determines an ordering of overlays, so that those 102 /// with higher numbers overpaint those with lower numbers that intersect 103 virtual unsigned SequenceNumber() const = 0; 104 105 // nonvirtual wrapper 106 std::pair<wxRect, bool> GetRectangle(wxSize size); 107 108 // size passes the dimensions of the backing dc 109 // First member of pair is the rectangle that would be erased 110 // Second member of pair indicates whether the overlay is out of date 111 virtual std::pair<wxRect, bool> DoGetRectangle(wxSize size) = 0; 112 113 // Default implementation blits from backing store over GetRectangle().first 114 virtual void Erase(wxDC &dc, wxDC &src); 115 116 // Draw; dc.GetSize() tells you the total dimensions, and the panel is supplied 117 // as context 118 virtual void Draw(OverlayPanel &panel, wxDC &dc) = 0; 119 }; 120 121 #endif 122