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
22 #include <framework/mlt.h>
32 #include <QTextDecoder>
34 static void close_qimg( void* qimg )
36 delete static_cast<QImage*>( qimg );
39 static void close_qpath( void* qpath )
41 delete static_cast<QPainterPath*>( qpath );
44 static void copy_qimage_to_mlt_image( QImage* qImg, uint8_t* mImg )
46 int height = qImg->height();
47 int width = qImg->width();
50 // convert qimage to mlt
54 QRgb* src = (QRgb*) qImg->scanLine( height - y );
58 *mImg++ = qRed( *src );
59 *mImg++ = qGreen( *src );
60 *mImg++ = qBlue( *src );
61 *mImg++ = qAlpha( *src );
67 static void copy_image_to_alpha( uint8_t* image, uint8_t* alpha, int width, int height )
69 register int len = width * height;
70 // Extract the alpha mask from the RGBA image using Duff's Device
71 register uint8_t *s = image + 3; // start on the alpha component
72 register uint8_t *d = alpha;
73 register int n = ( len + 7 ) / 8;
77 case 0: do { *d++ = *s; s += 4;
78 case 7: *d++ = *s; s += 4;
79 case 6: *d++ = *s; s += 4;
80 case 5: *d++ = *s; s += 4;
81 case 4: *d++ = *s; s += 4;
82 case 3: *d++ = *s; s += 4;
83 case 2: *d++ = *s; s += 4;
84 case 1: *d++ = *s; s += 4;
90 /** Check if the qpath needs to be regenerated. Return true if regeneration is required.
93 static bool check_qpath( mlt_properties producer_properties )
96 char new_path_sig[MAX_SIG];
98 // Generate a signature that represents the current properties
99 snprintf( new_path_sig, MAX_SIG, "%s%s%s%s%s%s%s%s%s%s%s",
100 mlt_properties_get( producer_properties, "text" ),
101 mlt_properties_get( producer_properties, "fgcolour" ),
102 mlt_properties_get( producer_properties, "bgcolour" ),
103 mlt_properties_get( producer_properties, "olcolour" ),
104 mlt_properties_get( producer_properties, "outline" ),
105 mlt_properties_get( producer_properties, "align" ),
106 mlt_properties_get( producer_properties, "pad" ),
107 mlt_properties_get( producer_properties, "size" ),
108 mlt_properties_get( producer_properties, "style" ),
109 mlt_properties_get( producer_properties, "weight" ),
110 mlt_properties_get( producer_properties, "encoding" ) );
111 new_path_sig[ MAX_SIG - 1 ] = '\0';
113 // Check if the properties have changed by comparing this signature with the
115 char* last_path_sig = mlt_properties_get( producer_properties, "_path_sig" );
117 if( !last_path_sig || strcmp( new_path_sig, last_path_sig ) )
119 mlt_properties_set( producer_properties, "_path_sig", new_path_sig );
125 static void generate_qpath( mlt_properties producer_properties )
127 QPainterPath* qPath = static_cast<QPainterPath*>( mlt_properties_get_data( producer_properties, "_qpath", NULL ) );
128 int outline = mlt_properties_get_int( producer_properties, "outline" );
129 char* align = mlt_properties_get( producer_properties, "align" );
130 char* style = mlt_properties_get( producer_properties, "style" );
131 char* text = mlt_properties_get( producer_properties, "text" );
132 char* encoding = mlt_properties_get( producer_properties, "encoding" );
133 int pad = mlt_properties_get_int( producer_properties, "pad" );
134 int offset = pad + ( outline / 2 );
138 // Make the path empty
139 *qPath = QPainterPath();
141 // Get the strings to display
142 QTextCodec *codec = QTextCodec::codecForName( encoding );
143 QTextDecoder *decoder = codec->makeDecoder();
144 QString s = decoder->toUnicode( text );
146 QStringList lines = s.split( "\n" );
148 // Configure the font
150 font.setPixelSize( mlt_properties_get_int( producer_properties, "size" ) );
151 font.setFamily( mlt_properties_get( producer_properties, "family" ) );
152 font.setWeight( ( mlt_properties_get_int( producer_properties, "weight" ) / 10 ) -1 );
157 font.setStyle( QFont::StyleItalic );
160 QFontMetrics fm( font );
162 // Determine the text rectangle size
163 height = fm.lineSpacing() * lines.size();
164 for( int i = 0; i < lines.size(); ++i )
166 int line_width = fm.width( lines.at(i) );
167 if( line_width > width ) width = line_width;
170 // Lay out the text in the path
172 int y = fm.ascent() + 1 + offset;
173 for( int i = 0; i < lines.size(); ++i )
175 QString line = lines.at(i);
185 x += ( width - fm.width( line ) ) / 2;
189 x += width - fm.width( line );
192 qPath->addText( x, y, font, line );
193 y += fm.lineSpacing();
196 // Account for outline and pad
198 height += offset * 2;
200 if( width == 0 ) width = 1;
201 if( height == 0 ) height = 1;
202 mlt_properties_set_int( producer_properties, "meta.media.width", width );
203 mlt_properties_set_int( producer_properties, "meta.media.height", height );
206 static bool check_qimage( mlt_properties frame_properties )
208 mlt_producer producer = static_cast<mlt_producer>( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) );
209 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
210 QImage* qImg = static_cast<QImage*>( mlt_properties_get_data( producer_properties, "_qimg", NULL ) );
211 QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ),
212 mlt_properties_get_int( frame_properties, "rescale_height" ) );
213 QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ),
214 mlt_properties_get_int( frame_properties, "meta.media.height" ) );
216 // Check if the last image signature is different from the path signature
218 char* last_img_sig = mlt_properties_get( producer_properties, "_img_sig" );
219 char* path_sig = mlt_properties_get( frame_properties, "_path_sig" );
221 if( !last_img_sig || strcmp( path_sig, last_img_sig ) )
223 mlt_properties_set( producer_properties, "_img_sig", path_sig );
227 // Check if the last image size matches the requested image size
228 QSize output_size = target_size;
229 if( output_size.isEmpty() )
231 output_size = native_size;
233 if( output_size != qImg->size() )
241 static void generate_qimage( mlt_properties frame_properties )
243 mlt_producer producer = static_cast<mlt_producer>( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) );
244 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
245 QImage* qImg = static_cast<QImage*>( mlt_properties_get_data( producer_properties, "_qimg", NULL ) );
246 QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ),
247 mlt_properties_get_int( frame_properties, "rescale_height" ) );
248 QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ),
249 mlt_properties_get_int( frame_properties, "meta.media.height" ) );
250 QPainterPath* qPath = static_cast<QPainterPath*>( mlt_properties_get_data( frame_properties, "_qpath", NULL ) );
251 mlt_color bg_color = mlt_properties_get_color( frame_properties, "_bgcolour" );
252 mlt_color fg_color = mlt_properties_get_color( frame_properties, "_fgcolour" );
253 mlt_color ol_color = mlt_properties_get_color( frame_properties, "_olcolour" );
254 int outline = mlt_properties_get_int( frame_properties, "_outline" );
258 // Create a new image and set up scaling
259 if( !target_size.isEmpty() && target_size != native_size )
261 *qImg = QImage( target_size, QImage::Format_ARGB32 );
262 sx = (qreal)target_size.width() / (qreal)native_size.width();
263 sy = (qreal)target_size.height() / (qreal)native_size.height();
267 *qImg = QImage( native_size, QImage::Format_ARGB32 );
269 qImg->fill( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ).rgba() );
272 QPainter painter( qImg );
273 // Scale the painter rather than the image for better looking results.
274 painter.scale( sx, sy );
275 painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing );
278 pen.setWidth( outline );
281 pen.setColor( QColor( ol_color.r, ol_color.g, ol_color.b, ol_color.a ) );
285 pen.setColor( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ) );
287 painter.setPen( pen );
288 QBrush brush( QColor( fg_color.r, fg_color.g, fg_color.b, fg_color.a ) );
289 painter.setBrush( brush );
290 painter.drawPath( *qPath );
293 static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable )
295 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
296 mlt_producer producer = static_cast<mlt_producer>( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) );
297 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
300 uint8_t* alpha = NULL;
301 QImage* qImg = static_cast<QImage*>( mlt_properties_get_data( producer_properties, "_qimg", NULL ) );
303 mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) );
305 // Regenerate the qimage if necessary
306 if( check_qimage( frame_properties ) == true )
308 generate_qimage( frame_properties );
311 *format = mlt_image_rgb24a;
312 *width = qImg->width();
313 *height = qImg->height();
315 // Allocate and fill the image buffer
316 img_size = mlt_image_format_size( *format, *width, *height, NULL );
317 *buffer = static_cast<uint8_t*>( mlt_pool_alloc( img_size ) );
318 copy_qimage_to_mlt_image( qImg, *buffer );
320 mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) );
322 // Allocate and fill the alpha buffer
323 alpha_size = *width * *height;
324 alpha = static_cast<uint8_t*>( mlt_pool_alloc( alpha_size ) );
325 copy_image_to_alpha( *buffer, alpha, *width, *height );
328 mlt_frame_set_image( frame, *buffer, img_size, mlt_pool_release );
329 mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release );
330 mlt_properties_set_int( frame_properties, "width", *width );
331 mlt_properties_set_int( frame_properties, "height", *height );
336 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
339 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
341 if ( *frame != NULL )
343 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );
344 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
346 // Regenerate the QPainterPath if necessary
347 if( check_qpath( producer_properties ) )
349 generate_qpath( producer_properties );
352 // Give the frame a copy of the painter path
353 QPainterPath* prodPath = static_cast<QPainterPath*>( mlt_properties_get_data( producer_properties, "_qpath", NULL ) );
354 QPainterPath* framePath = new QPainterPath( *prodPath );
355 mlt_properties_set_data( frame_properties, "_qpath", static_cast<void*>( framePath ), 0, close_qpath, NULL );
357 // Pass properties to the frame that will be needed to render the path
358 mlt_properties_set( frame_properties, "_path_sig", mlt_properties_get( producer_properties, "_path_sig" ) );
359 mlt_properties_set( frame_properties, "_bgcolour", mlt_properties_get( producer_properties, "bgcolour" ) );
360 mlt_properties_set( frame_properties, "_fgcolour", mlt_properties_get( producer_properties, "fgcolour" ) );
361 mlt_properties_set( frame_properties, "_olcolour", mlt_properties_get( producer_properties, "olcolour" ) );
362 mlt_properties_set( frame_properties, "_outline", mlt_properties_get( producer_properties, "outline" ) );
363 mlt_properties_set_data( frame_properties, "_producer_qtext", static_cast<void*>( producer ), 0, NULL, NULL );
365 // Set frame properties
366 mlt_properties_set_int( frame_properties, "progressive", 1 );
367 double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" );
368 if ( force_ratio > 0.0 )
369 mlt_properties_set_double( frame_properties, "aspect_ratio", force_ratio );
371 mlt_properties_set_double( frame_properties, "aspect_ratio", 1.0);
373 // Update time code on the frame
374 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
376 // Configure callbacks
377 mlt_frame_push_get_image( *frame, producer_get_image );
380 // Calculate the next time code
381 mlt_producer_prepare_next( producer );
386 static void producer_close( mlt_producer producer )
388 producer->close = NULL;
389 mlt_producer_close( producer );
397 mlt_producer producer_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
399 // Create a new producer object
400 mlt_producer producer = mlt_producer_new( profile );
401 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
403 // Initialize the producer
406 if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) )
408 mlt_producer_close( producer );
412 mlt_properties_set( producer_properties, "text", "" );
413 mlt_properties_set( producer_properties, "fgcolour", "0xffffffff" );
414 mlt_properties_set( producer_properties, "bgcolour", "0x00000000" );
415 mlt_properties_set( producer_properties, "olcolour", "0x00000000" );
416 mlt_properties_set( producer_properties, "outline", "0" );
417 mlt_properties_set( producer_properties, "align", "left" );
418 mlt_properties_set( producer_properties, "pad", "0" );
419 mlt_properties_set( producer_properties, "family", "Sans" );
420 mlt_properties_set( producer_properties, "size", "48" );
421 mlt_properties_set( producer_properties, "style", "normal" );
422 mlt_properties_set( producer_properties, "weight", "400" );
423 mlt_properties_set( producer_properties, "encoding", "UTF-8" );
425 // Parse the filename argument
426 if ( filename == NULL ||
427 !strcmp( filename, "" ) ||
428 strstr( filename, "<producer>" ) )
431 else if( filename[ 0 ] == '+' || strstr( filename, "/+" ) )
433 char *copy = strdup( filename + 1 );
435 if ( strstr( tmp, "/+" ) )
436 tmp = strstr( tmp, "/+" ) + 2;
437 if ( strrchr( tmp, '.' ) )
438 ( *strrchr( tmp, '.' ) ) = '\0';
439 while ( strchr( tmp, '~' ) )
440 ( *strchr( tmp, '~' ) ) = '\n';
441 mlt_properties_set( producer_properties, "text", tmp );
442 mlt_properties_set( producer_properties, "resource", filename );
447 FILE *f = fopen( filename, "r" );
455 while ( fgets( line, 80, f ) )
457 size += strlen( line ) + 1;
460 tmp = (char*)realloc( tmp, size );
466 tmp = strdup( line );
471 if ( tmp && tmp[ strlen( tmp ) - 1 ] == '\n' )
472 tmp[ strlen( tmp ) - 1 ] = '\0';
475 mlt_properties_set( producer_properties, "text", tmp );
476 mlt_properties_set( producer_properties, "resource", filename );
481 // Create QT objects to be reused.
482 mlt_properties_set_data( producer_properties, "_qimg", static_cast<void*>( new QImage() ), 0, close_qimg, NULL );
483 mlt_properties_set_data( producer_properties, "_qpath", static_cast<void*>( new QPainterPath() ), 0, close_qpath, NULL );
485 // Callback registration
486 producer->get_frame = producer_get_frame;
487 producer->close = ( mlt_destructor )producer_close;