]> git.sesse.net Git - kdenlive/blobdiff - src/clipitem.cpp
Workaround problems with clip transparency. Transparency is now achieved by inserting...
[kdenlive] / src / clipitem.cpp
index 82ec280c84852d6b8ccf0d1e431e62149b45e76a..e9f81b2cfa18a2d7b16663ec29d4c9d763aa82b7 100644 (file)
@@ -40,8 +40,8 @@
 #include "kthumb.h"
 
 
-ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, double fps)
-        : AbstractClipItem(info, QRectF(), fps), m_clip(clip), m_resizeMode(NONE), m_grabPoint(0), m_maxTrack(0), m_hasThumbs(false), startThumbTimer(NULL), endThumbTimer(NULL), m_effectsCounter(1), audioThumbWasDrawn(false), m_opacity(1.0), m_timeLine(0), m_startThumbRequested(false), m_endThumbRequested(false), m_startFade(0), m_endFade(0), m_hover(false), m_selectedEffect(-1), m_speed(1.0), framePixelWidth(0) {
+ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, double fps, bool generateThumbs)
+        : AbstractClipItem(info, QRectF(), fps), m_clip(clip), m_resizeMode(NONE), m_grabPoint(0), m_maxTrack(0), m_hasThumbs(false), startThumbTimer(NULL), endThumbTimer(NULL), audioThumbWasDrawn(false), m_opacity(1.0), m_timeLine(0), m_startThumbRequested(false), m_endThumbRequested(false), m_startFade(0), m_endFade(0), m_hover(false), m_selectedEffect(-1), m_speed(1.0), framePixelWidth(0), m_startPix(QPixmap()), m_endPix(QPixmap()) {
     setRect(0, 0, (info.endPos - info.startPos).frames(fps) - 0.02, (qreal)(KdenliveSettings::trackheight() - 2));
     setPos((qreal) info.startPos.frames(fps), (qreal)(info.track * KdenliveSettings::trackheight()) + 1);
 
@@ -77,9 +77,11 @@ ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, double fps)
         connect(endThumbTimer, SIGNAL(timeout()), this, SLOT(slotGetEndThumb()));
 
         connect(this, SIGNAL(getThumb(int, int)), clip->thumbProducer(), SLOT(extractImage(int, int)));
+        //connect(this, SIGNAL(getThumb(int, int)), clip->thumbProducer(), SLOT(getVideoThumbs(int, int)));
+
         connect(clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap)));
         connect(clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData()));
