1 /* 2 SPDX-FileCopyrightText: 2010 Simon Andreas Eugster <simon.eu@gmail.com> 3 This file is part of kdenlive. See www.kdenlive.org. 4 5 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 6 */ 7 8 #ifndef ABSTRACTSCOPEWIDGET_H 9 #define ABSTRACTSCOPEWIDGET_H 10 11 #include <QFuture> 12 #include <QMenu> 13 #include <QSemaphore> 14 #include <QWidget> 15 16 /** @class AbstractScopeWidget 17 @brief Abstract class for audio/colour scopes (receive data and paint it). 18 19 This abstract widget is a proof that abstract things sometimes \b *are* useful. 20 21 The widget expects three layers which 22 \li Will be painted on top of each other on each update 23 \li Are rendered in a separate thread so that the UI is not blocked 24 \li Are rendered only if necessary (e.g., if a layer does not depend 25 on input images, it will not be re-rendered for incoming frames) 26 27 The layer order is as follows: 28 \verbatim 29 _____________________ 30 / \ 31 / HUD Layer \ 32 / \ 33 --------------------------- 34 _____________________ 35 / \ 36 / Scope Layer \ 37 / \ 38 --------------------------- 39 _____________________ 40 / \ 41 / Background Layer \ 42 / \ 43 --------------------------- 44 \endverbatim 45 46 Colors of Scope Widgets are defined in here (and thus don't need to be 47 re-defined in the implementation of the widget's .ui file). 48 49 The custom context menu already contains entries, like for enabling auto- 50 refresh. It can certainly be extended in the implementation of the widget. 51 52 If you intend to write an own widget inheriting from this one, please read 53 the comments on the unimplemented methods carefully. They are not only here 54 for optical amusement, but also contain important information. 55 */ 56 class AbstractScopeWidget : public QWidget 57 { 58 Q_OBJECT 59 60 public: 61 /** 62 \param trackMouse enables mouse tracking; The variables m_mousePos and m_mouseWithinWidget will be set 63 if mouse tracking is enabled. 64 \see signalMousePositionChanged(): Emitted when mouse tracking is enabled 65 */ 66 explicit AbstractScopeWidget(bool trackMouse = false, QWidget *parent = nullptr); 67 ~AbstractScopeWidget() override; // Must be virtual because of inheritance, to avoid memory leaks 68 69 enum RescaleDirection { North, Northeast, East, Southeast }; 70 71 QPalette m_scopePalette; 72 73 /** Initializes widget settings (reads configuration). 74 Has to be called in the implementing object. */ 75 virtual void init(); 76 77 /** Tell whether this scope has auto-refresh enabled. Required for determining whether 78 new data (e.g. an image frame) has to be delivered to this widget. */ 79 bool autoRefreshEnabled() const; 80 81 bool needsSingleFrame(); 82 83 ///// Unimplemented ///// 84 85 virtual QString widgetName() const = 0; 86 87 ///// Variables ///// 88 static const QColor colHighlightLight; 89 static const QColor colHighlightDark; 90 static const QColor colDarkWhite; 91 92 static const QPen penThick; 93 static const QPen penThin; 94 static const QPen penLight; 95 static const QPen penLightDots; 96 static const QPen penLighter; 97 static const QPen penDark; 98 static const QPen penDarkDots; 99 static const QPen penBackground; 100 101 static const QString directions[]; // Mainly for debug output 102 103 protected: 104 ///// Variables ///// 105 106 /** The context menu. Feel free to add new entries in your implementation. */ 107 QMenu *m_menu; 108 109 /** Enables auto refreshing of the scope. 110 This is when fresh data is incoming. 111 Resize events always force a recalculation. */ 112 QAction *m_aAutoRefresh; 113 114 /** Realtime rendering. Should be disabled if it is not supported. 115 Use the accelerationFactor variable passed to the render functions as a hint of 116 how many times faster the scope should be calculated. */ 117 QAction *m_aRealtime; 118 119 /** The mouse position; Updated when the mouse enters the widget 120 AND mouse tracking has been enabled. */ 121 QPoint m_mousePos; 122 /** Knows whether the mouse currently lies within the widget or not. 123 Can e.g. be used for drawing a HUD only when the mouse is in the widget. */ 124 bool m_mouseWithinWidget{false}; 125 126 /** Offset from the widget's borders */ 127 const uchar offset{5}; 128 129 /** The rect on the widget we're painting in. 130 Can be used by the implementing widget, e.g. in the render methods. 131 Is updated when necessary (size changes). */ 132 QRect m_scopeRect; 133 134 /** Images storing the calculated layers. Will be used on repaint events. */ 135 QImage m_imgHUD; 136 QImage m_imgScope; 137 QImage m_imgBackground; 138 139 /** The acceleration factors can be accessed also by other renderer tasks, 140 e.g. to display the scope's acceleration factor in the HUD renderer. */ 141 int m_accelFactorHUD{1}; 142 int m_accelFactorScope{1}; 143 int m_accelFactorBackground{1}; 144 145 /** Reads the widget's configuration. 146 Can be extended in the implementing subclass (make sure to run readConfig as well). */ 147 virtual void readConfig(); 148 /** Writes the widget configuration. 149 Implementing widgets have to implement an own method and run it in their destructor. */ 150 void writeConfig(); 151 /** Identifier for the widget's configuration. */ 152 QString configName(); 153 154 ///// Unimplemented Methods ///// 155 156 /** Where on the widget we can paint in. 157 May also update other variables, like m_scopeRect or custom ones, 158 that have to change together with the widget's size. */ 159 virtual QRect scopeRect() = 0; 160 161 /** @brief HUD renderer. Must emit signalHUDRenderingFinished(). 162 @see renderScope(uint). */ 163 virtual QImage renderHUD(uint accelerationFactor) = 0; 164 /** @brief Rendering function for the scope layer. 165 This function \b must emit signalScopeRenderingFinished(), otherwise the scope 166 will not attempt to ever call this function again. This signal is required for multi-threading; 167 not emitting it on unused rendering function may increase performance. 168 @param accelerationFactor hints how much faster than usual the calculation should be accomplished, if possible. 169 @see renderHUD(uint) for the upper layer 170 @see renderBackground(uint) for the layer below 171 */ 172 virtual QImage renderScope(uint accelerationFactor) = 0; 173 /** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). 174 @see renderScope(uint) */ 175 virtual QImage renderBackground(uint accelerationFactor) = 0; 176 177 /** Must return true if the HUD layer depends on the input data. 178 If it does not, then it does not need to be re-calculated when 179 fresh data is incoming. */ 180 virtual bool isHUDDependingOnInput() const = 0; 181 /** @see isHUDDependingOnInput() */ 182 virtual bool isScopeDependingOnInput() const = 0; 183 /** @see isHUDDependingOnInput() */ 184 virtual bool isBackgroundDependingOnInput() const = 0; 185 186 ///// Can be reimplemented ///// 187 /** Calculates the acceleration factor to be used by the render thread. 188 This method can be refined in the subclass if required. */ 189 virtual uint calculateAccelFactorHUD(uint oldMseconds, uint oldFactor); 190 virtual uint calculateAccelFactorScope(uint oldMseconds, uint oldFactor); 191 virtual uint calculateAccelFactorBackground(uint oldMseconds, uint oldFactor); 192 193 /** The Abstract Scope will try to detect the movement direction when dragging on the widget with the mouse. 194 As soon as the direction is determined it will execute this method. Can be used e.g. for re-scaling content. 195 This is just a dummy function, re-implement to add functionality. */ 196 virtual void handleMouseDrag(const QPoint &movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers); 197 198 ///// Reimplemented ///// 199 200 void mouseMoveEvent(QMouseEvent *event) override; 201 void mousePressEvent(QMouseEvent *event) override; 202 void mouseReleaseEvent(QMouseEvent *event) override; 203 void leaveEvent(QEvent *) override; 204 void paintEvent(QPaintEvent *) override; 205 void resizeEvent(QResizeEvent *) override; 206 void showEvent(QShowEvent *) override; // Called when the widget is activated via the Menu entry 207 // void raise(); // Called only when manually calling the event -> useless 208 209 public slots: 210 /** Forces an update of all layers. */ 211 void forceUpdate(bool doUpdate = true); 212 void forceUpdateHUD(); 213 void forceUpdateScope(); 214 void forceUpdateBackground(); 215 216 protected slots: 217 void slotAutoRefreshToggled(bool); 218 219 signals: 220 /** 221 \param mseconds represents the time taken for the calculation. 222 \param accelerationFactor is the acceleration factor that has been used for this calculation. 223 */ 224 void signalHUDRenderingFinished(uint mseconds, uint accelerationFactor); 225 void signalScopeRenderingFinished(uint mseconds, uint accelerationFactor); 226 void signalBackgroundRenderingFinished(uint mseconds, uint accelerationFactor); 227 228 /** For the mouse position itself see m_mousePos. 229 To check whether the mouse has leaved the widget, see m_mouseWithinWidget. 230 This signal is typically connected to forceUpdateHUD(). */ 231 void signalMousePositionChanged(); 232 233 /** Do we need the renderer to send its frames to us? 234 Emitted when auto-refresh is toggled. */ 235 void requestAutoRefresh(bool); 236 237 private: 238 /** Counts the number of data frames that have been rendered in the active monitor. 239 The frame number will be reset when the calculation starts for the current data set. */ 240 QAtomicInt m_newHUDFrames; 241 QAtomicInt m_newScopeFrames; 242 QAtomicInt m_newBackgroundFrames; 243 244 /** Counts the number of updates that, unlike new frames, force a recalculation 245 of the scope, like for example a resize event. */ 246 QAtomicInt m_newHUDUpdates; 247 QAtomicInt m_newScopeUpdates; 248 QAtomicInt m_newBackgroundUpdates; 249 250 /** The semaphores ensure that the QFutures for the HUD/Scope/Background threads cannot 251 be assigned a new thread while it is still running. (Could cause deadlocks and other 252 nasty things known from parallelism.) */ 253 QSemaphore m_semaphoreHUD; 254 QSemaphore m_semaphoreScope; 255 QSemaphore m_semaphoreBackground; 256 257 QFuture<QImage> m_threadHUD; 258 QFuture<QImage> m_threadScope; 259 QFuture<QImage> m_threadBackground; 260 261 bool m_initialDimensionUpdateDone{false}; 262 bool m_requestForcedUpdate{false}; 263 264 QImage m_scopeImage; 265 266 QString m_widgetName; 267 268 void prodHUDThread(); 269 void prodScopeThread(); 270 void prodBackgroundThread(); 271 272 ///// Movement detection ///// 273 const int m_rescaleMinDist{4}; 274 const float m_rescaleVerticalThreshold{2.0f}; 275 276 bool m_rescaleActive{false}; 277 bool m_rescalePropertiesLocked{false}; 278 bool m_rescaleFirstRescaleDone{true}; 279 Qt::KeyboardModifiers m_rescaleModifiers; 280 RescaleDirection m_rescaleDirection{North}; 281 QPoint m_rescaleStartPoint; 282 283 bool m_scopeWarningPrinted{false}; 284 285 protected slots: 286 void slotContextMenuRequested(const QPoint &pos); 287 /** To be called when a new frame has been received. 288 The scope then decides whether and when it wants to recalculate the scope, depending 289 on whether it is currently visible and whether a calculation thread is already running. */ 290 void slotRenderZoneUpdated(); 291 /** The following slots are called when rendering of a component has finished. They e.g. update 292 the widget and decide whether to immediately restart the calculation thread. */ 293 void slotHUDRenderingFinished(uint mseconds, uint accelerationFactor); 294 void slotScopeRenderingFinished(uint mseconds, uint accelerationFactor); 295 void slotBackgroundRenderingFinished(uint mseconds, uint accelerationFactor); 296 297 /** Resets the acceleration factors to 1 when realtime rendering is disabled. */ 298 void slotResetRealtimeFactor(bool realtimeChecked); 299 }; 300 301 #endif // ABSTRACTSCOPEWIDGET_H 302