]> git.sesse.net Git - mlt/blob - src/modules/gtk2/producer_pango.c
add sample aspect ratio scaling output to producer_pixbuf, fix a bug in rgb to yuv...
[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                 this->is_pal = 1;
53                 g_type_init();
54
55                 return producer;
56         }
57         free( this );
58         return NULL;
59 }
60
61 static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
62 {
63         // Obtain properties of frame
64         mlt_properties properties = mlt_frame_properties( this );
65
66         // May need to know the size of the image to clone it
67         int size = 0;
68
69         // Get the image
70         uint8_t *image = mlt_properties_get_data( properties, "image", &size );
71
72         // Get width and height
73         *width = mlt_properties_get_int( properties, "width" );
74         *height = mlt_properties_get_int( properties, "height" );
75
76         // Clone if necessary
77         if ( writable )
78         {
79                 // Clone our image
80                 uint8_t *copy = malloc( size );
81                 memcpy( copy, image, size );
82
83                 // We're going to pass the copy on
84                 image = copy;
85
86                 // Now update properties so we free the copy after
87                 mlt_properties_set_data( properties, "image", copy, size, free, NULL );
88         }
89
90         // Pass on the image
91         *buffer = image;
92
93         return 0;
94 }
95
96 static uint8_t *producer_get_alpha_mask( mlt_frame this )
97 {
98         // Obtain properties of frame
99         mlt_properties properties = mlt_frame_properties( this );
100
101         // Return the alpha mask
102         return mlt_properties_get_data( properties, "alpha", NULL );
103 }
104
105 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
106 {
107         producer_pango this = producer->child;
108         GdkPixbuf *pixbuf = NULL;
109
110         // Generate a frame
111         *frame = mlt_frame_init( );
112
113         // Obtain properties of frame
114         mlt_properties properties = mlt_frame_properties( *frame );
115
116     // optimization for subsequent iterations on single picture
117         if ( this->image != NULL )
118         {
119                 // Set width/height
120                 mlt_properties_set_int( properties, "width", this->width );
121                 mlt_properties_set_int( properties, "height", this->height );
122
123                 // if picture sequence pass the image and alpha data without destructor
124                 mlt_properties_set_data( properties, "image", this->image, 0, NULL, NULL );
125                 mlt_properties_set_data( properties, "alpha", this->alpha, 0, NULL, NULL );
126
127                 // Set alpha mask call back
128         ( *frame )->get_alpha_mask = producer_get_alpha_mask;
129
130                 // Stack the get image callback
131                 mlt_frame_push_get_image( *frame, producer_get_image );
132
133         }
134         else
135         {
136                 // the following four will be replaced by properties
137                 rgba_color fg = { 0xff, 0xff, 0xff, 0xff };
138                 rgba_color bg = { 0, 0, 0, 0x7f };
139                 int pad = 8;
140                 int align = 0; /* left */
141                 
142                 // Render the title
143                 pixbuf = pango_get_pixbuf( this->markup, fg, bg, pad, align );
144         }
145
146         // If we have a pixbuf
147         if ( pixbuf )
148         {
149                 // Scale to adjust for sample aspect ratio
150                 if ( this->is_pal )
151                 {
152                         GdkPixbuf *temp = pixbuf;
153                         GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf,
154                                 (gint) ( (float) gdk_pixbuf_get_width( pixbuf ) * 54.0/59.0),
155                                 gdk_pixbuf_get_height( pixbuf ), GDK_INTERP_HYPER );
156                         pixbuf = scaled;
157                         g_object_unref( temp );
158                 }
159                 else
160                 {
161                         GdkPixbuf *temp = pixbuf;
162                         GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf,
163                                 (gint) ( (float) gdk_pixbuf_get_width( pixbuf ) * 11.0/10.0 ),
164                                 gdk_pixbuf_get_height( pixbuf ), GDK_INTERP_HYPER );
165                         pixbuf = scaled;
166                         g_object_unref( temp );
167                 }
168
169                 // Store width and height
170                 this->width = gdk_pixbuf_get_width( pixbuf );
171                 this->height = gdk_pixbuf_get_height( pixbuf );
172
173                 // Allocate/define image and alpha
174                 uint8_t *image = malloc( this->width * this->height * 2 );
175                 uint8_t *alpha = NULL;
176
177                 // Extract YUV422 and alpha
178                 if ( gdk_pixbuf_get_has_alpha( pixbuf ) )
179                 {
180                         // Allocate the alpha mask
181                         alpha = malloc( this->width * this->height );
182
183                         // Convert the image
184                         mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
185                                                                                   this->width, this->height,
186                                                                                   gdk_pixbuf_get_rowstride( pixbuf ),
187                                                                                   image, alpha );
188                 }
189                 else
190                 { 
191                         // No alpha to extract
192                         mlt_convert_rgb24_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
193                                                                                  this->width, this->height,
194                                                                                  gdk_pixbuf_get_rowstride( pixbuf ),
195                                                                                  image );
196                 }
197
198                 // Finished with pixbuf now
199                 g_object_unref( pixbuf );
200
201                 // Set width/height of frame
202                 mlt_properties_set_int( properties, "width", this->width );
203                 mlt_properties_set_int( properties, "height", this->height );
204
205                 // if single picture, reference the image and alpha in the producer
206                 this->image = image;
207                 this->alpha = alpha;
208
209                 // pass the image and alpha data without destructor
210                 mlt_properties_set_data( properties, "image", image, 0, NULL, NULL );
211                 mlt_properties_set_data( properties, "alpha", alpha, 0, NULL, NULL );
212
213                 // Set alpha call back
214                 ( *frame )->get_alpha_mask = producer_get_alpha_mask;
215
216                 // Push the get_image method
217                 mlt_frame_push_get_image( *frame, producer_get_image );
218         }
219
220         // Update timecode on the frame we're creating
221         mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) );
222
223         // Calculate the next timecode
224         mlt_producer_prepare_next( producer );
225
226         return 0;
227 }
228
229 static void producer_close( mlt_producer parent )
230 {
231         producer_pango this = parent->child;
232         if ( this->markup )
233                 free( this->markup );
234         if ( this->image )
235                 free( this->image );
236         if ( this->alpha )
237                 free( this->alpha );
238         parent->close = NULL;
239         mlt_producer_close( parent );
240         free( this );
241 }
242
243 static void pango_draw_background( GdkPixbuf *pixbuf, rgba_color bg )
244 {
245         int ww = gdk_pixbuf_get_width( pixbuf );
246         int hh = gdk_pixbuf_get_height( pixbuf );
247         uint8_t *p = gdk_pixbuf_get_pixels( pixbuf );
248         int i, j;
249
250         for ( j = 0; j < hh; j++ )
251         {
252                 for ( i = 0; i < ww; i++ )
253                 {
254                         *p++ = bg.r;
255                         *p++ = bg.g;
256                         *p++ = bg.b;
257                         *p++ = bg.a;
258                 }
259         }
260 }
261
262 static GdkPixbuf *pango_get_pixbuf( const char *markup, rgba_color fg, rgba_color bg, int pad, int align )
263 {
264         PangoFT2FontMap *fontmap = (PangoFT2FontMap*) pango_ft2_font_map_new();
265         PangoContext *context = pango_ft2_font_map_create_context( fontmap );
266         PangoLayout *layout = pango_layout_new( context );
267 //      PangoFontDescription *font;
268         int w, h, x;
269         int i, j;
270         GdkPixbuf *pixbuf = NULL;
271         FT_Bitmap bitmap;
272         uint8_t *src = NULL;
273         uint8_t* dest = NULL;
274         int stride;
275
276         pango_ft2_font_map_set_resolution( fontmap, 72, 72 );
277         pango_layout_set_width( layout, -1 ); // set wrapping constraints
278 //      pango_layout_set_font_description( layout, "Sans 48" );
279 //      pango_layout_set_spacing( layout, space );
280         pango_layout_set_alignment( layout, ( PangoAlignment ) align  );
281         pango_layout_set_markup( layout, markup, (markup == NULL ? 0 : strlen( markup ) ) );
282         pango_layout_get_pixel_size( layout, &w, &h );
283
284         pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE /* has alpha */, 8, w + 2 * pad, h + 2 * pad );
285         pango_draw_background( pixbuf, bg );
286
287         stride = gdk_pixbuf_get_rowstride( pixbuf );
288
289         bitmap.width     = w;
290         bitmap.pitch     = 32 * ( ( w + 31 ) / 31 );
291         bitmap.rows      = h;
292         bitmap.buffer    = ( unsigned char * ) calloc( 1, h * bitmap.pitch );
293         bitmap.num_grays = 256;
294         bitmap.pixel_mode = ft_pixel_mode_grays;
295
296         pango_ft2_render_layout( &bitmap, layout, 0, 0 );
297
298         src = bitmap.buffer;
299         x = ( gdk_pixbuf_get_width( pixbuf ) - w - 2 * pad ) * align / 2 + pad;
300         dest = gdk_pixbuf_get_pixels( pixbuf ) + 4 * x + pad * stride;
301         for ( j = 0; j < h; j++ )
302         {
303                 uint8_t *d = dest;
304                 for ( i = 0; i < w; i++ )
305                 {
306                         float a = ( float ) bitmap.buffer[ j * bitmap.pitch + i ] / 255.0;
307                         *d++ = ( int ) ( a * fg.r + ( 1 - a ) * bg.r );
308                         *d++ = ( int ) ( a * fg.g + ( 1 - a ) * bg.g );
309                         *d++ = ( int ) ( a * fg.b + ( 1 - a ) * bg.b );
310                         *d++ = ( int ) ( a * fg.a + ( 1 - a ) * bg.a );
311                 }
312                 dest += stride;
313         }
314         free( bitmap.buffer );
315
316         g_object_unref( layout );
317         g_object_unref( context );
318         g_object_unref( fontmap );
319
320         return pixbuf;
321 }
322