Commit d96ef19b authored by Simon Eugster's avatar Simon Eugster
Browse files

Abstract Scope Widget extended.

* Auto-Refreshing added
* More doc added
Waveform .ui file
* Color palette removed (is inherited from the superclass)

svn path=/trunk/kdenlive/; revision=4603
parent 9531ebfd
......@@ -31,7 +31,8 @@ AbstractScopeWidget::AbstractScopeWidget(Monitor *projMonitor, Monitor *clipMoni
offset(5),
m_semaphoreHUD(1),
m_semaphoreScope(1),
m_semaphoreBackground(1)
m_semaphoreBackground(1),
initialDimensionUpdateDone(false)
{
m_scopePalette = QPalette();
......@@ -62,9 +63,12 @@ AbstractScopeWidget::AbstractScopeWidget(Monitor *projMonitor, Monitor *clipMoni
m_activeRender = m_clipMonitor->render;
}
connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
connect(this, SIGNAL(signalScopeRenderingFinished()), this, SLOT(slotScopeRenderingFinished()));
connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
connect(this, SIGNAL(signalHUDRenderingFinished(uint)), this, SLOT(slotHUDRenderingFinished(uint)));
connect(this, SIGNAL(signalScopeRenderingFinished(uint)), this, SLOT(slotScopeRenderingFinished(uint)));
connect(this, SIGNAL(signalBackgroundRenderingFinished(uint)), this, SLOT(slotBackgroundRenderingFinished(uint)));
}
......@@ -75,11 +79,15 @@ AbstractScopeWidget::~AbstractScopeWidget()
delete m_aAutoRefresh;
}
void AbstractScopeWidget::prodHUDThread() {}
void AbstractScopeWidget::prodScopeThread()
{
if (m_semaphoreScope.tryAcquire(1)) {
Q_ASSERT(!m_threadScope.isRunning());
m_newScopeFrames.fetchAndStoreRelaxed(0);
m_newScopeUpdates.fetchAndStoreRelaxed(0);
m_threadScope = QtConcurrent::run(this, &AbstractScopeWidget::renderScope);
qDebug() << "Scope thread started in " << widgetName();
......@@ -88,13 +96,16 @@ void AbstractScopeWidget::prodScopeThread()
}
}
void AbstractScopeWidget::prodBackgroundThread() {}
///// Events /////
void AbstractScopeWidget::mouseReleaseEvent(QMouseEvent *event)
{
// TODO Render again
prodHUDThread();
prodScopeThread();
prodBackgroundThread();
QWidget::mouseReleaseEvent(event);
}
......@@ -107,12 +118,25 @@ void AbstractScopeWidget::resizeEvent(QResizeEvent *event)
m_newScopeUpdates.fetchAndAddRelaxed(1);
m_newBackgroundUpdates.fetchAndAddRelaxed(1);
prodHUDThread();
prodScopeThread();
prodBackgroundThread();
QWidget::resizeEvent(event);
// TODO Calculation
}
void AbstractScopeWidget::paintEvent(QPaintEvent *)
{
// qDebug() << "Painting now on scope " << widgetName();
if (!initialDimensionUpdateDone) {
// This is a workaround.
// When updating the dimensions in the constructor, the size
// of the control items at the top are simply ignored! So do
// it here instead.
m_scopeRect = scopeRect();
initialDimensionUpdateDone = true;
}
QPainter davinci(this);
davinci.drawImage(scopeRect().topLeft(), m_imgBackground);
davinci.drawImage(scopeRect().topLeft(), m_imgScope);
......@@ -128,36 +152,48 @@ void AbstractScopeWidget::customContextMenuRequested(const QPoint &pos)
///// Slots /////
void AbstractScopeWidget::slotHUDRenderingFinished()
void AbstractScopeWidget::slotHUDRenderingFinished(uint)
{
}
void AbstractScopeWidget::slotScopeRenderingFinished()
void AbstractScopeWidget::slotScopeRenderingFinished(uint)
{
qDebug() << "Scope rendering has finished, waiting for termination in " << widgetName();
m_threadScope.waitForFinished();
m_imgScope = m_threadScope.result();
m_semaphoreScope.release(1);
this->update();
if ( (m_newScopeFrames > 0 && m_aAutoRefresh->isChecked()) || m_newScopeUpdates > 0) {
qDebug() << "Trying to start a new thread for " << widgetName()
<< ". New frames/updates: " << m_newScopeFrames << "/" << m_newScopeUpdates;
}
}
void AbstractScopeWidget::slotBackgroundRenderingFinished()
void AbstractScopeWidget::slotBackgroundRenderingFinished(uint)
{
}
void AbstractScopeWidget::slotActiveMonitorChanged(bool isClipMonitor)
{
qDebug() << "Active monitor has changed in " << widgetName() << ". Is the clip monitor active now? " << isClipMonitor;
if (isClipMonitor) {
m_activeRender = m_clipMonitor->render;
disconnect(this, SLOT(slotRenderZoneUpdated()));
connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
qDebug() << "Connected to clip monitor.";
} else {
m_activeRender = m_projMonitor->render;
disconnect(this, SLOT(slotRenderZoneUpdated()));
connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
qDebug() << "Connected to project monitor.";
}
// Update the scope for the new monitor.
prodHUDThread();
prodScopeThread();
prodBackgroundThread();
}
void AbstractScopeWidget::slotRenderZoneUpdated()
......@@ -173,7 +209,9 @@ void AbstractScopeWidget::slotRenderZoneUpdated()
qDebug() << "Scope of widget " << widgetName() << " is not at the top, not rendering.";
} else {
if (m_aAutoRefresh->isChecked()) {
// TODO run the updater functions here.
prodHUDThread();
prodScopeThread();
prodBackgroundThread();
}
}
}
......@@ -9,6 +9,15 @@
***************************************************************************/
/**
This abstract widget is a proof that abstract things sometimes *are* useful.
The widget expects three layers which
* Will be painted on top of each other on each update
* Are rendered in a separate thread so that the UI is not blocked
* Are rendered only if necessary (e.g., if a layer does not depend
on input images, it will not be re-rendered for incoming frames)
The layer order is as follows:
_____________________
/ \
/ HUD Layer \
......@@ -24,6 +33,16 @@
/ Background Layer \
/ \
---------------------------
Colors of Scope Widgets are defined in here (and thus don't need to be
re-defined in the implementation of the widget's .ui file).
The custom context menu already contains entries, like for enabling auto-
refresh. It can certainly be extended in the implementation of the widget.
If you intend to write an own widget inheriting from this one, please read
the comments on the unimplemented methods carefully. They are not only here
for optical amusement, but also contain important information.
*/
#ifndef ABSTRACTSCOPEWIDGET_H
......@@ -55,6 +74,7 @@ protected:
Monitor *m_clipMonitor;
Render *m_activeRender;
/** The context menu. Feel free to add new entries in your implementation. */
QMenu *m_menu;
QAction *m_aAutoRefresh;
QAction *m_aRealtime;
......@@ -67,64 +87,78 @@ protected:
QImage m_imgScope;
QImage m_imgBackground;
/** Counts the number of frames that have been rendered in the active monitor.
The frame number will be reset when the calculation starts for the current frame. */
QAtomicInt m_newHUDFrames;
QAtomicInt m_newScopeFrames;
QAtomicInt m_newBackgroundFrames;
/** Counts the number of updates that, unlike new frames, force a recalculation
of the scope, like for example a resize event. */
QAtomicInt m_newHUDUpdates;
QAtomicInt m_newScopeUpdates;
QAtomicInt m_newBackgroundUpdates;
QFuture<QImage> m_threadHUD;
QFuture<QImage> m_threadScope;
QFuture<QImage> m_threadBackground;
QSemaphore m_semaphoreHUD;
QSemaphore m_semaphoreScope;
QSemaphore m_semaphoreBackground;
///// Unimplemented Methods /////
/** Where on the widget we can paint in */
virtual QRect scopeRect() = 0;
/** HUD renderer. Must emit signalHUDRenderingFinished(). */
/** @brief HUD renderer.
Must emit signalHUDRenderingFinished().
Should */
virtual QImage renderHUD() = 0;
/** Scope renderer. Must emit signalScopeRenderingFinished(). */
/** @brief Scope renderer. Must emit signalScopeRenderingFinished(). */
virtual QImage renderScope() = 0;
/** Background renderer. Must emit signalBackgroundRenderingFinished(). */
/** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). */
virtual QImage renderBackground() = 0;
/** Must return true if the HUD layer depends on the input monitor.
If it does not, then it does not need to be re-calculated when
a new frame from the monitor is incoming. */
virtual bool isHUDDependingOnInput() const = 0;
/** @see isHUDDependingOnInput() */
virtual bool isScopeDependingOnInput() const = 0;
/** @see isHUDDependingOnInput() */
virtual bool isBackgroundDependingOnInput() const = 0;
///// Methods /////
void mouseReleaseEvent(QMouseEvent *);
void paintEvent(QPaintEvent *);
void resizeEvent(QResizeEvent *);
protected slots:
/** Called when the active monitor has shown a new frame. */
void slotRenderZoneUpdated();
void slotHUDRenderingFinished();
void slotScopeRenderingFinished();
void slotBackgroundRenderingFinished();
signals:
void signalHUDRenderingFinished();
void signalScopeRenderingFinished();
void signalBackgroundRenderingFinished();
void signalHUDRenderingFinished(uint mseconds);
void signalScopeRenderingFinished(uint mseconds);
void signalBackgroundRenderingFinished(uint mseconds);
private:
/** Counts the number of frames that have been rendered in the active monitor.
The frame number will be reset when the calculation starts for the current frame. */
QAtomicInt m_newHUDFrames;
QAtomicInt m_newScopeFrames;
QAtomicInt m_newBackgroundFrames;
/** Counts the number of updates that, unlike new frames, force a recalculation
of the scope, like for example a resize event. */
QAtomicInt m_newHUDUpdates;
QAtomicInt m_newScopeUpdates;
QAtomicInt m_newBackgroundUpdates;
QSemaphore m_semaphoreHUD;
QSemaphore m_semaphoreScope;
QSemaphore m_semaphoreBackground;
QFuture<QImage> m_threadHUD;
QFuture<QImage> m_threadScope;
QFuture<QImage> m_threadBackground;
bool initialDimensionUpdateDone;
void prodHUDThread();
void prodScopeThread();
void prodBackgroundThread();
private slots:
void customContextMenuRequested(const QPoint &pos);
/** @brief Must be called when the active monitor has shown a new frame.
This slot must be connected in the implementing class, it is *not*
done in this abstract class. */
void slotActiveMonitorChanged(bool isClipMonitor);
void customContextMenuRequested(const QPoint &pos);
void slotRenderZoneUpdated();
void slotHUDRenderingFinished(uint mseconds);
void slotScopeRenderingFinished(uint mseconds);
void slotBackgroundRenderingFinished(uint mseconds);
};
......
......@@ -34,29 +34,29 @@ TestWidget::~TestWidget()
QImage TestWidget::renderHUD()
{
emit signalHUDRenderingFinished();
emit signalHUDRenderingFinished(0);
return QImage();
}
QImage TestWidget::renderScope()
{
emit signalScopeRenderingFinished();
emit signalScopeRenderingFinished(0);
return QImage();
}
QImage TestWidget::renderBackground()
{
emit signalBackgroundRenderingFinished();
emit signalBackgroundRenderingFinished(0);
return QImage();
}
QString TestWidget::widgetName() const { return "Testwidget"; }
bool TestWidget::isHUDDependingOnInput() const { return false; }
bool TestWidget::isScopeDependingOnInput() const { return false; }
bool TestWidget::isBackgroundDependingOnInput() const { return false; }
QRect TestWidget::scopeRect()
{
return QRect(QPoint(offset, ui->line->y() + 2*offset), this->rect().bottomRight() - QPoint(offset, offset));
}
QString TestWidget::widgetName() const
{
return "Testwidget";
}
......@@ -30,6 +30,9 @@ public:
QImage renderHUD();
QImage renderScope();
QImage renderBackground();
bool isHUDDependingOnInput() const;
bool isScopeDependingOnInput() const;
bool isBackgroundDependingOnInput() const;
protected:
QRect scopeRect();
......
......@@ -96,7 +96,7 @@ Vectorscope::Vectorscope(Monitor *projMonitor, Monitor *clipMonitor, QWidget *pa
backgroundMode->addItem(i18n("YUV"), QVariant(BG_YUV));
backgroundMode->addItem(i18n("Modified YUV (Chroma)"), QVariant(BG_CHROMA));
cbAutoRefresh->setChecked(true);
// cbAutoRefresh->setChecked(true);
connect(backgroundMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotBackgroundChanged()));
connect(cbMagnify, SIGNAL(stateChanged(int)), this, SLOT(slotMagnifyChanged()));
......
......@@ -25,8 +25,6 @@ Waveform::Waveform(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent)
ui->setupUi(this);
m_waveformGenerator = new WaveformGenerator();
connect(m_waveformGenerator, SIGNAL(signalCalculationFinished(QImage,uint)), this, SLOT(slotWaveformCalculated(QImage,uint)));
}
Waveform::~Waveform()
......@@ -46,86 +44,34 @@ QRect Waveform::scopeRect()
return QRect(topleft, this->size() - QSize(offset+topleft.x(), offset+topleft.y()));
}
QString Waveform::widgetName() const
{
return QString("Waveform");
}
///// Implemented methods /////
QString Waveform::widgetName() const { return QString("Waveform"); }
bool Waveform::isHUDDependingOnInput() const { return false; }
bool Waveform::isScopeDependingOnInput() const { return true; }
bool Waveform::isBackgroundDependingOnInput() const { return false; }
QImage Waveform::renderHUD()
{
emit signalHUDRenderingFinished();
emit signalHUDRenderingFinished(0);
return QImage();
}
QImage Waveform::renderScope()
{
QTime start = QTime::currentTime();
start.start();
QImage wave = m_waveformGenerator->calculateWaveform(scopeRect().size(),
m_activeRender->extractFrame(m_activeRender->seekFramePosition()), true);
emit signalScopeRenderingFinished();
emit signalScopeRenderingFinished(start.elapsed());
return wave;
}
QImage Waveform::renderBackground()
{
emit signalBackgroundRenderingFinished();
emit signalBackgroundRenderingFinished(0);
return QImage();
}
///// Events /////
void Waveform::paintEvent(QPaintEvent *)
{
if (!initialDimensionUpdateDone) {
// This is a workaround.
// When updating the dimensions in the constructor, the size
// of the control items at the top are simply ignored! So do
// it here instead.
scopeRect();
initialDimensionUpdateDone = true;
}
QPainter davinci(this);
QPoint vinciPoint;
davinci.setRenderHint(QPainter::Antialiasing, true);
davinci.fillRect(0, 0, this->size().width(), this->size().height(), QColor(25,25,23));
davinci.drawImage(m_scopeRect.topLeft(), m_waveform);
}
//void Waveform::resizeEvent(QResizeEvent *event)
//{
// updateDimensions();
// m_waveformGenerator->calculateWaveform(m_scopeRect.size(), m_activeRender->extractFrame(m_activeRender->seekFramePosition()), true);
// QWidget::resizeEvent(event);
//}
//void Waveform::mouseReleaseEvent(QMouseEvent *)
//{
// qDebug() << "Calculating scope now. Size: " << m_scopeRect.size().width() << "x" << m_scopeRect.size().height();
// m_waveformGenerator->calculateWaveform(m_scopeRect.size(), m_activeRender->extractFrame(m_activeRender->seekFramePosition()), true);
//}
///// Slots /////
void Waveform::slotWaveformCalculated(QImage waveform, const uint &msec)
{
qDebug() << "Waveform received. Time taken for rendering: " << msec << " ms. Painting it.";
m_waveform = waveform;
this->update();
}
void Waveform::slotRenderZoneUpdated()
{
qDebug() << "Monitor incoming.";//New frames total: " << newFrames;
// Monitor has shown a new frame
// newFrames.fetchAndAddRelaxed(1);
// if (cbAutoRefresh->isChecked()) {
// prodCalcThread();
// }
}
......@@ -27,11 +27,6 @@ public:
virtual QString widgetName() const;
protected:
void paintEvent(QPaintEvent *);
// void resizeEvent(QResizeEvent *);
// void mouseReleaseEvent(QMouseEvent *);
private:
Ui::Waveform_UI *ui;
......@@ -46,10 +41,9 @@ private:
QImage renderHUD();
QImage renderScope();
QImage renderBackground();
private slots:
void slotRenderZoneUpdated();
void slotWaveformCalculated(QImage waveform, const uint &msec);
bool isHUDDependingOnInput() const;
bool isScopeDependingOnInput() const;
bool isBackgroundDependingOnInput() const;
};
......
......@@ -10,178 +10,6 @@
<height>300</height>
</rect>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>250</red>
<green>238</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>40</red>
<green>40</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>250</red>
<green>238</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>250</red>
<green>238</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>40</red>
<green>40</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>40</red>
<green>40</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>250</red>
<green>238</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>40</red>
<green>40</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>250</red>
<green>238</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>250</red>
<green>238</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>40</red>
<green>40</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>40</red>
<green>40</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>146</red>
<green>145</green>
<blue>144</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>40</red>
<green>40</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">