]> git.sesse.net Git - kdenlive/commitdiff
Auto-Refresh for Vectorscope added.
authorSimon A. Eugster <simon.eu@gmail.com>
Wed, 14 Jul 2010 15:00:22 +0000 (15:00 +0000)
committerSimon A. Eugster <simon.eu@gmail.com>
Wed, 14 Jul 2010 15:00:22 +0000 (15:00 +0000)
The vectorscope data is calculated in a different thread to avoid jerky monitors.

svn path=/trunk/kdenlive/; revision=4579

src/mainwindow.cpp
src/monitormanager.h
src/renderer.h
src/vectorscope.cpp
src/vectorscope.h

index 2df930dbd173f8c0dcc2f5eed2ab1cb4535d8573..b796883323a85c89f361fa7746d82d04e34e3f6d 100644 (file)
@@ -213,7 +213,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, QWidget *parent
     m_effectListDock->setWidget(m_effectList);
     addDockWidget(Qt::TopDockWidgetArea, m_effectListDock);
 
-    m_vectorscope = new Vectorscope(m_projectMonitor->render, m_clipMonitor->render, this);
+    m_vectorscope = new Vectorscope(m_projectMonitor, m_clipMonitor, this);
     m_vectorscopeDock = new QDockWidget(i18n("Vectorscope"), this);
     m_vectorscopeDock->setObjectName("vectorscope");
     m_vectorscopeDock->setWidget(m_vectorscope);
index 3dcfc7aa889f434570a114d6861d841d56a5aaa3..51b83a730580b2916418bfd10f4f49976751df04 100644 (file)
@@ -68,6 +68,7 @@ private:
     bool m_blocked;
 
 signals:
+    /** @brief Emitted when the active monitor changes */
     void raiseClipMonitor(bool);
 
 };
index 3036d08f59cc2fb9900645428818d371a52a8993..370179ffd02b08d2559d2c17bb8fb8016bc35062 100644 (file)
@@ -323,7 +323,9 @@ signals:
     /** @brief The renderer started rendering. */
     void rendering(const GenTime &);
 
