diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index be3d37a98..766e7d16f 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -21,107 +21,6 @@ Roger Dannenberg \n Oct 2010 \n -This is a brief guide to Audacity redisplay -- it may not be complete. It -is my attempt to understand the complicated graphics strategy. - -One basic idea is that redrawing waveforms is rather slow, so Audacity -saves waveform images in bitmaps to make redrawing faster. In particular, -during audio playback (and recording), the vertical time indicator is -drawn over the waveform about 20 times per second. To avoid unnecessary -computation, the indicator is erased by copying a column of pixels from -a bitmap image of the waveform. Notice that this implies a two-stage -process: first, waveforms are drawn to the bitmp; then, the bitmap -(or pieces of it) are copied to the screen, perhaps along with other -graphics. - -The bitmap is for the entire track panel, i.e. multiple tracks, and -includes things like the Gain and Pan slders to the left of the -waveform images. - -The screen update uses a mixture of direct drawing and indirect paint -events. The "normal" way to update a graphical display is to call -the Refresh() method when something invalidates the screen. Later, the -system calls OnPaint(), which the application overrides to (re)draw the -screen. In wxWidgets, you can also draw directly to the screen without -calling Refresh() and without waiting for OnPaint() to be called. - -I would expect there to be a 2-level invalidation scheme: Some changes -invalidate the bitmap, forcing a bitmap redraw *and* a screen redraw. -Other changes merely update the screen using pre-existing bitmaps. In -Audacity, the "2-level" invalidation works like this: Anything -that invalidates the bitmap calls TrackPanel::Refresh(), which -has an eraseBackground parameter. This flag says to redraw the -bitmap when OnPaint() is called. If eraseBackground is false, the -existing bitmap can be used for waveform images. Audacity also -draws directly to the screen to update the time indicator during -playback. To move the indicator, one column of pixels is drawn to -the screen to remove the indicator. Then the indicator is drawn at -a NEW time location. - -The track panel consists of many components. The tree of calls that -update the bitmap looks like this: - -\code -TrackPanel::DrawTracks(), calls - TrackArtist::DrawTracks(); - TrackPanel::DrawEverythingElse(); - for each track, - TrackPanel::DrawOutside(); - TrackPanel::DrawOutsideOfTrack(); - TrackPanel::DrawBordersAroundTrack(); - TrackPanel::DrawShadow(); - TrackInfo::DrawBordersWithin(); - various TrackInfo sliders and buttons - TrackArtist::DrawVRuler(); - TrackPanel::DrawZooming(); - draws horizontal dashed lines during zoom-drag - TrackPanel::HighlightFocusedTrack(); - draws yellow highlight on selected track - draw snap guidelines if any -\endcode - -After drawing the bitmap and blitting the bitmap to the screen, -the following calls are (sometimes) made. To keep track of what has -been drawn on screen over the bitmap images, -\li \c mLastCursor is the position of the vertical line representing sel0, - the selected time position -\li \c mLastIndicator is the position of the moving vertical line during - playback - -\code -TrackPanel::DoDrawIndicator(); - copy pixel column from bitmap to screen to erase indicator line - TrackPanel::DoDrawCursor(); [if mLastCursor == mLastIndicator] - TrackPanel::DisplaySelection(); - AdornedRulerPanel::DrawIndicator(); [not part of TrackPanel graphics] - draw indicator on each track -TrackPanel::DoDrawCursor(); - draw cursor on each track [at selectedRegion.t0()] - AdornedRulerPanel::DrawCursor(); [not part of TrackPanel graphics] - TrackPanel::DisplaySelection(); -\endcode - -To move the indicator, TrackPanel::OnTimer() calls the following, using -a drawing context (DC) for the screen. (Refresh is not called to create -an OnPaint event. Instead, drawing is direct to the screen.) -\code -TrackPanel::DrawIndicator(); - TrackPanel::DoDrawIndicator(); -\endcode - -Notice that TrackPanel::DrawZooming(), TrackPanel::HighlightFocusedTrack(), -and snap guidelines could be drawn directly to the screen rather than to -the bitmap, generally eliminating redraw work. - -One problem is slider udpates. Sliders are in the left area of the track -panel. They are not wxWindows like wxSliders, but instead are just drawn -on the TrackPanel. When slider state changes, *all* tracks do a full -refresh, including recomputing the backing store. It would make more sense -to just invalidate the region containing the slider. However, doing that -would require either incrementally updating the bitmap (not currently done), -or maintaining the sliders and other track info on the screen and not in -the bitmap. - In my opinion, the bitmap should contain only the waveform, note, and label images along with gray selection highlights. The track info (sliders, buttons, title, etc.), track selection highlight, cursor, and diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 7ee00e702..0cb9b8785 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -11,25 +11,10 @@ ********************************************************************//*! -\todo - Refactoring of the TrackPanel, possibly as described - in \ref TrackPanelRefactor - -*//*****************************************************************//*! - \file TrackPanel.cpp \brief Implements TrackPanel and TrackInfo. - TrackPanel.cpp is currently some of the worst code in Audacity. - It's not really unreadable, there's just way too much stuff in this - one file. Rather than apply a quick fix, the long-term plan - is to create a GUITrack class that knows how to draw itself - and handle events. Then this class just helps coordinate - between tracks. - - Plans under discussion are described in \ref TrackPanelRefactor - *//********************************************************************/ // Documentation: Rather than have a lengthy \todo section, having @@ -65,9 +50,6 @@ It has the menus, pan and gain controls displayed in it. So "Info" is somewhat a misnomer. Should possibly be "TrackControls". - TrackPanel and not TrackInfo takes care of the functionality for - each of the buttons in that panel. - In its current implementation TrackInfo is not derived from a wxWindow. Following the original coding style, it has been coded as a 'flyweight' class, which is passed @@ -78,72 +60,10 @@ *//**************************************************************//** -\class TrackPanelListener -\brief A now badly named class which is used to give access to a -subset of the TrackPanel methods from all over the place. - -*//**************************************************************//** - -\class TrackList -\brief A list of TrackListNode items. - -*//**************************************************************//** - -\class TrackListNode -\brief Used by TrackList, points to a Track. - -*//**************************************************************//** - \class TrackPanel::AudacityTimer -\brief Timer class dedicated to infomring the TrackPanel that it +\brief Timer class dedicated to informing the TrackPanel that it is time to refresh some aspect of the screen. -*//*****************************************************************//** - -\page TrackPanelRefactor Track Panel Refactor -\brief Planned refactoring of TrackPanel.cpp - - - Move menus from current TrackPanel into TrackInfo. - - Convert TrackInfo from 'flyweight' to heavyweight. - - Split GuiStereoTrack and GuiWaveTrack out from TrackPanel. - - JKC: Incremental refactoring started April/2003 - - Possibly aiming for Gui classes something like this - it's under - discussion: - -
-   +----------------------------------------------------+
-   |      AdornedRulerPanel                             |
-   +----------------------------------------------------+
-   +----------------------------------------------------+
-   |+------------+ +-----------------------------------+|
-   ||            | | (L)  GuiWaveTrack                 ||
-   || TrackInfo  | +-----------------------------------+|
-   ||            | +-----------------------------------+|
-   ||            | | (R)  GuiWaveTrack                 ||
-   |+------------+ +-----------------------------------+|
-   +-------- GuiStereoTrack ----------------------------+
-   +----------------------------------------------------+
-   |+------------+ +-----------------------------------+|
-   ||            | | (L)  GuiWaveTrack                 ||
-   || TrackInfo  | +-----------------------------------+|
-   ||            | +-----------------------------------+|
-   ||            | | (R)  GuiWaveTrack                 ||
-   |+------------+ +-----------------------------------+|
-   +-------- GuiStereoTrack ----------------------------+
-
- - With the whole lot sitting in a TrackPanel which forwards - events to the sub objects. - - The GuiStereoTrack class will do the special logic for - Stereo channel grouping. - - The precise names of the classes are subject to revision. - Have deliberately not created NEW files for the NEW classes - such as AdornedRulerPanel and TrackInfo - yet. - *//*****************************************************************/ #include "Audacity.h" @@ -184,10 +104,11 @@ wxDEFINE_EVENT(EVT_TRACK_PANEL_TIMER, wxCommandEvent); /* This is a diagram of TrackPanel's division of one (non-stereo) track rectangle. -Total height equals Track::GetHeight()'s value. Total width is the wxWindow's width. -Each charater that is not . represents one pixel. +Total height equals Track::GetHeight()'s value. Total width is the wxWindow's +width. Each charater that is not . represents one pixel. -Inset space of this track, and top inset of the next track, are used to draw the focus highlight. +Inset space of this track, and top inset of the next track, are used to draw the +focus highlight. Top inset of the right channel of a stereo track, and bottom shadow line of the left channel, are used for the channel separator. @@ -196,10 +117,11 @@ left channel, are used for the channel separator. shadow plus border (right and bottom). TrackInfo::GetTrackInfoWidth() == GetVRulerOffset() -counts columns from the left edge up to and including controls, and is a constant. +counts columns from the left edge up to and including controls, and is a +constant. -GetVRulerWidth() is variable -- all tracks have the same ruler width at any time, -but that width may be adjusted when tracks change their vertical scales. +GetVRulerWidth() is variable -- all tracks have the same ruler width at any +time, but that width may be adjusted when tracks change their vertical scales. GetLabelWidth() counts columns up to and including the VRuler. GetLeftOffset() is yet one more -- it counts the "one pixel" column. @@ -211,7 +133,7 @@ FindCell() for vruler returns a rectangle right of the label, up to and including the One Pixel column, and OMITS top and bottom margins FindCell() for track returns a rectangle with x == GetLeftOffset(), and OMITS -right top, and bottom margins +right, top, and bottom margins +--------------- ... ------ ... --------------------- ... ... -------------+ | Top Inset | diff --git a/src/TrackPanelListener.h b/src/TrackPanelListener.h index cb479b7bd..88d650d48 100644 --- a/src/TrackPanelListener.h +++ b/src/TrackPanelListener.h @@ -15,6 +15,11 @@ class ToolsToolBar; class ControlToolBar; enum class UndoPush : unsigned char; +/* +\brief A now badly named abstract class which was a failed attempt to let +TrackPanel code pretend it doesn't completely know what an AudacityProject is +and use only a limited number of its services. +*/ class AUDACITY_DLL_API TrackPanelListener /* not final */ { public: diff --git a/src/widgets/Overlay.h b/src/widgets/Overlay.h index 4f723e1ff..914c28027 100644 --- a/src/widgets/Overlay.h +++ b/src/widgets/Overlay.h @@ -16,6 +16,78 @@ class wxDC; class wxRect; class wxSize; +/* +How Audacity Redisplay Works \n + Roger Dannenberg \n +Oct 2010 \n + +This is a brief guide to Audacity redisplay -- it may not be complete. It +is my attempt to understand the complicated graphics strategy. + +One basic idea is that redrawing waveforms is rather slow, so Audacity +saves waveform images in bitmaps to make redrawing faster. In particular, +during audio playback (and recording), the vertical time indicator is +drawn over the waveform about 20 times per second. To avoid unnecessary +computation, the indicator is erased by copying a column of pixels from +a bitmap image of the waveform. Notice that this implies a two-stage +process: first, waveforms are drawn to the bitmap; then, the bitmap +(or pieces of it) are copied to the screen, perhaps along with other +graphics. + +The bitmap is for the entire track panel, i.e. multiple tracks, and +includes things like the Gain and Pan slders to the left of the +waveform images. + +The screen update uses a mixture of direct drawing and indirect paint +events. The "normal" way to update a graphical display is to call +the Refresh() method when something invalidates the screen. Later, the +system calls OnPaint(), which the application overrides to (re)draw the +screen. In wxWidgets, you can also draw directly to the screen without +calling Refresh() and without waiting for OnPaint() to be called. + +I would expect there to be a 2-level invalidation scheme: Some changes +invalidate the bitmap, forcing a bitmap redraw *and* a screen redraw. +Other changes merely update the screen using pre-existing bitmaps. In +Audacity, the "2-level" invalidation works like this: Anything +that invalidates the bitmap calls TrackPanel::Refresh(), which +has an eraseBackground parameter. This flag says to redraw the +bitmap when OnPaint() is called. If eraseBackground is false, the +existing bitmap can be used for waveform images. Audacity also +draws directly to the screen to update the time indicator during +playback. To move the indicator, one column of pixels is drawn to +the screen to remove the indicator. Then the indicator is drawn at +a NEW time location. + +Notice that the zoom guidelines, the focused track highlight, +and snap guidelines could be drawn directly to the screen rather than to +the bitmap, generally eliminating redraw work. + +One problem is slider udpates. Sliders are in the left area of the track +panel. They are not wxWindows like wxSliders, but instead are just drawn +on the TrackPanel. When slider state changes, *all* tracks do a full +refresh, including recomputing the backing store. It would make more sense +to just invalidate the region containing the slider. However, doing that +would require either incrementally updating the bitmap (not currently done), +or maintaining the sliders and other track info on the screen and not in +the bitmap. + +*/ + +/* +PRL: above explanation was formerly in TrackArtist.cpp. + +What it says is correct but also applies to other things than the play +indicator, such as the point editing cursor and the quick-play indicator +line. I abstracted out class Overlay to describe these drawables, and +cooperating class OverlayPanel that can manage multiple Overlays, figuring +out when the invalidation of one of them necessitates invalidation of other +overlapping ones. + +The base class OverlayPanel, of TrackPanel, was also reused by the +AdornedRulerPanel. + +*/ + class Overlay { public: