]> git.sesse.net Git - mlt/blob - src/modules/gtk2/producer_pango.c
add video_standard enum to mlt_frame, add mlt_consumer_properties, add properties...
[mlt] / src / modules / gtk2 / producer_pango.c
1 /*
2  * producer_pango.c -- a pango-based titler
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "producer_pango.h"
22 #include <framework/mlt_frame.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 #include <pango/pangoft2.h>
27 #include <freetype/freetype.h>
28
29 // special color type used by internal pango routines
30 typedef struct
31 {
32         uint8_t r, g, b, a;
33 } rgba_color;
34
35 // Forward declarations
36 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
37 static void producer_close( mlt_producer parent );
38 static void pango_draw_background( GdkPixbuf *pixbuf, rgba_color bg );
39 static GdkPixbuf *pango_get_pixbuf( const char *markup, rgba_color fg, rgba_color bg, int pad, int align );
40
41 mlt_producer producer_pango_init( const char *markup )
42 {
43         producer_pango this = calloc( sizeof( struct producer_pango_s ), 1 );
44         if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
45         {
46                 mlt_producer producer = &this->parent;
47
48                 producer->get_frame = producer_get_frame;
49                 producer->close = producer_close;
50
51                 this->markup = strdup( markup );
52                 g_type_init();
53
54                 // Get the properties interface
55                 mlt_properties properties = mlt_producer_properties( &this->parent );
56
57                 // Set the default properties
58                 mlt_properties_set_int( properties, "video_standard", mlt_video_standard_pal );
59                 mlt_properties_set_int( properties, "fgcolor", 0xffffffff );
60                 mlt_properties_set_int( properties, "bgcolor", 0x00000000 );
61                 mlt_properties_set_int( properties, "align", pango_align_left );
62                 mlt_properties_set_int( properties, "pad", 0 );
63
64                 return producer;
65         }
66         free( this );
67         return NULL;
68 }
69
70 static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
71 {
72         // Obtain properties of frame
73         mlt_properties properties = mlt_frame_properties( this );
74
75         // May need to know the size of the image to clone it
76         int size = 0;
77
78         // Get the image
79         uint8_t *image = mlt_properties_get_data( properties, "image", &size );
80
81         // Get width and height
82         *width = mlt_properties_get_int( properties, "width" );
83         *height = mlt_properties_get_int( properties, "height" );
84
85         // Clone if necessary
86         if ( writable )
87         {
88                 // Clone our image
89                 uint8_t *copy = malloc( size );
90                 memcpy( copy, image, size );
91
92                 // We're going to pass the copy on
93                 image = copy;
94
95                 // Now update properties so we free the copy after
96                 mlt_properties_set_data( properties, "image", copy, size, free, NULL );
97         }
98
99         // Pass on the image
100         *buffer = image;
101
102         return 0;
103 }
104
105 static uint8_t *producer_get_alpha_mask( mlt_frame this )
106 {
107         // Obtain properties of frame
108         mlt_properties properties = mlt_frame_properties( this );
109
110         // Return the alpha mask
111         return mlt_properties_get_data( properties, "alpha", NULL );
112 }
113
114 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
115 {
116         producer_pango this = producer->child;
117         GdkPixbuf *pixbuf = NULL;
118
119         // Generate a frame
120         *frame = mlt_frame_init( );
121
122         // Obtain properties of frame
123         mlt_properties properties = mlt_frame_properties( *frame );
124
125     // optimization for subsequent iterations on single picture
126         if ( this->image != NULL )
127         {
128                 // Set width/height
129                 mlt_properties_set_int( properties, "width", this->width );
130                 mlt_properties_set_int( properties, "height", this->height );
131
132                 // if picture sequence pass the image and alpha data without destructor
133                 mlt_properties_set_data( properties, "image", this->image, 0, NULL, NULL );
134                 mlt_properties_set_data( properties, "alpha", this->alpha, 0, NULL, NULL );
135
136                 // Set alpha mask call back
137         ( *frame )->get_alpha_mask = producer_get_alpha_mask;
138
139                 // Stack the get image callback
140                 mlt_frame_push_get_image( *frame, producer_get_image );
141
142         }
143         else
144         {
145                 // Obtain properties of producer
146                 mlt_properties props = mlt_producer_properties( producer );
147
148                 // Get properties
149                 int fg = mlt_properties_get_int( props, "fgcolor" );
150                 int bg = mlt_properties_get_int( props, "bgcolor" );
151                 int align = mlt_properties_get_int( props, "align" );
152                 int pad = mlt_properties_get_int( props, "pad" );
153                 rgba_color fgcolor =
154                 {
155                         ( fg & 0xff000000 ) >> 24,
156                         ( fg & 0x00ff0000 ) >> 16,
157                         ( fg & 0x0000ff00 ) >> 8,
158                         ( fg & 0x000000ff )
159                 };
160                 rgba_color bgcolor =
161                 {
162                         ( bg & 0xff000000 ) >> 24,
163                         ( bg & 0x00ff0000 ) >> 16,
164                         ( bg & 0x0000ff00 ) >> 8,
165                         ( bg & 0x000000ff )
166                 };
167                 
168                 // Render the title
169                 pixbuf = pango_get_pixbuf( this->markup, fgcolor, bgcolor, pad, align );
170         }
171
172         // If we have a pixbuf
173         if ( pixbuf )
174         {
175                 // Scale to adjust for sample aspect ratio
176                 if ( mlt_properties_get_int( properties, "video_standard" ) == mlt_video_standard_pal )
177                 {
178                         GdkPixbuf *temp = pixbuf;
179                         GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf,
180                                 (gint) ( (float) gdk_pixbuf_get_width( pixbuf ) * 54.0/59.0),
181                                 gdk_pixbuf_get_height( pixbuf ), GDK_INTERP_HYPER );
182                         pixbuf = scaled;
183                         g_object_unref( temp );
184                 }
185                 else
186                 {
187                         GdkPixbuf *temp = pixbuf;
188                         GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf,
189                                 (gint) ( (float) gdk_pixbuf_get_width( pixbuf ) * 11.0/10.0 ),
190                                 gdk_pixbuf_get_height( pixbuf ), GDK_INTERP_HYPER );
191                         pixbuf = scaled;
192                         g_object_unref( temp );
193                 }
194
195                 // Store width and height
196                 this->width = gdk_pixbuf_get_width( pixbuf );
197                 this->height = gdk_pixbuf_get_height( pixbuf );
198
199                 // Allocate/define image and alpha
200                 uint8_t *image = malloc( this->width * this->height * 2 );
201                 uint8_t *alpha = NULL;
202
203                 // Extract YUV422 and alpha
204                 if ( gdk_pixbuf_get_has_alpha( pixbuf ) )
205                 {
206                         // Allocate the alpha mask
207                         alpha = malloc( this->width * this->height );
208
209                         // Convert the image
210                         mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
211                                                                                   this->width, this->height,
212                                                                                   gdk_pixbuf_get_rowstride( pixbuf ),
213                                                                                   image, alpha );
214                 }
215                 else
216                 { 
217                         // No alpha to extract
218                         mlt_convert_rgb24_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
219                                                                                  this->width, this->height,
220                                                                                  gdk_pixbuf_get_rowstride( pixbuf ),
221                                                                                  image );
222                 }
223
224                 // Finished with pixbuf now
225                 g_object_unref( pixbuf );
226
227                 // Set width/height of frame
228                 mlt_properties_set_int( properties, "width", this->width );
229                 mlt_properties_set_int( properties, "height", this->height );
230
231                 // if single picture, reference the image and alpha in the producer
232                 this->image = image;
233                 this->alpha = alpha;
234
235                 // pass the image and alpha data without destructor
236                 mlt_properties_set_data( properties, "image", image, 0, NULL, NULL );
237                 mlt_properties_set_data( properties, "alpha", alpha, 0, NULL, NULL );
238
239                 // Set alpha call back
240                 ( *frame )->get_alpha_mask = producer_get_alpha_mask;
241
242                 // Push the get_image method
243                 mlt_frame_push_get_image( *frame, producer_get_image );
244         }
245
246         // Update timecode on the frame we're creating
247         mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) );
248
249         // Calculate the next timecode
250         mlt_producer_prepare_next( producer );
251
252         return 0;
253 }
254
255 static void producer_close( mlt_producer parent )
256 {
257         producer_pango this = parent->child;
258         if ( this->markup )
259                 free( this->markup );
260         if ( this->image )
261                 free( this->image );
262         if ( this->alpha )
263                 free( this->alpha );
264         parent->close = NULL;
265         mlt_producer_close( parent );
266         free( this );
267 }
268
269 static void pango_draw_background( GdkPixbuf *pixbuf, rgba_color bg )
270 {
271         int ww = gdk_pixbuf_get_width( pixbuf );
272         int hh = gdk_pixbuf_get_height( pixbuf );
273         uint8_t *p = gdk_pixbuf_get_pixels( pixbuf );
274         int i, j;
275
276         for ( j = 0; j < hh; j++ )
277         {
278                 for ( i = 0; i < ww; i++ )
279                 {
280                         *p++ = bg.r;
281                         *p++ = bg.g;
282                         *p++ = bg.b;
283                         *p++ = bg.a;
284                 }
285         }
286 }
287
288 static GdkPixbuf *pango_get_pixbuf( const char *markup, rgba_color fg, rgba_color bg, int pad, int align )
289 {
290         PangoFT2FontMap *fontmap = (PangoFT2FontMap*) pango_ft2_font_map_new();
291         PangoContext *context = pango_ft2_font_map_create_context( fontmap );
292         PangoLayout *layout = pango_layout_new( context );
293 //      PangoFontDescription *font;
294         int w, h, x;
295         int i, j;
296         GdkPixbuf *pixbuf = NULL;
297         FT_Bitmap bitmap;
298         uint8_t *src = NULL;
299         uint8_t* dest = NULL;
300         int stride;
301
302         pango_ft2_font_map_set_resolution( fontmap, 72, 72 );
303         pango_layout_set_width( layout, -1 ); // set wrapping constraints
304 //      pango_layout_set_font_description( layout, "Sans 48" );
305 //      pango_layout_set_spacing( layout, space );
306         pango_layout_set_alignment( layout, ( PangoAlignment ) align  );
307         pango_layout_set_markup( layout, markup, (markup == NULL ? 0 : strlen( markup ) ) );
308         pango_layout_get_pixel_size( layout, &w, &h );
309
310         pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE /* has alpha */, 8, w + 2 * pad, h + 2 * pad );
311         pango_draw_background( pixbuf, bg );
312
313         stride = gdk_pixbuf_get_rowstride( pixbuf );
314
315         bitmap.width     = w;
316         bitmap.pitch     = 32 * ( ( w + 31 ) / 31 );
317         bitmap.rows      = h;
318         bitmap.buffer    = ( unsigned char * ) calloc( 1, h * bitmap.pitch );
319         bitmap.num_grays = 256;
320         bitmap.pixel_mode = ft_pixel_mode_grays;
321
322         pango_ft2_render_layout( &bitmap, layout, 0, 0 );
323
324         src = bitmap.buffer;
325         x = ( gdk_pixbuf_get_width( pixbuf ) - w - 2 * pad ) * align / 2 + pad;
326         dest = gdk_pixbuf_get_pixels( pixbuf ) + 4 * x + pad * stride;
327         for ( j = 0; j < h; j++ )
328         {
329                 uint8_t *d = dest;
330                 for ( i = 0; i < w; i++ )
331                 {
332                         float a = ( float ) bitmap.buffer[ j * bitmap.pitch + i ] / 255.0;
333                         *d++ = ( int ) ( a * fg.r + ( 1 - a ) * bg.r );
334                         *d++ = ( int ) ( a * fg.g + ( 1 - a ) * bg.g );
335                         *d++ = ( int ) ( a * fg.b + ( 1 - a ) * bg.b );
336                         *d++ = ( int ) ( a * fg.a + ( 1 - a ) * bg.a );
337                 }
338                 dest += stride;
339         }
340         free( bitmap.buffer );
341
342         g_object_unref( layout );
343         g_object_unref( context );
344         g_object_unref( fontmap );
345
346         return pixbuf;
347 }
348