-    /** @brief The rendering has finished. */
+    /** @brief The rendering has finished.
+        @see consumer_frame_show
+        This signal seems to be useless; use renderPosition(int) instead --Granjow */
     void renderFinished();
 
     /* @brief The current seek position has been changed by the renderer.
index f873ac4a10173daddc5db4ed8f8d23cd7b730417..72625230a88159a1552f7ae6f1881834bdbe8375 100644 (file)
@@ -1,5 +1,6 @@
 /***************************************************************************
  *   Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com)      *
+ *   This file is part of kdenlive. See www.kdenlive.org.                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
@@ -38,6 +39,9 @@ mRgb2Yuv =                       r =
 #include <QPainter>
 #include <QDebug>
 
+#include <qtconcurrentrun.h>
+#include <QThread>
+
 #include "vectorscope.h"
 
 const float SCALING = 1/.7; // See class docs
@@ -54,26 +58,31 @@ const QPen penThin(QBrush(QColor(250,250,250)), 1, Qt::SolidLine);
 const QPen penLight(QBrush(QColor(144,255,100,50)), 1, Qt::SolidLine);
 
 
-Vectorscope::Vectorscope(Render *projRender, Render *clipRender, QWidget *parent) :
+Vectorscope::Vectorscope(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) :
     QWidget(parent),
-    m_projRender(projRender),
-    m_clipRender(clipRender),
-    m_activeRender(clipRender),
+    m_projMonitor(projMonitor),
+    m_clipMonitor(clipMonitor),
+    m_activeRender(clipMonitor->render),
     iPaintMode(GREEN),
     scaling(1),
-    circleOnly(false)
+    circleEnabled(false),
+    initialDimensionUpdateDone(false)
 {
     setupUi(this);
 
     paintMode->insertItem(GREEN, i18n("Green"));
     paintMode->insertItem(CHROMA, i18n("Chroma"));
     paintMode->insertItem(ORIG, i18n("Original Color"));
-    cbAutoRefresh->setEnabled(false);
 
     connect(paintMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPaintModeChanged(int)));
     connect(cbMagnify, SIGNAL(stateChanged(int)), this, SLOT(slotMagnifyChanged()));
+    connect(this, SIGNAL(signalScopeCalculationFinished()), this, SLOT(slotScopeCalculationFinished()));
+    connect(cbMagnify, SIGNAL(stateChanged(int)), this, SLOT(slotRenderZoneUpdated()));
+
+    newFrames.fetchAndStoreRelaxed(0);
 
     this->setMouseTracking(true);
+    updateDimensions();
 }
 
 Vectorscope::~Vectorscope()
@@ -109,7 +118,93 @@ QPoint Vectorscope::mapToCanvas(QRect inside, QPointF point)
                  -point.y()/2*inside.height() + inside.height()/2 + inside.y());
 }
 
-void Vectorscope::paintEvent(QPaintEvent *)
+bool Vectorscope::prodCalcThread()
+{
+    if (m_scopeCalcThread.isRunning()) {
+        qDebug() << "Calc thread still running.";
+        return false;
+    } else {
+        // See http://doc.qt.nokia.com/latest/qtconcurrentrun.html#run about
+        // running member functions in a thread
+        qDebug() << "Calc thread not running anymore, finished: " << m_scopeCalcThread.isFinished() << ", Starting new thread";
+        m_scopeCalcThread = QtConcurrent::run(this, &Vectorscope::calculateScope);
+        return true;
+    }
+}
+
+void Vectorscope::calculateScope()
+{
+    // Prepare the vectorscope data
+    QImage scope(cw, cw, QImage::Format_ARGB32);
+    scope.fill(qRgba(0,0,0,0));
+
+    QImage img(m_activeRender->extractFrame(m_activeRender->seekFramePosition()));
+    newFrames.fetchAndStoreRelease(0); // Reset number of new frames, as we just got the newest
+    uchar *bits = img.bits();
+
+    int r,g,b;
+    double dy, dr, dg, db, dmax;
+    double y,u,v;
+    QPoint pt;
+    QRgb px;
+
+    for (int i = 0; i < img.byteCount(); i+= 4) {
+        QRgb *col = (QRgb *) bits;
+
+        r = qRed(*col);
+        g = qGreen(*col);
+        b = qBlue(*col);
+
+        y = (double) 0.001173 * r + 0.002302 * g + 0.0004471 * b;
+        u = (double) -0.0005781*r -0.001135*g +0.001713*b;
+        v = (double) 0.002411*r -0.002019*g -0.0003921*b;
+
+        pt = mapToCanvas(QRect(QPoint(0,0), scope.size()), QPointF(SCALING*scaling*u, SCALING*scaling*v));
+
+        if (!(pt.x() <= scopeRect.width() && pt.x() >= 0
+            && pt.y() <= scopeRect.height() && pt.y() >= 0)) {
+            // Point lies outside (because of scaling), don't plot it
+            continue;
+        }
+
+        // Draw the pixel using the chosen draw mode
+        switch (iPaintMode) {
+        case CHROMA:
+            dy = 200;
+            dr = dy + 290.8*v;
+            dg = dy - 100.6*u - 148*v;
+            db = dy + 517.2*u;
+
+            dmax = dr;
+            if (dg > dmax) dmax = dg;
+            if (db > dmax) dmax = db;
+            dmax = 255/dmax;
+
+            dr *= dmax;
+            dg *= dmax;
+            db *= dmax;
+
+            scope.setPixel(pt, qRgba(dr, dg, db, 255));
+            break;
+        case ORIG:
+            scope.setPixel(pt, *col);
+            break;
+        case GREEN:
+            px = scope.pixel(pt);
+            scope.setPixel(pt, qRgba(qRed(px)+(255-qRed(px))/40, 255, qBlue(px)+(255-qBlue(px))/40, qAlpha(px)+(255-qAlpha(px))/20));
+            break;
+        }
+
+        bits += 4;
+    }
+
+    m_scope = scope;
+
+    qDebug() << "Scope rendered";
+    emit signalScopeCalculationFinished();
+}
+
+void Vectorscope::updateDimensions()
 {
     // Widget width/height
     int ww = this->size().width();
@@ -123,18 +218,30 @@ void Vectorscope::paintEvent(QPaintEvent *)
     QPoint topleft(offset, controlsArea->geometry().height()+offset);
 
     // Circle Width: min of width and height
-    int cw = wh - topleft.y();
+    cw = wh - topleft.y();
     if (ww < cw) { cw = ww; }
     cw -= 2*offset;
-    QRect scopeRect(topleft, QPoint(cw, cw) + topleft);
+    scopeRect = QRect(topleft, QPoint(cw, cw) + topleft);
+}
+
+void Vectorscope::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.
+        updateDimensions();
+        initialDimensionUpdateDone = true;
+    }
 
     // Draw the vectorscope circle
     QPainter davinci(this);
     QPoint vinciPoint;
     QPoint centerPoint = mapToCanvas(scopeRect, QPointF(0,0));
     davinci.setRenderHint(QPainter::Antialiasing, true);
-    davinci.fillRect(0, 0, ww, wh, QColor(25,25,23));
+    davinci.fillRect(0, 0, this->size().width(), this->size().height(), QColor(25,25,23));
 
     davinci.setPen(penThick);
     davinci.drawEllipse(scopeRect);
@@ -175,82 +282,17 @@ void Vectorscope::paintEvent(QPaintEvent *)
     davinci.drawEllipse(mapToCanvas(scopeRect, P75*SCALING*YUV_Yl), 3,3);
 
 
-    // If no renderer available, there is nothing more to paint now.
-    if (m_activeRender == 0) return;
 
 
-    // Prepare the vectorscope data
-    QImage scope(cw, cw, QImage::Format_ARGB32);
-    scope.fill(qRgba(0,0,0,0));
+    davinci.setPen(penLight);
 
-    QImage img(m_activeRender->extractFrame(m_activeRender->seekFramePosition()));
-    uchar *bits = img.bits();
 
-    //davinci.setCompositionMode(QPainter::CompositionMode_Plus);
-    davinci.setPen(penLight);
-    
-    if (!circleOnly) {
-        int r,g,b;
-        double dy, dr, dg, db, dmax;
-        double y,u,v;
-        QPoint pt;
-        QRgb px;
-
-        for (int i = 0; i < img.byteCount(); i+= 4) {
-            QRgb *col = (QRgb *) bits;
-
-            r = qRed(*col);
-            g = qGreen(*col);
-            b = qBlue(*col);
-
-            y = (double) 0.001173 * r + 0.002302 * g + 0.0004471 * b;
-            u = (double) -0.0005781*r -0.001135*g +0.001713*b;
-            v = (double) 0.002411*r -0.002019*g -0.0003921*b;
-
-            pt = mapToCanvas(QRect(QPoint(0,0), scope.size()), QPointF(SCALING*scaling*u, SCALING*scaling*v));
-
-            if (!(pt.x() <= scopeRect.width() && pt.x() >= 0
-                && pt.y() <= scopeRect.height() && pt.y() >= 0)) {
-                // Point lies outside (because of scaling), don't plot it
-                continue;
-            }
-
-            // Draw the pixel using the chosen draw mode
-            switch (iPaintMode) {
-            case CHROMA:
-                dy = 200;
-                dr = dy + 290.8*v;
-                dg = dy - 100.6*u - 148*v;
-                db = dy + 517.2*u;
-
-                dmax = dr;
-                if (dg > dmax) dmax = dg;
-                if (db > dmax) dmax = db;
-                dmax = 255/dmax;
-
-                dr *= dmax;
-                dg *= dmax;
-                db *= dmax;
-
-                scope.setPixel(pt, qRgba(dr, dg, db, 255));
-                break;
-            case ORIG:
-                scope.setPixel(pt, *col);
-                break;
-            case GREEN:
-                px = scope.pixel(pt);
-                scope.setPixel(pt, qRgba(qRed(px)+(255-qRed(px))/40, 255, qBlue(px)+(255-qBlue(px))/40, qAlpha(px)+(255-qAlpha(px))/20));
-                break;
-            }
-
-            bits += 4;
-        }
+    // Draw the scope data (previously calculated in a separate thread)
+    davinci.drawImage(scopeRect.topLeft(), m_scope);
 
-        davinci.drawImage(scopeRect.topLeft(), scope);
-        m_scope = scope;
-    } else {
-        // Mouse moved: Draw a circle over the (already calculated) scope
-        davinci.drawImage(scopeRect.topLeft(), m_scope);
+
+    if (circleEnabled) {
+        // Mouse moved: Draw a circle over the scope
 
         int dx = centerPoint.x()-mousePos.x();
         int dy = centerPoint.y()-mousePos.y();
@@ -258,16 +300,14 @@ void Vectorscope::paintEvent(QPaintEvent *)
         QPoint reference = mapToCanvas(scopeRect, QPointF(1,0));
 
         int r = sqrt(dx*dx + dy*dy);
-        float percent = (float) 100*r/SCALING/(reference.x() - centerPoint.x());
+        float percent = (float) 100*r/SCALING/scaling/(reference.x() - centerPoint.x());
 
-        qDebug() << "dx: " << dx << " dy: " << dy << " r: " << r;
         davinci.drawEllipse(centerPoint, r,r);
         davinci.setPen(penThin);
         davinci.drawText(scopeRect.bottomRight()-QPoint(40,0), QVariant((int)percent).toString().append(" %"));
 
-        circleOnly = false;
+        circleEnabled = false;
     }
-
 }
 
 void Vectorscope::slotPaintModeChanged(int index)
@@ -288,18 +328,31 @@ void Vectorscope::slotMagnifyChanged()
 
 void Vectorscope::slotActiveMonitorChanged(bool isClipMonitor)
 {
-    qDebug() << "Active monitor changed. Is clip? " << isClipMonitor;
     if (isClipMonitor) {
-        m_activeRender = m_clipRender;
+        m_activeRender = m_clipMonitor->render;
+        disconnect(this, SLOT(slotRenderZoneUpdated()));
+        connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
     } else {
-        m_activeRender = m_projRender;
+        m_activeRender = m_projMonitor->render;
+        disconnect(this, SLOT(slotRenderZoneUpdated()));
+        connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
+    }
+}
+
+void Vectorscope::slotRenderZoneUpdated()
+{
+    qDebug() << "Monitor incoming. New frames total: " << newFrames;
+    // Monitor has shown a new frame
+    newFrames.fetchAndAddRelaxed(1);
+    if (cbAutoRefresh->isChecked()) {
+        prodCalcThread();
     }
 }
 
 void Vectorscope::mousePressEvent(QMouseEvent *)
 {
     // Update the scope on mouse press
-    this->update();
+    prodCalcThread();
 }
 
 void Vectorscope::mouseMoveEvent(QMouseEvent *event)
@@ -307,8 +360,27 @@ void Vectorscope::mouseMoveEvent(QMouseEvent *event)
     // Draw a circle around the center,
     // showing percentage number of the radius length
 
-    circleOnly = true;
+    circleEnabled = true;
     mousePos = event->pos();
-    qDebug() << "event: " << mousePos.x() << "/" << mousePos.y();
     this->update();
 }
+
+void Vectorscope::resizeEvent(QResizeEvent *event)
+{
+    qDebug() << "Resized.";
+    updateDimensions();
+    QWidget::resizeEvent(event);
+}
+
+void Vectorscope::slotScopeCalculationFinished()
+{
+    this->update();
+    qDebug() << "Scope updated.";
+
+    // If auto-refresh is enabled and new frames are available,
+    // just start the next calculation.
+    if (newFrames > 0 && cbAutoRefresh->isChecked()) {
+        qDebug() << "More frames in the queue: " << newFrames;
+        prodCalcThread();
+    }
+}
index d9a2922d2abebdacd4d09e076d9f2568be0c25f1..5f17ad6130c5e8930567fb95d4389d688f3affee 100644 (file)
@@ -1,5 +1,6 @@
 /***************************************************************************
  *   Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com)      *
+ *   This file is part of kdenlive. See www.kdenlive.org.                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
 #ifndef VECTORSCOPE_H
 #define VECTORSCOPE_H
 
+#include <QtCore>
 #include "renderer.h"
+#include "monitor.h"
 #include "ui_vectorscope_ui.h"
 
 class Render;
+class Monitor;
 class Vectorscope_UI;
 
 enum PAINT_MODE { GREEN = 0, ORIG = 1, CHROMA = 2 };
@@ -22,30 +26,56 @@ class Vectorscope : public QWidget, public Ui::Vectorscope_UI {
     Q_OBJECT
 
 public:
-    Vectorscope(Render *projRender, Render *clipRender, QWidget *parent = 0);
+    Vectorscope(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent = 0);
     ~Vectorscope();
 
 protected:
     void paintEvent(QPaintEvent *);
+    void resizeEvent(QResizeEvent *event);
     void mousePressEvent(QMouseEvent *);
     void mouseMoveEvent(QMouseEvent *event);
 
 private:
-    Render *m_projRender;
-    Render *m_clipRender;
+    Monitor *m_projMonitor;
+    Monitor *m_clipMonitor;
     Render *m_activeRender;
+
     QImage m_scope;
     int iPaintMode;
     float scaling;
     QPoint mapToCanvas(QRect inside, QPointF point);
 
-    bool circleOnly;
+    bool circleEnabled;
     QPoint mousePos;
 
+    void updateDimensions();
+    bool initialDimensionUpdateDone;
+    QRect scopeRect;
+    int cw;
+
+
+    QFuture<void> m_scopeCalcThread;
+    void calculateScope();
+
+    /** Prods the Scope calculation thread. If it is running, do nothing. If it is not,
+      run a new thread.
+      Returns true if a new thread has been started. */
+    bool prodCalcThread();
+
+    /** Counts the number of frames that have been rendered in one of the monitors.
+      The frame number will be reset when the vectorscope starts calculating the
+      current frame. */
+    QAtomicInt newFrames;
+
+signals:
+    void signalScopeCalculationFinished();
+
 private slots:
     void slotPaintModeChanged(int index);
     void slotMagnifyChanged();
     void slotActiveMonitorChanged(bool isClipMonitor);
+    void slotRenderZoneUpdated();
+    void slotScopeCalculationFinished();
 
 };