-        QTimer::singleShot(200, this, SLOT(slotFetchThumbs()));
+        if (generateThumbs) QTimer::singleShot(200, this, SLOT(slotFetchThumbs()));
 
         /*if (m_clip->producer()) {
             videoThumbProducer.init(this, m_clip->producer(), KdenliveSettings::trackheight() * KdenliveSettings::project_display_ratio(), KdenliveSettings::trackheight());
@@ -109,7 +111,7 @@ ClipItem *ClipItem::clone(ItemInfo info) const {
     if (info.cropStart == cropStart()) duplicate->slotSetStartThumb(m_startPix);
     if (info.cropStart + (info.endPos - info.startPos) == m_cropStart + m_cropDuration) duplicate->slotSetEndThumb(m_endPix);
     kDebug() << "// CLoning clip: " << (info.cropStart + (info.endPos - info.startPos)).frames(m_fps) << ", CURRENT end: " << (cropStart() + duration()).frames(m_fps);
-    duplicate->setEffectList(m_effectList);
+    duplicate->setEffectList(m_effectList.clone());
     duplicate->setSpeed(m_speed);
     return duplicate;
 }
@@ -148,6 +150,64 @@ void ClipItem::initEffect(QDomElement effect) {
     }
 }
 
+bool ClipItem::checkKeyFrames() {
+    bool clipEffectsModified = false;
+    for (int ix = 0; ix < m_effectList.count(); ix ++) {
+        QString kfr = keyframes(ix);
+        if (!kfr.isEmpty()) {
+            const QStringList keyframes = kfr.split(";", QString::SkipEmptyParts);
+            QStringList newKeyFrames;
+            bool cutKeyFrame = false;
+            bool modified = false;
+            int lastPos = -1;
+            double lastValue = -1;
+            int start = m_cropStart.frames(m_fps);
+            int end = (m_cropStart + m_cropDuration).frames(m_fps);
+            foreach(const QString str, keyframes) {
+                int pos = str.section(":", 0, 0).toInt();
+                double val = str.section(":", 1, 1).toDouble();
+                if (pos - start < 0) {
+                    // a keyframe is defined before the start of the clip
+                    cutKeyFrame = true;
+                } else if (cutKeyFrame) {
+                    // create new keyframe at clip start, calculate interpolated value
+                    if (pos > start) {
+                        int diff = pos - lastPos;
+                        double ratio = (double)(start - lastPos) / diff;
+                        double newValue = lastValue + (val - lastValue) * ratio;
+                        newKeyFrames.append(QString::number(start) + ":" + QString::number(newValue));
+                        modified = true;
+                    }
+                    cutKeyFrame = false;
+                }
+                if (!cutKeyFrame) {
+                    if (pos > end) {
+                        // create new keyframe at clip end, calculate interpolated value
+                        int diff = pos - lastPos;
+                        if (diff != 0) {
+                            double ratio = (double)(end - lastPos) / diff;
+                            double newValue = lastValue + (val - lastValue) * ratio;
+                            newKeyFrames.append(QString::number(end) + ":" + QString::number(newValue));
+                            modified = true;
+                        }
+                        break;
+                    } else {
+                        newKeyFrames.append(QString::number(pos) + ":" + QString::number(val));
+                    }
+                }
+                lastPos = pos;
+                lastValue = val;
+            }
+            if (modified) {
+                // update KeyFrames
+                setKeyframes(ix, newKeyFrames.join(";"));
+                clipEffectsModified = true;
+            }
+        }
+    }
+    return clipEffectsModified;
+}
+
 void ClipItem::setKeyframes(const int ix, const QString keyframes) {
     QDomElement effect = effectAt(ix);
     if (effect.attribute("disabled") == "1") return;
@@ -336,6 +396,7 @@ void ClipItem::slotSetEndThumb(QImage img) {
 }
 
 void ClipItem::slotThumbReady(int frame, QPixmap pix) {
+    if (scene() == NULL) return;
     QRectF r = sceneBoundingRect();
     double width = m_startPix.width() / projectScene()->scale();
     if (m_startThumbRequested && frame == m_cropStart.frames(m_fps)) {
@@ -594,12 +655,11 @@ void ClipItem::paint(QPainter *painter,
         painter->setPen(Qt::black);
     }
 
-
     // Draw clip name
-    QRectF txtBounding = painter->boundingRect(mapped, Qt::AlignHCenter | Qt::AlignTop, " " + m_clipName + " ");
-    //painter->fillRect(txtBounding, QBrush(QColor(255, 255, 255, 150)));
-    painter->setPen(QColor(0, 0, 0, 180));
-    painter->drawText(txtBounding, Qt::AlignCenter, m_clipName);
+    QRectF txtBounding = painter->boundingRect(mapped, Qt::AlignHCenter | Qt::AlignVCenter, " " + m_clipName + " ");
+    painter->fillRect(txtBounding, QBrush(QColor(0, 0, 0, 150)));
+    //painter->setPen(QColor(0, 0, 0, 180));
+    //painter->drawText(txtBounding, Qt::AlignCenter, m_clipName);
     txtBounding.translate(QPointF(1, 1));
     painter->setPen(QColor(255, 255, 255, 255));
     painter->drawText(txtBounding, Qt::AlignCenter, m_clipName);
@@ -607,11 +667,9 @@ void ClipItem::paint(QPainter *painter,
 
     // draw transition handles on hover
     if (m_hover && itemWidth * scale > 40) {
-        QPainterPath transitionHandle;
-        const int handle_size = 4;
         QPointF p1 = painter->matrix().map(QPointF(0, itemHeight / 2)) + QPointF(10, 0);
         painter->drawPixmap(p1, projectScene()->m_transitionPixmap);
-        p1 = painter->matrix().map(QPointF(itemWidth, itemHeight / 2)) - QPointF(10 + handle_size * 3, 0);
+        p1 = painter->matrix().map(QPointF(itemWidth, itemHeight / 2)) - QPointF(22, 0);
         painter->drawPixmap(p1, projectScene()->m_transitionPixmap);
     }
 
@@ -651,12 +709,7 @@ OPERATIONTYPE ClipItem::operationMode(QPointF pos) {
     }
     QRectF rect = sceneBoundingRect();
     const double scale = projectScene()->scale();
-    double maximumOffset;
-    if (scale > 3) maximumOffset = 25 / scale;
-    else maximumOffset = 6 / scale;
-    QMatrix matrix;
-    matrix.scale(scale, 0);
-    //kDebug()<<"// Item rect: "<<rect.x()<<". pos. "<<pos.x()<<", scale: "<<scale<<", ratio: "<<qAbs((int)(pos.x() - rect.x())) / scale;
+    double maximumOffset = 6 / scale;
 
     if (qAbs((int)(pos.x() - (rect.x() + m_startFade))) < maximumOffset  && qAbs((int)(pos.y() - rect.y())) < 6) {
         if (m_startFade == 0) setToolTip(i18n("Add audio fade"));
@@ -672,10 +725,10 @@ OPERATIONTYPE ClipItem::operationMode(QPointF pos) {
     } else if (qAbs((int)(pos.x() - (rect.x() + rect.width()))) < maximumOffset) {
         setToolTip(i18n("Clip duration: %1s", duration().seconds()));
         return RESIZEEND;
-    } else if (qAbs((int)(pos.x() - (rect.x() + 16))) < maximumOffset && qAbs((int)(pos.y() - (rect.y() + rect.height() / 2 + 5))) < 6) {
+    } else if (qAbs((int)(pos.x() - (rect.x() + 16 / scale))) < maximumOffset && qAbs((int)(pos.y() - (rect.y() + rect.height() / 2 + 9))) < 6) {
         setToolTip(i18n("Add transition"));
         return TRANSITIONSTART;
-    } else if (qAbs((int)(pos.x() - (rect.x() + rect.width() - 21))) < maximumOffset && qAbs((int)(pos.y() - (rect.y() + rect.height() / 2 + 5))) < 6) {
+    } else if (qAbs((int)(pos.x() - (rect.x() + rect.width() - 21 / scale))) < maximumOffset && qAbs((int)(pos.y() - (rect.y() + rect.height() / 2 + 9))) < 6) {
         setToolTip(i18n("Add transition"));
         return TRANSITIONEND;
     }
@@ -716,7 +769,6 @@ QList <CommentedTime> ClipItem::commentedSnapMarkers() const {
 }
 
 void ClipItem::slotPrepareAudioThumb(double pixelForOneFrame, int startpixel, int endpixel, int channels) {
-
     QRectF re =  sceneBoundingRect();
     if (m_clipType == AV) re.setTop(re.y() + re.height() / 2);
 
@@ -876,13 +928,13 @@ void ClipItem::resizeStart(int posx) {
     }
 }
 
-void ClipItem::resizeEnd(int posx) {
+void ClipItem::resizeEnd(int posx, bool updateKeyFrames) {
     const int max = (startPos() - cropStart() + maxDuration()).frames(m_fps) + 1;
     if (posx > max) posx = max;
     if (posx == endPos().frames(m_fps)) return;
     const int previous = (cropStart() + duration()).frames(m_fps);
     AbstractClipItem::resizeEnd(posx);
-    checkEffectsKeyframesPos(previous, (cropStart() + duration()).frames(m_fps), false);
+    if (updateKeyFrames) checkEffectsKeyframesPos(previous, (cropStart() + duration()).frames(m_fps), false);
     if (m_hasThumbs && KdenliveSettings::videothumbnails()) {
         /*connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap)));*/
         endThumbTimer->start(100);
