1 /*
2 ** Copyright (C) 2009 Tadej Borovšak <tadeboro@gmail.com>
3 ** Copyright (C) 2010 Robert Chéramy <robert@cheramy.net>
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include "subtitles.h"
21 #include "support.h"
22
23 /* Border width around image (no text is placed there) */
24 #define BORDER 20
25
26 /* Wrap width for subtitles (fraction of image size) */
27 #define WRAP_WIDTH 0.75
28
29 /* ****************************************************************************
30 * Local declarations
31 * ************************************************************************* */
32 static void
33 img_calc_text_pos( gint surface_w,
34 gint surface_h,
35 gint layout_w,
36 gint layout_h,
37 ImgSubPos position,
38 gint *posx,
39 gint *posy );
40
41 static void
42 img_text_ani_fade( cairo_t *cr,
43 PangoLayout *layout,
44 gint sw,
45 gint sh,
46 gint lw,
47 gint lh,
48 gint posx,
49 gint posy,
50 gdouble progress,
51 gdouble *font_color,
52 gdouble *font_bgcolor);
53
54 static void
55 img_text_draw_layout( cairo_t *cr,
56 PangoLayout *layout,
57 gint posx,
58 gint posy,
59 gdouble *font_color,
60 gdouble *font_bgcolor);
61
62 static void
63 img_text_from_left( cairo_t *cr,
64 PangoLayout *layout,
65 gint sw,
66 gint sh,
67 gint lw,
68 gint lh,
69 gint posx,
70 gint posy,
71 gdouble progress,
72 gdouble *font_color,
73 gdouble *font_bgcolor);
74
75 static void
76 img_text_from_right( cairo_t *cr,
77 PangoLayout *layout,
78 gint sw,
79 gint sh,
80 gint lw,
81 gint lh,
82 gint posx,
83 gint posy,
84 gdouble progress,
85 gdouble *font_color,
86 gdouble *font_bgcolor);
87
88 static void
89 img_text_from_top( cairo_t *cr,
90 PangoLayout *layout,
91 gint sw,
92 gint sh,
93 gint lw,
94 gint lh,
95 gint posx,
96 gint posy,
97 gdouble progress,
98 gdouble *font_color,
99 gdouble *font_bgcolor);
100
101 static void
102 img_text_from_bottom( cairo_t *cr,
103 PangoLayout *layout,
104 gint sw,
105 gint sh,
106 gint lw,
107 gint lh,
108 gint posx,
109 gint posy,
110 gdouble progress,
111 gdouble *font_color,
112 gdouble *font_bgcolor);
113
114 static void
115 img_text_grow( cairo_t *cr,
116 PangoLayout *layout,
117 gint sw,
118 gint sh,
119 gint lw,
120 gint lh,
121 gint posx,
122 gint posy,
123 gdouble progress,
124 gdouble *font_color,
125 gdouble *font_bgcolor);
126
127
128 /* ****************************************************************************
129 * Function definitions
130 * ************************************************************************* */
131
132 /*
133 * img_get_text_animation_list:
134 * @animations: location to put list of available text animations
135 *
136 * This function is here to simplify accessing all available animations.
137 *
138 * Any newly added exporters should be listed in array returned by this function
139 * or Imagination WILL NOT create combo box entries for them.
140 *
141 * List that is placed in exporters parameter should be considered read-only and
142 * freed after usage with img_free_text_animation_list. If @animations is NULL,
143 * only number of available animations is returned.
144 *
145 * Return value: Size of list in animations.
146 */
147 gint
img_get_text_animation_list(TextAnimation ** animations)148 img_get_text_animation_list( TextAnimation **animations )
149 {
150 TextAnimation *list; /* List of all animations */
151 gint no_animations = 7; /* Number of animations */
152 gint i = 0;
153
154 if( animations )
155 {
156 /* Populate list */
157 /* DO NOT SHUFFLE THIS LIST! ONLY ADD NEW ANIMATIONS AT THE END OF THE
158 * LIST OR LOADING OF OLD PROJECTS WON'T WORK PROPERLY!!!! */
159 list = g_slice_alloc( sizeof( TextAnimation ) * no_animations );
160
161 /* No animation function (id = 0) */
162 list[i].name = g_strdup( _("None") );
163 list[i].id = i;
164 list[i++].func = NULL;
165
166 list[i].name = g_strdup( _("Fade") );
167 list[i].id = i;
168 list[i++].func = img_text_ani_fade;
169
170 list[i].name = g_strdup( _("Slide from left") );
171 list[i].id = i;
172 list[i++].func = img_text_from_left;
173
174 list[i].name = g_strdup( _("Slide from right") );
175 list[i].id = i;
176 list[i++].func = img_text_from_right;
177
178 list[i].name = g_strdup( _("Slide from top") );
179 list[i].id = i;
180 list[i++].func = img_text_from_top;
181
182 list[i].name = g_strdup( _("Slide from bottom") );
183 list[i].id = i;
184 list[i++].func = img_text_from_bottom;
185
186 list[i].name = g_strdup( _("Grow") );
187 list[i].id = i;
188 list[i++].func = img_text_grow;
189
190 /* FIXME: Add more animations here.
191 *
192 * DO NOT FORGET TO UPDATE no_animations VARIABLE AT THE TOP OF THIS
193 * FUNCTION WHEN ADDING NEW ANIMATIONS!! */
194
195 *animations = list;
196 }
197
198 return( no_animations );
199 }
200
201 /*
202 * img_free_text_animation_list:
203 * @no_animations: number of animations in @animations
204 * @animations: array of TextAnimation structs
205 *
206 * This function takes care of freeing any memory allocated by
207 * img_get_text_animation_list function.
208 */
209 void
img_free_text_animation_list(gint no_animations,TextAnimation * animations)210 img_free_text_animation_list( gint no_animations,
211 TextAnimation *animations )
212 {
213 register gint i;
214
215 for( i = 0; i < no_animations; i++ )
216 g_free( animations[i].name );
217
218 g_slice_free1( sizeof( TextAnimation ) * no_animations, animations );
219 }
220
221 void
img_render_subtitle(cairo_t * cr,gint width,gint height,gdouble zoom,ImgSubPos position,ImgRelPlacing placing,gdouble factor,gdouble offx,gdouble offy,gchar * subtitle,PangoFontDescription * font_desc,gdouble * font_color,gdouble * font_bgcolor,TextAnimationFunc func,gdouble progress)222 img_render_subtitle( cairo_t *cr,
223 gint width,
224 gint height,
225 gdouble zoom,
226 ImgSubPos position,
227 ImgRelPlacing placing,
228 gdouble factor,
229 gdouble offx,
230 gdouble offy,
231 gchar *subtitle,
232 PangoFontDescription *font_desc,
233 gdouble *font_color,
234 gdouble *font_bgcolor,
235 TextAnimationFunc func,
236 gdouble progress )
237 {
238 gint lw, /* Layout width */
239 lh, /* Layout height */
240 posx, /* Final subtitle position */
241 posy;
242 PangoLayout *layout;
243
244 /* Save cairo state */
245 cairo_save( cr );
246
247 /* Transform cairo context to get proper text measurements */
248 if( placing == IMG_REL_PLACING_ORIGINAL_IMAGE )
249 {
250 cairo_scale( cr, factor * zoom, factor * zoom );
251 cairo_translate( cr, offx / factor, offy / factor );
252 }
253 else
254 cairo_scale( cr, zoom, zoom );
255
256 /* Create pango layout and measure it */
257 layout = pango_cairo_create_layout( cr );
258 pango_layout_set_font_description( layout, font_desc );
259 /* Disable wrapping
260 pango_layout_set_wrap( layout, PANGO_WRAP_WORD );
261 */
262 switch( position )
263 {
264 case IMG_SUB_POS_TOP_LEFT:
265 case IMG_SUB_POS_MIDDLE_LEFT:
266 case IMG_SUB_POS_BOTTOM_LEFT:
267 pango_layout_set_alignment( layout, PANGO_ALIGN_LEFT );
268 break;
269
270 case IMG_SUB_POS_TOP_CENTER:
271 case IMG_SUB_POS_MIDDLE_CENTER:
272 case IMG_SUB_POS_BOTTOM_CENTER:
273 pango_layout_set_alignment( layout, PANGO_ALIGN_CENTER );
274 break;
275
276 case IMG_SUB_POS_TOP_RIGHT:
277 case IMG_SUB_POS_MIDDLE_RIGHT:
278 case IMG_SUB_POS_BOTTOM_RIGHT:
279 pango_layout_set_alignment( layout, PANGO_ALIGN_RIGHT );
280 break;
281 }
282 pango_layout_set_text( layout, subtitle, -1 );
283 pango_layout_get_size( layout, &lw, &lh );
284 lw /= PANGO_SCALE;
285
286 /* Disable wrapping
287 if( lw > ( width * WRAP_WIDTH ) )
288 {
289 pango_layout_set_width( layout, width * WRAP_WIDTH * PANGO_SCALE );
290 pango_layout_get_size( layout, &lw, &lh );
291 lw /= PANGO_SCALE;
292 }
293 */
294 lh /= PANGO_SCALE;
295
296 /* Calculate relative dimensions and final position of this subtitle */
297 img_calc_text_pos( width, height, lw, lh, position, &posx, &posy );
298
299 /* Do animation */
300 if( func )
301 (*func)( cr, layout, width, height, lw, lh, posx, posy, progress, font_color, font_bgcolor );
302 else
303 {
304 /* No animation renderer */
305 img_text_draw_layout(cr, layout, posx, posy, font_color, font_bgcolor);
306 }
307
308 /* Destroy layout */
309 g_object_unref( G_OBJECT( layout ) );
310
311 /* Restore cairo */
312 cairo_restore( cr );
313 }
314
315 static void
img_calc_text_pos(gint surface_w,gint surface_h,gint layout_w,gint layout_h,ImgSubPos position,gint * posx,gint * posy)316 img_calc_text_pos( gint surface_w,
317 gint surface_h,
318 gint layout_w,
319 gint layout_h,
320 ImgSubPos position,
321 gint *posx,
322 gint *posy )
323 {
324 switch( position )
325 {
326 case IMG_SUB_POS_TOP_LEFT:
327 *posx = BORDER;
328 *posy = BORDER;
329 break;
330
331 case IMG_SUB_POS_TOP_CENTER:
332 *posx = ( surface_w - layout_w ) / 2;
333 *posy = BORDER;
334 break;
335
336 case IMG_SUB_POS_TOP_RIGHT:
337 *posx = surface_w - BORDER - layout_w;
338 *posy = BORDER;
339 break;
340
341 case IMG_SUB_POS_MIDDLE_LEFT:
342 *posx = BORDER;
343 *posy = ( surface_h - layout_h ) / 2;
344 break;
345
346 case IMG_SUB_POS_MIDDLE_CENTER:
347 *posx = ( surface_w - layout_w ) / 2;
348 *posy = ( surface_h - layout_h ) / 2;
349 break;
350
351 case IMG_SUB_POS_MIDDLE_RIGHT:
352 *posx = surface_w - BORDER - layout_w;
353 *posy = ( surface_h - layout_h ) / 2;
354 break;
355
356 case IMG_SUB_POS_BOTTOM_LEFT:
357 *posx = BORDER;
358 *posy = surface_h - BORDER - layout_h;
359 break;
360
361 case IMG_SUB_POS_BOTTOM_CENTER:
362 *posx = ( surface_w - layout_w ) / 2;
363 *posy = surface_h - BORDER - layout_h;
364 break;
365
366 case IMG_SUB_POS_BOTTOM_RIGHT:
367 *posx = surface_w - BORDER - layout_w;
368 *posy = surface_h - BORDER - layout_h;
369 break;
370 }
371 }
372
373
374 /* ****************************************************************************
375 * Text animation renderers
376 * ************************************************************************* */
377 static void
img_text_ani_fade(cairo_t * cr,PangoLayout * layout,gint sw,gint sh,gint lw,gint lh,gint posx,gint posy,gdouble progress,gdouble * font_color,gdouble * font_bgcolor)378 img_text_ani_fade( cairo_t *cr,
379 PangoLayout *layout,
380 gint sw,
381 gint sh,
382 gint lw,
383 gint lh,
384 gint posx,
385 gint posy,
386 gdouble progress,
387 gdouble *font_color,
388 gdouble *font_bgcolor)
389 {
390 gdouble progress_font_color[4], progress_font_bgcolor[4];
391
392 /* Calculate colors */
393 progress_font_color[0] = font_color[0];
394 progress_font_color[1] = font_color[1];
395 progress_font_color[2] = font_color[2];
396 progress_font_color[3] = font_color[3] * progress;
397
398 progress_font_bgcolor[0] = font_bgcolor[0];
399 progress_font_bgcolor[1] = font_bgcolor[1];
400 progress_font_bgcolor[2] = font_bgcolor[2];
401 progress_font_bgcolor[3] = font_bgcolor[3] * pow(progress, 6);
402
403 /* Paint text */
404 img_text_draw_layout(cr, layout, posx, posy, progress_font_color, progress_font_bgcolor);
405 }
406
407 static void
img_text_draw_layout(cairo_t * cr,PangoLayout * layout,gint posx,gint posy,gdouble * font_color,gdouble * font_bgcolor)408 img_text_draw_layout( cairo_t *cr,
409 PangoLayout *layout,
410 gint posx,
411 gint posy,
412 gdouble *font_color,
413 gdouble *font_bgcolor)
414 {
415 gint x,y;
416
417 /* Draw the background border */
418 cairo_set_source_rgba(cr, font_bgcolor[0],
419 font_bgcolor[1],
420 font_bgcolor[2],
421 font_bgcolor[3] );
422 for (x=-1; x <=1; x++)
423 {
424 for (y=-1; y<=1; y++)
425 {
426 cairo_move_to( cr, posx + x, posy + y );
427 pango_cairo_show_layout( cr, layout );
428 }
429 }
430
431 /* Draw the subtitle */
432 /* Set source color */
433 cairo_set_source_rgba( cr, font_color[0],
434 font_color[1],
435 font_color[2],
436 font_color[3] );
437
438 /* Move to proper place and paint text */
439 cairo_move_to( cr, posx, posy );
440 pango_cairo_show_layout( cr, layout );
441 }
442
443 static void
img_text_from_left(cairo_t * cr,PangoLayout * layout,gint sw,gint sh,gint lw,gint lh,gint posx,gint posy,gdouble progress,gdouble * font_color,gdouble * font_bgcolor)444 img_text_from_left( cairo_t *cr,
445 PangoLayout *layout,
446 gint sw,
447 gint sh,
448 gint lw,
449 gint lh,
450 gint posx,
451 gint posy,
452 gdouble progress,
453 gdouble *font_color,
454 gdouble *font_bgcolor)
455 {
456 img_text_draw_layout(cr, layout,
457 posx * progress - lw * ( 1 - progress ),
458 posy,
459 font_color, font_bgcolor);
460 }
461
462 static void
img_text_from_right(cairo_t * cr,PangoLayout * layout,gint sw,gint sh,gint lw,gint lh,gint posx,gint posy,gdouble progress,gdouble * font_color,gdouble * font_bgcolor)463 img_text_from_right( cairo_t *cr,
464 PangoLayout *layout,
465 gint sw,
466 gint sh,
467 gint lw,
468 gint lh,
469 gint posx,
470 gint posy,
471 gdouble progress,
472 gdouble *font_color,
473 gdouble *font_bgcolor)
474 {
475 img_text_draw_layout(cr, layout,
476 posx * progress + sw * ( 1 - progress ),
477 posy,
478 font_color, font_bgcolor);
479 }
480
481 static void
img_text_from_top(cairo_t * cr,PangoLayout * layout,gint sw,gint sh,gint lw,gint lh,gint posx,gint posy,gdouble progress,gdouble * font_color,gdouble * font_bgcolor)482 img_text_from_top( cairo_t *cr,
483 PangoLayout *layout,
484 gint sw,
485 gint sh,
486 gint lw,
487 gint lh,
488 gint posx,
489 gint posy,
490 gdouble progress,
491 gdouble *font_color,
492 gdouble *font_bgcolor)
493 {
494 img_text_draw_layout(cr, layout,
495 posx,
496 posy * progress - lh * ( 1 - progress ),
497 font_color, font_bgcolor);
498 }
499
500 static void
img_text_from_bottom(cairo_t * cr,PangoLayout * layout,gint sw,gint sh,gint lw,gint lh,gint posx,gint posy,gdouble progress,gdouble * font_color,gdouble * font_bgcolor)501 img_text_from_bottom( cairo_t *cr,
502 PangoLayout *layout,
503 gint sw,
504 gint sh,
505 gint lw,
506 gint lh,
507 gint posx,
508 gint posy,
509 gdouble progress,
510 gdouble *font_color,
511 gdouble *font_bgcolor)
512 {
513 img_text_draw_layout(cr, layout,
514 posx,
515 posy * progress + sh * ( 1 - progress ),
516 font_color, font_bgcolor);
517 }
518
519 static void
img_text_grow(cairo_t * cr,PangoLayout * layout,gint sw,gint sh,gint lw,gint lh,gint posx,gint posy,gdouble progress,gdouble * font_color,gdouble * font_bgcolor)520 img_text_grow( cairo_t *cr,
521 PangoLayout *layout,
522 gint sw,
523 gint sh,
524 gint lw,
525 gint lh,
526 gint posx,
527 gint posy,
528 gdouble progress,
529 gdouble *font_color,
530 gdouble *font_bgcolor)
531 {
532 cairo_translate( cr, posx + lw * 0.5, posy + lh * 0.5 );
533 cairo_scale( cr, progress, progress );
534
535 img_text_draw_layout(cr, layout,
536 - lw * 0.5,
537 - lh * 0.5,
538 font_color, font_bgcolor);
539 }
540
541