]> git.sesse.net Git - kdenlive/blobdiff - src/titledocument.cpp
Add 3d rotation to objects in titler, based on patch from Till Theato
[kdenlive] / src / titledocument.cpp
index ad2cc5b77d7a8c898fb8ce36216891d5436de9b3..ed9bc0b2815d9f6ce85e485a0d7d6464272ea913 100644 (file)
@@ -20,6 +20,9 @@
 #include <KDebug>
 #include <KTemporaryFile>
 #include <kio/netaccess.h>
+#include <KApplication>
+#include <KLocale>
+#include <KMessageBox>
 
 #include <QGraphicsScene>
 #include <QDomElement>
 #include <QGraphicsSvgItem>
 #include <QFontInfo>
 #include <QFile>
+#include <QTextCursor>
 
+#if QT_VERSION >= 0x040600
+#include <QGraphicsEffect>
+#include <QGraphicsBlurEffect>
+#include <QGraphicsDropShadowEffect>
+#endif
 
 TitleDocument::TitleDocument()
 {
     m_scene = NULL;
 }
 
-void TitleDocument::setScene(QGraphicsScene* _scene)
+void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
 {
     m_scene = _scene;
+    m_width = width;
+    m_height = height;
 }
 
-QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv)
+QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* endv)
 {
     QDomDocument doc;
 
     QDomElement main = doc.createElement("kdenlivetitle");
+    main.setAttribute("width", m_width);
+    main.setAttribute("height", m_height);
     doc.appendChild(main);
 
     foreach(QGraphicsItem* item, m_scene->items()) {
@@ -79,20 +92,52 @@ QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonIt
             content.appendChild(doc.createTextNode(t->toPlainText()));
             font = t->font();
             content.setAttribute("font", font.family());
-            content.setAttribute("font-bold", font.bold());
+            content.setAttribute("font-weight", font.weight());
             content.setAttribute("font-pixel-size", font.pixelSize());
             content.setAttribute("font-italic", font.italic());
             content.setAttribute("font-underline", font.underline());
-            content.setAttribute("font-color", colorToString(t->defaultTextColor()));
+            {
+                QTextCursor cursor(t->document());
+                cursor.select(QTextCursor::Document);
+                QColor fontcolor = cursor.charFormat().foreground().color();
+                content.setAttribute("font-color", colorToString(fontcolor));
+                if (!t->data(101).isNull()) content.setAttribute("font-outline", t->data(101).toDouble());
+                if (!t->data(102).isNull()) content.setAttribute("font-outline-color", colorToString(QColor(t->data(102).toString())));
+            }
+            if (!t->data(100).isNull()) {
+                QStringList effectParams = t->data(100).toStringList();
+                QString effectName = effectParams.takeFirst();
+                content.setAttribute(effectName, effectParams.join(";"));
+            }
+
+            // Only save when necessary.
+            if (t->data(OriginXLeft).toInt() == AxisInverted) {
+                content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
+            }
+            if (t->data(OriginYTop).toInt() == AxisInverted) {
+                content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
+            }
+            if (t->textWidth() != -1) {
+                content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
+            }
             break;
         default:
             continue;
         }
+
+        // position
         QDomElement pos = doc.createElement("position");
         pos.setAttribute("x", item->pos().x());
         pos.setAttribute("y", item->pos().y());
         QTransform transform = item->transform();
         QDomElement tr = doc.createElement("transform");
+        if (!item->data(ZOOMFACTOR).isNull()) {
+            tr.setAttribute("zoom", QString::number(item->data(ZOOMFACTOR).toInt()));
+        }
+        if (!item->data(ROTATEFACTOR).isNull()) {
+            QList<QVariant> rotlist = item->data(ROTATEFACTOR).toList();
+            tr.setAttribute("rotation", QString("%1,%2,%3").arg(rotlist[0].toDouble()).arg(rotlist[1].toDouble()).arg(rotlist[2].toDouble()));
+        }
         tr.appendChild(doc.createTextNode(
                            QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
                                transform.m11()).arg(transform.m12()).arg(transform.m13()).arg(transform.m21()).arg(transform.m22()).arg(transform.m23()).arg(transform.m31()).arg(transform.m32()).arg(transform.m33())
@@ -101,24 +146,40 @@ QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonIt
         e.setAttribute("z-index", item->zValue());
         pos.appendChild(tr);
 
+#if QT_VERSION >= 0x040600
+        // effects
+        QGraphicsEffect *eff = item->graphicsEffect();
+        if (eff) {
+            QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
+            QDomElement effect = doc.createElement("effect");
+            if (blur) {
+                effect.setAttribute("type", "blur");
+                effect.setAttribute("blurradius", blur->blurRadius());
+            } else {
+                QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
+                if (shadow) {
+                    effect.setAttribute("type", "shadow");
+                    effect.setAttribute("blurradius", shadow->blurRadius());
+                    effect.setAttribute("xoffset", shadow->xOffset());
+                    effect.setAttribute("yoffset", shadow->yOffset());
+                }
+            }
+            e.appendChild(effect);
+        }
+#endif
 
         e.appendChild(pos);
         e.appendChild(content);
-        if (item->zValue() > -1100) main.appendChild(e);
+        if (item->zValue() > -1000) main.appendChild(e);
     }
     if (startv && endv) {
         QDomElement endp = doc.createElement("endviewport");
         QDomElement startp = doc.createElement("startviewport");
-        endp.setAttribute("x", endv->pos().x());
-        endp.setAttribute("y", endv->pos().y());
-        endp.setAttribute("size", endv->sceneBoundingRect().width() / 2);
-
-        startp.setAttribute("x", startv->pos().x());
-        startp.setAttribute("y", startv->pos().y());
-        startp.setAttribute("size", startv->sceneBoundingRect().width() / 2);
+        QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
+        endp.setAttribute("rect", rectFToString(r));
+        QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
+        startp.setAttribute("rect", rectFToString(r2));
 
-        startp.setAttribute("z-index", startv->zValue());
-        endp.setAttribute("z-index", endv->zValue());
         main.appendChild(startp);
         main.appendChild(endp);
     }
@@ -148,12 +209,13 @@ QColor TitleDocument::getBackgroundColor()
 }
 
 
-bool TitleDocument::saveDocument(const KUrl& url, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv)
+bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
 {
     if (!m_scene)
         return false;
 
     QDomDocument doc = xml(startv, endv);
+    doc.documentElement().setAttribute("out", out);
     KTemporaryFile tmpfile;
     if (!tmpfile.open()) {
         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
@@ -170,34 +232,31 @@ bool TitleDocument::saveDocument(const KUrl& url, QGraphicsPolygonItem* startv,
     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
 }
 
-int TitleDocument::loadDocument(const KUrl& url, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv)
+int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
 {
-    QString tmpfile;
-    QDomDocument doc;
-    if (!m_scene)
-        return -1;
-
-    if (KIO::NetAccess::download(url, tmpfile, 0)) {
-        QFile file(tmpfile);
-        if (file.open(QIODevice::ReadOnly)) {
-            doc.setContent(&file, false);
-            file.close();
-        } else
-            return -1;
-        KIO::NetAccess::removeTempFile(tmpfile);
-        return loadFromXml(doc, startv, endv);
+    QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
+    //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
+    if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
+        int doc_width = doc.documentElement().attribute("width").toInt();
+        int doc_height = doc.documentElement().attribute("height").toInt();
+        if (doc_width != m_width || doc_height != m_height) {
+            KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
+            //TODO: convert using QTransform
+            m_width = doc_width;
+            m_height = doc_height;
+        }
     }
-    return -1;
-}
+    //TODO: get default title duration instead of hardcoded one
+    if (doc.documentElement().hasAttribute("out"))
+        *out = doc.documentElement().attribute("out").toInt();
+    else
+        *out = 125;
 
-int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* /*startv*/, QGraphicsPolygonItem* /*endv*/)
-{
-    QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
     int maxZValue = 0;
     if (titles.size()) {
 
         QDomNodeList items = titles.item(0).childNodes();
-        for (int i = 0;i < items.count();i++) {
+        for (int i = 0; i < items.count(); i++) {
             QGraphicsItem *gitem = NULL;
             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
@@ -205,19 +264,69 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* /*startv*
                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
                     QFont font(txtProperties.namedItem("font").nodeValue());
-                    font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
+
+                    QDomNode node = txtProperties.namedItem("font-bold");
+                    if (!node.isNull()) {
+                        // Old: Bold/Not bold.
+                        font.setBold(node.nodeValue().toInt());
+                    } else {
+                        // New: Font weight (QFont::)
+                        font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
+                    }
+                    //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
                     // Older Kdenlive version did not store pixel size but point size
                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
+                        KMessageBox::information(kapp->activeWindow(), i18n("Some of your text clips were saved with size in points, which means different sizes on different displays. They will be converted to pixel size, making them portable, but you could have to adjust their size."), i18n("Text Clips Updated"));
                         QFont f2;
                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
                         font.setPixelSize(QFontInfo(f2).pixelSize());
-                    } else font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
+                    } else
+                        font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
-                    txt->setDefaultTextColor(col);
+                    QTextCursor cursor(txt->document());
+                    cursor.select(QTextCursor::Document);
+                    QTextCharFormat format;
+                    if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
+                        txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
+                        txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
+                        format.setTextOutline(
+                            QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
+                                 txtProperties.namedItem("font-outline").nodeValue().toDouble(),
+                                 Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)
+                        );
+
+                    }
+                    format.setForeground(QBrush(col));
+                    cursor.mergeCharFormat(format);
                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
+                    if (txtProperties.namedItem("alignment").isNull() == false) {
+                        txt->setTextWidth(txt->boundingRect().width());
+                        QTextCursor cur = txt->textCursor();
+                        QTextBlockFormat format = cur.blockFormat();
+                        format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
+                        cur.select(QTextCursor::Document);
+                        cur.setBlockFormat(format);
+                        txt->setTextCursor(cur);
+                        cur.clearSelection();
+                        txt->setTextCursor(cur);
+                    }
+
+                    if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
+                        txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
+                    }
+                    if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
+                        txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
+                    }
+
+                    // Effects
+                    if (!txtProperties.namedItem("typewriter").isNull()) {
+                        QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
+                        txt->setData(100, effData);
+                    }
+
                     gitem = txt;
                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
@@ -245,12 +354,36 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* /*startv*
                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
                 gitem->setPos(p);
-                gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
+                QDomElement trans = items.item(i).namedItem("position").firstChild().toElement();
+                gitem->setTransform(stringToTransform(trans.firstChild().nodeValue()));
+                QString rotate = trans.attribute("rotation");
+                if (!rotate.isEmpty()) gitem->setData(ROTATEFACTOR, stringToList(rotate));
+                QString zoom = trans.attribute("zoom");
+                if (!zoom.isEmpty()) gitem->setData(ZOOMFACTOR, zoom.toInt());
                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
                 if (zValue > maxZValue) maxZValue = zValue;
                 gitem->setZValue(zValue);
                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
+
+#if QT_VERSION >= 0x040600
+                // effects
+                QDomNode eff = items.item(i).namedItem("effect");
+                if (!eff.isNull()) {
+                    QDomElement e = eff.toElement();
+                    if (e.attribute("type") == "blur") {
+                        QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
+                        blur->setBlurRadius(e.attribute("blurradius").toInt());
+                        gitem->setGraphicsEffect(blur);
+                    } else if (e.attribute("type") == "shadow") {
+                        QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
+                        shadow->setBlurRadius(e.attribute("blurradius").toInt());
+                        shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
+                        gitem->setGraphicsEffect(shadow);
+                    }
+                }
+#endif
             }
+
             if (items.item(i).nodeName() == "background") {
                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
@@ -262,21 +395,17 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* /*startv*
                         break;
                     }
                 }
-            } /*else if (items.item(i).nodeName() == "startviewport" && startv) {
-                    QPointF p(items.item(i).attributes().namedItem("x").nodeValue().toDouble(), items.item(i).attributes().namedItem("y").nodeValue().toDouble());
-                    double width = items.item(i).attributes().namedItem("size").nodeValue().toDouble();
-                    QRectF rect(-width, -width / aspect_ratio, width*2.0, width / aspect_ratio*2.0);
-                    kDebug() << width << rect;
-                    startv->setPolygon(rect);
-                    startv->setPos(p);
-                } else if (items.item(i).nodeName() == "endviewport" && endv) {
-                    QPointF p(items.item(i).attributes().namedItem("x").nodeValue().toDouble(), items.item(i).attributes().namedItem("y").nodeValue().toDouble());
-                    double width = items.item(i).attributes().namedItem("size").nodeValue().toDouble();
-                    QRectF rect(-width, -width / aspect_ratio, width*2.0, width / aspect_ratio*2.0);
-                    kDebug() << width << rect;
-                    endv->setPolygon(rect);
-                    endv->setPos(p);
-                }*/
+            } else if (items.item(i).nodeName() == "startviewport" && startv) {
+                QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
+                QRectF r = stringToRect(rect);
+                startv->setRect(0, 0, r.width(), r.height());
+                startv->setPos(r.topLeft());
+            } else if (items.item(i).nodeName() == "endviewport" && endv) {
+                QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
+                QRectF r = stringToRect(rect);
+                endv->setRect(0, 0, r.width(), r.height());
+                endv->setPos(r.topLeft());
+            }
         }
     }
     return maxZValue;
@@ -292,7 +421,7 @@ QString TitleDocument::colorToString(const QColor& c)
 QString TitleDocument::rectFToString(const QRectF& c)
 {
     QString ret = "%1,%2,%3,%4";
-    ret = ret.arg(c.top()).arg(c.left()).arg(c.width()).arg(c.height());
+    ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
     return ret;
 }
 
@@ -302,7 +431,7 @@ QRectF TitleDocument::stringToRect(const QString & s)
     QStringList l = s.split(',');
     if (l.size() < 4)
         return QRectF();
-    return QRectF(l[0].toDouble(), l[1].toDouble(), l[2].toDouble(), l[3].toDouble()).normalized();
+    return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
 }
 
 QColor TitleDocument::stringToColor(const QString & s)
@@ -310,16 +439,37 @@ QColor TitleDocument::stringToColor(const QString & s)
     QStringList l = s.split(',');
     if (l.size() < 4)
         return QColor();
-    return QColor(l[0].toInt(), l[1].toInt(), l[2].toInt(), l[3].toInt());;
+    return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
 }
+
 QTransform TitleDocument::stringToTransform(const QString& s)
 {
     QStringList l = s.split(',');
     if (l.size() < 9)
         return QTransform();
     return QTransform(
-               l[0].toDouble(), l[1].toDouble(), l[2].toDouble(),
-               l[3].toDouble(), l[4].toDouble(), l[5].toDouble(),
-               l[6].toDouble(), l[7].toDouble(), l[8].toDouble()
+               l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
+               l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
+               l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
            );
 }
+
+QList<QVariant> TitleDocument::stringToList(const QString & s)
+{
+    QStringList l = s.split(',');
+    if (l.size() < 3)
+        return QList<QVariant>();
+    return QList<QVariant>() << QVariant(l.at(0).toDouble()) << QVariant(l.at(1).toDouble()) << QVariant(l.at(2).toDouble());
+}
+
+int TitleDocument::frameWidth() const
+{
+    return m_width;
+}
+
+int TitleDocument::frameHeight() const
+{
+    return m_height;
+}
+
+