@@ -900,9 +952,11 @@ void ClipItem::checkEffectsKeyframesPos(const int previous, const int current, b
                 // parse keyframes and adjust values
                 const QStringList keyframes = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
                 QMap <int, double> kfr;
+                int pos;
+                double val;
                 foreach(const QString str, keyframes) {
-                    int pos = str.section(":", 0, 0).toInt();
-                    double val = str.section(":", 1, 1).toDouble();
+                    pos = str.section(":", 0, 0).toInt();
+                    val = str.section(":", 1, 1).toDouble();
                     if (pos == previous) kfr[current] = val;
                     else {
                         if (fromStart && pos >= current) kfr[pos] = val;
@@ -930,7 +984,7 @@ QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value)
         // calculate new position.
         if (group() != 0) return pos();
         QPointF newPos = value.toPointF();
-        kDebug() << "/// MOVING CLIP ITEM.------------";
+        //kDebug() << "/// MOVING CLIP ITEM.------------";
         int xpos = projectScene()->getSnapPointForPos((int) newPos.x(), KdenliveSettings::snaptopoints());
         xpos = qMax(xpos, 0);
         newPos.setX(xpos);
@@ -959,7 +1013,7 @@ QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value)
                         sceneShape.translate(newPos);
                         QList<QGraphicsItem*> subitems = scene()->items(sceneShape, Qt::IntersectsItemShape);
                         items.removeAll(this);
-                        for (int j = 0; j < subitems.count(); i++) {
+                        for (int j = 0; j < subitems.count(); j++) {
                             if (subitems.at(j)->type() == type()) return pos();
                         }
                     } else {
@@ -971,7 +1025,7 @@ QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value)
                         sceneShape.translate(newPos);
                         QList<QGraphicsItem*> subitems = scene()->items(sceneShape, Qt::IntersectsItemShape);
                         items.removeAll(this);
-                        for (int j = 0; j < subitems.count(); i++) {
+                        for (int j = 0; j < subitems.count(); j++) {
                             if (subitems.at(j)->type() == type()) return pos();
                         }
                     }
@@ -994,13 +1048,17 @@ QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value)
 }*/
 
 int ClipItem::effectsCounter() {
-    return m_effectsCounter++;
+    return effectsCount() + 1;
 }
 
 int ClipItem::effectsCount() {
     return m_effectList.size();
 }
 
+int ClipItem::hasEffect(const QString &tag, const QString &id) const {
+    return m_effectList.hasEffect(tag, id);
+}
+
 QStringList ClipItem::effectNames() {
     return m_effectList.effectNames();
 }
@@ -1012,6 +1070,7 @@ QDomElement ClipItem::effectAt(int ix) {
 
 void ClipItem::setEffectAt(int ix, QDomElement effect) {
     kDebug() << "CHange EFFECT AT: " << ix << ", CURR: " << m_effectList.at(ix).attribute("tag") << ", NEW: " << effect.attribute("tag");
+    effect.setAttribute("kdenlive_ix", ix + 1);
     m_effectList.insert(ix, effect);
     m_effectList.removeAt(ix + 1);
     m_effectNames = m_effectList.effectNames().join(" / ");
@@ -1028,7 +1087,7 @@ QHash <QString, QString> ClipItem::addEffect(QDomElement effect, bool animate) {
     bool needRepaint = false;
     /*QDomDocument doc;
     doc.appendChild(doc.importNode(effect, true));
-    kDebug() << "///////  CLIP ADD EFFECT: "<< doc.toString();*/
+    kDebug() << "///////  CLIP ADD EFFECT: " << doc.toString();*/
     m_effectList.append(effect);
     effectParams["tag"] = effect.attribute("tag");
     QString effectId = effect.attribute("id");
@@ -1066,7 +1125,7 @@ QHash <QString, QString> ClipItem::addEffect(QDomElement effect, bool animate) {
                     else if (e.attribute("name") == "in") fade += e.attribute("value").toInt();
                 }
             } else {
-                effectParams[e.attribute("name")] =  QString::number(effectParams[e.attribute("name")].toDouble() / f);
+                effectParams[e.attribute("name")] =  QString::number(e.attribute("value").toDouble() / f);
             }
         }
     }
@@ -1131,8 +1190,10 @@ QHash <QString, QString> ClipItem::getEffectArgs(QDomElement effect) {
 
 void ClipItem::deleteEffect(QString index) {
     bool needRepaint = false;
+    QString ix;
     for (int i = 0; i < m_effectList.size(); ++i) {
-        if (m_effectList.at(i).attribute("kdenlive_ix") == index) {
+        ix = m_effectList.at(i).attribute("kdenlive_ix");
+        if (ix == index) {
             if (m_effectList.at(i).attribute("id") == "fadein") {
                 m_startFade = 0;
                 needRepaint = true;
@@ -1141,8 +1202,7 @@ void ClipItem::deleteEffect(QString index) {
                 needRepaint = true;
             }
             m_effectList.removeAt(i);
-            break;
-        }
+        } else if (ix.toInt() > index.toInt()) m_effectList[i].setAttribute("kdenlive_ix", ix.toInt() - 1);
     }
     m_effectNames = m_effectList.effectNames().join(" / ");
     if (needRepaint) update(boundingRect());