X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmodules%2Fqimage%2Fkdenlivetitle_wrapper.cpp;h=2d5bc4d6bf0df243cebfbc52348a552213771f4a;hb=51b566869164b53f76f915959e9298b5dcb31bb3;hp=db5ca400eb59e1dd7184900056f206da5341ba26;hpb=23b02b9b7c4c11153931b18d1edce85914009905;p=mlt diff --git a/src/modules/qimage/kdenlivetitle_wrapper.cpp b/src/modules/qimage/kdenlivetitle_wrapper.cpp old mode 100644 new mode 100755 index db5ca400..2d5bc4d6 --- a/src/modules/qimage/kdenlivetitle_wrapper.cpp +++ b/src/modules/qimage/kdenlivetitle_wrapper.cpp @@ -1,6 +1,7 @@ /* * kdenlivetitle_wrapper.cpp -- kdenlivetitle wrapper * Copyright (c) 2009 Marco Gittler + * Copyright (c) 2009 Jean-Baptiste Mardelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,301 +20,520 @@ #include "kdenlivetitle_wrapper.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040600 +#include +#include +#include +#endif static QApplication *app = NULL; +Q_DECLARE_METATYPE(QTextCursor); -extern "C" +class ImageItem: public QGraphicsItem { - -#include -#include +public: + ImageItem(QImage img) + { + m_img = img; + } + QImage m_img; - static QMutex g_mutex; - void refresh_kdenlivetitle( mlt_producer producer, uint8_t* buffer, int width, int height , double position, int force_refresh ) - { - if (force_refresh) { - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); - loadFromXml( producer, NULL, mlt_properties_get( producer_props, "xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); - } - drawKdenliveTitle( producer, buffer, width, height, position ); - } - - static void qscene_delete( void *data ) - { - QGraphicsScene *scene = ( QGraphicsScene * )data; - delete scene; - scene = NULL; - } + +protected: + +virtual QRectF boundingRect() const +{ + return QRectF(0, 0, m_img.width(), m_img.height()); } +virtual void paint( QPainter *painter, + const QStyleOptionGraphicsItem * /*option*/, + QWidget* ) +{ + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->drawImage(QPoint(), m_img); +} +}; -void drawKdenliveTitle( mlt_producer producer, uint8_t * buffer, int width, int height, double position ) +QRectF stringToRect( const QString & s ) { - - // restore QGraphicsScene - g_mutex.lock(); - mlt_cache_item qscene_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qscene" ); - QGraphicsScene *scene = static_cast( mlt_cache_item_data( qscene_cache, NULL ) ); - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); - - if ( scene == NULL ) - { - int argc = 1; - char* argv[1]; - argv[0] = "xxx"; - if (qApp) { - app = qApp; - } - else { - app=new QApplication( argc,argv ); - } - scene = new QGraphicsScene(0, 0, (qreal) width, (qreal) height, app); - loadFromXml( producer, scene, mlt_properties_get( producer_props, "xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); - mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qscene", scene, 0, ( mlt_destructor )qscene_delete ); - } - - g_mutex.unlock(); - - //must be extracted from kdenlive title - QImage *img = new QImage( width,height,QImage::Format_ARGB32 ); - img->fill( 0 ); - QPainter p1; - p1.begin( img ); - p1.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); - //| QPainter::SmoothPixmapTransform ); - - QRectF m_start = stringToRect( QString( mlt_properties_get( producer_props, "startrect" ) ) ); - QRectF m_end = stringToRect( QString( mlt_properties_get( producer_props, "endrect" ) ) ); - - if (m_start.isNull() && m_end.isNull()) { - scene->render( &p1 ); - } - else { - QPointF topleft=m_start.topLeft()+( m_end.topLeft()-m_start.topLeft() )*position; - QPointF bottomRight=m_start.bottomRight()+( m_end.bottomRight()-m_start.bottomRight() )*position; - const QRectF r1( 0, 0, width, height ); - const QRectF r2( topleft, bottomRight ); - scene->render( &p1, r1, r2 ); - } - p1.end(); - uint8_t *pointer=img->bits(); - QRgb* src = ( QRgb* ) pointer; - for ( int i = 0; i < width * height * 4; i += 4 ) - { - *buffer++=qRed( *src ); - *buffer++=qGreen( *src ); - *buffer++=qBlue( *src ); - *buffer++=qAlpha( *src ); - src++; - } - delete img; + + QStringList l = s.split( ',' ); + if ( l.size() < 4 ) + return QRectF(); + return QRectF( l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), l.at( 3 ).toDouble() ).normalized(); } +QColor stringToColor( const QString & s ) +{ + QStringList l = s.split( ',' ); + if ( l.size() < 4 ) + return QColor(); + return QColor( l.at( 0 ).toInt(), l.at( 1 ).toInt(), l.at( 2 ).toInt(), l.at( 3 ).toInt() ); + ; +} +QTransform stringToTransform( const QString& s ) +{ + QStringList l = s.split( ',' ); + if ( l.size() < 9 ) + return QTransform(); + return QTransform( + 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() + ); +} + +static void qscene_delete( void *data ) +{ + QGraphicsScene *scene = ( QGraphicsScene * )data; + if (scene) delete scene; + scene = NULL; +} + + void loadFromXml( mlt_producer producer, QGraphicsScene *scene, const char *templateXml, const char *templateText ) { - if (scene == NULL) { - mlt_cache_item qscene_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qscene" ); - scene = static_cast( mlt_cache_item_data( qscene_cache, NULL ) ); - if (scene == NULL) return; - } scene->clear(); mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); QDomDocument doc; - QString data(templateXml); - QString replacementText(templateText); + QString data = QString::fromUtf8(templateXml); + QString replacementText = QString::fromUtf8(templateText); doc.setContent(data); - QDomNodeList titles = doc.elementsByTagName( "kdenlivetitle" ); - int maxZValue = 0; - if ( titles.size() ) - { + QDomElement title = doc.documentElement(); - QDomNodeList items = titles.item( 0 ).childNodes(); - for ( int i = 0; i < items.count(); i++ ) + // Check for invalid title + if ( title.isNull() || title.tagName() != "kdenlivetitle" ) return; + + // Check title locale + if ( title.hasAttribute( "LC_NUMERIC" ) ) { + QString locale = title.attribute( "LC_NUMERIC" ); + QLocale::setDefault( locale ); + } + + int originalWidth; + int originalHeight; + if ( title.hasAttribute("width") ) { + originalWidth = title.attribute("width").toInt(); + originalHeight = title.attribute("height").toInt(); + scene->setSceneRect(0, 0, originalWidth, originalHeight); + } + else { + originalWidth = scene->sceneRect().width(); + originalHeight = scene->sceneRect().height(); + } + if ( title.hasAttribute( "out" ) ) { + mlt_properties_set_position( producer_props, "_animation_out", title.attribute( "out" ).toDouble() ); + } + else { + mlt_properties_set_position( producer_props, "_animation_out", mlt_producer_get_out( producer ) ); + } + + mlt_properties_set_int( producer_props, "_original_width", originalWidth ); + mlt_properties_set_int( producer_props, "_original_height", originalHeight ); + + QDomNode node; + QDomNodeList items = title.elementsByTagName("item"); + for ( int i = 0; i < items.count(); i++ ) + { + QGraphicsItem *gitem = NULL; + node = items.item( i ); + QDomNamedNodeMap nodeAttributes = node.attributes(); + int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); + if ( zValue > -1000 ) { - QGraphicsItem *gitem = NULL; - int zValue = items.item( i ).attributes().namedItem( "z-index" ).nodeValue().toInt(); - if ( zValue > -1000 ) + if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsTextItem" ) { - if ( items.item( i ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsTextItem" ) + QDomNamedNodeMap txtProperties = node.namedItem( "content" ).attributes(); + QFont font( txtProperties.namedItem( "font" ).nodeValue() ); + QDomNode propsNode = txtProperties.namedItem( "font-bold" ); + if ( !propsNode.isNull() ) { - QDomNamedNodeMap txtProperties = items.item( i ).namedItem( "content" ).attributes(); - QFont font( txtProperties.namedItem( "font" ).nodeValue() ); - - 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() ); - } + // Old: Bold/Not bold. + font.setBold( propsNode.nodeValue().toInt() ); + } + else + { + // New: Font weight (QFont::) + font.setWeight( txtProperties.namedItem( "font-weight" ).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() ) + { + 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() ); + QColor col( stringToColor( txtProperties.namedItem( "font-color" ).nodeValue() ) ); + QString text = node.namedItem( "content" ).firstChild().nodeValue(); + if ( !replacementText.isEmpty() ) + { + text = text.replace( "%s", replacementText ); + } + QGraphicsTextItem *txt = scene->addText(text, font); + if (txtProperties.namedItem("font-outline").nodeValue().toDouble()>0.0){ + QTextDocument *doc = txt->document(); + // Make sure some that the text item does not request refresh by itself + doc->blockSignals(true); + QTextCursor cursor(doc); + cursor.select(QTextCursor::Document); + QTextCharFormat format; + 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)); - //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() ) - { - 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() ); - QColor col( stringToColor( txtProperties.namedItem( "font-color" ).nodeValue() ) ); - QGraphicsTextItem *txt; - if ( !replacementText.isEmpty() ) - { - QString text = items.item( i ).namedItem( "content" ).firstChild().nodeValue(); - text = text.replace( "%s", replacementText ); - txt = scene->addText( text, font ); - } - else txt = scene->addText( items.item( i ).namedItem( "content" ).firstChild().nodeValue(), font ); + cursor.mergeCharFormat(format); + } else { txt->setDefaultTextColor( col ); - 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()); - } - - gitem = txt; } - else if ( items.item( i ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsRectItem" ) + + // Effects + if (!txtProperties.namedItem( "typewriter" ).isNull()) { + // typewriter effect + mlt_properties_set_int( producer_props, "_animated", 1 ); + QStringList effetData = QStringList() << "typewriter" << text << txtProperties.namedItem( "typewriter" ).nodeValue(); + txt->setData(0, effetData); + if ( !txtProperties.namedItem( "textwidth" ).isNull() ) + txt->setData( 1, txtProperties.namedItem( "textwidth" ).nodeValue() ); + } + + if ( txtProperties.namedItem( "alignment" ).isNull() == false ) { - QString rect = items.item( i ).namedItem( "content" ).attributes().namedItem( "rect" ).nodeValue(); - QString br_str = items.item( i ).namedItem( "content" ).attributes().namedItem( "brushcolor" ).nodeValue(); - QString pen_str = items.item( i ).namedItem( "content" ).attributes().namedItem( "pencolor" ).nodeValue(); - double penwidth = items.item( i ).namedItem( "content" ).attributes().namedItem( "penwidth" ).nodeValue().toDouble(); - QGraphicsRectItem *rec = scene->addRect( stringToRect( rect ), QPen( QBrush( stringToColor( pen_str ) ), penwidth ), QBrush( stringToColor( br_str ) ) ); - gitem = rec; + txt->setTextWidth( txt->boundingRect().width() ); + QTextOption opt = txt->document()->defaultTextOption (); + opt.setAlignment(( Qt::Alignment ) txtProperties.namedItem( "alignment" ).nodeValue().toInt() ); + txt->document()->setDefaultTextOption (opt); } - else if ( items.item( i ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsPixmapItem" ) + if ( !txtProperties.namedItem( "kdenlive-axis-x-inverted" ).isNull() ) { - QString url = items.item( i ).namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); - QPixmap pix( url ); - QGraphicsPixmapItem *rec = scene->addPixmap( pix ); - rec->setData( Qt::UserRole, url ); - gitem = rec; + //txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt()); } - else if ( items.item( i ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsSvgItem" ) + if ( !txtProperties.namedItem( "kdenlive-axis-y-inverted" ).isNull() ) { - QString url = items.item( i ).namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); - //QGraphicsSvgItem *rec = new QGraphicsSvgItem(url); - //m_scene->addItem(rec); - //rec->setData(Qt::UserRole, url); - //gitem = rec; + //txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt()); } + gitem = txt; } - //pos and transform - if ( gitem ) + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsRectItem" ) { - 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() ) ); - 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); + QString rect = node.namedItem( "content" ).attributes().namedItem( "rect" ).nodeValue(); + QString br_str = node.namedItem( "content" ).attributes().namedItem( "brushcolor" ).nodeValue(); + QString pen_str = node.namedItem( "content" ).attributes().namedItem( "pencolor" ).nodeValue(); + double penwidth = node.namedItem( "content" ).attributes().namedItem( "penwidth") .nodeValue().toDouble(); + QGraphicsRectItem *rec = scene->addRect( stringToRect( rect ), QPen( QBrush( stringToColor( pen_str ) ), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin ), QBrush( stringToColor( br_str ) ) ); + gitem = rec; } - if ( items.item( i ).nodeName() == "background" ) + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsPixmapItem" ) { - QColor color = QColor( stringToColor( items.item( i ).attributes().namedItem( "color" ).nodeValue() ) ); - //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt()); - QList items = scene->items(); - for ( int i = 0; i < items.size(); i++ ) - { - if ( items.at( i )->zValue() == -1100 ) - { - (( QGraphicsRectItem * )items.at( i ) )->setBrush( QBrush( color ) ); - break; - } + const QString url = node.namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); + const QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); + QImage img; + if (base64.isEmpty()){ + img.load(url); + }else{ + img.loadFromData(QByteArray::fromBase64(base64.toLatin1())); } + ImageItem *rec = new ImageItem(img); + scene->addItem( rec ); + gitem = rec; } - else if ( items.item( i ).nodeName() == "startviewport" ) + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsSvgItem" ) { - QString rect = items.item( i ).attributes().namedItem( "rect" ).nodeValue(); - - mlt_properties_set( producer_props, "startrect", rect.toUtf8().data() ); + QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue(); + QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); + QGraphicsSvgItem *rec = NULL; + if (base64.isEmpty()){ + rec = new QGraphicsSvgItem(url); + }else{ + rec = new QGraphicsSvgItem(); + QSvgRenderer *renderer= new QSvgRenderer(QByteArray::fromBase64(base64.toLatin1()), rec ); + rec->setSharedRenderer(renderer); + } + if (rec){ + scene->addItem(rec); + gitem = rec; + } } - else if ( items.item( i ).nodeName() == "endviewport" ) - { - QString rect = items.item( i ).attributes().namedItem( "rect" ).nodeValue(); - mlt_properties_set( producer_props, "endrect", rect.toUtf8().data() ); + } + //pos and transform + if ( gitem ) + { + QPointF p( node.namedItem( "position" ).attributes().namedItem( "x" ).nodeValue().toDouble(), + node.namedItem( "position" ).attributes().namedItem( "y" ).nodeValue().toDouble() ); + gitem->setPos( p ); + gitem->setTransform( stringToTransform( node.namedItem( "position" ).firstChild().firstChild().nodeValue() ) ); + int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); + gitem->setZValue( zValue ); + +#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 } } + + QDomNode n = title.firstChildElement("background"); + if (!n.isNull()) { + QColor color = QColor( stringToColor( n.attributes().namedItem( "color" ).nodeValue() ) ); + if (color.alpha() > 0) { + QGraphicsRectItem *rec = scene->addRect(0, 0, scene->width(), scene->height() , QPen( Qt::NoPen ), QBrush( color ) ); + rec->setZValue(-1100); + } + + } + + QString startRect; + n = title.firstChildElement( "startviewport" ); + // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport + if (!n.isNull() && !n.toElement().hasAttribute("x")) + { + startRect = n.attributes().namedItem( "rect" ).nodeValue(); + } + n = title.firstChildElement( "endviewport" ); + // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport + if (!n.isNull() && !n.toElement().hasAttribute("x")) + { + QString rect = n.attributes().namedItem( "rect" ).nodeValue(); + if (startRect != rect) + mlt_properties_set( producer_props, "_endrect", rect.toUtf8().data() ); + } + if (!startRect.isEmpty()) { + mlt_properties_set( producer_props, "_startrect", startRect.toUtf8().data() ); + } return; } -QString colorToString( const QColor& c ) -{ - QString ret = "%1,%2,%3,%4"; - ret = ret.arg( c.red() ).arg( c.green() ).arg( c.blue() ).arg( c.alpha() ); - return ret; -} -QString rectFToString( const QRectF& c ) +void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int height, double position, int force_refresh ) { - QString ret = "%1,%2,%3,%4"; - ret = ret.arg( c.top() ).arg( c.left() ).arg( c.width() ).arg( c.height() ); - return ret; -} + // Obtain the producer + mlt_producer producer = &self->parent; + mlt_profile profile = mlt_service_profile ( MLT_PRODUCER_SERVICE( producer ) ) ; + mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); -QRectF stringToRect( const QString & s ) -{ + // Obtain properties of frame + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + + pthread_mutex_lock( &self->mutex ); + + // Check if user wants us to reload the image + if ( mlt_properties_get( producer_props, "_animated" ) != NULL || force_refresh == 1 || width != self->current_width || height != self->current_height || mlt_properties_get( producer_props, "_endrect" ) != NULL ) + { + //mlt_cache_item_close( self->image_cache ); + self->current_image = NULL; + mlt_properties_set_data( producer_props, "cached_image", NULL, 0, NULL, NULL ); + mlt_properties_set_int( producer_props, "force_reload", 0 ); + } + + if (self->current_image == NULL) { + // restore QGraphicsScene + QGraphicsScene *scene = static_cast (mlt_properties_get_data( producer_props, "qscene", NULL )); - QStringList l = s.split( ',' ); - if ( l.size() < 4 ) - return QRectF(); - return QRectF( l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), l.at( 3 ).toDouble() ).normalized(); -} + if ( force_refresh == 1 && scene ) + { + scene = NULL; + mlt_properties_set_data( producer_props, "qscene", NULL, 0, NULL, NULL ); + } -QColor stringToColor( const QString & s ) -{ - QStringList l = s.split( ',' ); - if ( l.size() < 4 ) - return QColor(); - return QColor( l.at( 0 ).toInt(), l.at( 1 ).toInt(), l.at( 2 ).toInt(), l.at( 3 ).toInt() ); - ; -} -QTransform stringToTransform( const QString& s ) -{ - QStringList l = s.split( ',' ); - if ( l.size() < 9 ) - return QTransform(); - return QTransform( - 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() - ); + if ( scene == NULL ) + { + int argc = 1; + char* argv[1]; + argv[0] = (char*) "xxx"; + + // Warning: all Qt graphic objects (QRect, ...) must be initialized AFTER + // the QApplication is created, otherwise their will be NULL + + if ( app == NULL ) { + if ( qApp ) { + app = qApp; + } + else { +#ifdef linux + if ( getenv("DISPLAY") == 0 ) + { + mlt_log_panic( MLT_PRODUCER_SERVICE( producer ), "Error, cannot render titles without an X11 environment.\nPlease either run melt from an X session or use a fake X server like xvfb:\nxvfb-run -a melt (...)\n" ); + pthread_mutex_unlock( &self->mutex ); + return; + } +#endif + app = new QApplication( argc, argv ); + const char *localename = mlt_properties_get_lcnumeric( MLT_SERVICE_PROPERTIES( MLT_PRODUCER_SERVICE( producer ) ) ); + QLocale::setDefault( QLocale( localename ) ); + } + qRegisterMetaType( "QTextCursor" ); + } + scene = new QGraphicsScene(); + scene->setItemIndexMethod( QGraphicsScene::NoIndex ); + scene->setSceneRect(0, 0, mlt_properties_get_int( properties, "width" ), mlt_properties_get_int( properties, "height" )); + if ( mlt_properties_get( producer_props, "resource" ) && mlt_properties_get( producer_props, "resource" )[0] != '\0' ) + { + // The title has a resource property, so we read all properties from the resource. + // Do not serialize the xmldata + loadFromXml( producer, scene, mlt_properties_get( producer_props, "_xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); + } + else + { + // The title has no resource, all data should be serialized + loadFromXml( producer, scene, mlt_properties_get( producer_props, "xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); + + } + mlt_properties_set_data( producer_props, "qscene", scene, 0, ( mlt_destructor )qscene_delete, NULL ); + } + + QRectF start = stringToRect( QString( mlt_properties_get( producer_props, "_startrect" ) ) ); + QRectF end = stringToRect( QString( mlt_properties_get( producer_props, "_endrect" ) ) ); + + int originalWidth = mlt_properties_get_int( producer_props, "_original_width" ); + int originalHeight= mlt_properties_get_int( producer_props, "_original_height" ); + const QRectF source( 0, 0, width, height ); + if (start.isNull()) { + start = QRectF( 0, 0, originalWidth, originalHeight ); + } + + // Effects + QList items = scene->items(); + QGraphicsTextItem *titem = NULL; + for (int i = 0; i < items.count(); i++) { + titem = static_cast ( items.at( i ) ); + if (titem && !titem->data( 0 ).isNull()) { + QStringList params = titem->data( 0 ).toStringList(); + if (params.at( 0 ) == "typewriter" ) { + // typewriter effect has 2 param values: + // the keystroke delay and a start offset, both in frames + QStringList values = params.at( 2 ).split( ";" ); + int interval = qMax( 0, ( ( int ) position - values.at( 1 ).toInt()) / values.at( 0 ).toInt() ); + QTextCursor cursor = titem->textCursor(); + cursor.movePosition(QTextCursor::EndOfBlock); + // get the font format + QTextCharFormat format = cursor.charFormat(); + cursor.select(QTextCursor::Document); + QString txt = params.at( 1 ).left( interval ); + // If the string to insert is empty, insert a space / linebreak so that we don't loose + // formatting infos for the next iterations + int lines = params.at( 1 ).count( '\n' ); + QString empty = " "; + for (int i = 0; i < lines; i++) + empty.append( "\n " ); + cursor.insertText( txt.isEmpty() ? empty : txt, format ); + if ( !titem->data( 1 ).isNull() ) + titem->setTextWidth( titem->data( 1 ).toDouble() ); + } + } + } + + //must be extracted from kdenlive title + QImage img( width, height, QImage::Format_ARGB32 ); + img.fill( 0 ); + QPainter p1; + p1.begin( &img ); + p1.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); + //| QPainter::SmoothPixmapTransform ); + mlt_position anim_out = mlt_properties_get_position( producer_props, "_animation_out" ); + + if (end.isNull()) + { + scene->render( &p1, source, start, Qt::IgnoreAspectRatio ); + } + else if ( position > anim_out ) { + scene->render( &p1, source, end, Qt::IgnoreAspectRatio ); + } + else { + double percentage = 0; + if ( position && anim_out ) + percentage = position / anim_out; + QPointF topleft = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage; + QPointF bottomRight = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage; + const QRectF r1( topleft, bottomRight ); + scene->render( &p1, source, r1, Qt::IgnoreAspectRatio ); + if ( profile && !profile->progressive ){ + int line=0; + double percentage_next_filed = ( position + 0.5 ) / anim_out; + QPointF topleft_next_field = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage_next_filed; + QPointF bottomRight_next_field = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage_next_filed; + const QRectF r2( topleft_next_field, bottomRight_next_field ); + QImage img1( width, height, QImage::Format_ARGB32 ); + img1.fill( 0 ); + QPainter p2; + p2.begin(&img1); + p2.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); + scene->render(&p2,source,r2, Qt::IgnoreAspectRatio ); + p2.end(); + int next_field_line = ( mlt_properties_get_int( producer_props, "top_field_first" ) ? 1 : 0 ); + for (line = next_field_line ;linecurrent_image = ( uint8_t * )mlt_pool_alloc( size ); + uint8_t *dst = self->current_image; + + for ( int i = 0; i < width * height * 4; i += 4 ) + { + *dst++=qRed( *src ); + *dst++=qGreen( *src ); + *dst++=qBlue( *src ); + *dst++=qAlpha( *src ); + src++; + } + + mlt_properties_set_data( producer_props, "cached_image", self->current_image, size, mlt_pool_release, NULL ); + self->current_width = width; + self->current_height = height; + } + + pthread_mutex_unlock( &self->mutex ); + mlt_properties_set_int( properties, "width", self->current_width ); + mlt_properties_set_int( properties, "height", self->current_height ); } + +