2 * producer_qtext.c -- text generating producer
3 * Copyright (C) 2013 Brian Matherly
4 * Author: Brian Matherly <pez4brian@yahoo.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <framework/mlt.h>
25 #include <QApplication>
33 #include <QTextDecoder>
35 /** Init QT (if necessary)
38 static bool init_qt( mlt_producer producer )
42 argv[0] = (char*) "xxx";
47 if ( getenv("DISPLAY") == 0 )
49 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" );
53 new QApplication( argc, argv );
54 const char *localename = mlt_properties_get_lcnumeric( MLT_SERVICE_PROPERTIES( MLT_PRODUCER_SERVICE( producer ) ) );
55 QLocale::setDefault( QLocale( localename ) );
60 static void close_qimg( void* qimg )
62 delete static_cast<QImage*>( qimg );
65 static void close_qpath( void* qpath )
67 delete static_cast<QPainterPath*>( qpath );
70 static void copy_qimage_to_mlt_image( QImage* qImg, uint8_t* mImg )
72 int height = qImg->height();
73 int width = qImg->width();
76 // convert qimage to mlt
80 QRgb* src = (QRgb*) qImg->scanLine( height - y );
84 *mImg++ = qRed( *src );
85 *mImg++ = qGreen( *src );
86 *mImg++ = qBlue( *src );
87 *mImg++ = qAlpha( *src );
93 static void copy_image_to_alpha( uint8_t* image, uint8_t* alpha, int width, int height )
95 register int len = width * height;
96 // Extract the alpha mask from the RGBA image using Duff's Device
97 register uint8_t *s = image + 3; // start on the alpha component
98 register uint8_t *d = alpha;
99 register int n = ( len + 7 ) / 8;
103 case 0: do { *d++ = *s; s += 4;
104 case 7: *d++ = *s; s += 4;
105 case 6: *d++ = *s; s += 4;
106 case 5: *d++ = *s; s += 4;
107 case 4: *d++ = *s; s += 4;
108 case 3: *d++ = *s; s += 4;
109 case 2: *d++ = *s; s += 4;
110 case 1: *d++ = *s; s += 4;
116 /** Check if the qpath needs to be regenerated. Return true if regeneration is required.
119 static bool check_qpath( mlt_properties producer_properties )
122 char new_path_sig[MAX_SIG];
124 // Generate a signature that represents the current properties
125 snprintf( new_path_sig, MAX_SIG, "%s%s%s%s%s%s%s%s%s%s%s",
126 mlt_properties_get( producer_properties, "text" ),
127 mlt_properties_get( producer_properties, "fgcolour" ),
128 mlt_properties_get( producer_properties, "bgcolour" ),
129 mlt_properties_get( producer_properties, "olcolour" ),
130 mlt_properties_get( producer_properties, "outline" ),
131 mlt_properties_get( producer_properties, "align" ),
132 mlt_properties_get( producer_properties, "pad" ),
133 mlt_properties_get( producer_properties, "size" ),
134 mlt_properties_get( producer_properties, "style" ),
135 mlt_properties_get( producer_properties, "weight" ),
136 mlt_properties_get( producer_properties, "encoding" ) );
137 new_path_sig[ MAX_SIG - 1 ] = '\0';
139 // Check if the properties have changed by comparing this signature with the
141 char* last_path_sig = mlt_properties_get( producer_properties, "_path_sig" );
143 if( !last_path_sig || strcmp( new_path_sig, last_path_sig ) )
145 mlt_properties_set( producer_properties, "_path_sig", new_path_sig );
151 static void generate_qpath( mlt_properties producer_properties )
153 QPainterPath* qPath = static_cast<QPainterPath*>( mlt_properties_get_data( producer_properties, "_qpath", NULL ) );
154 int outline = mlt_properties_get_int( producer_properties, "outline" );
155 char* align = mlt_properties_get( producer_properties, "align" );
156 char* style = mlt_properties_get( producer_properties, "style" );
157 char* text = mlt_properties_get( producer_properties, "text" );
158 char* encoding = mlt_properties_get( producer_properties, "encoding" );
159 int pad = mlt_properties_get_int( producer_properties, "pad" );
160 int offset = pad + ( outline / 2 );
164 // Make the path empty
165 *qPath = QPainterPath();
167 // Get the strings to display
168 QTextCodec *codec = QTextCodec::codecForName( encoding );
169 QTextDecoder *decoder = codec->makeDecoder();
170 QString s = decoder->toUnicode( text );
172 QStringList lines = s.split( "\n" );
174 // Configure the font
176 font.setPixelSize( mlt_properties_get_int( producer_properties, "size" ) );
177 font.setFamily( mlt_properties_get( producer_properties, "family" ) );
178 font.setWeight( ( mlt_properties_get_int( producer_properties, "weight" ) / 10 ) -1 );
183 font.setStyle( QFont::StyleItalic );
186 QFontMetrics fm( font );
188 // Determine the text rectangle size
189 height = fm.lineSpacing() * lines.size();
190 for( int i = 0; i < lines.size(); ++i )
192 int line_width = fm.width( lines.at(i) );
193 if( line_width > width ) width = line_width;
196 // Lay out the text in the path
198 int y = fm.ascent() + 1 + offset;
199 for( int i = 0; i < lines.size(); ++i )
201 QString line = lines.at(i);
211 x += ( width - fm.width( line ) ) / 2;
215 x += width - fm.width( line );
218 qPath->addText( x, y, font, line );
219 y += fm.lineSpacing();
222 // Account for outline and pad
224 height += offset * 2;
226 if( width == 0 ) width = 1;
227 if( height == 0 ) height = 1;
228 mlt_properties_set_int( producer_properties, "meta.media.width", width );
229 mlt_properties_set_int( producer_properties, "meta.media.height", height );
232 static bool check_qimage( mlt_properties frame_properties )
234 mlt_producer producer = static_cast<mlt_producer>( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) );
235 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
236 QImage* qImg = static_cast<QImage*>( mlt_properties_get_data( producer_properties, "_qimg", NULL ) );
237 QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ),
238 mlt_properties_get_int( frame_properties, "rescale_height" ) );
239 QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ),
240 mlt_properties_get_int( frame_properties, "meta.media.height" ) );
242 // Check if the last image signature is different from the path signature
244 char* last_img_sig = mlt_properties_get( producer_properties, "_img_sig" );
245 char* path_sig = mlt_properties_get( frame_properties, "_path_sig" );
247 if( !last_img_sig || strcmp( path_sig, last_img_sig ) )
249 mlt_properties_set( producer_properties, "_img_sig", path_sig );
253 // Check if the last image size matches the requested image size
254 QSize output_size = target_size;
255 if( output_size.isEmpty() )
257 output_size = native_size;
259 if( output_size != qImg->size() )
267 static void generate_qimage( mlt_properties frame_properties )
269 mlt_producer producer = static_cast<mlt_producer>( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) );
270 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
271 QImage* qImg = static_cast<QImage*>( mlt_properties_get_data( producer_properties, "_qimg", NULL ) );
272 QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ),
273 mlt_properties_get_int( frame_properties, "rescale_height" ) );
274 QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ),
275 mlt_properties_get_int( frame_properties, "meta.media.height" ) );
276 QPainterPath* qPath = static_cast<QPainterPath*>( mlt_properties_get_data( frame_properties, "_qpath", NULL ) );
277 mlt_color bg_color = mlt_properties_get_color( frame_properties, "_bgcolour" );
278 mlt_color fg_color = mlt_properties_get_color( frame_properties, "_fgcolour" );
279 mlt_color ol_color = mlt_properties_get_color( frame_properties, "_olcolour" );
280 int outline = mlt_properties_get_int( frame_properties, "_outline" );
284 // Create a new image and set up scaling
285 if( !target_size.isEmpty() && target_size != native_size )
287 *qImg = QImage( target_size, QImage::Format_ARGB32 );
288 sx = (qreal)target_size.width() / (qreal)native_size.width();
289 sy = (qreal)target_size.height() / (qreal)native_size.height();
293 *qImg = QImage( native_size, QImage::Format_ARGB32 );
295 qImg->fill( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ).rgba() );
298 QPainter painter( qImg );
299 // Scale the painter rather than the image for better looking results.
300 painter.scale( sx, sy );
301 painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing );
304 pen.setWidth( outline );
307 pen.setColor( QColor( ol_color.r, ol_color.g, ol_color.b, ol_color.a ) );
311 pen.setColor( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ) );
313 painter.setPen( pen );
314 QBrush brush( QColor( fg_color.r, fg_color.g, fg_color.b, fg_color.a ) );
315 painter.setBrush( brush );
316 painter.drawPath( *qPath );
319 static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable )
321 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
322 mlt_producer producer = static_cast<mlt_producer>( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) );
323 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
326 uint8_t* alpha = NULL;
327 QImage* qImg = static_cast<QImage*>( mlt_properties_get_data( producer_properties, "_qimg", NULL ) );
329 mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) );
331 // Regenerate the qimage if necessary
332 if( check_qimage( frame_properties ) == true )
334 generate_qimage( frame_properties );
337 *format = mlt_image_rgb24a;
338 *width = qImg->width();
339 *height = qImg->height();
341 // Allocate and fill the image buffer
342 img_size = mlt_image_format_size( *format, *width, *height, NULL );
343 *buffer = static_cast<uint8_t*>( mlt_pool_alloc( img_size ) );
344 copy_qimage_to_mlt_image( qImg, *buffer );
346 mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) );
348 // Allocate and fill the alpha buffer
349 alpha_size = *width * *height;
350 alpha = static_cast<uint8_t*>( mlt_pool_alloc( alpha_size ) );
351 copy_image_to_alpha( *buffer, alpha, *width, *height );
354 mlt_frame_set_image( frame, *buffer, img_size, mlt_pool_release );
355 mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release );
356 mlt_properties_set_int( frame_properties, "width", *width );
357 mlt_properties_set_int( frame_properties, "height", *height );
362 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
365 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
367 if ( *frame != NULL )
369 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );
370 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
372 // Regenerate the QPainterPath if necessary
373 if( check_qpath( producer_properties ) )
375 generate_qpath( producer_properties );
378 // Give the frame a copy of the painter path
379 QPainterPath* prodPath = static_cast<QPainterPath*>( mlt_properties_get_data( producer_properties, "_qpath", NULL ) );
380 QPainterPath* framePath = new QPainterPath( *prodPath );
381 mlt_properties_set_data( frame_properties, "_qpath", static_cast<void*>( framePath ), 0, close_qpath, NULL );
383 // Pass properties to the frame that will be needed to render the path
384 mlt_properties_set( frame_properties, "_path_sig", mlt_properties_get( producer_properties, "_path_sig" ) );
385 mlt_properties_set( frame_properties, "_bgcolour", mlt_properties_get( producer_properties, "bgcolour" ) );
386 mlt_properties_set( frame_properties, "_fgcolour", mlt_properties_get( producer_properties, "fgcolour" ) );
387 mlt_properties_set( frame_properties, "_olcolour", mlt_properties_get( producer_properties, "olcolour" ) );
388 mlt_properties_set( frame_properties, "_outline", mlt_properties_get( producer_properties, "outline" ) );
389 mlt_properties_set_data( frame_properties, "_producer_qtext", static_cast<void*>( producer ), 0, NULL, NULL );
391 // Set frame properties
392 mlt_properties_set_int( frame_properties, "progressive", 1 );
393 double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" );
394 if ( force_ratio > 0.0 )
395 mlt_properties_set_double( frame_properties, "aspect_ratio", force_ratio );
397 mlt_properties_set_double( frame_properties, "aspect_ratio", 1.0);
399 // Update time code on the frame
400 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
402 // Configure callbacks
403 mlt_frame_push_get_image( *frame, producer_get_image );
406 // Calculate the next time code
407 mlt_producer_prepare_next( producer );
412 static void producer_close( mlt_producer producer )
414 producer->close = NULL;
415 mlt_producer_close( producer );
423 mlt_producer producer_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
425 // Create a new producer object
426 mlt_producer producer = mlt_producer_new( profile );
427 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
429 // Initialize the producer
432 if( init_qt( producer ) == false )
434 mlt_producer_close( producer );
438 mlt_properties_set( producer_properties, "text", "" );
439 mlt_properties_set( producer_properties, "fgcolour", "0xffffffff" );
440 mlt_properties_set( producer_properties, "bgcolour", "0x00000000" );
441 mlt_properties_set( producer_properties, "olcolour", "0x00000000" );
442 mlt_properties_set( producer_properties, "outline", "0" );
443 mlt_properties_set( producer_properties, "align", "left" );
444 mlt_properties_set( producer_properties, "pad", "0" );
445 mlt_properties_set( producer_properties, "family", "Sans" );
446 mlt_properties_set( producer_properties, "size", "48" );
447 mlt_properties_set( producer_properties, "style", "normal" );
448 mlt_properties_set( producer_properties, "weight", "400" );
449 mlt_properties_set( producer_properties, "encoding", "UTF-8" );
451 // Parse the filename argument
452 if ( filename == NULL ||
453 !strcmp( filename, "" ) ||
454 strstr( filename, "<producer>" ) )
457 else if( filename[ 0 ] == '+' || strstr( filename, "/+" ) )
459 char *copy = strdup( filename + 1 );
461 if ( strstr( tmp, "/+" ) )
462 tmp = strstr( tmp, "/+" ) + 2;
463 if ( strrchr( tmp, '.' ) )
464 ( *strrchr( tmp, '.' ) ) = '\0';
465 while ( strchr( tmp, '~' ) )
466 ( *strchr( tmp, '~' ) ) = '\n';
467 mlt_properties_set( producer_properties, "text", tmp );
468 mlt_properties_set( producer_properties, "resource", filename );
473 FILE *f = fopen( filename, "r" );
481 while ( fgets( line, 80, f ) )
483 size += strlen( line ) + 1;
486 tmp = (char*)realloc( tmp, size );
492 tmp = strdup( line );
497 if ( tmp && tmp[ strlen( tmp ) - 1 ] == '\n' )
498 tmp[ strlen( tmp ) - 1 ] = '\0';
501 mlt_properties_set( producer_properties, "text", tmp );
502 mlt_properties_set( producer_properties, "resource", filename );
507 // Create QT objects to be reused.
508 mlt_properties_set_data( producer_properties, "_qimg", static_cast<void*>( new QImage() ), 0, close_qimg, NULL );
509 mlt_properties_set_data( producer_properties, "_qpath", static_cast<void*>( new QPainterPath() ), 0, close_qpath, NULL );
511 // Callback registration
512 producer->get_frame = producer_get_frame;
513 producer->close = ( mlt_destructor )producer_close;