File indexing completed on 2024-04-14 04:47:02
0001 /* 0002 SPDX-FileCopyrightText: 2010 Simon Andreas Eugster <simon.eu@gmail.com> 0003 This file is part of kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #pragma once 0009 0010 #include <QFuture> 0011 #include <QMenu> 0012 #include <QSemaphore> 0013 #include <QWidget> 0014 0015 /** @class AbstractScopeWidget 0016 @brief Abstract class for audio/colour scopes (receive data and paint it). 0017 0018 This abstract widget is a proof that abstract things sometimes \b *are* useful. 0019 0020 The widget expects three layers which 0021 \li Will be painted on top of each other on each update 0022 \li Are rendered in a separate thread so that the UI is not blocked 0023 \li Are rendered only if necessary (e.g., if a layer does not depend 0024 on input images, it will not be re-rendered for incoming frames) 0025 0026 The layer order is as follows: 0027 \verbatim 0028 _____________________ 0029 / \ 0030 / HUD Layer \ 0031 / \ 0032 --------------------------- 0033 _____________________ 0034 / \ 0035 / Scope Layer \ 0036 / \ 0037 --------------------------- 0038 _____________________ 0039 / \ 0040 / Background Layer \ 0041 / \ 0042 --------------------------- 0043 \endverbatim 0044 0045 Colors of Scope Widgets are defined in here (and thus don't need to be 0046 re-defined in the implementation of the widget's .ui file). 0047 0048 The custom context menu already contains entries, like for enabling auto- 0049 refresh. It can certainly be extended in the implementation of the widget. 0050 0051 If you intend to write an own widget inheriting from this one, please read 0052 the comments on the unimplemented methods carefully. They are not only here 0053 for optical amusement, but also contain important information. 0054 */ 0055 class AbstractScopeWidget : public QWidget 0056 { 0057 Q_OBJECT 0058 0059 public: 0060 /** 0061 \param trackMouse enables mouse tracking; The variables m_mousePos and m_mouseWithinWidget will be set 0062 if mouse tracking is enabled. 0063 \see signalMousePositionChanged(): Emitted when mouse tracking is enabled 0064 */ 0065 explicit AbstractScopeWidget(bool trackMouse = false, QWidget *parent = nullptr); 0066 ~AbstractScopeWidget() override; // Must be virtual because of inheritance, to avoid memory leaks 0067 0068 enum RescaleDirection { North, Northeast, East, Southeast }; 0069 0070 QPalette m_scopePalette; 0071 0072 /** Initializes widget settings (reads configuration). 0073 Has to be called in the implementing object. */ 0074 virtual void init(); 0075 0076 /** Tell whether this scope has auto-refresh enabled. Required for determining whether 0077 new data (e.g. an image frame) has to be delivered to this widget. */ 0078 bool autoRefreshEnabled() const; 0079 0080 bool needsSingleFrame(); 0081 0082 ///// Unimplemented ///// 0083 0084 virtual QString widgetName() const = 0; 0085 0086 ///// Variables ///// 0087 static const QColor colHighlightLight; 0088 static const QColor colHighlightDark; 0089 static const QColor colDarkWhite; 0090 0091 static const QPen penThick; 0092 static const QPen penThin; 0093 static const QPen penLight; 0094 static const QPen penLightDots; 0095 static const QPen penLighter; 0096 static const QPen penDark; 0097 static const QPen penDarkDots; 0098 static const QPen penBackground; 0099 0100 static const QString directions[]; // Mainly for debug output 0101 0102 protected: 0103 ///// Variables ///// 0104 0105 /** The context menu. Feel free to add new entries in your implementation. */ 0106 QMenu *m_menu; 0107 0108 /** Enables auto refreshing of the scope. 0109 This is when fresh data is incoming. 0110 Resize events always force a recalculation. */ 0111 QAction *m_aAutoRefresh; 0112 0113 /** Realtime rendering. Should be disabled if it is not supported. 0114 Use the accelerationFactor variable passed to the render functions as a hint of 0115 how many times faster the scope should be calculated. */ 0116 QAction *m_aRealtime; 0117 0118 /** The mouse position; Updated when the mouse enters the widget 0119 AND mouse tracking has been enabled. */ 0120 QPoint m_mousePos; 0121 /** Knows whether the mouse currently lies within the widget or not. 0122 Can e.g. be used for drawing a HUD only when the mouse is in the widget. */ 0123 bool m_mouseWithinWidget{false}; 0124 0125 /** Offset from the widget's borders */ 0126 const uchar offset{5}; 0127 0128 /** The rect on the widget we're painting in. 0129 Can be used by the implementing widget, e.g. in the render methods. 0130 Is updated when necessary (size changes). */ 0131 QRect m_scopeRect; 0132 0133 /** Images storing the calculated layers. Will be used on repaint events. */ 0134 QImage m_imgHUD; 0135 QImage m_imgScope; 0136 QImage m_imgBackground; 0137 0138 /** The acceleration factors can be accessed also by other renderer tasks, 0139 e.g. to display the scope's acceleration factor in the HUD renderer. */ 0140 int m_accelFactorHUD{1}; 0141 int m_accelFactorScope{1}; 0142 int m_accelFactorBackground{1}; 0143 0144 /** Reads the widget's configuration. 0145 Can be extended in the implementing subclass (make sure to run readConfig as well). */ 0146 virtual void readConfig(); 0147 /** Writes the widget configuration. 0148 Implementing widgets have to implement an own method and run it in their destructor. */ 0149 void writeConfig(); 0150 /** Identifier for the widget's configuration. */ 0151 QString configName(); 0152 0153 ///// Unimplemented Methods ///// 0154 0155 /** Where on the widget we can paint in. 0156 May also update other variables, like m_scopeRect or custom ones, 0157 that have to change together with the widget's size. */ 0158 virtual QRect scopeRect() = 0; 0159 0160 /** @brief HUD renderer. Must emit signalHUDRenderingFinished(). 0161 @see renderScope(uint). */ 0162 virtual QImage renderHUD(uint accelerationFactor) = 0; 0163 /** @brief Rendering function for the scope layer. 0164 This function \b must emit signalScopeRenderingFinished(), otherwise the scope 0165 will not attempt to ever call this function again. This signal is required for multi-threading; 0166 not emitting it on unused rendering function may increase performance. 0167 @param accelerationFactor hints how much faster than usual the calculation should be accomplished, if possible. 0168 @see renderHUD(uint) for the upper layer 0169 @see renderBackground(uint) for the layer below 0170 */ 0171 virtual QImage renderScope(uint accelerationFactor) = 0; 0172 /** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). 0173 @see renderScope(uint) */ 0174 virtual QImage renderBackground(uint accelerationFactor) = 0; 0175 0176 /** Must return true if the HUD layer depends on the input data. 0177 If it does not, then it does not need to be re-calculated when 0178 fresh data is incoming. */ 0179 virtual bool isHUDDependingOnInput() const = 0; 0180 /** @see isHUDDependingOnInput() */ 0181 virtual bool isScopeDependingOnInput() const = 0; 0182 /** @see isHUDDependingOnInput() */ 0183 virtual bool isBackgroundDependingOnInput() const = 0; 0184 0185 ///// Can be reimplemented ///// 0186 /** Calculates the acceleration factor to be used by the render thread. 0187 This method can be refined in the subclass if required. */ 0188 virtual uint calculateAccelFactorHUD(uint oldMseconds, uint oldFactor); 0189 virtual uint calculateAccelFactorScope(uint oldMseconds, uint oldFactor); 0190 virtual uint calculateAccelFactorBackground(uint oldMseconds, uint oldFactor); 0191 0192 /** The Abstract Scope will try to detect the movement direction when dragging on the widget with the mouse. 0193 As soon as the direction is determined it will execute this method. Can be used e.g. for re-scaling content. 0194 This is just a dummy function, re-implement to add functionality. */ 0195 virtual void handleMouseDrag(const QPoint &movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers); 0196 0197 ///// Reimplemented ///// 0198 0199 void mouseMoveEvent(QMouseEvent *event) override; 0200 void mousePressEvent(QMouseEvent *event) override; 0201 void mouseReleaseEvent(QMouseEvent *event) override; 0202 void leaveEvent(QEvent *) override; 0203 void paintEvent(QPaintEvent *) override; 0204 void resizeEvent(QResizeEvent *) override; 0205 void showEvent(QShowEvent *) override; // Called when the widget is activated via the Menu entry 0206 // void raise(); // Called only when manually calling the event -> useless 0207 0208 public Q_SLOTS: 0209 /** Forces an update of all layers. */ 0210 void forceUpdate(bool doUpdate = true); 0211 void forceUpdateHUD(); 0212 void forceUpdateScope(); 0213 void forceUpdateBackground(); 0214 0215 protected Q_SLOTS: 0216 void slotAutoRefreshToggled(bool); 0217 0218 Q_SIGNALS: 0219 /** 0220 \param mseconds represents the time taken for the calculation. 0221 \param accelerationFactor is the acceleration factor that has been used for this calculation. 0222 */ 0223 void signalHUDRenderingFinished(uint mseconds, uint accelerationFactor); 0224 void signalScopeRenderingFinished(uint mseconds, uint accelerationFactor); 0225 void signalBackgroundRenderingFinished(uint mseconds, uint accelerationFactor); 0226 0227 /** For the mouse position itself see m_mousePos. 0228 To check whether the mouse has leaved the widget, see m_mouseWithinWidget. 0229 This signal is typically connected to forceUpdateHUD(). */ 0230 void signalMousePositionChanged(); 0231 0232 /** Do we need the renderer to send its frames to us? 0233 Emitted when auto-refresh is toggled. */ 0234 void requestAutoRefresh(bool); 0235 0236 private: 0237 /** Counts the number of data frames that have been rendered in the active monitor. 0238 The frame number will be reset when the calculation starts for the current data set. */ 0239 QAtomicInt m_newHUDFrames; 0240 QAtomicInt m_newScopeFrames; 0241 QAtomicInt m_newBackgroundFrames; 0242 0243 /** Counts the number of updates that, unlike new frames, force a recalculation 0244 of the scope, like for example a resize event. */ 0245 QAtomicInt m_newHUDUpdates; 0246 QAtomicInt m_newScopeUpdates; 0247 QAtomicInt m_newBackgroundUpdates; 0248 0249 /** The semaphores ensure that the QFutures for the HUD/Scope/Background threads cannot 0250 be assigned a new thread while it is still running. (Could cause deadlocks and other 0251 nasty things known from parallelism.) */ 0252 QSemaphore m_semaphoreHUD; 0253 QSemaphore m_semaphoreScope; 0254 QSemaphore m_semaphoreBackground; 0255 0256 QFuture<QImage> m_threadHUD; 0257 QFuture<QImage> m_threadScope; 0258 QFuture<QImage> m_threadBackground; 0259 0260 bool m_initialDimensionUpdateDone{false}; 0261 bool m_requestForcedUpdate{false}; 0262 0263 QImage m_scopeImage; 0264 0265 QString m_widgetName; 0266 0267 void prodHUDThread(); 0268 void prodScopeThread(); 0269 void prodBackgroundThread(); 0270 0271 ///// Movement detection ///// 0272 const int m_rescaleMinDist{4}; 0273 const float m_rescaleVerticalThreshold{2.0f}; 0274 0275 bool m_rescaleActive{false}; 0276 bool m_rescalePropertiesLocked{false}; 0277 bool m_rescaleFirstRescaleDone{true}; 0278 Qt::KeyboardModifiers m_rescaleModifiers; 0279 RescaleDirection m_rescaleDirection{North}; 0280 QPoint m_rescaleStartPoint; 0281 0282 bool m_scopeWarningPrinted{false}; 0283 0284 protected Q_SLOTS: 0285 void slotContextMenuRequested(const QPoint &pos); 0286 /** To be called when a new frame has been received. 0287 The scope then decides whether and when it wants to recalculate the scope, depending 0288 on whether it is currently visible and whether a calculation thread is already running. */ 0289 void slotRenderZoneUpdated(); 0290 /** The following slots are called when rendering of a component has finished. They e.g. update 0291 the widget and decide whether to immediately restart the calculation thread. */ 0292 void slotHUDRenderingFinished(uint mseconds, uint accelerationFactor); 0293 void slotScopeRenderingFinished(uint mseconds, uint accelerationFactor); 0294 void slotBackgroundRenderingFinished(uint mseconds, uint accelerationFactor); 0295 0296 /** Resets the acceleration factors to 1 when realtime rendering is disabled. */ 0297 void slotResetRealtimeFactor(bool realtimeChecked); 0298 };