File indexing completed on 2024-04-28 08:44:28

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 };