From 5b01f57919d38649fd6b54bae45707b0c2379608 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 31 Oct 2018 08:03:40 -0400 Subject: [PATCH] Update comments about TrackPanel and TrackArtist... ... Remove mention of obsolete plans for TrackPanel refactor, superseded by a different refactor now accomplished. Remove detailed mention of names of functions and data members that implement drawing. Move most of Roger's comments about drawing into Overlay.h which is now the more relevant place. Commented more correctly what TrackPanelListener is. --- src/TrackArtist.cpp | 101 --------------------------------------- src/TrackPanel.cpp | 98 ++++--------------------------------- src/TrackPanelListener.h | 5 ++ src/widgets/Overlay.h | 72 ++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 189 deletions(-) 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: