X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fqimage%2Fkdenlivetitle_wrapper.cpp;h=2d5bc4d6bf0df243cebfbc52348a552213771f4a;hb=51b566869164b53f76f915959e9298b5dcb31bb3;hp=da75ee623f35f6abfa1c267e5d069b87665ca89c;hpb=6405ea8c2e089dd120d03a082c1645d5d781de45;p=mlt diff --git a/src/modules/qimage/kdenlivetitle_wrapper.cpp b/src/modules/qimage/kdenlivetitle_wrapper.cpp index da75ee62..2d5bc4d6 100755 --- a/src/modules/qimage/kdenlivetitle_wrapper.cpp +++ b/src/modules/qimage/kdenlivetitle_wrapper.cpp @@ -20,32 +20,35 @@ #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 +#include + +#include + +#include +#include +#include +#include #include #if QT_VERSION >= 0x040600 -#include -#include -#include +#include +#include +#include #endif static QApplication *app = NULL; +Q_DECLARE_METATYPE(QTextCursor); class ImageItem: public QGraphicsItem { @@ -74,26 +77,6 @@ virtual void paint( QPainter *painter, }; -QString rectTransform( QString s, QTransform t ) -{ - QStringList l = s.split( ',' ); - return QString::number(l.at(0).toDouble() * t.m11()) + ',' + QString::number(l.at(1).toDouble() * t.m22()) + ',' + QString::number(l.at(2).toDouble() * t.m11()) + ',' + QString::number(l.at(3).toDouble() * t.m22()); -} - -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 ) -{ - QString ret = "%1,%2,%3,%4"; - ret = ret.arg( c.top() ).arg( c.left() ).arg( c.width() ).arg( c.height() ); - return ret; -} - QRectF stringToRect( const QString & s ) { @@ -140,10 +123,16 @@ void loadFromXml( mlt_producer producer, QGraphicsScene *scene, const char *temp QString replacementText = QString::fromUtf8(templateText); doc.setContent(data); QDomElement title = doc.documentElement(); - + // 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") ) { @@ -165,29 +154,32 @@ void loadFromXml( mlt_producer producer, QGraphicsScene *scene, const char *temp 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; - int zValue = items.item( i ).attributes().namedItem( "z-index" ).nodeValue().toInt(); + node = items.item( i ); + QDomNamedNodeMap nodeAttributes = node.attributes(); + int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); if ( zValue > -1000 ) { - if ( items.item( i ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsTextItem" ) + if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsTextItem" ) { - QDomNamedNodeMap txtProperties = items.item( i ).namedItem( "content" ).attributes(); + QDomNamedNodeMap txtProperties = node.namedItem( "content" ).attributes(); QFont font( txtProperties.namedItem( "font" ).nodeValue() ); - QDomNode node = txtProperties.namedItem( "font-bold" ); - if ( !node.isNull() ) + QDomNode propsNode = txtProperties.namedItem( "font-bold" ); + if ( !propsNode.isNull() ) { - // Old: Bold/Not bold. - font.setBold( node.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.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() ) @@ -199,24 +191,47 @@ void loadFromXml( mlt_producer producer, QGraphicsScene *scene, const char *temp else font.setPixelSize( txtProperties.namedItem( "font-pixel-size" ).nodeValue().toInt() ); QColor col( stringToColor( txtProperties.namedItem( "font-color" ).nodeValue() ) ); - QString text = items.item( i ).namedItem( "content" ).firstChild().nodeValue(); + QString text = node.namedItem( "content" ).firstChild().nodeValue(); if ( !replacementText.isEmpty() ) { text = text.replace( "%s", replacementText ); } QGraphicsTextItem *txt = scene->addText(text, font); - txt->setDefaultTextColor( col ); + 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)); + + cursor.mergeCharFormat(format); + } else { + txt->setDefaultTextColor( col ); + } + + // 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 ) { 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 ); + QTextOption opt = txt->document()->defaultTextOption (); + opt.setAlignment(( Qt::Alignment ) txtProperties.namedItem( "alignment" ).nodeValue().toInt() ); + txt->document()->setDefaultTextOption (opt); } if ( !txtProperties.namedItem( "kdenlive-axis-x-inverted" ).isNull() ) { @@ -228,39 +243,55 @@ void loadFromXml( mlt_producer producer, QGraphicsScene *scene, const char *temp } gitem = txt; } - else if ( items.item( i ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsRectItem" ) + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsRectItem" ) { - 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 ) ) ); + 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; } - else if ( items.item( i ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsPixmapItem" ) + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsPixmapItem" ) { - const QString url = items.item( i ).namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); - QImage img( url ); + 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 ).attributes().namedItem( "type" ).nodeValue() == "QGraphicsSvgItem" ) + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsSvgItem" ) { - const QString url = items.item( i ).namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); - QGraphicsSvgItem *rec = new QGraphicsSvgItem(url); - scene->addItem(rec); - gitem = rec; + 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; + } } } //pos and transform if ( gitem ) { - QPointF p( items.item( i ).namedItem( "position" ).attributes().namedItem( "x" ).nodeValue().toDouble(), - items.item( i ).namedItem( "position" ).attributes().namedItem( "y" ).nodeValue().toDouble() ); + QPointF p( node.namedItem( "position" ).attributes().namedItem( "x" ).nodeValue().toDouble(), + node.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(); + gitem->setTransform( stringToTransform( node.namedItem( "position" ).firstChild().firstChild().nodeValue() ) ); + int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); gitem->setZValue( zValue ); #if QT_VERSION >= 0x040600 @@ -320,6 +351,7 @@ void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int he { // 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 ); // Obtain properties of frame @@ -328,7 +360,7 @@ void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int he pthread_mutex_lock( &self->mutex ); // Check if user wants us to reload the image - if ( force_refresh == 1 || width != self->current_width || height != self->current_height || mlt_properties_get( producer_props, "_endrect" ) != NULL ) + 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; @@ -355,26 +387,40 @@ void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int he // Warning: all Qt graphic objects (QRect, ...) must be initialized AFTER // the QApplication is created, otherwise their will be 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 ); - exit(1); - return; + 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 ); - //fix to let the decimal point for every locale be: "." - setlocale(LC_NUMERIC,"POSIX"); + 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" )); - loadFromXml( producer, scene, mlt_properties_get( producer_props, "xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); + 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 ); } @@ -387,7 +433,38 @@ void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int he 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 ); @@ -405,11 +482,32 @@ void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int he scene->render( &p1, source, end, Qt::IgnoreAspectRatio ); } else { - double percentage = position / anim_out; + 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 ;line