r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / plugins / titler / title.C
blob67fc38452305b8abcc494b100a2b2d739432b477
1 // Originally developed by Heroine Virtual Ltd.
2 // Support for multiple encodings, outline (stroke) by 
3 // Andraz Tori <Andraz.tori1@guest.arnes.si>
6 #include "clip.h"
7 #include "colormodels.h"
8 #include "filexml.h"
9 #include "filesystem.h"
10 #include "ft2build.h"
11 #include FT_GLYPH_H
12 #include FT_BBOX_H
13 #include FT_OUTLINE_H
14 #include FT_STROKER_H
16 #include "picon_png.h"
17 #include "plugincolors.h"
18 #include "title.h"
19 #include "titlewindow.h"
22 #include <errno.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <endian.h>
27 #include <byteswap.h>
28 #include <iconv.h>
31 #include <libintl.h>
32 #define _(String) gettext(String)
33 #define gettext_noop(String) String
34 #define N_(String) gettext_noop (String)
37 #define ZERO (1.0 / 64.0)
39 #define FONT_SEARCHPATH "fonts"
40 //#define FONT_SEARCHPATH "/usr/X11R6/lib/X11/fonts"
43 REGISTER_PLUGIN(TitleMain)
46 TitleConfig::TitleConfig()
48         style = 0;
49         color = BLACK;
50         color_stroke = 0xff0000;
51         size = 24;
52         motion_strategy = NO_MOTION;
53         loop = 0;
54         hjustification = JUSTIFY_CENTER;
55         vjustification = JUSTIFY_MID;
56         fade_in = 0.0;
57         fade_out = 0.0;
58         x = 0.0;
59         y = 0.0;
60         dropshadow = 10;
61         sprintf(font, "fixed");
62         sprintf(text, _("hello world"));
63 #define DEFAULT_ENCODING "ISO8859-1"
64         sprintf(encoding, DEFAULT_ENCODING);
65         pixels_per_second = 1.0;
66         timecode = 0;
67         stroke_width = 1.0;
70 // Does not test equivalency but determines if redrawing text is necessary.
71 int TitleConfig::equivalent(TitleConfig &that)
73         return dropshadow == that.dropshadow &&
74                 style == that.style &&
75                 size == that.size &&
76                 color == that.color &&
77                 color_stroke == that.color_stroke &&
78                 stroke_width == that.stroke_width &&
79                 timecode == that.timecode && 
80                 hjustification == that.hjustification &&
81                 vjustification == that.vjustification &&
82                 EQUIV(pixels_per_second, that.pixels_per_second) &&
83                 !strcasecmp(font, that.font) &&
84                 !strcasecmp(encoding, that.encoding) &&
85                 !strcmp(text, that.text);
88 void TitleConfig::copy_from(TitleConfig &that)
90         strcpy(font, that.font);
91         style = that.style;
92         size = that.size;
93         color = that.color;
94         color_stroke = that.color_stroke;
95         stroke_width = that.stroke_width;
96         pixels_per_second = that.pixels_per_second;
97         motion_strategy = that.motion_strategy;
98         loop = that.loop;
99         hjustification = that.hjustification;
100         vjustification = that.vjustification;
101         fade_in = that.fade_in;
102         fade_out = that.fade_out;
103         x = that.x;
104         y = that.y;
105         dropshadow = that.dropshadow;
106         timecode = that.timecode;
107         strcpy(text, that.text);
108         strcpy(encoding, that.encoding);
111 void TitleConfig::interpolate(TitleConfig &prev, 
112         TitleConfig &next, 
113         int64_t prev_frame, 
114         int64_t next_frame, 
115         int64_t current_frame)
117         strcpy(font, prev.font);
118         strcpy(encoding, prev.encoding);
119         style = prev.style;
120         size = prev.size;
121         color = prev.color;
122         color_stroke = prev.color_stroke;
123         stroke_width = prev.stroke_width;
124         motion_strategy = prev.motion_strategy;
125         loop = prev.loop;
126         hjustification = prev.hjustification;
127         vjustification = prev.vjustification;
128         fade_in = prev.fade_in;
129         fade_out = prev.fade_out;
130         pixels_per_second = prev.pixels_per_second;
131         strcpy(text, prev.text);
133         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
134         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
137 //      this->x = prev.x * prev_scale + next.x * next_scale;
138 //      this->y = prev.y * prev_scale + next.y * next_scale;
139         this->x = prev.x;
140         this->y = prev.y;
141         timecode = prev.timecode;
142 //      this->dropshadow = (int)(prev.dropshadow * prev_scale + next.dropshadow * next_scale);
143         this->dropshadow = prev.dropshadow;
162 FontEntry::FontEntry()
164         path = 0;
165         foundary = 0;
166         family = 0;
167         weight = 0;
168         slant = 0;
169         swidth = 0;
170         adstyle = 0;
171         spacing = 0;
172         registry = 0;
173         encoding = 0;
174         fixed_title = 0;
175         fixed_style = 0;
178 FontEntry::~FontEntry()
180         if(path) delete [] path;
181         if(foundary) delete [] foundary;
182         if(family) delete [] family;
183         if(weight) delete [] weight;
184         if(slant) delete [] slant;
185         if(swidth) delete [] swidth;
186         if(adstyle) delete [] adstyle;
187         if(spacing) delete [] spacing;
188         if(registry) delete [] registry;
189         if(encoding) delete [] encoding;
190         if(fixed_title) delete [] fixed_title;
193 void FontEntry::dump()
195         printf("%s: %s %s %s %s %s %s %d %d %d %d %s %d %s %s\n",
196                 path,
197                 foundary,
198                 family,
199                 weight,
200                 slant,
201                 swidth,
202                 adstyle,
203                 pixelsize,
204                 pointsize,
205                 xres,
206                 yres,
207                 spacing,
208                 avg_width,
209                 registry,
210                 encoding);
213 TitleGlyph::TitleGlyph()
215         char_code = 0;
216         c=0;
217         data = 0;
218         data_stroke = 0;
222 TitleGlyph::~TitleGlyph()
224 //printf("TitleGlyph::~TitleGlyph 1\n");
225         if(data) delete data;
226         if(data_stroke) delete data_stroke;
239 GlyphPackage::GlyphPackage() : LoadPackage()
243 GlyphUnit::GlyphUnit(TitleMain *plugin, GlyphEngine *server)
244  : LoadClient(server)
246         this->plugin = plugin;
247         current_font = 0;
248         freetype_library = 0;
249         freetype_face = 0;
252 GlyphUnit::~GlyphUnit()
254         if(freetype_library) FT_Done_FreeType(freetype_library);
257 void GlyphUnit::process_package(LoadPackage *package)
259         GlyphPackage *pkg = (GlyphPackage*)package;
260         TitleGlyph *glyph = pkg->glyph;
261         int result = 0;
263         if(!freetype_library)
264         {
265                 current_font = plugin->get_font();
267                 if(plugin->load_freetype_face(freetype_library,
268                         freetype_face,
269                         current_font->path))
270                 {
271                         printf(_("GlyphUnit::process_package FT_New_Face failed.\n"));
272                         result = 1;
273                 }
274                 else
275                 {
276                         FT_Set_Pixel_Sizes(freetype_face, plugin->config.size, 0);
277                 }
278         }
280         if(!result)
281         {
282                 int gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
284 //printf("GlyphUnit::process_package 1 %c\n", glyph->char_code);
285 // Char not found
286                 if (gindex == 0) 
287                 {
288 // carrige return
289                         if (glyph->char_code != 10)  
290                                 printf(_("GlyphUnit::process_package FT_Load_Char failed - char: %i.\n"),
291                                         glyph->char_code);
292 // Prevent a crash here
293                         glyph->width = 8;
294                         glyph->height = 8;
295                         glyph->pitch = 8;
296                         glyph->left = 9;
297                         glyph->top = 9;
298                         glyph->freetype_index = 0;
299                         glyph->advance_w = 8;
300                         glyph->data = new VFrame(0,
301                                 8,
302                                 8,
303                                 BC_A8,
304                                 8);
305                         glyph->data->clear_frame();
306                         glyph->data_stroke = 0;
310 // create outline glyph
311                         if (plugin->config.stroke_width >= ZERO && 
312                                 (plugin->config.style & FONT_OUTLINE))
313                         {
314                                 glyph->data_stroke = new VFrame(0,
315                                         8,
316                                         8,
317                                         BC_A8,
318                                         8);
319                                 glyph->data_stroke->clear_frame();
320                         }
324                 }
325                 else
326 // char found and no outline desired
327                 if (plugin->config.stroke_width < ZERO ||
328                         !(plugin->config.style & FONT_OUTLINE)) 
329                 {
330                         FT_Glyph glyph_image;
331                         FT_BBox bbox;
332                         FT_Bitmap bm;
333                         FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
334                         FT_Get_Glyph(freetype_face->glyph, &glyph_image);
335                         FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
336 //                      printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
337 //                                      bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax);
339                         FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
340                                 - bbox.xMin,
341                                 - bbox.yMin);
342                         glyph->width = bm.width = ((bbox.xMax - bbox.xMin + 63) >> 6);
343                         glyph->height = bm.rows = ((bbox.yMax - bbox.yMin + 63) >> 6);
344                         glyph->pitch = bm.pitch = bm.width;
345                         bm.pixel_mode = FT_PIXEL_MODE_GRAY;
346                         bm.num_grays = 256;
347                         glyph->left = (bbox.xMin + 31) >> 6;
348                         if (glyph->left < 0) glyph->left = 0;
349                         glyph->top = (bbox.yMax + 31) >> 6;
350                         glyph->freetype_index = gindex;
351                         glyph->advance_w = ((freetype_face->glyph->advance.x + 31) >> 6);
352 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n", 
353 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
354         
355                         glyph->data = new VFrame(0,
356                                 glyph->width,
357                                 glyph->height,
358                                 BC_A8,
359                                 glyph->pitch);
360                         glyph->data->clear_frame();
361                         bm.buffer = glyph->data->get_data();
362                         FT_Outline_Get_Bitmap( freetype_library,
363                                 &((FT_OutlineGlyph) glyph_image)->outline,
364                                 &bm);
365                         FT_Done_Glyph(glyph_image);
366                 }
367                 else 
368 // Outline desired and glyph found
369                 {
370                         FT_Glyph glyph_image;
371                         int no_outline = 0;
372                         FT_Stroker stroker;
373                         FT_Outline outline;
374                         FT_Bitmap bm;
375                         FT_BBox bbox;
376                         FT_UInt npoints, ncontours;     
378                         typedef struct  FT_LibraryRec_ 
379                         {    
380                                 FT_Memory memory; 
381                         } FT_LibraryRec;
383                         FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
384                         FT_Get_Glyph(freetype_face->glyph, &glyph_image);
386 // check if the outline is ok (non-empty);
387                         FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
388                         if (bbox.xMin == 0 && bbox.xMax == 0 && bbox.yMin ==0 && bbox.yMax == 0)
389                         {
390                                 FT_Done_Glyph(glyph_image);
391                                 glyph->data =  new VFrame(0, 0, BC_A8,0);
392                                 glyph->data_stroke =  new VFrame(0, 0, BC_A8,0);;
393                                 glyph->width=0;
394                                 glyph->height=0;
395                                 glyph->top=0;
396                                 glyph->left=0;
397                                 glyph->advance_w =((int)(freetype_face->glyph->advance.x + 
398                                         plugin->config.stroke_width * 64)) >> 6;
399                                 return;
400                         }
401                         FT_Stroker_New(((FT_LibraryRec *)freetype_library)->memory, &stroker);
402                         FT_Stroker_Set(stroker, (int)(plugin->config.stroke_width * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
403                         FT_Stroker_ParseOutline(stroker, &((FT_OutlineGlyph) glyph_image)->outline,1);
404                         FT_Stroker_GetCounts(stroker,&npoints, &ncontours);
405                         if (npoints ==0 && ncontours == 0) 
406                         {
407 // this never happens, but FreeType has a bug regarding Linotype's Palatino font
408                                 FT_Stroker_Done(stroker);
409                                 FT_Done_Glyph(glyph_image);
410                                 glyph->data =  new VFrame(0, 0, BC_A8,0);
411                                 glyph->data_stroke =  new VFrame(0, 0, BC_A8,0);;
412                                 glyph->width=0;
413                                 glyph->height=0;
414                                 glyph->top=0;
415                                 glyph->left=0;
416                                 glyph->advance_w =((int)(freetype_face->glyph->advance.x + 
417                                         plugin->config.stroke_width * 64)) >> 6;
418                                 return;
419                         };
421                         FT_Outline_New(freetype_library, npoints, ncontours, &outline);
422                         outline.n_points=0;
423                         outline.n_contours=0;
424                         FT_Stroker_Export (stroker, &outline);
425                         FT_Outline_Get_BBox(&outline, &bbox);
426                 
427                         FT_Outline_Translate(&outline,
428                                         - bbox.xMin,
429                                         - bbox.yMin);
430                 
431                         FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
432                                         - bbox.xMin,
433                                         - bbox.yMin + (int)(plugin->config.stroke_width*32));
434 //                      printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\nFill        Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
435 //                                      bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax,
436 //                                      bbox_fill.xMin,bbox_fill.xMax, bbox_fill.yMin, bbox_fill.yMax);
437         
438                         glyph->width = bm.width = ((bbox.xMax - bbox.xMin) >> 6)+1;
439                         glyph->height = bm.rows = ((bbox.yMax - bbox.yMin) >> 6) +1;
440                         glyph->pitch = bm.pitch = bm.width;
441                         bm.pixel_mode = FT_PIXEL_MODE_GRAY;
442                         bm.num_grays = 256;
443                         glyph->left = (bbox.xMin + 31) >> 6;
444                         if (glyph->left < 0) glyph->left = 0;
445                         glyph->top = (bbox.yMax + 31) >> 6;
446                         glyph->freetype_index = gindex;
447                         int real_advance = ((int)ceil((float)freetype_face->glyph->advance.x + 
448                                 plugin->config.stroke_width * 64) >> 6);
449                         glyph->advance_w = glyph->width + glyph->left;
450                         if (real_advance > glyph->advance_w) 
451                                 glyph->advance_w = real_advance;
452 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n", 
453 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
456 //printf("GlyphUnit::process_package 1\n");
457                         glyph->data = new VFrame(0,
458                                 glyph->width,
459                                 glyph->height,
460                                 BC_A8,
461                                 glyph->pitch);
462                         glyph->data_stroke = new VFrame(0,
463                                 glyph->width,
464                                 glyph->height,
465                                 BC_A8,
466                                 glyph->pitch);
467                         glyph->data->clear_frame();
468                         glyph->data_stroke->clear_frame();
469 // for debugging        memset( glyph->data_stroke->get_data(), 60, glyph->pitch * glyph->height);
470                         bm.buffer=glyph->data->get_data();
471                         FT_Outline_Get_Bitmap( freetype_library,
472                                 &((FT_OutlineGlyph) glyph_image)->outline,
473                                 &bm);   
474                         bm.buffer=glyph->data_stroke->get_data();
475                         FT_Outline_Get_Bitmap( freetype_library,
476                         &outline,
477                                 &bm);
478                         FT_Outline_Done(freetype_library,&outline);
479                         FT_Stroker_Done(stroker);
480                         FT_Done_Glyph(glyph_image);
482 //printf("GlyphUnit::process_package 2\n");
483                 }
484         }
487 GlyphEngine::GlyphEngine(TitleMain *plugin, int cpus)
488  : LoadServer(cpus, cpus)
490         this->plugin = plugin;
492 void GlyphEngine::init_packages()
494         int current_package = 0;
495         for(int i = 0; i < plugin->glyphs.total; i++)
496         {
497                 if(!plugin->glyphs.values[i]->data)
498                 {
499                         GlyphPackage *pkg = (GlyphPackage*)packages[current_package++];
500                         pkg->glyph = plugin->glyphs.values[i];
501                 }
502         }
504 LoadClient* GlyphEngine::new_client()
506         return new GlyphUnit(plugin, this);
508 LoadPackage* GlyphEngine::new_package()
510         return new GlyphPackage;
525 // Copy a single character to the text mask
526 TitlePackage::TitlePackage()
527  : LoadPackage()
532 TitleUnit::TitleUnit(TitleMain *plugin, TitleEngine *server)
533  : LoadClient(server)
535         this->plugin = plugin;
538 void TitleUnit::draw_glyph(VFrame *output, TitleGlyph *glyph, int x, int y)
540         int glyph_w = glyph->data->get_w();
541         int glyph_h = glyph->data->get_h();
542         int output_w = output->get_w();
543         int output_h = output->get_h();
544         unsigned char **in_rows = glyph->data->get_rows();
545         unsigned char **out_rows = output->get_rows();
547 //printf("TitleUnit::draw_glyph 1 %c %d %d\n", glyph->c, x, y);
548         for(int in_y = 0; in_y < glyph_h; in_y++)
549         {
550 //              int y_out = y + plugin->ascent + in_y - glyph->top;
551                 int y_out = y + plugin->get_char_height() + in_y - glyph->top;
553 //printf("TitleUnit::draw_glyph 1 %d\n", y_out);
554                 if(y_out >= 0 && y_out < output_h)
555                 {
556                         unsigned char *out_row = out_rows[y_out];
557                         unsigned char *in_row = in_rows[in_y];
558                         for(int in_x = 0; in_x < glyph_w; in_x++)
559                         {
560                                 int x_out = x + glyph->left + in_x;
561                                 if(x_out >= 0 && x_out < output_w)
562                                 {
563                                         if(in_row[in_x] > 0)
564                                                 out_row[x_out] = in_row[in_x];
565 //out_row[x_out] = 0xff;
566                                 }
567                         }
568                 }
569         }
573 void TitleUnit::process_package(LoadPackage *package)
575         TitlePackage *pkg = (TitlePackage*)package;
576         
577         if(pkg->c != 0xa)
578         {
579                 for(int i = 0; i < plugin->glyphs.total; i++)
580                 {
581                         TitleGlyph *glyph = plugin->glyphs.values[i];
582                         if(glyph->c == pkg->c)
583                         {
584                                 draw_glyph(plugin->text_mask, glyph, pkg->x, pkg->y);
585                                 if(plugin->config.stroke_width >= ZERO &&
586                                         (plugin->config.style & FONT_OUTLINE)) 
587                                 {
588                                         VFrame *tmp = glyph->data;
589                                         glyph->data = glyph->data_stroke;
590                                         draw_glyph(plugin->text_mask_stroke, glyph, pkg->x, pkg->y);
591                                         glyph->data = tmp;
592                                 }
593                                 break;
594                         }
595                 }
596         }
599 TitleEngine::TitleEngine(TitleMain *plugin, int cpus)
600  : LoadServer(cpus, cpus)
602         this->plugin = plugin;
605 void TitleEngine::init_packages()
607 //printf("TitleEngine::init_packages 1\n");
608         int visible_y1 = plugin->visible_row1 * plugin->get_char_height();
609 //printf("TitleEngine::init_packages 1\n");
610         int current_package = 0;
611         for(int i = plugin->visible_char1; i < plugin->visible_char2; i++)
612         {
613                 title_char_position_t *char_position = plugin->char_positions + i;
614                 TitlePackage *pkg = (TitlePackage*)get_package(current_package);
615 //printf("TitleEngine::init_packages 1\n");
616                 pkg->x = char_position->x;
617 //printf("TitleEngine::init_packages 1\n");
618                 pkg->y = char_position->y - visible_y1;
619 //printf("TitleEngine::init_packages 1\n");
620                 pkg->c = plugin->config.text[i];
621 //printf("TitleEngine::init_packages 2\n");
622                 current_package++;
623         }
626 LoadClient* TitleEngine::new_client()
628         return new TitleUnit(plugin, this);
631 LoadPackage* TitleEngine::new_package()
633         return new TitlePackage;
646 TitleTranslatePackage::TitleTranslatePackage()
647  : LoadPackage()
652 TitleTranslateUnit::TitleTranslateUnit(TitleMain *plugin, TitleTranslate *server)
653  : LoadClient(server)
655         this->plugin = plugin;
660 #define TRANSLATE(type, max, components, r, g, b) \
661 { \
662         unsigned char **in_rows = plugin->text_mask->get_rows(); \
663         type **out_rows = (type**)plugin->output->get_rows(); \
665         for(int i = pkg->y1; i < pkg->y2; i++) \
666         { \
667                 if(i + server->out_y1_int >= 0 && \
668                         i + server->out_y1_int < server->output_h) \
669                 { \
670                         int in_y1, in_y2; \
671                         float y_fraction1, y_fraction2; \
672                         float y_output_fraction; \
673                         in_y1 = server->y_table[i].in_x1; \
674                         in_y2 = server->y_table[i].in_x2; \
675                         y_fraction1 = server->y_table[i].in_fraction1; \
676                         y_fraction2 = server->y_table[i].in_fraction2; \
677                         y_output_fraction = server->y_table[i].output_fraction; \
678                         unsigned char *in_row1 = in_rows[in_y1]; \
679                         unsigned char *in_row2 = in_rows[in_y2]; \
680                         type *out_row = out_rows[i + server->out_y1_int]; \
682                         for(int j = server->out_x1_int; j < server->out_x2_int; j++) \
683                         { \
684                                 if(j >= 0 && j < server->output_w) \
685                                 { \
686                                         int in_x1; \
687                                         int in_x2; \
688                                         float x_fraction1; \
689                                         float x_fraction2; \
690                                         float x_output_fraction; \
691                                         in_x1 =  \
692                                                 server->x_table[j - server->out_x1_int].in_x1; \
693                                         in_x2 =  \
694                                                 server->x_table[j - server->out_x1_int].in_x2; \
695                                         x_fraction1 =  \
696                                                 server->x_table[j - server->out_x1_int].in_fraction1; \
697                                         x_fraction2 =  \
698                                                 server->x_table[j - server->out_x1_int].in_fraction2; \
699                                         x_output_fraction =  \
700                                                 server->x_table[j - server->out_x1_int].output_fraction; \
702                                         float fraction1 = x_fraction1 * y_fraction1; \
703                                         float fraction2 = x_fraction2 * y_fraction1; \
704                                         float fraction3 = x_fraction1 * y_fraction2; \
705                                         float fraction4 = x_fraction2 * y_fraction2; \
706                                         int input = (int)(in_row1[in_x1] * fraction1 +  \
707                                                                 in_row1[in_x2] * fraction2 +  \
708                                                                 in_row2[in_x1] * fraction3 +  \
709                                                                 in_row2[in_x2] * fraction4 + 0.5); \
710                                         input *= plugin->alpha; \
711 /* Alpha is 0 - 256 */ \
712                                         input >>= 8; \
714                                         int anti_input = 0xff - input; \
715                                         if(components == 4) \
716                                         { \
717                                                 out_row[j * components + 0] =  \
718                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
719                                                 out_row[j * components + 1] =  \
720                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
721                                                 out_row[j * components + 2] =  \
722                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
723                                                 if(max == 0xffff) \
724                                                         out_row[j * components + 3] =  \
725                                                                 MAX((input << 8) | input, out_row[j * components + 3]); \
726                                                 else \
727                                                         out_row[j * components + 3] =  \
728                                                                 MAX(input, out_row[j * components + 3]); \
729                                         } \
730                                         else \
731                                         { \
732                                                 out_row[j * components + 0] =  \
733                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
734                                                 out_row[j * components + 1] =  \
735                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
736                                                 out_row[j * components + 2] =  \
737                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
738                                         } \
739                                 } \
740                         } \
741                 } \
742         } \
746 #define TRANSLATEA(type, max, components, r, g, b) \
747 { \
748         unsigned char **in_rows = plugin->text_mask->get_rows(); \
749         type **out_rows = (type**)plugin->output->get_rows(); \
751         for(int i = pkg->y1; i < pkg->y2; i++) \
752         { \
753                 if(i + server->out_y1_int >= 0 && \
754                         i + server->out_y1_int < server->output_h) \
755                 { \
756                         unsigned char *in_row = in_rows[i]; \
757                         type *out_row = out_rows[i + server->out_y1_int]; \
759                         for(int j = server->out_x1; j < server->out_x2_int; j++) \
760                         { \
761                                 if(j  >= 0 && \
762                                         j < server->output_w) \
763                                 { \
764                                         int input = (int)(in_row[j - server->out_x1]);  \
766                                         input *= plugin->alpha; \
767 /* Alpha is 0 - 256 */ \
768                                         input >>= 8; \
770                                         int anti_input = 0xff - input; \
771                                         if(components == 4) \
772                                         { \
773                                                 out_row[j * components + 0] =  \
774                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
775                                                 out_row[j * components + 1] =  \
776                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
777                                                 out_row[j * components + 2] =  \
778                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
779                                                 if(max == 0xffff) \
780                                                         out_row[j * components + 3] =  \
781                                                                 MAX((input << 8) | input, out_row[j * components + 3]); \
782                                                 else \
783                                                         out_row[j * components + 3] =  \
784                                                                 MAX(input, out_row[j * components + 3]); \
785                                         } \
786                                         else \
787                                         { \
788                                                 out_row[j * components + 0] =  \
789                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
790                                                 out_row[j * components + 1] =  \
791                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
792                                                 out_row[j * components + 2] =  \
793                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
794                                         } \
795                                 } \
796                         } \
797                 } \
798         } \
801 static YUV yuv;
803 void TitleTranslateUnit::process_package(LoadPackage *package)
805         TitleTranslatePackage *pkg = (TitleTranslatePackage*)package;
806         TitleTranslate *server = (TitleTranslate*)this->server;
807         int r_in, g_in, b_in;
809 //printf("TitleTranslateUnit::process_package 1 %d %d\n", pkg->y1, pkg->y2);
811         r_in = (plugin->config.color & 0xff0000) >> 16;
812         g_in = (plugin->config.color & 0xff00) >> 8;
813         b_in = plugin->config.color & 0xff;
815         switch(plugin->output->get_color_model())
816         {
817                 case BC_RGB888:
818                 {
819                         TRANSLATE(unsigned char, 0xff, 3, r_in, g_in, b_in);
820                         break;
821                 }
822                 case BC_YUV888:
823                 {
824                         unsigned char y, u, v;
825                         yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
826                         TRANSLATE(unsigned char, 0xff, 3, y, u, v);
827                         break;
828                 }
829                 case BC_RGB161616:
830                 {
831                         uint16_t r, g, b;
832                         r = (r_in << 8) | r_in;
833                         g = (g_in << 8) | g_in;
834                         b = (b_in << 8) | b_in;
835                         TRANSLATE(uint16_t, 0xffff, 3, r, g, b);
836                         break;
837                 }
838                 case BC_YUV161616:
839                 {
840                         uint16_t y, u, v;
841                         yuv.rgb_to_yuv_16(
842                                 (r_in << 8) | r_in, 
843                                 (g_in << 8) | g_in, 
844                                 (b_in << 8) | b_in, 
845                                 y, 
846                                 u, 
847                                 v);
848                         TRANSLATE(uint16_t, 0xffff, 3, y, u, v);
849                         break;
850                 }
851                 case BC_RGBA8888:
852                 {       
853                         TRANSLATE(unsigned char, 0xff, 4, r_in, g_in, b_in);
854                         break;
855                 }
856                 case BC_YUVA8888:
857                 {       
858                         unsigned char y, u, v;
859                         yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
860                         TRANSLATE(unsigned char, 0xff, 4, y, u, v);
861                         break;
862                 }
863                 case BC_RGBA16161616:
864                 {       
865                         uint16_t r, g, b;
866                         r = (r_in << 8) | r_in;
867                         g = (g_in << 8) | g_in;
868                         b = (b_in << 8) | b_in;
869                         TRANSLATE(uint16_t, 0xffff, 4, r, g, b);
870                         break;
871                 }
872                 case BC_YUVA16161616:
873                 {       
874                         uint16_t y, u, v;
875                         yuv.rgb_to_yuv_16(
876                                 (r_in << 8) | r_in, 
877                                 (g_in << 8) | g_in, 
878                                 (b_in << 8) | b_in, 
879                                 y, 
880                                 u, 
881                                 v);
882                         TRANSLATE(uint16_t, 0xffff, 4, y, u, v);
883                         break;
884                 }
885         }
887 //printf("TitleTranslateUnit::process_package 5\n");
893 TitleTranslate::TitleTranslate(TitleMain *plugin, int cpus)
894  : LoadServer(1, 1)
896         this->plugin = plugin;
897         x_table = y_table = 0;
900 TitleTranslate::~TitleTranslate()
902         if(x_table) delete [] x_table;
903         if(y_table) delete [] y_table;
906 void TitleTranslate::init_packages()
908 //printf("TitleTranslate::init_packages 1\n");
909 // Generate scaling tables
910         if(x_table) delete [] x_table;
911         if(y_table) delete [] y_table;
912 //printf("TitleTranslate::init_packages 1\n");
914         output_w = plugin->output->get_w();
915         output_h = plugin->output->get_h();
916 //printf("TitleTranslate::init_packages 1 %f %d\n", plugin->text_x1, plugin->text_w);
919         TranslateUnit::translation_array_f(x_table, 
920                 plugin->text_x1, 
921                 plugin->text_x1 + plugin->text_w,
922                 0,
923                 plugin->text_w,
924                 plugin->text_w, 
925                 output_w, 
926                 out_x1_int,
927                 out_x2_int);
928 //printf("TitleTranslate::init_packages 1 %f %f\n", plugin->mask_y1, plugin->mask_y2);
930         TranslateUnit::translation_array_f(y_table, 
931                 plugin->mask_y1, 
932                 plugin->mask_y1 + plugin->text_mask->get_h(),
933                 0,
934                 plugin->text_mask->get_h(),
935                 plugin->text_mask->get_h(), 
936                 output_h, 
937                 out_y1_int,
938                 out_y2_int);
940 //printf("TitleTranslate::init_packages 1\n");
942         
943         out_y1 = out_y1_int;
944         out_y2 = out_y2_int;
945         out_x1 = out_x1_int;
946         out_x2 = out_x2_int;
947         int increment = (out_y2 - out_y1) / get_total_packages() + 1;
949 //printf("TitleTranslate::init_packages 1 %d %d %d %d\n", 
950 //      out_y1, out_y2, out_y1_int, out_y2_int);
951         for(int i = 0; i < get_total_packages(); i++)
952         {
953                 TitleTranslatePackage *pkg = (TitleTranslatePackage*)get_package(i);
954                 pkg->y1 = i * increment;
955                 pkg->y2 = i * increment + increment;
956                 if(pkg->y1 > out_y2 - out_y1)
957                         pkg->y1 = out_y2 - out_y1;
958                 if(pkg->y2 > out_y2 - out_y1)
959                         pkg->y2 = out_y2 - out_y1;
960         }
961 //printf("TitleTranslate::init_packages 2\n");
964 LoadClient* TitleTranslate::new_client()
966         return new TitleTranslateUnit(plugin, this);
969 LoadPackage* TitleTranslate::new_package()
971         return new TitleTranslatePackage;
988 ArrayList<FontEntry*>* TitleMain::fonts = 0;
992 TitleMain::TitleMain(PluginServer *server)
993  : PluginVClient(server)
995         PLUGIN_CONSTRUCTOR_MACRO
997 // Build font database
998         build_fonts();
999         text_mask = 0;
1000         text_mask_stroke = 0;
1001         glyph_engine = 0;
1002         title_engine = 0;
1003         freetype_library = 0;
1004         freetype_face = 0;
1005         char_positions = 0;
1006         rows_bottom = 0;
1007         translate = 0;
1008         need_reconfigure = 1;
1011 TitleMain::~TitleMain()
1013 //printf("TitleMain::~TitleMain 1\n");
1014         PLUGIN_DESTRUCTOR_MACRO
1015         if(text_mask) delete text_mask;
1016         if(text_mask_stroke) delete text_mask_stroke;
1017         if(char_positions) delete [] char_positions;
1018         if(rows_bottom) delete [] rows_bottom;
1019         clear_glyphs();
1020         if(glyph_engine) delete glyph_engine;
1021         if(title_engine) delete title_engine;
1022         if(freetype_library) FT_Done_FreeType(freetype_library);
1023         if(translate) delete translate;
1026 char* TitleMain::plugin_title() { return _("Title"); }
1027 int TitleMain::is_realtime() { return 1; }
1028 int TitleMain::is_synthesis() { return 1; }
1030 VFrame* TitleMain::new_picon()
1032         return new VFrame(picon_png);
1036 void TitleMain::build_fonts()
1038         if(!fonts)
1039         {
1040                 fonts = new ArrayList<FontEntry*>;
1041 // Construct path from location of the plugin
1042                 char search_path[BCTEXTLEN];
1043                 strcpy(search_path, PluginClient::get_path());
1044                 char *ptr = strrchr(search_path, '/');
1045                 strcpy(ptr + 1, FONT_SEARCHPATH);
1046                 char command_line[BCTEXTLEN];
1048                 sprintf(command_line, 
1049                         "find %s -name 'fonts.dir' -print -exec cat {} \\;", 
1050                         search_path);
1051 //printf("TitleMain::build_fonts %s\n", command_line);
1053                 FILE *in = popen(command_line, "r");
1054                 char current_dir[BCTEXTLEN];
1055                 FT_Library freetype_library = 0;        // Freetype library
1056                 FT_Face freetype_face = 0;
1058 //              FT_Init_FreeType(&freetype_library);
1059                 current_dir[0] = 0;
1061                 while(!feof(in))
1062                 {
1063                         char string[BCTEXTLEN], string2[BCTEXTLEN];
1064                         fgets(string, BCTEXTLEN, in);
1065                         if(!strlen(string)) break;
1067                         char *in_ptr = string;
1068                         char *out_ptr;
1070 // Get current directory
1071                         
1072                         if(string[0] == '/')
1073                         {
1074                                 out_ptr = current_dir;
1075                                 while(*in_ptr != 0 && *in_ptr != 0xa)
1076                                         *out_ptr++ = *in_ptr++;
1077                                 out_ptr--;
1078                                 while(*out_ptr != '/')
1079                                         *out_ptr-- = 0;
1080                         }
1081                         else
1082                         {
1085 //printf("TitleMain::build_fonts %s\n", string);
1086                                 FontEntry *entry = new FontEntry;
1088 // Path
1089                                 out_ptr = string2;
1090                                 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa)
1091                                 {
1092                                         *out_ptr++ = *in_ptr++;
1093                                 }
1094                                 *out_ptr = 0;
1095                                 if(string2[0] == '/')
1096                                 {
1097                                         entry->path = new char[strlen(string2) + 1];
1098                                         sprintf(entry->path, "%s", string2);
1099                                 }
1100                                 else
1101                                 {
1102                                         entry->path = new char[strlen(current_dir) + strlen(string2) + 1];
1103                                         sprintf(entry->path, "%s%s", current_dir, string2);
1104                                 }
1106 // Foundary
1107                                 while(*in_ptr != 0 && *in_ptr != 0xa && (*in_ptr == ' ' || *in_ptr == '-'))
1108                                         in_ptr++;
1110                                 out_ptr = string2;
1111                                 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa && *in_ptr != '-')
1112                                 {
1113                                         *out_ptr++ = *in_ptr++;
1114                                 }
1115                                 *out_ptr = 0;
1116                                 entry->foundary = new char[strlen(string2) + 1];
1117                                 strcpy(entry->foundary, string2);
1118                                 if(*in_ptr == '-') in_ptr++;
1121 // Family
1122                                 out_ptr = string2;
1123                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1124                                 {
1125                                         *out_ptr++ = *in_ptr++;
1126                                 }
1127                                 *out_ptr = 0;
1128                                 entry->family = new char[strlen(string2) + 1];
1129                                 strcpy(entry->family, string2);
1130                                 if(*in_ptr == '-') in_ptr++;
1132 // Weight
1133                                 out_ptr = string2;
1134                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1135                                 {
1136                                         *out_ptr++ = *in_ptr++;
1137                                 }
1138                                 *out_ptr = 0;
1139                                 entry->weight = new char[strlen(string2) + 1];
1140                                 strcpy(entry->weight, string2);
1141                                 if(*in_ptr == '-') in_ptr++;
1143 // Slant
1144                                 out_ptr = string2;
1145                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1146                                 {
1147                                         *out_ptr++ = *in_ptr++;
1148                                 }
1149                                 *out_ptr = 0;
1150                                 entry->slant = new char[strlen(string2) + 1];
1151                                 strcpy(entry->slant, string2);
1152                                 if(*in_ptr == '-') in_ptr++;
1154 // SWidth
1155                                 out_ptr = string2;
1156                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1157                                 {
1158                                         *out_ptr++ = *in_ptr++;
1159                                 }
1160                                 *out_ptr = 0;
1161                                 entry->swidth = new char[strlen(string2) + 1];
1162                                 strcpy(entry->swidth, string2);
1163                                 if(*in_ptr == '-') in_ptr++;
1165 // Adstyle
1166                                 out_ptr = string2;
1167                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1168                                 {
1169                                         *out_ptr++ = *in_ptr++;
1170                                 }
1171                                 *out_ptr = 0;
1172                                 entry->adstyle = new char[strlen(string2) + 1];
1173                                 strcpy(entry->adstyle, string2);
1174                                 if(*in_ptr == '-') in_ptr++;
1176 // pixelsize
1177                                 out_ptr = string2;
1178                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1179                                 {
1180                                         *out_ptr++ = *in_ptr++;
1181                                 }
1182                                 *out_ptr = 0;
1183                                 entry->pixelsize = atol(string2);
1184                                 if(*in_ptr == '-') in_ptr++;
1186 // pointsize
1187                                 out_ptr = string2;
1188                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1189                                 {
1190                                         *out_ptr++ = *in_ptr++;
1191                                 }
1192                                 *out_ptr = 0;
1193                                 entry->pointsize = atol(string2);
1194                                 if(*in_ptr == '-') in_ptr++;
1196 // xres
1197                                 out_ptr = string2;
1198                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1199                                 {
1200                                         *out_ptr++ = *in_ptr++;
1201                                 }
1202                                 *out_ptr = 0;
1203                                 entry->xres = atol(string2);
1204                                 if(*in_ptr == '-') in_ptr++;
1206 // yres
1207                                 out_ptr = string2;
1208                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1209                                 {
1210                                         *out_ptr++ = *in_ptr++;
1211                                 }
1212                                 *out_ptr = 0;
1213                                 entry->yres = atol(string2);
1214                                 if(*in_ptr == '-') in_ptr++;
1216 // spacing
1217                                 out_ptr = string2;
1218                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1219                                 {
1220                                         *out_ptr++ = *in_ptr++;
1221                                 }
1222                                 *out_ptr = 0;
1223                                 entry->spacing = new char[strlen(string2) + 1];
1224                                 strcpy(entry->spacing, string2);
1225                                 if(*in_ptr == '-') in_ptr++;
1227 // avg_width
1228                                 out_ptr = string2;
1229                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1230                                 {
1231                                         *out_ptr++ = *in_ptr++;
1232                                 }
1233                                 *out_ptr = 0;
1234                                 entry->avg_width = atol(string2);
1235                                 if(*in_ptr == '-') in_ptr++;
1237 // registry
1238                                 out_ptr = string2;
1239                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1240                                 {
1241                                         *out_ptr++ = *in_ptr++;
1242                                 }
1243                                 *out_ptr = 0;
1244                                 entry->registry = new char[strlen(string2) + 1];
1245                                 strcpy(entry->registry, string2);
1246                                 if(*in_ptr == '-') in_ptr++;
1248 // encoding
1249                                 out_ptr = string2;
1250                                 while(*in_ptr != 0 && *in_ptr != 0xa)
1251                                 {
1252                                         *out_ptr++ = *in_ptr++;
1253                                 }
1254                                 *out_ptr = 0;
1255                                 entry->encoding = new char[strlen(string2) + 1];
1256                                 strcpy(entry->encoding, string2);
1260 // Add to list
1261                                 if(strlen(entry->foundary))
1262                                 {
1263 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1264 // This takes a real long time to do.  Instead just take all fonts
1265 //                                      if(!load_freetype_face(freetype_library, 
1266 //                                              freetype_face,
1267 //                                              entry->path))
1268 //                                      if(1)
1269                                         if(entry->family[0])
1270                                         {
1271 // Fix parameters
1272                                                 sprintf(string, "%s (%s)", entry->family, entry->foundary);
1273                                                 entry->fixed_title = new char[strlen(string) + 1];
1274                                                 strcpy(entry->fixed_title, string);
1276                                                 if(!strcasecmp(entry->weight, "demibold") ||
1277                                                         !strcasecmp(entry->weight, "bold")) 
1278                                                         entry->fixed_style |= FONT_BOLD;
1279                                                 if(!strcasecmp(entry->slant, "i") ||
1280                                                         !strcasecmp(entry->slant, "o")) 
1281                                                         entry->fixed_style |= FONT_ITALIC;
1282                                                 fonts->append(entry);
1283 //                                              printf("TitleMain::build_fonts %s: success\n",
1284 //                                                      entry->path);
1285 //printf("TitleMain::build_fonts 2\n");
1286                                         }
1287                                         else
1288                                         {
1289 //                                              printf("TitleMain::build_fonts %s: FT_New_Face failed\n",
1290 //                                                      entry->path);
1291 //printf("TitleMain::build_fonts 3\n");
1292                                                 delete entry;
1293                                         }
1294                                 }
1295                                 else
1296                                 {
1297                                         delete entry;
1298                                 }
1299                         }
1300                 }
1301                 pclose(in);
1303                 if(freetype_library) FT_Done_FreeType(freetype_library);
1304         }
1307 // for(int i = 0; i < fonts->total; i++)
1308 //      fonts->values[i]->dump();
1313 int TitleMain::load_freetype_face(FT_Library &freetype_library,
1314         FT_Face &freetype_face,
1315         char *path)
1317 //printf("TitleMain::load_freetype_face 1\n");
1318         if(!freetype_library) FT_Init_FreeType(&freetype_library);
1319         if(freetype_face) FT_Done_Face(freetype_face);
1320         freetype_face = 0;
1321 //printf("TitleMain::load_freetype_face 2\n");
1323 // Use freetype's internal function for loading font
1324         if(FT_New_Face(freetype_library, 
1325                 path, 
1326                 0,
1327                 &freetype_face))
1328         {
1329                 fprintf(stderr, _("TitleMain::load_freetype_face %s failed.\n"));
1330                 FT_Done_FreeType(freetype_library);
1331                 freetype_face = 0;
1332                 freetype_library = 0;
1333                 return 1;
1334         } else
1335         {
1336                 return 0;
1337         }
1339 //printf("TitleMain::load_freetype_face 4\n");
1342 FontEntry* TitleMain::get_font_entry(char *title,
1343         int style,
1344         int size)
1346 //printf("TitleMain::get_font_entry %s %d %d\n", title, style, size);
1347         FontEntry *result = 0;
1348         int got_title = 0;
1350         for(int i = 0; i < fonts->total; i++)
1351         {
1352                 FontEntry *entry = fonts->values[i];
1354                 if(!result) result = entry;
1356                 if(!strcmp(title, entry->fixed_title))
1357                 {
1358                         if(!got_title) result = entry;
1359                         got_title = 1;
1361 // Not every font has a size but every font has a style
1362                         if(entry->fixed_style == style)
1363                                 result = entry;
1365                         if(entry->fixed_style == style && entry->pointsize == size) 
1366                                 result = entry;
1368                 }
1369         }
1371         return result;
1375 FontEntry* TitleMain::get_font()
1377         return get_font_entry(config.font,
1378                 config.style,
1379                 config.size);
1387 int TitleMain::get_char_height()
1389 // this is height above the zero line, but does not include characters that go below
1390         int result = config.size;
1391         if((config.style & FONT_OUTLINE)) result += (int)ceil(config.stroke_width * 2);
1392         return result;
1395 int TitleMain::get_char_advance(int current, int next)
1397         FT_Vector kerning;
1398         int result = 0;
1399         TitleGlyph *current_glyph = 0;
1400         TitleGlyph *next_glyph = 0;
1402         if(current == 0xa) return 0;
1404         for(int i = 0; i < glyphs.total; i++)
1405         {
1406                 if(glyphs.values[i]->c == current)
1407                 {
1408                         current_glyph = glyphs.values[i];
1409                         break;
1410                 }
1411         }
1413         for(int i = 0; i < glyphs.total; i++)
1414         {
1415                 if(glyphs.values[i]->c == next)
1416                 {
1417                         next_glyph = glyphs.values[i];
1418                         break;
1419                 }
1420         }
1422         if(current_glyph)
1423                 result = current_glyph->advance_w;
1425 //printf("TitleMain::get_char_advance 1 %c %c %p %p\n", current, next, current_glyph, next_glyph);
1426         if(next_glyph)
1427                 FT_Get_Kerning(freetype_face, 
1428                                 current_glyph->freetype_index,
1429                                 next_glyph->freetype_index,
1430                                 ft_kerning_default,
1431                                 &kerning);
1432         else
1433                 kerning.x = 0;
1434 //printf("TitleMain::get_char_advance 2 %d %d\n", result, kerning.x);
1435         
1436         return result + (kerning.x >> 6);
1440 void TitleMain::draw_glyphs()
1442 // Build table of all glyphs needed
1443         int text_len = strlen(config.text);
1444         int total_packages = 0;
1445         iconv_t cd;
1446         cd = iconv_open ("UCS-4", config.encoding);
1449         if (cd == (iconv_t) -1)
1450         {
1451 /* Something went wrong.  */
1452                 fprintf (stderr, _("Iconv conversion from %s to Unicode UCS-4 not available\n"),config.encoding);
1453         };
1455         for(int i = 0; i < text_len; i++)
1456         {
1457                 FT_ULong char_code;     
1458                 int c = config.text[i];
1459                 int exists = 0;
1461 /* if iconv is working ok for current encoding */
1462                 if (cd != (iconv_t) -1)
1463                 {
1465                         size_t inbytes,outbytes;
1466                         char inbuf;
1467                         char *inp = (char*)&inbuf, *outp = (char *)&char_code;
1469                         inbuf = (char)c;
1470                         inbytes = 1;
1471                         outbytes = 4;
1472         
1473                         iconv (cd, &inp, &inbytes, &outp, &outbytes);
1474 #if     __BYTE_ORDER == __LITTLE_ENDIAN
1475                         char_code = bswap_32(char_code);
1476 #endif                          /* Big endian.  */
1478                 }
1479                 else 
1480                 {
1481                         char_code = c;
1482                 }
1484                 for(int j = 0; j < glyphs.total; j++)
1485                 {
1486                         if(glyphs.values[j]->char_code == char_code)
1487                         {
1488                                 exists = 1;
1489                                 break;
1490                         }
1491                 }
1493                 if(!exists)
1494                 {
1495                         total_packages++;
1496 //printf("TitleMain::draw_glyphs 1\n");
1497                         TitleGlyph *glyph = new TitleGlyph;
1498 //printf("TitleMain::draw_glyphs 2\n");
1499                         glyphs.append(glyph);
1500                         glyph->c = c;
1501                         glyph->char_code = char_code;
1502                 }
1503         }
1504         iconv_close(cd);
1506         if(!glyph_engine)
1507                 glyph_engine = new GlyphEngine(this, PluginClient::smp + 1);
1509         glyph_engine->set_package_count(total_packages);
1510 //printf("TitleMain::draw_glyphs 3 %d\n", glyphs.total);
1511         glyph_engine->process_packages();
1512 //printf("TitleMain::draw_glyphs 4\n");
1515 void TitleMain::get_total_extents()
1517 // Determine extents of total text
1518         int current_w = 0;
1519         int row_start = 0;
1520         text_len = strlen(config.text);
1521         if(!char_positions) char_positions = new title_char_position_t[text_len];
1522         
1523         text_rows = 0;
1524         text_w = 0;
1525         ascent = 0;
1527         for(int i = 0; i < glyphs.total; i++)
1528                 if(glyphs.values[i]->top > ascent) ascent = glyphs.values[i]->top;
1529 //printf("TitleMain::get_total_extents %d\n", ascent);
1531         // get the number of rows first
1532         for(int i = 0; i < text_len; i++)
1533         {
1534                 if(config.text[i] == 0xa || i == text_len - 1)
1535                 {
1536                         text_rows++;
1537                 }
1538         }
1539         if (!rows_bottom) rows_bottom = new int[text_rows+1];
1540         text_rows = 0;
1541         rows_bottom[0] = 0;
1543         for(int i = 0; i < text_len; i++)
1544         {
1545                 char_positions[i].x = current_w;
1546                 char_positions[i].y = text_rows * get_char_height();
1547                 char_positions[i].w = get_char_advance(config.text[i], config.text[i + 1]);
1548                 TitleGlyph *current_glyph = 0;
1549                 for(int j = 0; j < glyphs.total; j++)
1550                 {
1551                         if(glyphs.values[j]->c == config.text[i])
1552                         {
1553                                 current_glyph = glyphs.values[j];
1554                                 break;
1555                         }
1556                 }
1557                 int current_bottom = current_glyph->top - current_glyph->height;
1558                 if (current_bottom < rows_bottom[text_rows])
1559                         rows_bottom[text_rows] = current_bottom ;
1561 // printf("TitleMain::get_total_extents 1 %c %d %d %d\n", 
1562 //      config.text[i], 
1563 //      char_positions[i].x, 
1564 //      char_positions[i].y, 
1565 //      char_positions[i].w);
1566                 current_w += char_positions[i].w;
1568                 if(config.text[i] == 0xa || i == text_len - 1)
1569                 {
1570                         text_rows++;
1571                         rows_bottom[text_rows] = 0;
1572                         if(current_w > text_w) text_w = current_w;
1573                         current_w = 0;
1574                 }
1575         }
1576         text_w += config.dropshadow;
1577         text_h = text_rows * get_char_height();
1578         text_h += config.dropshadow;
1580 // Now that text_w is known
1581 // Justify rows based on configuration
1582         row_start = 0;
1583         for(int i = 0; i < text_len; i++)
1584         {
1585                 if(config.text[i] == 0xa || i == text_len - 1)
1586                 {
1587                         for(int j = row_start; j <= i; j++)
1588                         {
1589                                 switch(config.hjustification)
1590                                 {
1591                                         case JUSTIFY_LEFT:
1592                                                 break;
1594                                         case JUSTIFY_MID:
1595                                                 char_positions[j].x += (text_w - 
1596                                                                 char_positions[i].x - 
1597                                                                 char_positions[i].w) /
1598                                                         2;
1599                                                 break;
1601                                         case JUSTIFY_RIGHT:
1602                                                 char_positions[j].x += (text_w - 
1603                                                         char_positions[i].x - 
1604                                                         char_positions[i].w);
1605                                                 break;
1606                                 }
1607                         }
1608                         row_start = i + 1;
1609                 }
1610         }
1613 //printf("TitleMain::get_total_extents 2 %d %d\n", text_w, text_h);
1616 int TitleMain::draw_mask()
1618         int old_visible_row1 = visible_row1;
1619         int old_visible_row2 = visible_row2;
1622 // Determine y of visible text
1623         if(config.motion_strategy == BOTTOM_TO_TOP)
1624         {
1625 // printf("TitleMain::draw_mask 1 %d %d %d %d\n", 
1626 //      config.motion_strategy,
1627 //      get_source_position(), 
1628 //      get_source_start(),
1629 //      config.prev_keyframe_position);
1630                 float magnitude = config.pixels_per_second * 
1631                         ((get_source_position() - get_source_start()) - 
1632                                 (config.prev_keyframe_position - get_source_start())) / 
1633                         PluginVClient::project_frame_rate;
1634                 if(config.loop)
1635                 {
1636                         int loop_size = text_h + input->get_h();
1637                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1638                 }
1639                 text_y1 = config.y + input->get_h() - magnitude;
1640         }
1641         else
1642         if(config.motion_strategy == TOP_TO_BOTTOM)
1643         {
1644                 float magnitude = config.pixels_per_second * 
1645                         (get_source_position() - 
1646                                 get_source_start() -
1647                                 config.prev_keyframe_position) / 
1648                         PluginVClient::project_frame_rate;
1649                 if(config.loop)
1650                 {
1651                         int loop_size = text_h + input->get_h();
1652                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1653                 }
1654                 text_y1 = config.y + magnitude;
1655                 text_y1 -= text_h;
1656         }
1657         else
1658         if(config.vjustification == JUSTIFY_TOP)
1659         {
1660                 text_y1 = config.y;
1661         }
1662         else
1663         if(config.vjustification == JUSTIFY_MID)
1664         {
1665                 text_y1 = config.y + input->get_h() / 2 - text_h / 2;
1666         }
1667         else
1668         if(config.vjustification == JUSTIFY_BOTTOM)
1669         {
1670                 text_y1 = config.y + input->get_h() - text_h;
1671         }
1673         text_y2 = text_y1 + text_h + 0.5;
1675 // Determine x of visible text
1676         if(config.motion_strategy == RIGHT_TO_LEFT)
1677         {
1678                 float magnitude = config.pixels_per_second * 
1679                         (get_source_position() - 
1680                                 get_source_start() - 
1681                                 config.prev_keyframe_position) / 
1682                         PluginVClient::project_frame_rate;
1683                 if(config.loop)
1684                 {
1685                         int loop_size = text_w + input->get_w();
1686                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1687                 }
1688                 text_x1 = config.x + (float)input->get_w() - magnitude;
1689         }
1690         else
1691         if(config.motion_strategy == LEFT_TO_RIGHT)
1692         {
1693                 float magnitude = config.pixels_per_second * 
1694                         (get_source_position() - 
1695                                 get_source_start() - 
1696                                 config.prev_keyframe_position) / 
1697                         PluginVClient::project_frame_rate;
1698                 if(config.loop)
1699                 {
1700                         int loop_size = text_w + input->get_w();
1701                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1702                 }
1703                 text_x1 = config.x + -(float)text_w + magnitude;
1704         }
1705         else
1706         if(config.hjustification == JUSTIFY_LEFT)
1707         {
1708                 text_x1 = config.x;
1709         }
1710         else
1711         if(config.hjustification == JUSTIFY_MID)
1712         {
1713                 text_x1 = config.x + input->get_w() / 2 - text_w / 2;
1714         }
1715         else
1716         if(config.hjustification == JUSTIFY_RIGHT)
1717         {
1718                 text_x1 = config.x + input->get_w() - text_w;
1719         }
1725 // Determine y extents just of visible text
1726         visible_row1 = (int)(-text_y1 / get_char_height());
1727         if(visible_row1 < 0) visible_row1 = 0;
1729         visible_row2 = (int)((float)text_rows - (text_y2 - input->get_h()) / get_char_height() + 1);
1730         if(visible_row2 > text_rows) visible_row2 = text_rows;
1733         if(visible_row2 <= visible_row1) return 1;
1736         mask_y1 = text_y1 + visible_row1 * get_char_height();
1737         mask_y2 = text_y1 + visible_row2 * get_char_height();
1738         text_x1 += config.x;
1741         visible_char1 = visible_char2 = 0;
1742         int got_char1 = 0;
1743         for(int i = 0; i < text_len; i++)
1744         {
1745                 title_char_position_t *char_position = char_positions + i;
1746                 int char_row = char_position->y / get_char_height();
1747                 if(char_row >= visible_row1 &&
1748                         char_row < visible_row2)
1749                 
1750                 {
1751                         if(!got_char1)
1752                         {
1753                                 visible_char1 = i;
1754                                 got_char1 = 1;
1755                         }
1756                         visible_char2 = i;
1757                 }
1758         }
1759         visible_char2++;
1763         int visible_rows = visible_row2 - visible_row1;
1764         int need_redraw = 0;
1765         if(text_mask &&
1766                 (text_mask->get_w() != text_w ||
1767                 text_mask->get_h() != visible_rows * get_char_height() - rows_bottom[visible_row2 - 1]))
1768         {
1769                 delete text_mask;
1770                 delete text_mask_stroke;
1771                 text_mask = 0;
1772                 text_mask_stroke = 0;
1773         }
1775         if(!text_mask)
1776         {
1777                 text_mask = new VFrame(0,
1778                         text_w,
1779                         visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1780                         BC_A8);
1781                 text_mask_stroke = new VFrame(0,
1782                         text_w,
1783                         visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1784                         BC_A8);
1786                 need_redraw = 1;
1787         }
1791 // Draw on text mask if different
1792         if(old_visible_row1 != visible_row1 ||
1793                 old_visible_row2 != visible_row2 ||
1794                 need_redraw)
1795         {
1796                 text_mask->clear_frame();
1797                 text_mask_stroke->clear_frame();
1800                 if(!title_engine)
1801                         title_engine = new TitleEngine(this, PluginClient::smp + 1);
1803                 title_engine->set_package_count(visible_char2 - visible_char1);
1804                 title_engine->process_packages();
1805         }
1807         return 0;
1811 void TitleMain::overlay_mask()
1814         alpha = 0x100;
1815         if(!EQUIV(config.fade_in, 0))
1816         {
1817                 int fade_len = (int)(config.fade_in * PluginVClient::project_frame_rate);
1818                 int fade_position = get_source_position() - 
1819 /*                      get_source_start() -   */
1820                         config.prev_keyframe_position;
1823                 if(fade_position >= 0 && fade_position < fade_len)
1824                 {
1825                         alpha = (int)((float)0x100 * 
1826                                 fade_position /
1827                                 fade_len + 0.5);
1828                 }
1829         }
1831         if(!EQUIV(config.fade_out, 0))
1832         {
1833                 int fade_len = (int)(config.fade_out * 
1834                         PluginVClient::project_frame_rate);
1835                 int fade_position = config.next_keyframe_position - 
1836                         get_source_position();
1839                 if(fade_position > 0 && fade_position < fade_len)
1840                 {
1841                         alpha = (int)((float)0x100 *
1842                                 fade_position /
1843                                 fade_len + 0.5);
1844                 }
1845         }
1847         if(config.dropshadow)
1848         {
1849                 text_x1 += config.dropshadow;
1850                 text_x2 += config.dropshadow;
1851                 mask_y1 += config.dropshadow;
1852                 mask_y2 += config.dropshadow;
1853                 if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1854                         mask_y1 < input->get_h() && mask_y2 > 0)
1855                 {
1856                         if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1857 // Do 2 passes if dropshadow.
1858                         int temp_color = config.color;
1859                         config.color = 0x0;
1860                         translate->process_packages();
1861                         config.color = temp_color;
1862                 }
1863                 text_x1 -= config.dropshadow;
1864                 text_x2 -= config.dropshadow;
1865                 mask_y1 -= config.dropshadow;
1866                 mask_y2 -= config.dropshadow;
1867         }
1869         if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1870                 mask_y1 < input->get_h() && mask_y2 > 0)
1871         {
1872                 if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1873                 translate->process_packages();
1874                 if (config.stroke_width >= ZERO &&
1875                         (config.style & FONT_OUTLINE)) 
1876                 {
1877                         int temp_color = config.color;
1878                         VFrame *tmp_text_mask = this->text_mask;
1879                         config.color = config.color_stroke;
1880                         this->text_mask = this->text_mask_stroke;
1882                         translate->process_packages();
1883                         config.color = temp_color;
1884                         this->text_mask = tmp_text_mask;
1885                 }
1886         }
1889 void TitleMain::clear_glyphs()
1891 //printf("TitleMain::clear_glyphs 1\n");
1892         glyphs.remove_all_objects();
1895 char* TitleMain::motion_to_text(int motion)
1897         switch(motion)
1898         {
1899                 case NO_MOTION: return _("No motion"); break;
1900                 case BOTTOM_TO_TOP: return _("Bottom to top"); break;
1901                 case TOP_TO_BOTTOM: return _("Top to bottom"); break;
1902                 case RIGHT_TO_LEFT: return _("Right to left"); break;
1903                 case LEFT_TO_RIGHT: return _("Left to right"); break;
1904         }
1907 int TitleMain::text_to_motion(char *text)
1909         for(int i = 0; i < TOTAL_PATHS; i++)
1910         {
1911                 if(!strcasecmp(motion_to_text(i), text)) return i;
1912         }
1913         return 0;
1922 int TitleMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
1924         int result = 0;
1925         input = input_ptr;
1926         output = output_ptr;
1928         need_reconfigure |= load_configuration();
1931 // Always synthesize text and redraw it for timecode
1932         if(config.timecode)
1933         {
1934                 Units::totext(config.text, 
1935                                 (double)get_source_position() / PluginVClient::project_frame_rate, 
1936                                 TIME_HMSF, 
1937                                 0,
1938                                 PluginVClient::project_frame_rate, 
1939                                 0);
1940                 need_reconfigure = 1;
1941         }
1943 // Check boundaries
1944         if(config.size <= 0 || config.size >= 2048) config.size = 72;
1945         if(config.stroke_width < 0 || 
1946                 config.stroke_width >= 512) config.stroke_width = 0.0;
1947         if(!strlen(config.text)) return 0;
1948         if(!strlen(config.encoding)) strcpy(config.encoding, DEFAULT_ENCODING);
1950 //printf("TitleMain::process_realtime 4\n");
1952 // Handle reconfiguration
1953         if(need_reconfigure)
1954         {
1955 //printf("TitleMain::process_realtime 2\n");
1956                 if(text_mask) delete text_mask;
1957                 if(text_mask_stroke) delete text_mask_stroke;
1958                 text_mask = 0;
1959                 text_mask_stroke = 0;
1960 //printf("TitleMain::process_realtime 2\n");
1961                 if(freetype_face) FT_Done_Face(freetype_face);
1962                 freetype_face = 0;
1963 //printf("TitleMain::process_realtime 2\n");
1964                 if(glyph_engine) delete glyph_engine;
1965                 glyph_engine = 0;
1966 //printf("TitleMain::process_realtime 2\n");
1967                 if(char_positions) delete [] char_positions;
1968                 char_positions = 0;
1969                 if(rows_bottom) delete [] rows_bottom;
1970                 rows_bottom = 0;
1971 //printf("TitleMain::process_realtime 2\n");
1972                 clear_glyphs();
1973 //printf("TitleMain::process_realtime 2\n");
1974                 visible_row1 = 0;
1975                 visible_row2 = 0;
1976                 ascent = 0;
1978                 if(!freetype_library) 
1979                         FT_Init_FreeType(&freetype_library);
1981 //printf("TitleMain::process_realtime 2\n");
1982                 if(!freetype_face)
1983                 {
1984                         FontEntry *font = get_font();
1985 //printf("TitleMain::process_realtime 2.1 %s\n", font->path);
1986                         if(load_freetype_face(freetype_library,
1987                                 freetype_face,
1988                                 font->path))
1989                         {
1990                                 printf("TitleMain::process_realtime %s: FT_New_Face failed.\n",
1991                                         font->fixed_title);
1992                                 result = 1;
1993                         }
1994 //printf("TitleMain::process_realtime 2.2\n");
1996                         if(!result) FT_Set_Pixel_Sizes(freetype_face, config.size, 0);
1997 //printf("TitleMain::process_realtime 2.3\n");
1998                 }
2000 //printf("TitleMain::process_realtime 3\n");
2002                 if(!result)
2003                 {
2004                         draw_glyphs();
2005 //printf("TitleMain::process_realtime 4\n");
2006                         get_total_extents();
2007 //printf("TitleMain::process_realtime 5\n");
2008                         need_reconfigure = 0;
2009                 }
2010         }
2012         if(!result)
2013         {
2014 //printf("TitleMain::process_realtime 4\n");
2015 // Determine region of text visible on the output and draw mask
2016                 result = draw_mask();
2017         }
2018 //printf("TitleMain::process_realtime 50\n");
2021 // Overlay mask on output
2022         if(!result)
2023         {
2024                 overlay_mask();
2025         }
2026 //printf("TitleMain::process_realtime 60 %d\n", glyphs.total);
2028         return 0;
2031 int TitleMain::show_gui()
2033         load_configuration();
2034         thread = new TitleThread(this);
2035         thread->start();
2036         return 0;
2039 int TitleMain::set_string()
2041         if(thread) thread->window->set_title(gui_string);
2042         return 0;
2045 void TitleMain::raise_window()
2047         if(thread)
2048         {
2049                 thread->window->raise_window();
2050                 thread->window->flush();
2051         }
2054 void TitleMain::update_gui()
2056         if(thread)
2057         {
2058                 int reconfigure = load_configuration();
2059                 if(reconfigure)
2060                 {
2061                         thread->window->lock_window();
2062                         thread->window->update();
2063                         thread->window->unlock_window();
2064                 }
2065         }
2069 int TitleMain::load_defaults()
2071         char directory[1024], text_path[1024];
2072 // set the default directory
2073         sprintf(directory, "%stitle.rc", BCASTDIR);
2075 // load the defaults
2076         defaults = new Defaults(directory);
2077         defaults->load();
2079         defaults->get("FONT", config.font);
2080         defaults->get("ENCODING", config.encoding);
2081         config.style = defaults->get("STYLE", (int64_t)config.style);
2082         config.size = defaults->get("SIZE", config.size);
2083         config.color = defaults->get("COLOR", config.color);
2084         config.color_stroke = defaults->get("COLOR_STROKE", config.color_stroke);
2085         config.stroke_width = defaults->get("STROKE_WIDTH", config.stroke_width);
2086         config.motion_strategy = defaults->get("MOTION_STRATEGY", config.motion_strategy);
2087         config.loop = defaults->get("LOOP", config.loop);
2088         config.pixels_per_second = defaults->get("PIXELS_PER_SECOND", config.pixels_per_second);
2089         config.hjustification = defaults->get("HJUSTIFICATION", config.hjustification);
2090         config.vjustification = defaults->get("VJUSTIFICATION", config.vjustification);
2091         config.fade_in = defaults->get("FADE_IN", config.fade_in);
2092         config.fade_out = defaults->get("FADE_OUT", config.fade_out);
2093         config.x = defaults->get("TITLE_X", config.x);
2094         config.y = defaults->get("TITLE_Y", config.y);
2095         config.dropshadow = defaults->get("DROPSHADOW", config.dropshadow);
2096         config.timecode = defaults->get("TIMECODE", config.timecode);
2097         window_w = defaults->get("WINDOW_W", 660);
2098         window_h = defaults->get("WINDOW_H", 480);
2100 // Store text in separate path to isolate special characters
2101         FileSystem fs;
2102         sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2103         fs.complete_path(text_path);
2104         FILE *fd = fopen(text_path, "rb");
2105         if(fd)
2106         {
2107                 fseek(fd, 0, SEEK_END);
2108                 int64_t len = ftell(fd);
2109                 fseek(fd, 0, SEEK_SET);
2110                 fread(config.text, len, 1, fd);
2111                 config.text[len] = 0;
2112 //printf("TitleMain::load_defaults %s\n", config.text);
2113                 fclose(fd);
2114         }
2115         else
2116                 config.text[0] = 0;
2117         return 0;
2120 int TitleMain::save_defaults()
2122         char text_path[1024];
2124         defaults->update("FONT", config.font);
2125         defaults->update("ENCODING", config.encoding);
2126         defaults->update("STYLE", (int64_t)config.style);
2127         defaults->update("SIZE", config.size);
2128         defaults->update("COLOR", config.color);
2129         defaults->update("COLOR_STROKE", config.color_stroke);
2130         defaults->update("STROKE_WIDTH", config.stroke_width);
2131         defaults->update("MOTION_STRATEGY", config.motion_strategy);
2132         defaults->update("LOOP", config.loop);
2133         defaults->update("PIXELS_PER_SECOND", config.pixels_per_second);
2134         defaults->update("HJUSTIFICATION", config.hjustification);
2135         defaults->update("VJUSTIFICATION", config.vjustification);
2136         defaults->update("FADE_IN", config.fade_in);
2137         defaults->update("FADE_OUT", config.fade_out);
2138         defaults->update("TITLE_X", config.x);
2139         defaults->update("TITLE_Y", config.y);
2140         defaults->update("DROPSHADOW", config.dropshadow);
2141         defaults->update("TIMECODE", config.timecode);
2142         defaults->update("WINDOW_W", window_w);
2143         defaults->update("WINDOW_H", window_h);
2144         defaults->save();
2146 // Store text in separate path to isolate special characters
2147         FileSystem fs;
2148         sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2149         fs.complete_path(text_path);
2150         FILE *fd = fopen(text_path, "wb");
2151         if(fd)
2152         {
2153                 fwrite(config.text, strlen(config.text), 1, fd);
2154                 fclose(fd);
2155         }
2156 //      else
2157 //              perror("TitleMain::save_defaults");
2158         return 0;
2164 int TitleMain::load_configuration()
2166         KeyFrame *prev_keyframe, *next_keyframe;
2167         prev_keyframe = get_prev_keyframe(get_source_position());
2168         next_keyframe = get_next_keyframe(get_source_position());
2170 // printf("TitleMain::load_configuration 1 %d %d\n", 
2171 // prev_keyframe->position,
2172 // next_keyframe->position);
2174         TitleConfig old_config, prev_config, next_config;
2175         old_config.copy_from(config);
2176         read_data(prev_keyframe);
2177         prev_config.copy_from(config);
2178         read_data(next_keyframe);
2179         next_config.copy_from(config);
2181         config.prev_keyframe_position = prev_keyframe->position;
2182         config.next_keyframe_position = next_keyframe->position;
2183         if(config.next_keyframe_position == config.prev_keyframe_position)
2184                 config.next_keyframe_position = get_source_start() + get_total_len();
2187 // printf("TitleMain::load_configuration 10 %d %d\n", 
2188 // config.prev_keyframe_position,
2189 // config.next_keyframe_position);
2191         config.interpolate(prev_config, 
2192                 next_config, 
2193                 (next_keyframe->position == prev_keyframe->position) ?
2194                         get_source_position() :
2195                         prev_keyframe->position,
2196                 (next_keyframe->position == prev_keyframe->position) ?
2197                         get_source_position() + 1 :
2198                         next_keyframe->position,
2199                 get_source_position());
2201         if(!config.equivalent(old_config))
2202                 return 1;
2203         else
2204                 return 0;
2218 void TitleMain::save_data(KeyFrame *keyframe)
2220         FileXML output;
2222 // cause data to be stored directly in text
2223         output.set_shared_string(keyframe->data, MESSAGESIZE);
2224         output.tag.set_title("TITLE");
2225         output.tag.set_property("FONT", config.font);
2226         output.tag.set_property("ENCODING", config.encoding);
2227         output.tag.set_property("STYLE", (int64_t)config.style);
2228         output.tag.set_property("SIZE", config.size);
2229         output.tag.set_property("COLOR", config.color);
2230         output.tag.set_property("COLOR_STROKE", config.color_stroke);
2231         output.tag.set_property("STROKE_WIDTH", config.stroke_width);
2232         output.tag.set_property("MOTION_STRATEGY", config.motion_strategy);
2233         output.tag.set_property("LOOP", config.loop);
2234         output.tag.set_property("PIXELS_PER_SECOND", config.pixels_per_second);
2235         output.tag.set_property("HJUSTIFICATION", config.hjustification);
2236         output.tag.set_property("VJUSTIFICATION", config.vjustification);
2237         output.tag.set_property("FADE_IN", config.fade_in);
2238         output.tag.set_property("FADE_OUT", config.fade_out);
2239         output.tag.set_property("TITLE_X", config.x);
2240         output.tag.set_property("TITLE_Y", config.y);
2241         output.tag.set_property("DROPSHADOW", config.dropshadow);
2242         output.tag.set_property("TIMECODE", config.timecode);
2243         output.append_tag();
2244         output.append_newline();
2245         
2246         output.append_text(config.text);
2248         output.tag.set_title("/TITLE");
2249         output.append_tag();
2250         output.append_newline();
2251         output.terminate_string();
2252 //printf("TitleMain::save_data 1\n%s\n", output.string);
2253 //printf("TitleMain::save_data 2\n%s\n", config.text);
2256 void TitleMain::read_data(KeyFrame *keyframe)
2258         FileXML input;
2260         input.set_shared_string(keyframe->data, strlen(keyframe->data));
2262         int result = 0;
2263         int new_interlace = 0;
2264         int new_horizontal = 0;
2265         int new_luminance = 0;
2267         config.prev_keyframe_position = keyframe->position;
2268         while(!result)
2269         {
2270                 result = input.read_tag();
2272                 if(!result)
2273                 {
2274                         if(input.tag.title_is("TITLE"))
2275                         {
2276                                 input.tag.get_property("FONT", config.font);
2277                                 input.tag.get_property("ENCODING", config.encoding);
2278                                 config.style = input.tag.get_property("STYLE", (int64_t)config.style);
2279                                 config.size = input.tag.get_property("SIZE", config.size);
2280                                 config.color = input.tag.get_property("COLOR", config.color);
2281                                 config.color_stroke = input.tag.get_property("COLOR_STROKE", config.color_stroke);
2282                                 config.stroke_width = input.tag.get_property("STROKE_WIDTH", config.stroke_width);
2283                                 config.motion_strategy = input.tag.get_property("MOTION_STRATEGY", config.motion_strategy);
2284                                 config.loop = input.tag.get_property("LOOP", config.loop);
2285                                 config.pixels_per_second = input.tag.get_property("PIXELS_PER_SECOND", config.pixels_per_second);
2286                                 config.hjustification = input.tag.get_property("HJUSTIFICATION", config.hjustification);
2287                                 config.vjustification = input.tag.get_property("VJUSTIFICATION", config.vjustification);
2288                                 config.fade_in = input.tag.get_property("FADE_IN", config.fade_in);
2289                                 config.fade_out = input.tag.get_property("FADE_OUT", config.fade_out);
2290                                 config.x = input.tag.get_property("TITLE_X", config.x);
2291                                 config.y = input.tag.get_property("TITLE_Y", config.y);
2292                                 config.dropshadow = input.tag.get_property("DROPSHADOW", config.dropshadow);
2293                                 config.timecode = input.tag.get_property("TIMECODE", config.timecode);
2294                                 strcpy(config.text, input.read_text());
2295 //printf("TitleMain::read_data 1\n%s\n", input.string);
2296 //printf("TitleMain::read_data 2\n%s\n", config.text);
2297                         }
2298                         else
2299                         if(input.tag.title_is("/TITLE"))
2300                         {
2301                                 result = 1;
2302                         }
2303                 }
2304         }