r864: Merge 2.1:
[cinelerra_cv.git] / plugins / titler / title.C
blobc61a2e6e0e755fbaf4dd98a10ad0da1e31a1e020
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 "transportque.inc"
11 #include "ft2build.h"
12 #include FT_GLYPH_H
13 #include FT_BBOX_H
14 #include FT_OUTLINE_H
15 #include FT_STROKER_H
17 #include "language.h"
18 #include "mwindow.inc"
19 #include "picon_png.h"
20 #include "plugincolors.h"
21 #include "title.h"
22 #include "titlewindow.h"
23 #include "transportque.inc"
26 #include <errno.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <endian.h>
31 #include <byteswap.h>
32 #include <iconv.h>
33 #include <sys/stat.h>
35 #define ZERO (1.0 / 64.0)
37 #define FONT_SEARCHPATH "fonts"
38 //#define FONT_SEARCHPATH "/usr/X11R6/lib/X11/fonts"
41 REGISTER_PLUGIN(TitleMain)
44 TitleConfig::TitleConfig()
46         style = 0;
47         color = BLACK;
48         color_stroke = 0xff0000;
49         size = 24;
50         motion_strategy = NO_MOTION;
51         loop = 0;
52         hjustification = JUSTIFY_CENTER;
53         vjustification = JUSTIFY_MID;
54         fade_in = 0.0;
55         fade_out = 0.0;
56         x = 0.0;
57         y = 0.0;
58         dropshadow = 10;
59         sprintf(font, "fixed");
60         sprintf(text, _("hello world"));
61 #define DEFAULT_ENCODING "ISO8859-1"
62         sprintf(encoding, DEFAULT_ENCODING);
63 #define DEFAULT_TIMECODEFORMAT "h:mm:ss:ff"
64         sprintf(timecodeformat, DEFAULT_TIMECODEFORMAT);
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                 !strcasecmp(timecodeformat, that.timecodeformat) &&
81                 hjustification == that.hjustification &&
82                 vjustification == that.vjustification &&
83                 EQUIV(pixels_per_second, that.pixels_per_second) &&
84                 !strcasecmp(font, that.font) &&
85                 !strcasecmp(encoding, that.encoding) &&
86                 !strcmp(text, that.text);
89 void TitleConfig::copy_from(TitleConfig &that)
91         strcpy(font, that.font);
92         style = that.style;
93         size = that.size;
94         color = that.color;
95         color_stroke = that.color_stroke;
96         stroke_width = that.stroke_width;
97         pixels_per_second = that.pixels_per_second;
98         motion_strategy = that.motion_strategy;
99         loop = that.loop;
100         hjustification = that.hjustification;
101         vjustification = that.vjustification;
102         fade_in = that.fade_in;
103         fade_out = that.fade_out;
104         x = that.x;
105         y = that.y;
106         dropshadow = that.dropshadow;
107         timecode = that.timecode;
108         strcpy(timecodeformat, that.timecodeformat);
109         strcpy(text, that.text);
110         strcpy(encoding, that.encoding);
113 void TitleConfig::interpolate(TitleConfig &prev, 
114         TitleConfig &next, 
115         int64_t prev_frame, 
116         int64_t next_frame, 
117         int64_t current_frame)
119         strcpy(font, prev.font);
120         strcpy(encoding, prev.encoding);
121         style = prev.style;
122         size = prev.size;
123         color = prev.color;
124         color_stroke = prev.color_stroke;
125         stroke_width = prev.stroke_width;
126         motion_strategy = prev.motion_strategy;
127         loop = prev.loop;
128         hjustification = prev.hjustification;
129         vjustification = prev.vjustification;
130         fade_in = prev.fade_in;
131         fade_out = prev.fade_out;
132         pixels_per_second = prev.pixels_per_second;
133         strcpy(text, prev.text);
135         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
136         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
139 //      this->x = prev.x * prev_scale + next.x * next_scale;
140 //      this->y = prev.y * prev_scale + next.y * next_scale;
141         this->x = prev.x;
142         this->y = prev.y;
143         timecode = prev.timecode;
144         strcpy(timecodeformat, prev.timecodeformat);
145 //      this->dropshadow = (int)(prev.dropshadow * prev_scale + next.dropshadow * next_scale);
146         this->dropshadow = prev.dropshadow;
165 FontEntry::FontEntry()
167         path = 0;
168         foundary = 0;
169         family = 0;
170         weight = 0;
171         slant = 0;
172         swidth = 0;
173         adstyle = 0;
174         spacing = 0;
175         registry = 0;
176         encoding = 0;
177         fixed_title = 0;
178         fixed_style = 0;
181 FontEntry::~FontEntry()
183         if(path) delete [] path;
184         if(foundary) delete [] foundary;
185         if(family) delete [] family;
186         if(weight) delete [] weight;
187         if(slant) delete [] slant;
188         if(swidth) delete [] swidth;
189         if(adstyle) delete [] adstyle;
190         if(spacing) delete [] spacing;
191         if(registry) delete [] registry;
192         if(encoding) delete [] encoding;
193         if(fixed_title) delete [] fixed_title;
196 void FontEntry::dump()
198         printf("%s: %s %s %s %s %s %s %d %d %d %d %s %d %s %s\n",
199                 path,
200                 foundary,
201                 family,
202                 weight,
203                 slant,
204                 swidth,
205                 adstyle,
206                 pixelsize,
207                 pointsize,
208                 xres,
209                 yres,
210                 spacing,
211                 avg_width,
212                 registry,
213                 encoding);
216 TitleGlyph::TitleGlyph()
218         char_code = 0;
219         c=0;
220         data = 0;
221         data_stroke = 0;
225 TitleGlyph::~TitleGlyph()
227 //printf("TitleGlyph::~TitleGlyph 1\n");
228         if(data) delete data;
229         if(data_stroke) delete data_stroke;
242 GlyphPackage::GlyphPackage() : LoadPackage()
246 GlyphUnit::GlyphUnit(TitleMain *plugin, GlyphEngine *server)
247  : LoadClient(server)
249         this->plugin = plugin;
250         current_font = 0;
251         freetype_library = 0;
252         freetype_face = 0;
255 GlyphUnit::~GlyphUnit()
257         if(freetype_library) FT_Done_FreeType(freetype_library);
260 void GlyphUnit::process_package(LoadPackage *package)
262         GlyphPackage *pkg = (GlyphPackage*)package;
263         TitleGlyph *glyph = pkg->glyph;
264         int result = 0;
266         if(!freetype_library)
267         {
268                 current_font = plugin->get_font();
270                 if(plugin->load_freetype_face(freetype_library,
271                         freetype_face,
272                         current_font->path))
273                 {
274                         printf(_("GlyphUnit::process_package FT_New_Face failed.\n"));
275                         result = 1;
276                 }
277                 else
278                 {
279                         FT_Set_Pixel_Sizes(freetype_face, plugin->config.size, 0);
280                 }
281         }
283         if(!result)
284         {
285                 int gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
287 //printf("GlyphUnit::process_package 1 %c\n", glyph->char_code);
288 // Char not found
289                 if (gindex == 0) 
290                 {
291 // carrige return
292                         if (glyph->char_code != 10)  
293                                 printf(_("GlyphUnit::process_package FT_Load_Char failed - char: %i.\n"),
294                                         glyph->char_code);
295 // Prevent a crash here
296                         glyph->width = 8;
297                         glyph->height = 8;
298                         glyph->pitch = 8;
299                         glyph->left = 9;
300                         glyph->top = 9;
301                         glyph->freetype_index = 0;
302                         glyph->advance_w = 8;
303                         glyph->data = new VFrame(0,
304                                 8,
305                                 8,
306                                 BC_A8,
307                                 8);
308                         glyph->data->clear_frame();
309                         glyph->data_stroke = 0;
313 // create outline glyph
314                         if (plugin->config.stroke_width >= ZERO && 
315                                 (plugin->config.style & FONT_OUTLINE))
316                         {
317                                 glyph->data_stroke = new VFrame(0,
318                                         8,
319                                         8,
320                                         BC_A8,
321                                         8);
322                                 glyph->data_stroke->clear_frame();
323                         }
327                 }
328                 else
329 // char found and no outline desired
330                 if (plugin->config.stroke_width < ZERO ||
331                         !(plugin->config.style & FONT_OUTLINE)) 
332                 {
333                         FT_Glyph glyph_image;
334                         FT_BBox bbox;
335                         FT_Bitmap bm;
336                         FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
337                         FT_Get_Glyph(freetype_face->glyph, &glyph_image);
338                         FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
339 //                      printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
340 //                                      bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax);
342                         FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
343                                 - bbox.xMin,
344                                 - bbox.yMin);
345                         glyph->width = bm.width = ((bbox.xMax - bbox.xMin + 63) >> 6);
346                         glyph->height = bm.rows = ((bbox.yMax - bbox.yMin + 63) >> 6);
347                         glyph->pitch = bm.pitch = bm.width;
348                         bm.pixel_mode = FT_PIXEL_MODE_GRAY;
349                         bm.num_grays = 256;
350                         glyph->left = (bbox.xMin + 31) >> 6;
351                         if (glyph->left < 0) glyph->left = 0;
352                         glyph->top = (bbox.yMax + 31) >> 6;
353                         glyph->freetype_index = gindex;
354                         glyph->advance_w = ((freetype_face->glyph->advance.x + 31) >> 6);
355 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n", 
356 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
357         
358                         glyph->data = new VFrame(0,
359                                 glyph->width,
360                                 glyph->height,
361                                 BC_A8,
362                                 glyph->pitch);
363                         glyph->data->clear_frame();
364                         bm.buffer = glyph->data->get_data();
365                         FT_Outline_Get_Bitmap( freetype_library,
366                                 &((FT_OutlineGlyph) glyph_image)->outline,
367                                 &bm);
368                         FT_Done_Glyph(glyph_image);
369                 }
370                 else 
371 // Outline desired and glyph found
372                 {
373                         FT_Glyph glyph_image;
374                         int no_outline = 0;
375                         FT_Stroker stroker;
376                         FT_Outline outline;
377                         FT_Bitmap bm;
378                         FT_BBox bbox;
379                         FT_UInt npoints, ncontours;     
381                         typedef struct  FT_LibraryRec_ 
382                         {    
383                                 FT_Memory memory; 
384                         } FT_LibraryRec;
386                         FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
387                         FT_Get_Glyph(freetype_face->glyph, &glyph_image);
389 // check if the outline is ok (non-empty);
390                         FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
391                         if (bbox.xMin == 0 && bbox.xMax == 0 && bbox.yMin ==0 && bbox.yMax == 0)
392                         {
393                                 FT_Done_Glyph(glyph_image);
394                                 glyph->data =  new VFrame(0, 0, BC_A8,0);
395                                 glyph->data_stroke =  new VFrame(0, 0, BC_A8,0);;
396                                 glyph->width=0;
397                                 glyph->height=0;
398                                 glyph->top=0;
399                                 glyph->left=0;
400                                 glyph->advance_w =((int)(freetype_face->glyph->advance.x + 
401                                         plugin->config.stroke_width * 64)) >> 6;
402                                 return;
403                         }
404 #if FREETYPE_MAJOR > 2 || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 2)
405                         FT_Stroker_New(freetype_library, &stroker);
406 #else
407                         FT_Stroker_New(((FT_LibraryRec *)freetype_library)->memory, &stroker);
408 #endif
409                         FT_Stroker_Set(stroker, (int)(plugin->config.stroke_width * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
410                         FT_Stroker_ParseOutline(stroker, &((FT_OutlineGlyph) glyph_image)->outline,1);
411                         FT_Stroker_GetCounts(stroker,&npoints, &ncontours);
412                         if (npoints ==0 && ncontours == 0) 
413                         {
414 // this never happens, but FreeType has a bug regarding Linotype's Palatino font
415                                 FT_Stroker_Done(stroker);
416                                 FT_Done_Glyph(glyph_image);
417                                 glyph->data =  new VFrame(0, 0, BC_A8,0);
418                                 glyph->data_stroke =  new VFrame(0, 0, BC_A8,0);;
419                                 glyph->width=0;
420                                 glyph->height=0;
421                                 glyph->top=0;
422                                 glyph->left=0;
423                                 glyph->advance_w =((int)(freetype_face->glyph->advance.x + 
424                                         plugin->config.stroke_width * 64)) >> 6;
425                                 return;
426                         };
428                         FT_Outline_New(freetype_library, npoints, ncontours, &outline);
429                         outline.n_points=0;
430                         outline.n_contours=0;
431                         FT_Stroker_Export (stroker, &outline);
432                         FT_Outline_Get_BBox(&outline, &bbox);
433                 
434                         FT_Outline_Translate(&outline,
435                                         - bbox.xMin,
436                                         - bbox.yMin);
437                 
438                         FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
439                                         - bbox.xMin,
440                                         - bbox.yMin + (int)(plugin->config.stroke_width*32));
441 //                      printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\nFill        Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
442 //                                      bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax,
443 //                                      bbox_fill.xMin,bbox_fill.xMax, bbox_fill.yMin, bbox_fill.yMax);
444         
445                         glyph->width = bm.width = ((bbox.xMax - bbox.xMin) >> 6)+1;
446                         glyph->height = bm.rows = ((bbox.yMax - bbox.yMin) >> 6) +1;
447                         glyph->pitch = bm.pitch = bm.width;
448                         bm.pixel_mode = FT_PIXEL_MODE_GRAY;
449                         bm.num_grays = 256;
450                         glyph->left = (bbox.xMin + 31) >> 6;
451                         if (glyph->left < 0) glyph->left = 0;
452                         glyph->top = (bbox.yMax + 31) >> 6;
453                         glyph->freetype_index = gindex;
454                         int real_advance = ((int)ceil((float)freetype_face->glyph->advance.x + 
455                                 plugin->config.stroke_width * 64) >> 6);
456                         glyph->advance_w = glyph->width + glyph->left;
457                         if (real_advance > glyph->advance_w) 
458                                 glyph->advance_w = real_advance;
459 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n", 
460 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
463 //printf("GlyphUnit::process_package 1\n");
464                         glyph->data = new VFrame(0,
465                                 glyph->width,
466                                 glyph->height,
467                                 BC_A8,
468                                 glyph->pitch);
469                         glyph->data_stroke = new VFrame(0,
470                                 glyph->width,
471                                 glyph->height,
472                                 BC_A8,
473                                 glyph->pitch);
474                         glyph->data->clear_frame();
475                         glyph->data_stroke->clear_frame();
476 // for debugging        memset( glyph->data_stroke->get_data(), 60, glyph->pitch * glyph->height);
477                         bm.buffer=glyph->data->get_data();
478                         FT_Outline_Get_Bitmap( freetype_library,
479                                 &((FT_OutlineGlyph) glyph_image)->outline,
480                                 &bm);   
481                         bm.buffer=glyph->data_stroke->get_data();
482                         FT_Outline_Get_Bitmap( freetype_library,
483                         &outline,
484                                 &bm);
485                         FT_Outline_Done(freetype_library,&outline);
486                         FT_Stroker_Done(stroker);
487                         FT_Done_Glyph(glyph_image);
489 //printf("GlyphUnit::process_package 2\n");
490                 }
491         }
494 GlyphEngine::GlyphEngine(TitleMain *plugin, int cpus)
495  : LoadServer(cpus, cpus)
497         this->plugin = plugin;
499 void GlyphEngine::init_packages()
501         int current_package = 0;
502         for(int i = 0; i < plugin->glyphs.total; i++)
503         {
504                 if(!plugin->glyphs.values[i]->data)
505                 {
506                         GlyphPackage *pkg = (GlyphPackage*)get_package(current_package++);
507                         pkg->glyph = plugin->glyphs.values[i];
508                 }
509         }
511 LoadClient* GlyphEngine::new_client()
513         return new GlyphUnit(plugin, this);
515 LoadPackage* GlyphEngine::new_package()
517         return new GlyphPackage;
532 // Copy a single character to the text mask
533 TitlePackage::TitlePackage()
534  : LoadPackage()
539 TitleUnit::TitleUnit(TitleMain *plugin, TitleEngine *server)
540  : LoadClient(server)
542         this->plugin = plugin;
545 void TitleUnit::draw_glyph(VFrame *output, TitleGlyph *glyph, int x, int y)
547         int glyph_w = glyph->data->get_w();
548         int glyph_h = glyph->data->get_h();
549         int output_w = output->get_w();
550         int output_h = output->get_h();
551         unsigned char **in_rows = glyph->data->get_rows();
552         unsigned char **out_rows = output->get_rows();
554 //printf("TitleUnit::draw_glyph 1 %c %d %d\n", glyph->c, x, y);
555         for(int in_y = 0; in_y < glyph_h; in_y++)
556         {
557 //              int y_out = y + plugin->ascent + in_y - glyph->top;
558                 int y_out = y + plugin->get_char_height() + in_y - glyph->top;
560 //printf("TitleUnit::draw_glyph 1 %d\n", y_out);
561                 if(y_out >= 0 && y_out < output_h)
562                 {
563                         unsigned char *out_row = out_rows[y_out];
564                         unsigned char *in_row = in_rows[in_y];
565                         for(int in_x = 0; in_x < glyph_w; in_x++)
566                         {
567                                 int x_out = x + glyph->left + in_x;
568                                 if(x_out >= 0 && x_out < output_w)
569                                 {
570                                         if(in_row[in_x] > 0)
571                                                 out_row[x_out] = in_row[in_x];
572 //out_row[x_out] = 0xff;
573                                 }
574                         }
575                 }
576         }
580 void TitleUnit::process_package(LoadPackage *package)
582         TitlePackage *pkg = (TitlePackage*)package;
583         
584         if(pkg->c != 0xa)
585         {
586                 for(int i = 0; i < plugin->glyphs.total; i++)
587                 {
588                         TitleGlyph *glyph = plugin->glyphs.values[i];
589                         if(glyph->c == pkg->c)
590                         {
591                                 draw_glyph(plugin->text_mask, glyph, pkg->x, pkg->y);
592                                 if(plugin->config.stroke_width >= ZERO &&
593                                         (plugin->config.style & FONT_OUTLINE)) 
594                                 {
595                                         VFrame *tmp = glyph->data;
596                                         glyph->data = glyph->data_stroke;
597                                         draw_glyph(plugin->text_mask_stroke, glyph, pkg->x, pkg->y);
598                                         glyph->data = tmp;
599                                 }
600                                 break;
601                         }
602                 }
603         }
606 TitleEngine::TitleEngine(TitleMain *plugin, int cpus)
607  : LoadServer(cpus, cpus)
609         this->plugin = plugin;
612 void TitleEngine::init_packages()
614 //printf("TitleEngine::init_packages 1\n");
615         int visible_y1 = plugin->visible_row1 * plugin->get_char_height();
616 //printf("TitleEngine::init_packages 1\n");
617         int current_package = 0;
618         for(int i = plugin->visible_char1; i < plugin->visible_char2; i++)
619         {
620                 title_char_position_t *char_position = plugin->char_positions + i;
621                 TitlePackage *pkg = (TitlePackage*)get_package(current_package);
622 //printf("TitleEngine::init_packages 1\n");
623                 pkg->x = char_position->x;
624 //printf("TitleEngine::init_packages 1\n");
625                 pkg->y = char_position->y - visible_y1;
626 //printf("TitleEngine::init_packages 1\n");
627                 pkg->c = plugin->config.text[i];
628 //printf("TitleEngine::init_packages 2\n");
629                 current_package++;
630         }
633 LoadClient* TitleEngine::new_client()
635         return new TitleUnit(plugin, this);
638 LoadPackage* TitleEngine::new_package()
640         return new TitlePackage;
653 TitleTranslatePackage::TitleTranslatePackage()
654  : LoadPackage()
659 TitleTranslateUnit::TitleTranslateUnit(TitleMain *plugin, TitleTranslate *server)
660  : LoadClient(server)
662         this->plugin = plugin;
667 #define TRANSLATE(type, max, components, r, g, b) \
668 { \
669         unsigned char **in_rows = plugin->text_mask->get_rows(); \
670         type **out_rows = (type**)plugin->output->get_rows(); \
672         for(int i = pkg->y1; i < pkg->y2; i++) \
673         { \
674                 if(i + server->out_y1_int >= 0 && \
675                         i + server->out_y1_int < server->output_h) \
676                 { \
677                         int in_y1, in_y2; \
678                         float y_fraction1, y_fraction2; \
679                         float y_output_fraction; \
680                         in_y1 = server->y_table[i].in_x1; \
681                         in_y2 = server->y_table[i].in_x2; \
682                         y_fraction1 = server->y_table[i].in_fraction1; \
683                         y_fraction2 = server->y_table[i].in_fraction2; \
684                         y_output_fraction = server->y_table[i].output_fraction; \
685                         unsigned char *in_row1 = in_rows[in_y1]; \
686                         unsigned char *in_row2 = in_rows[in_y2]; \
687                         type *out_row = out_rows[i + server->out_y1_int]; \
689                         for(int j = server->out_x1_int; j < server->out_x2_int; j++) \
690                         { \
691                                 if(j >= 0 && j < server->output_w) \
692                                 { \
693                                         int in_x1; \
694                                         int in_x2; \
695                                         float x_fraction1; \
696                                         float x_fraction2; \
697                                         float x_output_fraction; \
698                                         in_x1 =  \
699                                                 server->x_table[j - server->out_x1_int].in_x1; \
700                                         in_x2 =  \
701                                                 server->x_table[j - server->out_x1_int].in_x2; \
702                                         x_fraction1 =  \
703                                                 server->x_table[j - server->out_x1_int].in_fraction1; \
704                                         x_fraction2 =  \
705                                                 server->x_table[j - server->out_x1_int].in_fraction2; \
706                                         x_output_fraction =  \
707                                                 server->x_table[j - server->out_x1_int].output_fraction; \
709                                         float fraction1 = x_fraction1 * y_fraction1; \
710                                         float fraction2 = x_fraction2 * y_fraction1; \
711                                         float fraction3 = x_fraction1 * y_fraction2; \
712                                         float fraction4 = x_fraction2 * y_fraction2; \
713                                         int input = (int)(in_row1[in_x1] * fraction1 +  \
714                                                                 in_row1[in_x2] * fraction2 +  \
715                                                                 in_row2[in_x1] * fraction3 +  \
716                                                                 in_row2[in_x2] * fraction4 + 0.5); \
717                                         input *= plugin->alpha; \
718 /* Alpha is 0 - 256 */ \
719                                         input >>= 8; \
721                                         int anti_input = 0xff - input; \
722                                         if(components == 4) \
723                                         { \
724                                                 out_row[j * components + 0] =  \
725                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
726                                                 out_row[j * components + 1] =  \
727                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
728                                                 out_row[j * components + 2] =  \
729                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
730                                                 if(max == 0xffff) \
731                                                         out_row[j * components + 3] =  \
732                                                                 MAX((input << 8) | input, out_row[j * components + 3]); \
733                                                 else \
734                                                         out_row[j * components + 3] =  \
735                                                                 MAX(input, out_row[j * components + 3]); \
736                                         } \
737                                         else \
738                                         { \
739                                                 out_row[j * components + 0] =  \
740                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
741                                                 out_row[j * components + 1] =  \
742                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
743                                                 out_row[j * components + 2] =  \
744                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
745                                         } \
746                                 } \
747                         } \
748                 } \
749         } \
753 #define TRANSLATEA(type, max, components, r, g, b) \
754 { \
755         unsigned char **in_rows = plugin->text_mask->get_rows(); \
756         type **out_rows = (type**)plugin->output->get_rows(); \
758         for(int i = pkg->y1; i < pkg->y2; i++) \
759         { \
760                 if(i + server->out_y1_int >= 0 && \
761                         i + server->out_y1_int < server->output_h) \
762                 { \
763                         unsigned char *in_row = in_rows[i]; \
764                         type *out_row = out_rows[i + server->out_y1_int]; \
766                         for(int j = server->out_x1; j < server->out_x2_int; j++) \
767                         { \
768                                 if(j  >= 0 && \
769                                         j < server->output_w) \
770                                 { \
771                                         int input = (int)(in_row[j - server->out_x1]);  \
773                                         input *= plugin->alpha; \
774 /* Alpha is 0 - 256 */ \
775                                         input >>= 8; \
777                                         int anti_input = 0xff - input; \
778                                         if(components == 4) \
779                                         { \
780                                                 out_row[j * components + 0] =  \
781                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
782                                                 out_row[j * components + 1] =  \
783                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
784                                                 out_row[j * components + 2] =  \
785                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
786                                                 if(max == 0xffff) \
787                                                         out_row[j * components + 3] =  \
788                                                                 MAX((input << 8) | input, out_row[j * components + 3]); \
789                                                 else \
790                                                         out_row[j * components + 3] =  \
791                                                                 MAX(input, out_row[j * components + 3]); \
792                                         } \
793                                         else \
794                                         { \
795                                                 out_row[j * components + 0] =  \
796                                                         (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
797                                                 out_row[j * components + 1] =  \
798                                                         (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
799                                                 out_row[j * components + 2] =  \
800                                                         (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
801                                         } \
802                                 } \
803                         } \
804                 } \
805         } \
808 static YUV yuv;
810 void TitleTranslateUnit::process_package(LoadPackage *package)
812         TitleTranslatePackage *pkg = (TitleTranslatePackage*)package;
813         TitleTranslate *server = (TitleTranslate*)this->server;
814         int r_in, g_in, b_in;
816 //printf("TitleTranslateUnit::process_package 1 %d %d\n", pkg->y1, pkg->y2);
818         r_in = (plugin->config.color & 0xff0000) >> 16;
819         g_in = (plugin->config.color & 0xff00) >> 8;
820         b_in = plugin->config.color & 0xff;
822         switch(plugin->output->get_color_model())
823         {
824                 case BC_RGB888:
825                 {
826                         TRANSLATE(unsigned char, 0xff, 3, r_in, g_in, b_in);
827                         break;
828                 }
829                 case BC_RGB_FLOAT:
830                 {
831                         float r, g, b;
832                         r = (float)r_in / 0xff;
833                         g = (float)g_in / 0xff;
834                         b = (float)b_in / 0xff;
835                         TRANSLATE(float, 0xff, 3, r, g, b);
836                         break;
837                 }
838                 case BC_YUV888:
839                 {
840                         unsigned char y, u, v;
841                         yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
842                         TRANSLATE(unsigned char, 0xff, 3, y, u, v);
843                         break;
844                 }
845                 case BC_RGB161616:
846                 {
847                         uint16_t r, g, b;
848                         r = (r_in << 8) | r_in;
849                         g = (g_in << 8) | g_in;
850                         b = (b_in << 8) | b_in;
851                         TRANSLATE(uint16_t, 0xffff, 3, r, g, b);
852                         break;
853                 }
854                 case BC_YUV161616:
855                 {
856                         uint16_t y, u, v;
857                         yuv.rgb_to_yuv_16(
858                                 (r_in << 8) | r_in, 
859                                 (g_in << 8) | g_in, 
860                                 (b_in << 8) | b_in, 
861                                 y, 
862                                 u, 
863                                 v);
864                         TRANSLATE(uint16_t, 0xffff, 3, y, u, v);
865                         break;
866                 }
867                 case BC_RGBA_FLOAT:
868                 {
869                         float r, g, b;
870                         r = (float)r_in / 0xff;
871                         g = (float)g_in / 0xff;
872                         b = (float)b_in / 0xff;
873                         TRANSLATE(float, 0xff, 4, r, g, b);
874                         break;
875                 }
876                 case BC_RGBA8888:
877                 {       
878                         TRANSLATE(unsigned char, 0xff, 4, r_in, g_in, b_in);
879                         break;
880                 }
881                 case BC_YUVA8888:
882                 {       
883                         unsigned char y, u, v;
884                         yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
885                         TRANSLATE(unsigned char, 0xff, 4, y, u, v);
886                         break;
887                 }
888                 case BC_RGBA16161616:
889                 {       
890                         uint16_t r, g, b;
891                         r = (r_in << 8) | r_in;
892                         g = (g_in << 8) | g_in;
893                         b = (b_in << 8) | b_in;
894                         TRANSLATE(uint16_t, 0xffff, 4, r, g, b);
895                         break;
896                 }
897                 case BC_YUVA16161616:
898                 {       
899                         uint16_t y, u, v;
900                         yuv.rgb_to_yuv_16(
901                                 (r_in << 8) | r_in, 
902                                 (g_in << 8) | g_in, 
903                                 (b_in << 8) | b_in, 
904                                 y, 
905                                 u, 
906                                 v);
907                         TRANSLATE(uint16_t, 0xffff, 4, y, u, v);
908                         break;
909                 }
910         }
912 //printf("TitleTranslateUnit::process_package 5\n");
918 TitleTranslate::TitleTranslate(TitleMain *plugin, int cpus)
919  : LoadServer(1, 1)
921         this->plugin = plugin;
922         x_table = y_table = 0;
925 TitleTranslate::~TitleTranslate()
927         if(x_table) delete [] x_table;
928         if(y_table) delete [] y_table;
931 void TitleTranslate::init_packages()
933 //printf("TitleTranslate::init_packages 1\n");
934 // Generate scaling tables
935         if(x_table) delete [] x_table;
936         if(y_table) delete [] y_table;
937 //printf("TitleTranslate::init_packages 1\n");
939         output_w = plugin->output->get_w();
940         output_h = plugin->output->get_h();
941 //printf("TitleTranslate::init_packages 1 %f %d\n", plugin->text_x1, plugin->text_w);
944         TranslateUnit::translation_array_f(x_table, 
945                 plugin->text_x1, 
946                 plugin->text_x1 + plugin->text_w,
947                 0,
948                 plugin->text_w,
949                 plugin->text_w, 
950                 output_w, 
951                 out_x1_int,
952                 out_x2_int);
953 //printf("TitleTranslate::init_packages 1 %f %f\n", plugin->mask_y1, plugin->mask_y2);
955         TranslateUnit::translation_array_f(y_table, 
956                 plugin->mask_y1, 
957                 plugin->mask_y1 + plugin->text_mask->get_h(),
958                 0,
959                 plugin->text_mask->get_h(),
960                 plugin->text_mask->get_h(), 
961                 output_h, 
962                 out_y1_int,
963                 out_y2_int);
965 //printf("TitleTranslate::init_packages 1\n");
967         
968         out_y1 = out_y1_int;
969         out_y2 = out_y2_int;
970         out_x1 = out_x1_int;
971         out_x2 = out_x2_int;
972         int increment = (out_y2 - out_y1) / get_total_packages() + 1;
974 //printf("TitleTranslate::init_packages 1 %d %d %d %d\n", 
975 //      out_y1, out_y2, out_y1_int, out_y2_int);
976         for(int i = 0; i < get_total_packages(); i++)
977         {
978                 TitleTranslatePackage *pkg = (TitleTranslatePackage*)get_package(i);
979                 pkg->y1 = i * increment;
980                 pkg->y2 = i * increment + increment;
981                 if(pkg->y1 > out_y2 - out_y1)
982                         pkg->y1 = out_y2 - out_y1;
983                 if(pkg->y2 > out_y2 - out_y1)
984                         pkg->y2 = out_y2 - out_y1;
985         }
986 //printf("TitleTranslate::init_packages 2\n");
989 LoadClient* TitleTranslate::new_client()
991         return new TitleTranslateUnit(plugin, this);
994 LoadPackage* TitleTranslate::new_package()
996         return new TitleTranslatePackage;
1013 ArrayList<FontEntry*>* TitleMain::fonts = 0;
1017 TitleMain::TitleMain(PluginServer *server)
1018  : PluginVClient(server)
1020         PLUGIN_CONSTRUCTOR_MACRO
1022 // Build font database
1023         build_fonts();
1024         text_mask = 0;
1025         text_mask_stroke = 0;
1026         glyph_engine = 0;
1027         title_engine = 0;
1028         freetype_library = 0;
1029         freetype_face = 0;
1030         char_positions = 0;
1031         rows_bottom = 0;
1032         translate = 0;
1033         need_reconfigure = 1;
1036 TitleMain::~TitleMain()
1038         PLUGIN_DESTRUCTOR_MACRO
1039         if(text_mask) delete text_mask;
1040         if(text_mask_stroke) delete text_mask_stroke;
1041         if(char_positions) delete [] char_positions;
1042         if(rows_bottom) delete [] rows_bottom;
1043         clear_glyphs();
1044         if(glyph_engine) delete glyph_engine;
1045         if(title_engine) delete title_engine;
1046         if(freetype_library) FT_Done_FreeType(freetype_library);
1047         if(translate) delete translate;
1050 char* TitleMain::plugin_title() { return N_("Title"); }
1051 int TitleMain::is_realtime() { return 1; }
1052 int TitleMain::is_synthesis() { return 1; }
1054 VFrame* TitleMain::new_picon()
1056         return new VFrame(picon_png);
1060 void TitleMain::build_fonts()
1062         if(!fonts)
1063         {
1064                 fonts = new ArrayList<FontEntry*>;
1065 // Construct path from location of the plugin
1066                 char search_path[BCTEXTLEN];
1067                 strcpy(search_path, PluginClient::get_path());
1068                 char *ptr = strrchr(search_path, '/');
1069                 strcpy(ptr + 1, FONT_SEARCHPATH);
1070                 char command_line[BCTEXTLEN];
1072                 sprintf(command_line, 
1073                         "find %s -name 'fonts.dir' -print -exec cat {} \\;", 
1074                         search_path);
1075 //printf("TitleMain::build_fonts %s\n", command_line);
1077                 FILE *in = popen(command_line, "r");
1080                 char current_dir[BCTEXTLEN];
1081                 FT_Library freetype_library = 0;        // Freetype library
1082                 FT_Face freetype_face = 0;
1084 //              FT_Init_FreeType(&freetype_library);
1085                 current_dir[0] = 0;
1087                 while(!feof(in))
1088                 {
1089                         char string[BCTEXTLEN], string2[BCTEXTLEN];
1090                         fgets(string, BCTEXTLEN, in);
1091                         if(!strlen(string)) break;
1093                         char *in_ptr = string;
1094                         char *out_ptr;
1096 // Get current directory
1098                         if(string[0] == '/')
1099                         {
1100                                 out_ptr = current_dir;
1101                                 while(*in_ptr != 0 && *in_ptr != 0xa)
1102                                         *out_ptr++ = *in_ptr++;
1103                                 out_ptr--;
1104                                 while(*out_ptr != '/')
1105                                         *out_ptr-- = 0;
1106                         }
1107                         else
1108                         {
1111 //printf("TitleMain::build_fonts %s\n", string);
1112                                 FontEntry *entry = new FontEntry;
1113                                 int result = 0;
1115 // Path
1116                                 out_ptr = string2;
1117                                 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa)
1118                                 {
1119                                         *out_ptr++ = *in_ptr++;
1120                                 }
1121                                 *out_ptr = 0;
1122                                 if(string2[0] == '/')
1123                                 {
1124                                         entry->path = new char[strlen(string2) + 1];
1125                                         sprintf(entry->path, "%s", string2);
1126                                 }
1127                                 else
1128                                 {
1129                                         entry->path = new char[strlen(current_dir) + strlen(string2) + 1];
1130                                         sprintf(entry->path, "%s%s", current_dir, string2);
1131                                 }
1134 // Test path existence
1135                                 struct stat test_stat;
1136                                 if(stat(entry->path, &test_stat))
1137                                 {
1138                                         result = 1;
1139                                 }
1140 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1142 // Foundary
1143                                 while(*in_ptr != 0 && *in_ptr != 0xa && (*in_ptr == ' ' || *in_ptr == '-'))
1144                                         in_ptr++;
1146                                 out_ptr = string2;
1147                                 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa && *in_ptr != '-')
1148                                 {
1149                                         *out_ptr++ = *in_ptr++;
1150                                 }
1151                                 *out_ptr = 0;
1152                                 entry->foundary = new char[strlen(string2) + 1];
1153                                 strcpy(entry->foundary, string2);
1154                                 if(*in_ptr == '-') in_ptr++;
1157 // Family
1158                                 out_ptr = string2;
1159                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1160                                 {
1161                                         *out_ptr++ = *in_ptr++;
1162                                 }
1163                                 *out_ptr = 0;
1164                                 entry->family = new char[strlen(string2) + 1];
1165                                 strcpy(entry->family, string2);
1166                                 if(*in_ptr == '-') in_ptr++;
1168 // Weight
1169                                 out_ptr = string2;
1170                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1171                                 {
1172                                         *out_ptr++ = *in_ptr++;
1173                                 }
1174                                 *out_ptr = 0;
1175                                 entry->weight = new char[strlen(string2) + 1];
1176                                 strcpy(entry->weight, string2);
1177                                 if(*in_ptr == '-') in_ptr++;
1179 // Slant
1180                                 out_ptr = string2;
1181                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1182                                 {
1183                                         *out_ptr++ = *in_ptr++;
1184                                 }
1185                                 *out_ptr = 0;
1186                                 entry->slant = new char[strlen(string2) + 1];
1187                                 strcpy(entry->slant, string2);
1188                                 if(*in_ptr == '-') in_ptr++;
1190 // SWidth
1191                                 out_ptr = string2;
1192                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1193                                 {
1194                                         *out_ptr++ = *in_ptr++;
1195                                 }
1196                                 *out_ptr = 0;
1197                                 entry->swidth = new char[strlen(string2) + 1];
1198                                 strcpy(entry->swidth, string2);
1199                                 if(*in_ptr == '-') in_ptr++;
1201 // Adstyle
1202                                 out_ptr = string2;
1203                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1204                                 {
1205                                         *out_ptr++ = *in_ptr++;
1206                                 }
1207                                 *out_ptr = 0;
1208                                 entry->adstyle = new char[strlen(string2) + 1];
1209                                 strcpy(entry->adstyle, string2);
1210                                 if(*in_ptr == '-') in_ptr++;
1212 // pixelsize
1213                                 out_ptr = string2;
1214                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1215                                 {
1216                                         *out_ptr++ = *in_ptr++;
1217                                 }
1218                                 *out_ptr = 0;
1219                                 entry->pixelsize = atol(string2);
1220                                 if(*in_ptr == '-') in_ptr++;
1222 // pointsize
1223                                 out_ptr = string2;
1224                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1225                                 {
1226                                         *out_ptr++ = *in_ptr++;
1227                                 }
1228                                 *out_ptr = 0;
1229                                 entry->pointsize = atol(string2);
1230                                 if(*in_ptr == '-') in_ptr++;
1232 // xres
1233                                 out_ptr = string2;
1234                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1235                                 {
1236                                         *out_ptr++ = *in_ptr++;
1237                                 }
1238                                 *out_ptr = 0;
1239                                 entry->xres = atol(string2);
1240                                 if(*in_ptr == '-') in_ptr++;
1242 // yres
1243                                 out_ptr = string2;
1244                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1245                                 {
1246                                         *out_ptr++ = *in_ptr++;
1247                                 }
1248                                 *out_ptr = 0;
1249                                 entry->yres = atol(string2);
1250                                 if(*in_ptr == '-') in_ptr++;
1252 // spacing
1253                                 out_ptr = string2;
1254                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1255                                 {
1256                                         *out_ptr++ = *in_ptr++;
1257                                 }
1258                                 *out_ptr = 0;
1259                                 entry->spacing = new char[strlen(string2) + 1];
1260                                 strcpy(entry->spacing, string2);
1261                                 if(*in_ptr == '-') in_ptr++;
1263 // avg_width
1264                                 out_ptr = string2;
1265                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1266                                 {
1267                                         *out_ptr++ = *in_ptr++;
1268                                 }
1269                                 *out_ptr = 0;
1270                                 entry->avg_width = atol(string2);
1271                                 if(*in_ptr == '-') in_ptr++;
1273 // registry
1274                                 out_ptr = string2;
1275                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1276                                 {
1277                                         *out_ptr++ = *in_ptr++;
1278                                 }
1279                                 *out_ptr = 0;
1280                                 entry->registry = new char[strlen(string2) + 1];
1281                                 strcpy(entry->registry, string2);
1282                                 if(*in_ptr == '-') in_ptr++;
1284 // encoding
1285                                 out_ptr = string2;
1286                                 while(*in_ptr != 0 && *in_ptr != 0xa)
1287                                 {
1288                                         *out_ptr++ = *in_ptr++;
1289                                 }
1290                                 *out_ptr = 0;
1291                                 entry->encoding = new char[strlen(string2) + 1];
1292                                 strcpy(entry->encoding, string2);
1296 // Add to list
1297                                 if(strlen(entry->foundary) && !result)
1298                                 {
1299 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1300 // This takes a real long time to do.  Instead just take all fonts
1301 //                                      if(!load_freetype_face(freetype_library, 
1302 //                                              freetype_face,
1303 //                                              entry->path))
1304 //                                      if(1)
1305                                         if(entry->family[0])
1306                                         {
1307 // Fix parameters
1308                                                 sprintf(string, "%s (%s)", entry->family, entry->foundary);
1309                                                 entry->fixed_title = new char[strlen(string) + 1];
1310                                                 strcpy(entry->fixed_title, string);
1312                                                 if(!strcasecmp(entry->weight, "demibold") ||
1313                                                         !strcasecmp(entry->weight, "bold")) 
1314                                                         entry->fixed_style |= FONT_BOLD;
1315                                                 if(!strcasecmp(entry->slant, "i") ||
1316                                                         !strcasecmp(entry->slant, "o")) 
1317                                                         entry->fixed_style |= FONT_ITALIC;
1318                                                 fonts->append(entry);
1319 //                                              printf("TitleMain::build_fonts %s: success\n",
1320 //                                                      entry->path);
1321 //printf("TitleMain::build_fonts 2\n");
1322                                         }
1323                                         else
1324                                         {
1325 //                                              printf("TitleMain::build_fonts %s: FT_New_Face failed\n",
1326 //                                                      entry->path);
1327 //printf("TitleMain::build_fonts 3\n");
1328                                                 delete entry;
1329                                         }
1330                                 }
1331                                 else
1332                                 {
1333                                         delete entry;
1334                                 }
1335                         }
1336                 }
1337                 pclose(in);
1339                 if(freetype_library) FT_Done_FreeType(freetype_library);
1340         }
1343 // for(int i = 0; i < fonts->total; i++)
1344 //      fonts->values[i]->dump();
1349 int TitleMain::load_freetype_face(FT_Library &freetype_library,
1350         FT_Face &freetype_face,
1351         char *path)
1353 //printf("TitleMain::load_freetype_face 1\n");
1354         if(!freetype_library) FT_Init_FreeType(&freetype_library);
1355         if(freetype_face) FT_Done_Face(freetype_face);
1356         freetype_face = 0;
1357 //printf("TitleMain::load_freetype_face 2\n");
1359 // Use freetype's internal function for loading font
1360         if(FT_New_Face(freetype_library, 
1361                 path, 
1362                 0,
1363                 &freetype_face))
1364         {
1365                 fprintf(stderr, _("TitleMain::load_freetype_face %s failed.\n"));
1366                 FT_Done_FreeType(freetype_library);
1367                 freetype_face = 0;
1368                 freetype_library = 0;
1369                 return 1;
1370         } else
1371         {
1372                 return 0;
1373         }
1375 //printf("TitleMain::load_freetype_face 4\n");
1378 FontEntry* TitleMain::get_font_entry(char *title,
1379         int style,
1380         int size)
1382 //printf("TitleMain::get_font_entry %s %d %d\n", title, style, size);
1383         FontEntry *result = 0;
1384         int got_title = 0;
1386         for(int i = 0; i < fonts->total; i++)
1387         {
1388                 FontEntry *entry = fonts->values[i];
1390                 if(!result) result = entry;
1392                 if(!strcmp(title, entry->fixed_title))
1393                 {
1394                         if(!got_title) result = entry;
1395                         got_title = 1;
1397 // Not every font has a size but every font has a style
1398                         if(entry->fixed_style == style)
1399                                 result = entry;
1401                         if(entry->fixed_style == style && entry->pointsize == size) 
1402                                 result = entry;
1404                 }
1405         }
1407         return result;
1411 FontEntry* TitleMain::get_font()
1413         return get_font_entry(config.font,
1414                 config.style,
1415                 config.size);
1423 int TitleMain::get_char_height()
1425 // this is height above the zero line, but does not include characters that go below
1426         int result = config.size;
1427         if((config.style & FONT_OUTLINE)) result += (int)ceil(config.stroke_width * 2);
1428         return result;
1431 int TitleMain::get_char_advance(int current, int next)
1433         FT_Vector kerning;
1434         int result = 0;
1435         TitleGlyph *current_glyph = 0;
1436         TitleGlyph *next_glyph = 0;
1438         if(current == 0xa) return 0;
1440         for(int i = 0; i < glyphs.total; i++)
1441         {
1442                 if(glyphs.values[i]->c == current)
1443                 {
1444                         current_glyph = glyphs.values[i];
1445                         break;
1446                 }
1447         }
1449         for(int i = 0; i < glyphs.total; i++)
1450         {
1451                 if(glyphs.values[i]->c == next)
1452                 {
1453                         next_glyph = glyphs.values[i];
1454                         break;
1455                 }
1456         }
1458         if(current_glyph)
1459                 result = current_glyph->advance_w;
1461 //printf("TitleMain::get_char_advance 1 %c %c %p %p\n", current, next, current_glyph, next_glyph);
1462         if(next_glyph)
1463                 FT_Get_Kerning(freetype_face, 
1464                                 current_glyph->freetype_index,
1465                                 next_glyph->freetype_index,
1466                                 ft_kerning_default,
1467                                 &kerning);
1468         else
1469                 kerning.x = 0;
1470 //printf("TitleMain::get_char_advance 2 %d %d\n", result, kerning.x);
1471         
1472         return result + (kerning.x >> 6);
1476 void TitleMain::draw_glyphs()
1478 // Build table of all glyphs needed
1479         int text_len = strlen(config.text);
1480         int total_packages = 0;
1481         iconv_t cd;
1482         cd = iconv_open ("UCS-4", config.encoding);
1485         if (cd == (iconv_t) -1)
1486         {
1487 /* Something went wrong.  */
1488                 fprintf (stderr, _("Iconv conversion from %s to Unicode UCS-4 not available\n"),config.encoding);
1489         };
1491         for(int i = 0; i < text_len; i++)
1492         {
1493                 FT_ULong char_code;     
1494                 int c = config.text[i];
1495                 int exists = 0;
1497 /* if iconv is working ok for current encoding */
1498                 if (cd != (iconv_t) -1)
1499                 {
1501                         size_t inbytes,outbytes;
1502                         char inbuf;
1503                         char *inp = (char*)&inbuf, *outp = (char *)&char_code;
1505                         inbuf = (char)c;
1506                         inbytes = 1;
1507                         outbytes = 4;
1508         
1509                         iconv (cd, &inp, &inbytes, &outp, &outbytes);
1510 #if     __BYTE_ORDER == __LITTLE_ENDIAN
1511                         char_code = bswap_32(char_code);
1512 #endif                          /* Big endian.  */
1514                 }
1515                 else 
1516                 {
1517                         char_code = c;
1518                 }
1520                 for(int j = 0; j < glyphs.total; j++)
1521                 {
1522                         if(glyphs.values[j]->char_code == char_code)
1523                         {
1524                                 exists = 1;
1525                                 break;
1526                         }
1527                 }
1529                 if(!exists)
1530                 {
1531                         total_packages++;
1532 //printf("TitleMain::draw_glyphs 1\n");
1533                         TitleGlyph *glyph = new TitleGlyph;
1534 //printf("TitleMain::draw_glyphs 2\n");
1535                         glyphs.append(glyph);
1536                         glyph->c = c;
1537                         glyph->char_code = char_code;
1538                 }
1539         }
1540         iconv_close(cd);
1542         if(!glyph_engine)
1543                 glyph_engine = new GlyphEngine(this, PluginClient::smp + 1);
1545         glyph_engine->set_package_count(total_packages);
1546 //printf("TitleMain::draw_glyphs 3 %d\n", glyphs.total);
1547         glyph_engine->process_packages();
1548 //printf("TitleMain::draw_glyphs 4\n");
1551 void TitleMain::get_total_extents()
1553 // Determine extents of total text
1554         int current_w = 0;
1555         int row_start = 0;
1556         text_len = strlen(config.text);
1557         if(!char_positions) char_positions = new title_char_position_t[text_len];
1558         
1559         text_rows = 0;
1560         text_w = 0;
1561         ascent = 0;
1563         for(int i = 0; i < glyphs.total; i++)
1564                 if(glyphs.values[i]->top > ascent) ascent = glyphs.values[i]->top;
1565 //printf("TitleMain::get_total_extents %d\n", ascent);
1567         // get the number of rows first
1568         for(int i = 0; i < text_len; i++)
1569         {
1570                 if(config.text[i] == 0xa || i == text_len - 1)
1571                 {
1572                         text_rows++;
1573                 }
1574         }
1575         if (!rows_bottom) rows_bottom = new int[text_rows+1];
1576         text_rows = 0;
1577         rows_bottom[0] = 0;
1579         for(int i = 0; i < text_len; i++)
1580         {
1581                 char_positions[i].x = current_w;
1582                 char_positions[i].y = text_rows * get_char_height();
1583                 char_positions[i].w = get_char_advance(config.text[i], config.text[i + 1]);
1584                 TitleGlyph *current_glyph = 0;
1585                 for(int j = 0; j < glyphs.total; j++)
1586                 {
1587                         if(glyphs.values[j]->c == config.text[i])
1588                         {
1589                                 current_glyph = glyphs.values[j];
1590                                 break;
1591                         }
1592                 }
1593                 int current_bottom = current_glyph->top - current_glyph->height;
1594                 if (current_bottom < rows_bottom[text_rows])
1595                         rows_bottom[text_rows] = current_bottom ;
1597 // printf("TitleMain::get_total_extents 1 %c %d %d %d\n", 
1598 //      config.text[i], 
1599 //      char_positions[i].x, 
1600 //      char_positions[i].y, 
1601 //      char_positions[i].w);
1602                 current_w += char_positions[i].w;
1604                 if(config.text[i] == 0xa || i == text_len - 1)
1605                 {
1606                         text_rows++;
1607                         rows_bottom[text_rows] = 0;
1608                         if(current_w > text_w) text_w = current_w;
1609                         current_w = 0;
1610                 }
1611         }
1612         text_w += config.dropshadow;
1613         text_h = text_rows * get_char_height();
1614         text_h += config.dropshadow;
1616 // Now that text_w is known
1617 // Justify rows based on configuration
1618         row_start = 0;
1619         for(int i = 0; i < text_len; i++)
1620         {
1621                 if(config.text[i] == 0xa || i == text_len - 1)
1622                 {
1623                         for(int j = row_start; j <= i; j++)
1624                         {
1625                                 switch(config.hjustification)
1626                                 {
1627                                         case JUSTIFY_LEFT:
1628                                                 break;
1630                                         case JUSTIFY_MID:
1631                                                 char_positions[j].x += (text_w - 
1632                                                                 char_positions[i].x - 
1633                                                                 char_positions[i].w) /
1634                                                         2;
1635                                                 break;
1637                                         case JUSTIFY_RIGHT:
1638                                                 char_positions[j].x += (text_w - 
1639                                                         char_positions[i].x - 
1640                                                         char_positions[i].w);
1641                                                 break;
1642                                 }
1643                         }
1644                         row_start = i + 1;
1645                 }
1646         }
1649 //printf("TitleMain::get_total_extents 2 %d %d\n", text_w, text_h);
1652 int TitleMain::draw_mask()
1654         int old_visible_row1 = visible_row1;
1655         int old_visible_row2 = visible_row2;
1658 // Determine y of visible text
1659         if(config.motion_strategy == BOTTOM_TO_TOP)
1660         {
1661 // printf("TitleMain::draw_mask 1 %d %lld %lld %lld\n", 
1662 //      config.motion_strategy,
1663 //      get_source_position(), 
1664 //      get_source_start(),
1665 //      config.prev_keyframe_position);
1666                 float magnitude = config.pixels_per_second * 
1667                         (get_source_position() - config.prev_keyframe_position) / 
1668                         PluginVClient::project_frame_rate;
1669                 if(config.loop)
1670                 {
1671                         int loop_size = text_h + input->get_h();
1672                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1673                 }
1674                 text_y1 = config.y + input->get_h() - magnitude;
1675         }
1676         else
1677         if(config.motion_strategy == TOP_TO_BOTTOM)
1678         {
1679                 float magnitude = config.pixels_per_second * 
1680                         (get_source_position() - config.prev_keyframe_position) / 
1681                         PluginVClient::project_frame_rate;
1682                 if(config.loop)
1683                 {
1684                         int loop_size = text_h + input->get_h();
1685                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1686                 }
1687                 text_y1 = config.y + magnitude;
1688                 text_y1 -= text_h;
1689         }
1690         else
1691         if(config.vjustification == JUSTIFY_TOP)
1692         {
1693                 text_y1 = config.y;
1694         }
1695         else
1696         if(config.vjustification == JUSTIFY_MID)
1697         {
1698                 text_y1 = config.y + input->get_h() / 2 - text_h / 2;
1699         }
1700         else
1701         if(config.vjustification == JUSTIFY_BOTTOM)
1702         {
1703                 text_y1 = config.y + input->get_h() - text_h;
1704         }
1706         text_y2 = text_y1 + text_h + 0.5;
1708 // Determine x of visible text
1709         if(config.motion_strategy == RIGHT_TO_LEFT)
1710         {
1711                 float magnitude = config.pixels_per_second * 
1712                         (get_source_position() - config.prev_keyframe_position) / 
1713                         PluginVClient::project_frame_rate;
1714                 if(config.loop)
1715                 {
1716                         int loop_size = text_w + input->get_w();
1717                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1718                 }
1719                 text_x1 = config.x + (float)input->get_w() - magnitude;
1720         }
1721         else
1722         if(config.motion_strategy == LEFT_TO_RIGHT)
1723         {
1724                 float magnitude = config.pixels_per_second * 
1725                         (get_source_position() - config.prev_keyframe_position) / 
1726                         PluginVClient::project_frame_rate;
1727                 if(config.loop)
1728                 {
1729                         int loop_size = text_w + input->get_w();
1730                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1731                 }
1732                 text_x1 = config.x + -(float)text_w + magnitude;
1733         }
1734         else
1735         if(config.hjustification == JUSTIFY_LEFT)
1736         {
1737                 text_x1 = config.x;
1738         }
1739         else
1740         if(config.hjustification == JUSTIFY_MID)
1741         {
1742                 text_x1 = config.x + input->get_w() / 2 - text_w / 2;
1743         }
1744         else
1745         if(config.hjustification == JUSTIFY_RIGHT)
1746         {
1747                 text_x1 = config.x + input->get_w() - text_w;
1748         }
1754 // Determine y extents just of visible text
1755         visible_row1 = (int)(-text_y1 / get_char_height());
1756         if(visible_row1 < 0) visible_row1 = 0;
1758         visible_row2 = (int)((float)text_rows - (text_y2 - input->get_h()) / get_char_height() + 1);
1759         if(visible_row2 > text_rows) visible_row2 = text_rows;
1762         if(visible_row2 <= visible_row1) return 1;
1765         mask_y1 = text_y1 + visible_row1 * get_char_height();
1766         mask_y2 = text_y1 + visible_row2 * get_char_height();
1767         text_x1 += config.x;
1770         visible_char1 = visible_char2 = 0;
1771         int got_char1 = 0;
1772         for(int i = 0; i < text_len; i++)
1773         {
1774                 title_char_position_t *char_position = char_positions + i;
1775                 int char_row = char_position->y / get_char_height();
1776                 if(char_row >= visible_row1 &&
1777                         char_row < visible_row2)
1778                 
1779                 {
1780                         if(!got_char1)
1781                         {
1782                                 visible_char1 = i;
1783                                 got_char1 = 1;
1784                         }
1785                         visible_char2 = i;
1786                 }
1787         }
1788         visible_char2++;
1792         int visible_rows = visible_row2 - visible_row1;
1793         int need_redraw = 0;
1794         if(text_mask &&
1795                 (text_mask->get_w() != text_w ||
1796                 text_mask->get_h() != visible_rows * get_char_height() - rows_bottom[visible_row2 - 1]))
1797         {
1798                 delete text_mask;
1799                 delete text_mask_stroke;
1800                 text_mask = 0;
1801                 text_mask_stroke = 0;
1802         }
1804         if(!text_mask)
1805         {
1806                 text_mask = new VFrame(0,
1807                         text_w,
1808                         visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1809                         BC_A8);
1810                 text_mask_stroke = new VFrame(0,
1811                         text_w,
1812                         visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1813                         BC_A8);
1815                 need_redraw = 1;
1816         }
1820 // Draw on text mask if different
1821         if(old_visible_row1 != visible_row1 ||
1822                 old_visible_row2 != visible_row2 ||
1823                 need_redraw)
1824         {
1825                 text_mask->clear_frame();
1826                 text_mask_stroke->clear_frame();
1829                 if(!title_engine)
1830                         title_engine = new TitleEngine(this, PluginClient::smp + 1);
1832                 title_engine->set_package_count(visible_char2 - visible_char1);
1833                 title_engine->process_packages();
1834         }
1836         return 0;
1840 void TitleMain::overlay_mask()
1843         alpha = 0x100;
1844         if(!EQUIV(config.fade_in, 0))
1845         {
1846                 int fade_len = (int)(config.fade_in * 
1847                         PluginVClient::project_frame_rate);
1848                 int fade_position = get_source_position() - 
1849                         config.prev_keyframe_position;
1852                 if(fade_position >= 0 && fade_position < fade_len)
1853                 {
1854                         alpha = (int)((float)0x100 * 
1855                                 fade_position /
1856                                 fade_len + 0.5);
1857                 }
1858         }
1860         if(!EQUIV(config.fade_out, 0))
1861         {
1863                 int fade_len = (int)(config.fade_out * 
1864                         PluginVClient::project_frame_rate);
1865                 int fade_position = config.next_keyframe_position - 
1866                         get_source_position();
1869                 if(fade_position > 0 && fade_position < fade_len)
1870                 {
1871                         alpha = (int)((float)0x100 *
1872                                 fade_position /
1873                                 fade_len + 0.5);
1874                 }
1875         }
1877         if(config.dropshadow)
1878         {
1879                 text_x1 += config.dropshadow;
1880                 text_x2 += config.dropshadow;
1881                 mask_y1 += config.dropshadow;
1882                 mask_y2 += config.dropshadow;
1883                 if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1884                         mask_y1 < input->get_h() && mask_y2 > 0)
1885                 {
1886                         if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1887 // Do 2 passes if dropshadow.
1888                         int temp_color = config.color;
1889                         config.color = 0x0;
1890                         translate->process_packages();
1891                         config.color = temp_color;
1892                 }
1893                 text_x1 -= config.dropshadow;
1894                 text_x2 -= config.dropshadow;
1895                 mask_y1 -= config.dropshadow;
1896                 mask_y2 -= config.dropshadow;
1897         }
1899         if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1900                 mask_y1 < input->get_h() && mask_y2 > 0)
1901         {
1902                 if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1903                 translate->process_packages();
1904                 if (config.stroke_width >= ZERO &&
1905                         (config.style & FONT_OUTLINE)) 
1906                 {
1907                         int temp_color = config.color;
1908                         VFrame *tmp_text_mask = this->text_mask;
1909                         config.color = config.color_stroke;
1910                         this->text_mask = this->text_mask_stroke;
1912                         translate->process_packages();
1913                         config.color = temp_color;
1914                         this->text_mask = tmp_text_mask;
1915                 }
1916         }
1919 void TitleMain::clear_glyphs()
1921 //printf("TitleMain::clear_glyphs 1\n");
1922         glyphs.remove_all_objects();
1925 char* TitleMain::motion_to_text(int motion)
1927         switch(motion)
1928         {
1929                 case NO_MOTION: return _("No motion"); break;
1930                 case BOTTOM_TO_TOP: return _("Bottom to top"); break;
1931                 case TOP_TO_BOTTOM: return _("Top to bottom"); break;
1932                 case RIGHT_TO_LEFT: return _("Right to left"); break;
1933                 case LEFT_TO_RIGHT: return _("Left to right"); break;
1934         }
1937 int TitleMain::text_to_motion(char *text)
1939         for(int i = 0; i < TOTAL_PATHS; i++)
1940         {
1941                 if(!strcasecmp(motion_to_text(i), text)) return i;
1942         }
1943         return 0;
1952 int TitleMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
1954         int result = 0;
1955         input = input_ptr;
1956         output = output_ptr;
1958         need_reconfigure |= load_configuration();
1961 // Always synthesize text and redraw it for timecode
1962         if(config.timecode)
1963         {
1964                 int64_t rendered_frame = get_source_position();
1965                 if (get_direction() == PLAY_REVERSE)
1966                         rendered_frame -= 1;
1968                 int tcf = Units::timeformat_totype(config.timecodeformat);
1969                 if (tcf < 0) {
1970                         tcf = TIME_HMSF;
1971                         strcpy(config.timecodeformat, DEFAULT_TIMECODEFORMAT);
1972                 }
1973                 Units::totext(config.text, 
1974                                 (double)rendered_frame / PluginVClient::project_frame_rate, 
1975                                 tcf, 
1976                                 0,
1977                                 PluginVClient::project_frame_rate, 
1978                                 0);
1979                 need_reconfigure = 1;
1980         }
1982 // Check boundaries
1983         if(config.size <= 0 || config.size >= 2048) config.size = 72;
1984         if(config.stroke_width < 0 || 
1985                 config.stroke_width >= 512) config.stroke_width = 0.0;
1986         if(!strlen(config.text)) return 0;
1987         if(!strlen(config.encoding)) strcpy(config.encoding, DEFAULT_ENCODING);
1989 //printf("TitleMain::process_realtime 4\n");
1991 // Handle reconfiguration
1992         if(need_reconfigure)
1993         {
1994 //printf("TitleMain::process_realtime 2\n");
1995                 if(text_mask) delete text_mask;
1996                 if(text_mask_stroke) delete text_mask_stroke;
1997                 text_mask = 0;
1998                 text_mask_stroke = 0;
1999 //printf("TitleMain::process_realtime 2\n");
2000                 if(freetype_face) FT_Done_Face(freetype_face);
2001                 freetype_face = 0;
2002 //printf("TitleMain::process_realtime 2\n");
2003                 if(glyph_engine) delete glyph_engine;
2004                 glyph_engine = 0;
2005 //printf("TitleMain::process_realtime 2\n");
2006                 if(char_positions) delete [] char_positions;
2007                 char_positions = 0;
2008                 if(rows_bottom) delete [] rows_bottom;
2009                 rows_bottom = 0;
2010 //printf("TitleMain::process_realtime 2\n");
2011                 clear_glyphs();
2012 //printf("TitleMain::process_realtime 2\n");
2013                 visible_row1 = 0;
2014                 visible_row2 = 0;
2015                 ascent = 0;
2017                 if(!freetype_library) 
2018                         FT_Init_FreeType(&freetype_library);
2020 //printf("TitleMain::process_realtime 2\n");
2021                 if(!freetype_face)
2022                 {
2023                         FontEntry *font = get_font();
2024 //printf("TitleMain::process_realtime 2.1 %s\n", font->path);
2025                         if(load_freetype_face(freetype_library,
2026                                 freetype_face,
2027                                 font->path))
2028                         {
2029                                 printf("TitleMain::process_realtime %s: FT_New_Face failed.\n",
2030                                         font->fixed_title);
2031                                 result = 1;
2032                         }
2033 //printf("TitleMain::process_realtime 2.2\n");
2035                         if(!result) FT_Set_Pixel_Sizes(freetype_face, config.size, 0);
2036 //printf("TitleMain::process_realtime 2.3\n");
2037                 }
2039 //printf("TitleMain::process_realtime 3\n");
2041                 if(!result)
2042                 {
2043                         draw_glyphs();
2044 //printf("TitleMain::process_realtime 4\n");
2045                         get_total_extents();
2046 //printf("TitleMain::process_realtime 5\n");
2047                         need_reconfigure = 0;
2048                 }
2049         }
2051         if(!result)
2052         {
2053 //printf("TitleMain::process_realtime 4\n");
2054 // Determine region of text visible on the output and draw mask
2055                 result = draw_mask();
2056         }
2057 //printf("TitleMain::process_realtime 50\n");
2060 // Overlay mask on output
2061         if(!result)
2062         {
2063                 overlay_mask();
2064         }
2065 //printf("TitleMain::process_realtime 60 %d\n", glyphs.total);
2067         return 0;
2070 int TitleMain::show_gui()
2072         load_configuration();
2073         thread = new TitleThread(this);
2074         thread->start();
2075         return 0;
2078 int TitleMain::set_string()
2080         if(thread) thread->window->set_title(gui_string);
2081         return 0;
2084 void TitleMain::raise_window()
2086         if(thread)
2087         {
2088                 thread->window->raise_window();
2089                 thread->window->flush();
2090         }
2093 void TitleMain::update_gui()
2095         if(thread)
2096         {
2097                 int reconfigure = load_configuration();
2098                 if(reconfigure)
2099                 {
2100                         thread->window->lock_window();
2101                         thread->window->update();
2102                         thread->window->unlock_window();
2103                         thread->window->color_thread->update_gui(config.color, 0);
2104                 }
2105         }
2109 int TitleMain::load_defaults()
2111         char directory[1024], text_path[1024];
2112 // set the default directory
2113         sprintf(directory, "%stitle.rc", BCASTDIR);
2115 // load the defaults
2116         defaults = new BC_Hash(directory);
2117         defaults->load();
2119         defaults->get("FONT", config.font);
2120         defaults->get("ENCODING", config.encoding);
2121         config.style = defaults->get("STYLE", (int64_t)config.style);
2122         config.size = defaults->get("SIZE", config.size);
2123         config.color = defaults->get("COLOR", config.color);
2124         config.color_stroke = defaults->get("COLOR_STROKE", config.color_stroke);
2125         config.stroke_width = defaults->get("STROKE_WIDTH", config.stroke_width);
2126         config.motion_strategy = defaults->get("MOTION_STRATEGY", config.motion_strategy);
2127         config.loop = defaults->get("LOOP", config.loop);
2128         config.pixels_per_second = defaults->get("PIXELS_PER_SECOND", config.pixels_per_second);
2129         config.hjustification = defaults->get("HJUSTIFICATION", config.hjustification);
2130         config.vjustification = defaults->get("VJUSTIFICATION", config.vjustification);
2131         config.fade_in = defaults->get("FADE_IN", config.fade_in);
2132         config.fade_out = defaults->get("FADE_OUT", config.fade_out);
2133         config.x = defaults->get("TITLE_X", config.x);
2134         config.y = defaults->get("TITLE_Y", config.y);
2135         config.dropshadow = defaults->get("DROPSHADOW", config.dropshadow);
2136         config.timecode = defaults->get("TIMECODE", config.timecode);
2137         defaults->get("TIMECODEFORMAT", config.timecodeformat);
2138         window_w = defaults->get("WINDOW_W", 660);
2139         window_h = defaults->get("WINDOW_H", 480);
2141 // Store text in separate path to isolate special characters
2142         FileSystem fs;
2143         sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2144         fs.complete_path(text_path);
2145         FILE *fd = fopen(text_path, "rb");
2146         if(fd)
2147         {
2148                 fseek(fd, 0, SEEK_END);
2149                 int64_t len = ftell(fd);
2150                 fseek(fd, 0, SEEK_SET);
2151                 fread(config.text, len, 1, fd);
2152                 config.text[len] = 0;
2153 //printf("TitleMain::load_defaults %s\n", config.text);
2154                 fclose(fd);
2155         }
2156         else
2157                 config.text[0] = 0;
2158         return 0;
2161 int TitleMain::save_defaults()
2163         char text_path[1024];
2165         defaults->update("FONT", config.font);
2166         defaults->update("ENCODING", config.encoding);
2167         defaults->update("STYLE", (int64_t)config.style);
2168         defaults->update("SIZE", config.size);
2169         defaults->update("COLOR", config.color);
2170         defaults->update("COLOR_STROKE", config.color_stroke);
2171         defaults->update("STROKE_WIDTH", config.stroke_width);
2172         defaults->update("MOTION_STRATEGY", config.motion_strategy);
2173         defaults->update("LOOP", config.loop);
2174         defaults->update("PIXELS_PER_SECOND", config.pixels_per_second);
2175         defaults->update("HJUSTIFICATION", config.hjustification);
2176         defaults->update("VJUSTIFICATION", config.vjustification);
2177         defaults->update("FADE_IN", config.fade_in);
2178         defaults->update("FADE_OUT", config.fade_out);
2179         defaults->update("TITLE_X", config.x);
2180         defaults->update("TITLE_Y", config.y);
2181         defaults->update("DROPSHADOW", config.dropshadow);
2182         defaults->update("TIMECODE", config.timecode);
2183         defaults->update("TIMECODEFORMAT", config.timecodeformat);
2184         defaults->update("WINDOW_W", window_w);
2185         defaults->update("WINDOW_H", window_h);
2186         defaults->save();
2188 // Store text in separate path to isolate special characters
2189         FileSystem fs;
2190         sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2191         fs.complete_path(text_path);
2192         FILE *fd = fopen(text_path, "wb");
2193         if(fd)
2194         {
2195                 fwrite(config.text, strlen(config.text), 1, fd);
2196                 fclose(fd);
2197         }
2198 //      else
2199 //              perror("TitleMain::save_defaults");
2200         return 0;
2206 int TitleMain::load_configuration()
2208         KeyFrame *prev_keyframe, *next_keyframe;
2209         prev_keyframe = get_prev_keyframe(get_source_position());
2210         next_keyframe = get_next_keyframe(get_source_position());
2212 // printf("TitleMain::load_configuration 1 %d %d\n", 
2213 // prev_keyframe->position,
2214 // next_keyframe->position);
2216         TitleConfig old_config, prev_config, next_config;
2217         old_config.copy_from(config);
2218         read_data(prev_keyframe);
2219         prev_config.copy_from(config);
2220         read_data(next_keyframe);
2221         next_config.copy_from(config);
2223         config.prev_keyframe_position = prev_keyframe->position;
2224         config.next_keyframe_position = next_keyframe->position;
2226         // if no previous keyframe exists, it should be start of the plugin, not start of the track
2227         if(config.next_keyframe_position == config.prev_keyframe_position)
2228                 config.next_keyframe_position = get_source_start() + get_total_len();
2229         if (config.prev_keyframe_position == 0) 
2230                 config.prev_keyframe_position = get_source_start();
2233 // printf("TitleMain::load_configuration 10 %d %d\n", 
2234 // config.prev_keyframe_position,
2235 // config.next_keyframe_position);
2237         config.interpolate(prev_config, 
2238                 next_config, 
2239                 (next_keyframe->position == prev_keyframe->position) ?
2240                         get_source_position() :
2241                         prev_keyframe->position,
2242                 (next_keyframe->position == prev_keyframe->position) ?
2243                         get_source_position() + 1 :
2244                         next_keyframe->position,
2245                 get_source_position());
2247         if(!config.equivalent(old_config))
2248                 return 1;
2249         else
2250                 return 0;
2264 void TitleMain::save_data(KeyFrame *keyframe)
2266         FileXML output;
2268 // cause data to be stored directly in text
2269         output.set_shared_string(keyframe->data, MESSAGESIZE);
2270         output.tag.set_title("TITLE");
2271         output.tag.set_property("FONT", config.font);
2272         output.tag.set_property("ENCODING", config.encoding);
2273         output.tag.set_property("STYLE", (int64_t)config.style);
2274         output.tag.set_property("SIZE", config.size);
2275         output.tag.set_property("COLOR", config.color);
2276         output.tag.set_property("COLOR_STROKE", config.color_stroke);
2277         output.tag.set_property("STROKE_WIDTH", config.stroke_width);
2278         output.tag.set_property("MOTION_STRATEGY", config.motion_strategy);
2279         output.tag.set_property("LOOP", config.loop);
2280         output.tag.set_property("PIXELS_PER_SECOND", config.pixels_per_second);
2281         output.tag.set_property("HJUSTIFICATION", config.hjustification);
2282         output.tag.set_property("VJUSTIFICATION", config.vjustification);
2283         output.tag.set_property("FADE_IN", config.fade_in);
2284         output.tag.set_property("FADE_OUT", config.fade_out);
2285         output.tag.set_property("TITLE_X", config.x);
2286         output.tag.set_property("TITLE_Y", config.y);
2287         output.tag.set_property("DROPSHADOW", config.dropshadow);
2288         output.tag.set_property("TIMECODE", config.timecode);
2289         output.tag.set_property("TIMECODEFORMAT", config.timecodeformat);
2290         output.append_tag();
2291         output.append_newline();
2292         
2293         output.encode_text(config.text);
2295         output.tag.set_title("/TITLE");
2296         output.append_tag();
2297         output.append_newline();
2298         output.terminate_string();
2299 //printf("TitleMain::save_data 1\n%s\n", output.string);
2300 //printf("TitleMain::save_data 2\n%s\n", config.text);
2303 void TitleMain::read_data(KeyFrame *keyframe)
2305         FileXML input;
2307         input.set_shared_string(keyframe->data, strlen(keyframe->data));
2309         int result = 0;
2310         int new_interlace = 0;
2311         int new_horizontal = 0;
2312         int new_luminance = 0;
2314         config.prev_keyframe_position = keyframe->position;
2315         while(!result)
2316         {
2317                 result = input.read_tag();
2319                 if(!result)
2320                 {
2321                         if(input.tag.title_is("TITLE"))
2322                         {
2323                                 input.tag.get_property("FONT", config.font);
2324                                 input.tag.get_property("ENCODING", config.encoding);
2325                                 config.style = input.tag.get_property("STYLE", (int64_t)config.style);
2326                                 config.size = input.tag.get_property("SIZE", config.size);
2327                                 config.color = input.tag.get_property("COLOR", config.color);
2328                                 config.color_stroke = input.tag.get_property("COLOR_STROKE", config.color_stroke);
2329                                 config.stroke_width = input.tag.get_property("STROKE_WIDTH", config.stroke_width);
2330                                 config.motion_strategy = input.tag.get_property("MOTION_STRATEGY", config.motion_strategy);
2331                                 config.loop = input.tag.get_property("LOOP", config.loop);
2332                                 config.pixels_per_second = input.tag.get_property("PIXELS_PER_SECOND", config.pixels_per_second);
2333                                 config.hjustification = input.tag.get_property("HJUSTIFICATION", config.hjustification);
2334                                 config.vjustification = input.tag.get_property("VJUSTIFICATION", config.vjustification);
2335                                 config.fade_in = input.tag.get_property("FADE_IN", config.fade_in);
2336                                 config.fade_out = input.tag.get_property("FADE_OUT", config.fade_out);
2337                                 config.x = input.tag.get_property("TITLE_X", config.x);
2338                                 config.y = input.tag.get_property("TITLE_Y", config.y);
2339                                 config.dropshadow = input.tag.get_property("DROPSHADOW", config.dropshadow);
2340                                 config.timecode = input.tag.get_property("TIMECODE", config.timecode);
2341                                 input.tag.get_property("TIMECODEFORMAT", config.timecodeformat);
2342                                 strcpy(config.text, input.read_text());
2343 //printf("TitleMain::read_data 1\n%s\n", input.string);
2344 //printf("TitleMain::read_data 2\n%s\n", config.text);
2345                         }
2346                         else
2347                         if(input.tag.title_is("/TITLE"))
2348                         {
2349                                 result = 1;
2350                         }
2351                 }
2352         }