r1053: Add Russian translation.
[cinelerra_cv.git] / plugins / titler / title.C
blob4f9cdbc7732d5988555ee644cd7db35831f8b06c
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;
821         switch(plugin->output->get_color_model())
822         {
823                 case BC_RGB888:
824                 {
825                         TRANSLATE(unsigned char, 0xff, 3, r_in, g_in, b_in);
826                         break;
827                 }
828                 case BC_RGB_FLOAT:
829                 {
830                         float r, g, b;
831                         r = (float)r_in / 0xff;
832                         g = (float)g_in / 0xff;
833                         b = (float)b_in / 0xff;
834                         TRANSLATE(float, 0xff, 3, r, g, b);
835                         break;
836                 }
837                 case BC_YUV888:
838                 {
839                         unsigned char y, u, v;
840                         yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
841                         TRANSLATE(unsigned char, 0xff, 3, y, u, v);
842                         break;
843                 }
844                 case BC_RGB161616:
845                 {
846                         uint16_t r, g, b;
847                         r = (r_in << 8) | r_in;
848                         g = (g_in << 8) | g_in;
849                         b = (b_in << 8) | b_in;
850                         TRANSLATE(uint16_t, 0xffff, 3, r, g, b);
851                         break;
852                 }
853                 case BC_YUV161616:
854                 {
855                         uint16_t y, u, v;
856                         yuv.rgb_to_yuv_16(
857                                 (r_in << 8) | r_in, 
858                                 (g_in << 8) | g_in, 
859                                 (b_in << 8) | b_in, 
860                                 y, 
861                                 u, 
862                                 v);
863                         TRANSLATE(uint16_t, 0xffff, 3, y, u, v);
864                         break;
865                 }
866                 case BC_RGBA_FLOAT:
867                 {
868                         float r, g, b;
869                         r = (float)r_in / 0xff;
870                         g = (float)g_in / 0xff;
871                         b = (float)b_in / 0xff;
872                         TRANSLATE(float, 0xff, 4, r, g, b);
873                         break;
874                 }
875                 case BC_RGBA8888:
876                 {       
877                         TRANSLATE(unsigned char, 0xff, 4, r_in, g_in, b_in);
878                         break;
879                 }
880                 case BC_YUVA8888:
881                 {       
882                         unsigned char y, u, v;
883                         yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
884                         TRANSLATE(unsigned char, 0xff, 4, y, u, v);
885                         break;
886                 }
887                 case BC_RGBA16161616:
888                 {       
889                         uint16_t r, g, b;
890                         r = (r_in << 8) | r_in;
891                         g = (g_in << 8) | g_in;
892                         b = (b_in << 8) | b_in;
893                         TRANSLATE(uint16_t, 0xffff, 4, r, g, b);
894                         break;
895                 }
896                 case BC_YUVA16161616:
897                 {       
898                         uint16_t y, u, v;
899                         yuv.rgb_to_yuv_16(
900                                 (r_in << 8) | r_in, 
901                                 (g_in << 8) | g_in, 
902                                 (b_in << 8) | b_in, 
903                                 y, 
904                                 u, 
905                                 v);
906                         TRANSLATE(uint16_t, 0xffff, 4, y, u, v);
907                         break;
908                 }
909         }
911 //printf("TitleTranslateUnit::process_package 5\n");
917 TitleTranslate::TitleTranslate(TitleMain *plugin, int cpus)
918  : LoadServer(1, 1)
920         this->plugin = plugin;
921         x_table = y_table = 0;
924 TitleTranslate::~TitleTranslate()
926         if(x_table) delete [] x_table;
927         if(y_table) delete [] y_table;
930 void TitleTranslate::init_packages()
932 //printf("TitleTranslate::init_packages 1\n");
933 // Generate scaling tables
934         if(x_table) delete [] x_table;
935         if(y_table) delete [] y_table;
936 //printf("TitleTranslate::init_packages 1\n");
938         output_w = plugin->output->get_w();
939         output_h = plugin->output->get_h();
940 //printf("TitleTranslate::init_packages 1 %f %d\n", plugin->text_x1, plugin->text_w);
943         TranslateUnit::translation_array_f(x_table, 
944                 plugin->text_x1, 
945                 plugin->text_x1 + plugin->text_w,
946                 0,
947                 plugin->text_w,
948                 plugin->text_w, 
949                 output_w, 
950                 out_x1_int,
951                 out_x2_int);
952 //printf("TitleTranslate::init_packages 1 %f %f\n", plugin->mask_y1, plugin->mask_y2);
954         TranslateUnit::translation_array_f(y_table, 
955                 plugin->mask_y1, 
956                 plugin->mask_y1 + plugin->text_mask->get_h(),
957                 0,
958                 plugin->text_mask->get_h(),
959                 plugin->text_mask->get_h(), 
960                 output_h, 
961                 out_y1_int,
962                 out_y2_int);
964 //printf("TitleTranslate::init_packages 1\n");
967         out_y1 = out_y1_int;
968         out_y2 = out_y2_int;
969         out_x1 = out_x1_int;
970         out_x2 = out_x2_int;
971         int increment = (out_y2 - out_y1) / get_total_packages() + 1;
973 //printf("TitleTranslate::init_packages 1 %d %d %d %d\n", 
974 //      out_y1, out_y2, out_y1_int, out_y2_int);
975         for(int i = 0; i < get_total_packages(); i++)
976         {
977                 TitleTranslatePackage *pkg = (TitleTranslatePackage*)get_package(i);
978                 pkg->y1 = i * increment;
979                 pkg->y2 = i * increment + increment;
980                 if(pkg->y1 > out_y2 - out_y1)
981                         pkg->y1 = out_y2 - out_y1;
982                 if(pkg->y2 > out_y2 - out_y1)
983                         pkg->y2 = out_y2 - out_y1;
984         }
985 //printf("TitleTranslate::init_packages 2\n");
988 LoadClient* TitleTranslate::new_client()
990         return new TitleTranslateUnit(plugin, this);
993 LoadPackage* TitleTranslate::new_package()
995         return new TitleTranslatePackage;
1012 ArrayList<FontEntry*>* TitleMain::fonts = 0;
1016 TitleMain::TitleMain(PluginServer *server)
1017  : PluginVClient(server)
1019         PLUGIN_CONSTRUCTOR_MACRO
1021 // Build font database
1022         build_fonts();
1023         text_mask = 0;
1024         text_mask_stroke = 0;
1025         glyph_engine = 0;
1026         title_engine = 0;
1027         freetype_library = 0;
1028         freetype_face = 0;
1029         char_positions = 0;
1030         rows_bottom = 0;
1031         translate = 0;
1032         need_reconfigure = 1;
1035 TitleMain::~TitleMain()
1037         PLUGIN_DESTRUCTOR_MACRO
1038         if(text_mask) delete text_mask;
1039         if(text_mask_stroke) delete text_mask_stroke;
1040         if(char_positions) delete [] char_positions;
1041         if(rows_bottom) delete [] rows_bottom;
1042         clear_glyphs();
1043         if(glyph_engine) delete glyph_engine;
1044         if(title_engine) delete title_engine;
1045         if(freetype_library) FT_Done_FreeType(freetype_library);
1046         if(translate) delete translate;
1049 char* TitleMain::plugin_title() { return N_("Title"); }
1050 int TitleMain::is_realtime() { return 1; }
1051 int TitleMain::is_synthesis() { return 1; }
1053 VFrame* TitleMain::new_picon()
1055         return new VFrame(picon_png);
1059 void TitleMain::build_fonts()
1061         if(!fonts)
1062         {
1063                 fonts = new ArrayList<FontEntry*>;
1064 // Construct path from location of the plugin
1065                 char search_path[BCTEXTLEN];
1066                 strcpy(search_path, PluginClient::get_path());
1067                 char *ptr = strrchr(search_path, '/');
1068                 strcpy(ptr + 1, FONT_SEARCHPATH);
1069                 char command_line[BCTEXTLEN];
1071                 sprintf(command_line, 
1072                         "find %s -name 'fonts.dir' -print -exec cat {} \\;", 
1073                         search_path);
1074 //printf("TitleMain::build_fonts %s\n", command_line);
1076                 FILE *in = popen(command_line, "r");
1079                 char current_dir[BCTEXTLEN];
1080                 FT_Library freetype_library = 0;        // Freetype library
1081                 FT_Face freetype_face = 0;
1083 //              FT_Init_FreeType(&freetype_library);
1084                 current_dir[0] = 0;
1086                 while(!feof(in))
1087                 {
1088                         char string[BCTEXTLEN], string2[BCTEXTLEN];
1089                         fgets(string, BCTEXTLEN, in);
1090                         if(!strlen(string)) break;
1092                         char *in_ptr = string;
1093                         char *out_ptr;
1095 // Get current directory
1096                         
1097                         if(string[0] == '/')
1098                         {
1099                                 out_ptr = current_dir;
1100                                 while(*in_ptr != 0 && *in_ptr != 0xa)
1101                                         *out_ptr++ = *in_ptr++;
1102                                 out_ptr--;
1103                                 while(*out_ptr != '/')
1104                                         *out_ptr-- = 0;
1105                         }
1106                         else
1107                         {
1110 //printf("TitleMain::build_fonts %s\n", string);
1111                                 FontEntry *entry = new FontEntry;
1112                                 int result = 0;
1114 // Path
1115                                 out_ptr = string2;
1116                                 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa)
1117                                 {
1118                                         *out_ptr++ = *in_ptr++;
1119                                 }
1120                                 *out_ptr = 0;
1121                                 if(string2[0] == '/')
1122                                 {
1123                                         entry->path = new char[strlen(string2) + 1];
1124                                         sprintf(entry->path, "%s", string2);
1125                                 }
1126                                 else
1127                                 {
1128                                         entry->path = new char[strlen(current_dir) + strlen(string2) + 1];
1129                                         sprintf(entry->path, "%s%s", current_dir, string2);
1130                                 }
1133 // Test path existence
1134                                 struct stat test_stat;
1135                                 if(stat(entry->path, &test_stat))
1136                                 {
1137                                         result = 1;
1138                                 }
1139 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1141 // Foundary
1142                                 while(*in_ptr != 0 && *in_ptr != 0xa && (*in_ptr == ' ' || *in_ptr == '-'))
1143                                         in_ptr++;
1145                                 out_ptr = string2;
1146                                 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa && *in_ptr != '-')
1147                                 {
1148                                         *out_ptr++ = *in_ptr++;
1149                                 }
1150                                 *out_ptr = 0;
1151                                 entry->foundary = new char[strlen(string2) + 1];
1152                                 strcpy(entry->foundary, string2);
1153                                 if(*in_ptr == '-') in_ptr++;
1156 // Family
1157                                 out_ptr = string2;
1158                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1159                                 {
1160                                         *out_ptr++ = *in_ptr++;
1161                                 }
1162                                 *out_ptr = 0;
1163                                 entry->family = new char[strlen(string2) + 1];
1164                                 strcpy(entry->family, string2);
1165                                 if(*in_ptr == '-') in_ptr++;
1167 // Weight
1168                                 out_ptr = string2;
1169                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1170                                 {
1171                                         *out_ptr++ = *in_ptr++;
1172                                 }
1173                                 *out_ptr = 0;
1174                                 entry->weight = new char[strlen(string2) + 1];
1175                                 strcpy(entry->weight, string2);
1176                                 if(*in_ptr == '-') in_ptr++;
1178 // Slant
1179                                 out_ptr = string2;
1180                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1181                                 {
1182                                         *out_ptr++ = *in_ptr++;
1183                                 }
1184                                 *out_ptr = 0;
1185                                 entry->slant = new char[strlen(string2) + 1];
1186                                 strcpy(entry->slant, string2);
1187                                 if(*in_ptr == '-') in_ptr++;
1189 // SWidth
1190                                 out_ptr = string2;
1191                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1192                                 {
1193                                         *out_ptr++ = *in_ptr++;
1194                                 }
1195                                 *out_ptr = 0;
1196                                 entry->swidth = new char[strlen(string2) + 1];
1197                                 strcpy(entry->swidth, string2);
1198                                 if(*in_ptr == '-') in_ptr++;
1200 // Adstyle
1201                                 out_ptr = string2;
1202                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1203                                 {
1204                                         *out_ptr++ = *in_ptr++;
1205                                 }
1206                                 *out_ptr = 0;
1207                                 entry->adstyle = new char[strlen(string2) + 1];
1208                                 strcpy(entry->adstyle, string2);
1209                                 if(*in_ptr == '-') in_ptr++;
1211 // pixelsize
1212                                 out_ptr = string2;
1213                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1214                                 {
1215                                         *out_ptr++ = *in_ptr++;
1216                                 }
1217                                 *out_ptr = 0;
1218                                 entry->pixelsize = atol(string2);
1219                                 if(*in_ptr == '-') in_ptr++;
1221 // pointsize
1222                                 out_ptr = string2;
1223                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1224                                 {
1225                                         *out_ptr++ = *in_ptr++;
1226                                 }
1227                                 *out_ptr = 0;
1228                                 entry->pointsize = atol(string2);
1229                                 if(*in_ptr == '-') in_ptr++;
1231 // xres
1232                                 out_ptr = string2;
1233                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1234                                 {
1235                                         *out_ptr++ = *in_ptr++;
1236                                 }
1237                                 *out_ptr = 0;
1238                                 entry->xres = atol(string2);
1239                                 if(*in_ptr == '-') in_ptr++;
1241 // yres
1242                                 out_ptr = string2;
1243                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1244                                 {
1245                                         *out_ptr++ = *in_ptr++;
1246                                 }
1247                                 *out_ptr = 0;
1248                                 entry->yres = atol(string2);
1249                                 if(*in_ptr == '-') in_ptr++;
1251 // spacing
1252                                 out_ptr = string2;
1253                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1254                                 {
1255                                         *out_ptr++ = *in_ptr++;
1256                                 }
1257                                 *out_ptr = 0;
1258                                 entry->spacing = new char[strlen(string2) + 1];
1259                                 strcpy(entry->spacing, string2);
1260                                 if(*in_ptr == '-') in_ptr++;
1262 // avg_width
1263                                 out_ptr = string2;
1264                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1265                                 {
1266                                         *out_ptr++ = *in_ptr++;
1267                                 }
1268                                 *out_ptr = 0;
1269                                 entry->avg_width = atol(string2);
1270                                 if(*in_ptr == '-') in_ptr++;
1272 // registry
1273                                 out_ptr = string2;
1274                                 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1275                                 {
1276                                         *out_ptr++ = *in_ptr++;
1277                                 }
1278                                 *out_ptr = 0;
1279                                 entry->registry = new char[strlen(string2) + 1];
1280                                 strcpy(entry->registry, string2);
1281                                 if(*in_ptr == '-') in_ptr++;
1283 // encoding
1284                                 out_ptr = string2;
1285                                 while(*in_ptr != 0 && *in_ptr != 0xa)
1286                                 {
1287                                         *out_ptr++ = *in_ptr++;
1288                                 }
1289                                 *out_ptr = 0;
1290                                 entry->encoding = new char[strlen(string2) + 1];
1291                                 strcpy(entry->encoding, string2);
1295 // Add to list
1296                                 if(strlen(entry->foundary) && !result)
1297                                 {
1298 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1299 // This takes a real long time to do.  Instead just take all fonts
1300 //                                      if(!load_freetype_face(freetype_library, 
1301 //                                              freetype_face,
1302 //                                              entry->path))
1303 //                                      if(1)
1304                                         if(entry->family[0])
1305                                         {
1306 // Fix parameters
1307                                                 sprintf(string, "%s (%s)", entry->family, entry->foundary);
1308                                                 entry->fixed_title = new char[strlen(string) + 1];
1309                                                 strcpy(entry->fixed_title, string);
1311                                                 if(!strcasecmp(entry->weight, "demibold") ||
1312                                                         !strcasecmp(entry->weight, "bold")) 
1313                                                         entry->fixed_style |= FONT_BOLD;
1314                                                 if(!strcasecmp(entry->slant, "i") ||
1315                                                         !strcasecmp(entry->slant, "o")) 
1316                                                         entry->fixed_style |= FONT_ITALIC;
1317                                                 fonts->append(entry);
1318 //                                              printf("TitleMain::build_fonts %s: success\n",
1319 //                                                      entry->path);
1320 //printf("TitleMain::build_fonts 2\n");
1321                                         }
1322                                         else
1323                                         {
1324 //                                              printf("TitleMain::build_fonts %s: FT_New_Face failed\n",
1325 //                                                      entry->path);
1326 //printf("TitleMain::build_fonts 3\n");
1327                                                 delete entry;
1328                                         }
1329                                 }
1330                                 else
1331                                 {
1332                                         delete entry;
1333                                 }
1334                         }
1335                 }
1336                 pclose(in);
1338                 if(freetype_library) FT_Done_FreeType(freetype_library);
1339         }
1342 // for(int i = 0; i < fonts->total; i++)
1343 //      fonts->values[i]->dump();
1348 int TitleMain::load_freetype_face(FT_Library &freetype_library,
1349         FT_Face &freetype_face,
1350         char *path)
1352 //printf("TitleMain::load_freetype_face 1\n");
1353         if(!freetype_library) FT_Init_FreeType(&freetype_library);
1354         if(freetype_face) FT_Done_Face(freetype_face);
1355         freetype_face = 0;
1356 //printf("TitleMain::load_freetype_face 2\n");
1358 // Use freetype's internal function for loading font
1359         if(FT_New_Face(freetype_library, 
1360                 path, 
1361                 0,
1362                 &freetype_face))
1363         {
1364                 fprintf(stderr, _("TitleMain::load_freetype_face %s failed.\n"));
1365                 FT_Done_FreeType(freetype_library);
1366                 freetype_face = 0;
1367                 freetype_library = 0;
1368                 return 1;
1369         } else
1370         {
1371                 return 0;
1372         }
1374 //printf("TitleMain::load_freetype_face 4\n");
1377 FontEntry* TitleMain::get_font_entry(char *title,
1378         int style,
1379         int size)
1381 //printf("TitleMain::get_font_entry %s %d %d\n", title, style, size);
1382         FontEntry *result = 0;
1383         int got_title = 0;
1385         for(int i = 0; i < fonts->total; i++)
1386         {
1387                 FontEntry *entry = fonts->values[i];
1389                 if(!result) result = entry;
1391                 if(!strcmp(title, entry->fixed_title))
1392                 {
1393                         if(!got_title) result = entry;
1394                         got_title = 1;
1396 // Not every font has a size but every font has a style
1397                         if(entry->fixed_style == style)
1398                                 result = entry;
1400                         if(entry->fixed_style == style && entry->pointsize == size) 
1401                                 result = entry;
1403                 }
1404         }
1406         return result;
1410 FontEntry* TitleMain::get_font()
1412         return get_font_entry(config.font,
1413                 config.style,
1414                 config.size);
1422 int TitleMain::get_char_height()
1424 // this is height above the zero line, but does not include characters that go below
1425         int result = config.size;
1426         if((config.style & FONT_OUTLINE)) result += (int)ceil(config.stroke_width * 2);
1427         return result;
1430 int TitleMain::get_char_advance(int current, int next)
1432         FT_Vector kerning;
1433         int result = 0;
1434         TitleGlyph *current_glyph = 0;
1435         TitleGlyph *next_glyph = 0;
1437         if(current == 0xa) return 0;
1439         for(int i = 0; i < glyphs.total; i++)
1440         {
1441                 if(glyphs.values[i]->c == current)
1442                 {
1443                         current_glyph = glyphs.values[i];
1444                         break;
1445                 }
1446         }
1448         for(int i = 0; i < glyphs.total; i++)
1449         {
1450                 if(glyphs.values[i]->c == next)
1451                 {
1452                         next_glyph = glyphs.values[i];
1453                         break;
1454                 }
1455         }
1457         if(current_glyph)
1458                 result = current_glyph->advance_w;
1460 //printf("TitleMain::get_char_advance 1 %c %c %p %p\n", current, next, current_glyph, next_glyph);
1461         if(next_glyph)
1462                 FT_Get_Kerning(freetype_face, 
1463                                 current_glyph->freetype_index,
1464                                 next_glyph->freetype_index,
1465                                 ft_kerning_default,
1466                                 &kerning);
1467         else
1468                 kerning.x = 0;
1469 //printf("TitleMain::get_char_advance 2 %d %d\n", result, kerning.x);
1471         return result + (kerning.x >> 6);
1475 void TitleMain::draw_glyphs()
1477 // Build table of all glyphs needed
1478         int text_len = strlen(config.text);
1479         int total_packages = 0;
1480         iconv_t cd;
1481         cd = iconv_open ("UCS-4", config.encoding);
1482         if (cd == (iconv_t) -1)
1483         {
1484                         /* Something went wrong.  */
1485                 fprintf (stderr, _("Iconv conversion from %s to Unicode UCS-4 not available\n"),config.encoding);
1486         };
1488         for(int i = 0; i < text_len; i++)
1489         {
1490                 FT_ULong char_code;     
1491                 int c = config.text[i];
1492                 int exists = 0;
1493                 /* if iconv is working ok for current encoding */
1494                 if (cd != (iconv_t) -1)
1495                 {
1497                         size_t inbytes,outbytes;
1498                         char inbuf;
1499                         char *inp = (char*)&inbuf, *outp = (char *)&char_code;
1500                         
1501                         inbuf = (char)c;
1502                         inbytes = 1;
1503                         outbytes = 4;
1504         
1505                         iconv (cd, &inp, &inbytes, &outp, &outbytes);
1506 #if     __BYTE_ORDER == __LITTLE_ENDIAN
1507                                 char_code = bswap_32(char_code);
1508 #endif                          /* Big endian.  */
1510                 } else {
1511                         char_code = c;
1512                 }
1514                 for(int j = 0; j < glyphs.total; j++)
1515                 {
1516                         if(glyphs.values[j]->char_code == char_code)
1517                         {
1518                                 exists = 1;
1519                                 break;
1520                         }
1521                 }
1523                 if(!exists)
1524                 {
1525                         total_packages++;
1526 //printf("TitleMain::draw_glyphs 1\n");
1527                         TitleGlyph *glyph = new TitleGlyph;
1528 //printf("TitleMain::draw_glyphs 2\n");
1529                         glyphs.append(glyph);
1530                         glyph->c = c;
1531                         glyph->char_code = char_code;
1532                 }
1533         }
1534         iconv_close(cd);
1536         if(!glyph_engine)
1537                 glyph_engine = new GlyphEngine(this, PluginClient::smp + 1);
1539         glyph_engine->set_package_count(total_packages);
1540 //printf("TitleMain::draw_glyphs 3 %d\n", glyphs.total);
1541         glyph_engine->process_packages();
1542 //printf("TitleMain::draw_glyphs 4\n");
1545 void TitleMain::get_total_extents()
1547 // Determine extents of total text
1548         int current_w = 0;
1549         int row_start = 0;
1550         text_len = strlen(config.text);
1551         if(!char_positions) char_positions = new title_char_position_t[text_len];
1552         text_rows = 0;
1553         text_w = 0;
1554         ascent = 0;
1556         for(int i = 0; i < glyphs.total; i++)
1557                 if(glyphs.values[i]->top > ascent) ascent = glyphs.values[i]->top;
1558 //printf("TitleMain::get_total_extents %d\n", ascent);
1560         // get the number of rows first
1561         for(int i = 0; i < text_len; i++)
1562         {
1563                 if(config.text[i] == 0xa || i == text_len - 1)
1564                 {
1565                         text_rows++;
1566                 }
1567         }
1568         if (!rows_bottom) rows_bottom = new int[text_rows+1];
1569         text_rows = 0;
1570         rows_bottom[0] = 0;
1572         for(int i = 0; i < text_len; i++)
1573         {
1574                 char_positions[i].x = current_w;
1575                 char_positions[i].y = text_rows * get_char_height();
1576                 char_positions[i].w = get_char_advance(config.text[i], config.text[i + 1]);
1577                 TitleGlyph *current_glyph = 0;
1578                 for(int j = 0; j < glyphs.total; j++)
1579                 {
1580                         if(glyphs.values[j]->c == config.text[i])
1581                         {
1582                                 current_glyph = glyphs.values[j];
1583                                 break;
1584                         }
1585                 }
1586                 int current_bottom = current_glyph->top - current_glyph->height;
1587                 if (current_bottom < rows_bottom[text_rows])
1588                         rows_bottom[text_rows] = current_bottom ;
1590 // printf("TitleMain::get_total_extents 1 %c %d %d %d\n", 
1591 //      config.text[i], 
1592 //      char_positions[i].x, 
1593 //      char_positions[i].y, 
1594 //      char_positions[i].w);
1595                 current_w += char_positions[i].w;
1597                 if(config.text[i] == 0xa || i == text_len - 1)
1598                 {
1599                         text_rows++;
1600                         rows_bottom[text_rows] = 0;
1601                         if(current_w > text_w) text_w = current_w;
1602                         current_w = 0;
1603                 }
1604         }
1605         text_w += config.dropshadow;
1606         text_h = text_rows * get_char_height();
1607         text_h += config.dropshadow;
1609 // Now that text_w is known
1610 // Justify rows based on configuration
1611         row_start = 0;
1612         for(int i = 0; i < text_len; i++)
1613         {
1614                 if(config.text[i] == 0xa || i == text_len - 1)
1615                 {
1616                         for(int j = row_start; j <= i; j++)
1617                         {
1618                                 switch(config.hjustification)
1619                                 {
1620                                         case JUSTIFY_LEFT:
1621                                                 break;
1623                                         case JUSTIFY_MID:
1624                                                 char_positions[j].x += (text_w - 
1625                                                                 char_positions[i].x - 
1626                                                                 char_positions[i].w) /
1627                                                         2;
1628                                                 break;
1630                                         case JUSTIFY_RIGHT:
1631                                                 char_positions[j].x += (text_w - 
1632                                                         char_positions[i].x - 
1633                                                         char_positions[i].w);
1634                                                 break;
1635                                 }
1636                         }
1637                         row_start = i + 1;
1638                 }
1639         }
1642 //printf("TitleMain::get_total_extents 2 %d %d\n", text_w, text_h);
1645 int TitleMain::draw_mask()
1647         int old_visible_row1 = visible_row1;
1648         int old_visible_row2 = visible_row2;
1651 // Determine y of visible text
1652         if(config.motion_strategy == BOTTOM_TO_TOP)
1653         {
1654 // printf("TitleMain::draw_mask 1 %d %lld %lld %lld\n", 
1655 //      config.motion_strategy,
1656 //      get_source_position(), 
1657 //      get_source_start(),
1658 //      config.prev_keyframe_position);
1659                 float magnitude = config.pixels_per_second * 
1660                         (get_source_position() - config.prev_keyframe_position) / 
1661                         PluginVClient::project_frame_rate;
1662                 if(config.loop)
1663                 {
1664                         int loop_size = text_h + input->get_h();
1665                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1666                 }
1667                 text_y1 = config.y + input->get_h() - magnitude;
1668         }
1669         else
1670         if(config.motion_strategy == TOP_TO_BOTTOM)
1671         {
1672                 float magnitude = config.pixels_per_second * 
1673                         (get_source_position() - config.prev_keyframe_position) / 
1674                         PluginVClient::project_frame_rate;
1675                 if(config.loop)
1676                 {
1677                         int loop_size = text_h + input->get_h();
1678                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1679                 }
1680                 text_y1 = config.y + magnitude;
1681                 text_y1 -= text_h;
1682         }
1683         else
1684         if(config.vjustification == JUSTIFY_TOP)
1685         {
1686                 text_y1 = config.y;
1687         }
1688         else
1689         if(config.vjustification == JUSTIFY_MID)
1690         {
1691                 text_y1 = config.y + input->get_h() / 2 - text_h / 2;
1692         }
1693         else
1694         if(config.vjustification == JUSTIFY_BOTTOM)
1695         {
1696                 text_y1 = config.y + input->get_h() - text_h;
1697         }
1699         text_y2 = text_y1 + text_h + 0.5;
1701 // Determine x of visible text
1702         if(config.motion_strategy == RIGHT_TO_LEFT)
1703         {
1704                 float magnitude = config.pixels_per_second * 
1705                         (get_source_position() - config.prev_keyframe_position) / 
1706                         PluginVClient::project_frame_rate;
1707                 if(config.loop)
1708                 {
1709                         int loop_size = text_w + input->get_w();
1710                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1711                 }
1712                 text_x1 = config.x + (float)input->get_w() - magnitude;
1713         }
1714         else
1715         if(config.motion_strategy == LEFT_TO_RIGHT)
1716         {
1717                 float magnitude = config.pixels_per_second * 
1718                         (get_source_position() - config.prev_keyframe_position) / 
1719                         PluginVClient::project_frame_rate;
1720                 if(config.loop)
1721                 {
1722                         int loop_size = text_w + input->get_w();
1723                         magnitude -= (int)(magnitude / loop_size) * loop_size;
1724                 }
1725                 text_x1 = config.x + -(float)text_w + magnitude;
1726         }
1727         else
1728         if(config.hjustification == JUSTIFY_LEFT)
1729         {
1730                 text_x1 = config.x;
1731         }
1732         else
1733         if(config.hjustification == JUSTIFY_MID)
1734         {
1735                 text_x1 = config.x + input->get_w() / 2 - text_w / 2;
1736         }
1737         else
1738         if(config.hjustification == JUSTIFY_RIGHT)
1739         {
1740                 text_x1 = config.x + input->get_w() - text_w;
1741         }
1747 // Determine y extents just of visible text
1748         visible_row1 = (int)(-text_y1 / get_char_height());
1749         if(visible_row1 < 0) visible_row1 = 0;
1751         visible_row2 = (int)((float)text_rows - (text_y2 - input->get_h()) / get_char_height() + 1);
1752         if(visible_row2 > text_rows) visible_row2 = text_rows;
1755         if(visible_row2 <= visible_row1) return 1;
1758         mask_y1 = text_y1 + visible_row1 * get_char_height();
1759         mask_y2 = text_y1 + visible_row2 * get_char_height();
1760         text_x1 += config.x;
1763 //printf("TitleMain::draw_mask %d %d\n", visible_row1, visible_row2);
1764         visible_char1 = visible_char2 = 0;
1765         int got_char1 = 0;
1766         for(int i = 0; i < text_len; i++)
1767         {
1768                 title_char_position_t *char_position = char_positions + i;
1769                 int char_row = char_position->y / get_char_height();
1770                 if(char_row >= visible_row1 &&
1771                         char_row < visible_row2)
1772                 
1773                 {
1774                         if(!got_char1)
1775                         {
1776                                 visible_char1 = i;
1777                                 got_char1 = 1;
1778                         }
1779                         visible_char2 = i;
1780                 }
1781         }
1782         visible_char2++;
1786         int visible_rows = visible_row2 - visible_row1;
1787         int need_redraw = 0;
1788         if(text_mask &&
1789                 (text_mask->get_w() != text_w ||
1790                 text_mask->get_h() != visible_rows * get_char_height() - rows_bottom[visible_row2 - 1]))
1791         {
1792                 delete text_mask;
1793                 delete text_mask_stroke;
1794                 text_mask = 0;
1795                 text_mask_stroke = 0;
1796         }
1798         if(!text_mask)
1799         {
1800                 text_mask = new VFrame(0,
1801                         text_w,
1802                         visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1803                         BC_A8);
1804                 text_mask_stroke = new VFrame(0,
1805                         text_w,
1806                         visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1807                         BC_A8);
1809                 need_redraw = 1;
1810         }
1812 //printf("TitleMain::draw_mask %d %d\n", text_w, visible_rows * get_char_height());
1815 //printf("TitleMain::draw_mask 1\n");
1816 // Draw on text mask if different
1817         if(old_visible_row1 != visible_row1 ||
1818                 old_visible_row2 != visible_row2 ||
1819                 need_redraw)
1820         {
1821 //printf("TitleMain::draw_mask 2\n");
1822                 text_mask->clear_frame();
1823                 text_mask_stroke->clear_frame();
1826 //printf("TitleMain::draw_mask 2\n");
1827                 if(!title_engine)
1828                         title_engine = new TitleEngine(this, PluginClient::smp + 1);
1829 //printf("TitleMain::draw_mask 2\n");
1831                 title_engine->set_package_count(visible_char2 - visible_char1);
1832 //printf("TitleMain::draw_mask 2\n");
1833                 title_engine->process_packages();
1834 //printf("TitleMain::draw_mask 3\n");
1835         }
1837         return 0;
1841 void TitleMain::overlay_mask()
1844 //printf("TitleMain::overlay_mask 1\n");
1845         alpha = 0x100;
1846         if(!EQUIV(config.fade_in, 0))
1847         {
1848                 int fade_len = lroundf(config.fade_in * PluginVClient::project_frame_rate);
1849                 int fade_position = get_source_position() - config.prev_keyframe_position;
1852                 if(fade_position >= 0 && fade_position < fade_len)
1853                 {
1854                         alpha = lroundf(256.0f * fade_position / fade_len);
1855                 }
1856         }
1857 //printf("TitleMain::overlay_mask 1\n");
1859         if(!EQUIV(config.fade_out, 0))
1860         {
1861                 int fade_len = lroundf(config.fade_out * PluginVClient::project_frame_rate);
1862                 int fade_position = config.next_keyframe_position - get_source_position();
1865                 if(fade_position >= 0 && fade_position < fade_len)
1866                 {
1867                         alpha = lroundf(256.0f * fade_position / fade_len);
1868                 }
1869         }
1870 //printf("TitleMain::overlay_mask 1\n");
1872         if(config.dropshadow)
1873         {
1874                 text_x1 += config.dropshadow;
1875                 text_x2 += config.dropshadow;
1876                 mask_y1 += config.dropshadow;
1877                 mask_y2 += config.dropshadow;
1878                 if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1879                         mask_y1 < input->get_h() && mask_y2 > 0)
1880                 {
1881                         if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1882 // Do 2 passes if dropshadow.
1883                         int temp_color = config.color;
1884                         config.color = 0x0;
1885                         translate->process_packages();
1886                         config.color = temp_color;
1887                 }
1888                 text_x1 -= config.dropshadow;
1889                 text_x2 -= config.dropshadow;
1890                 mask_y1 -= config.dropshadow;
1891                 mask_y2 -= config.dropshadow;
1892         }
1893 //printf("TitleMain::overlay_mask 1\n");
1895         if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1896                 mask_y1 < input->get_h() && mask_y2 > 0)
1897         {
1898                 if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1899                 translate->process_packages();
1900                 if (config.stroke_width >= ZERO &&
1901                         (config.style & FONT_OUTLINE)) 
1902                 {
1903                         int temp_color = config.color;
1904                         VFrame *tmp_text_mask = this->text_mask;
1905                         config.color = config.color_stroke;
1906                         this->text_mask = this->text_mask_stroke;
1908                         translate->process_packages();
1909                         config.color = temp_color;
1910                         this->text_mask = tmp_text_mask;
1911                 }
1912         }
1913 //printf("TitleMain::overlay_mask 200\n");
1916 void TitleMain::clear_glyphs()
1918 //printf("TitleMain::clear_glyphs 1\n");
1919         glyphs.remove_all_objects();
1922 char* TitleMain::motion_to_text(int motion)
1924         switch(motion)
1925         {
1926                 case NO_MOTION: return _("No motion"); break;
1927                 case BOTTOM_TO_TOP: return _("Bottom to top"); break;
1928                 case TOP_TO_BOTTOM: return _("Top to bottom"); break;
1929                 case RIGHT_TO_LEFT: return _("Right to left"); break;
1930                 case LEFT_TO_RIGHT: return _("Left to right"); break;
1931         }
1934 int TitleMain::text_to_motion(char *text)
1936         for(int i = 0; i < TOTAL_PATHS; i++)
1937         {
1938                 if(!strcasecmp(motion_to_text(i), text)) return i;
1939         }
1940         return 0;
1949 int TitleMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
1951         int result = 0;
1952         input = input_ptr;
1953         output = output_ptr;
1955         need_reconfigure |= load_configuration();
1956 //printf("TitleMain::process_realtime 1\n");
1959 // Always synthesize text and redraw it for timecode
1960         if(config.timecode)
1961         {
1962                 int64_t rendered_frame = get_source_position();
1963                 if (get_direction() == PLAY_REVERSE)
1964                         rendered_frame -= 1;
1966                 int tcf = Units::timeformat_totype(config.timecodeformat);
1967                 if (tcf < 0) {
1968                         tcf = TIME_HMSF;
1969                         strcpy(config.timecodeformat, DEFAULT_TIMECODEFORMAT);
1970                 }
1971                 Units::totext(config.text, 
1972                                 (double)rendered_frame / PluginVClient::project_frame_rate, 
1973                                 tcf, 
1974                                 0,
1975                                 PluginVClient::project_frame_rate, 
1976                                 0);
1977                 need_reconfigure = 1;
1978         }
1980 // Check boundaries
1981         if(config.size <= 0 || config.size >= 2048) config.size = 72;
1982         if(config.stroke_width < 0 || 
1983                 config.stroke_width >= 512) config.stroke_width = 0.0;
1984         if(!strlen(config.text)) return 0;
1985         if(!strlen(config.encoding)) strcpy(config.encoding, DEFAULT_ENCODING);
1987 //printf("TitleMain::process_realtime 10\n");
1989 // Handle reconfiguration
1990         if(need_reconfigure)
1991         {
1992 //printf("TitleMain::process_realtime 2\n");
1993                 if(text_mask) delete text_mask;
1994                 if(text_mask_stroke) delete text_mask_stroke;
1995                 text_mask = 0;
1996                 text_mask_stroke = 0;
1997 //printf("TitleMain::process_realtime 2\n");
1998                 if(freetype_face) FT_Done_Face(freetype_face);
1999                 freetype_face = 0;
2000 //printf("TitleMain::process_realtime 2\n");
2001                 if(glyph_engine) delete glyph_engine;
2002                 glyph_engine = 0;
2003 //printf("TitleMain::process_realtime 2\n");
2004                 if(char_positions) delete [] char_positions;
2005                 char_positions = 0;
2006                 if(rows_bottom) delete [] rows_bottom;
2007                 rows_bottom = 0;
2008 //printf("TitleMain::process_realtime 2\n");
2009                 clear_glyphs();
2010 //printf("TitleMain::process_realtime 2\n");
2011                 visible_row1 = 0;
2012                 visible_row2 = 0;
2013                 ascent = 0;
2015                 if(!freetype_library) 
2016                         FT_Init_FreeType(&freetype_library);
2018 //printf("TitleMain::process_realtime 2\n");
2019                 if(!freetype_face)
2020                 {
2021                         FontEntry *font = get_font();
2022 //printf("TitleMain::process_realtime 2.1 %s\n", font->path);
2023                         if(load_freetype_face(freetype_library,
2024                                 freetype_face,
2025                                 font->path))
2026                         {
2027                                 printf("TitleMain::process_realtime %s: FT_New_Face failed.\n",
2028                                         font->fixed_title);
2029                                 result = 1;
2030                         }
2031 //printf("TitleMain::process_realtime 2.2\n");
2033                         if(!result) FT_Set_Pixel_Sizes(freetype_face, config.size, 0);
2034 //printf("TitleMain::process_realtime 2.3\n");
2035                 }
2037 //printf("TitleMain::process_realtime 3\n");
2039                 if(!result)
2040                 {
2041                         draw_glyphs();
2042 //printf("TitleMain::process_realtime 4\n");
2043                         get_total_extents();
2044 //printf("TitleMain::process_realtime 5\n");
2045                         need_reconfigure = 0;
2046                 }
2047         }
2049         if(!result)
2050         {
2051 //printf("TitleMain::process_realtime 4\n");
2052 // Determine region of text visible on the output and draw mask
2053                 result = draw_mask();
2054         }
2055 //printf("TitleMain::process_realtime 50\n");
2058 // Overlay mask on output
2059         if(!result)
2060         {
2061                 overlay_mask();
2062         }
2063 //printf("TitleMain::process_realtime 60 %d\n", glyphs.total);
2065         return 0;
2068 int TitleMain::show_gui()
2070         load_configuration();
2071         thread = new TitleThread(this);
2072         thread->start();
2073         return 0;
2076 int TitleMain::set_string()
2078         if(thread) thread->window->set_title(gui_string);
2079         return 0;
2082 void TitleMain::raise_window()
2084         if(thread)
2085         {
2086                 thread->window->raise_window();
2087                 thread->window->flush();
2088         }
2091 void TitleMain::update_gui()
2093         if(thread)
2094         {
2095                 int reconfigure = load_configuration();
2096                 if(reconfigure)
2097                 {
2098                         thread->window->lock_window();
2099                         thread->window->update();
2100                         thread->window->unlock_window();
2101                         thread->window->color_thread->update_gui(config.color, 0);
2102                 }
2103         }
2107 int TitleMain::load_defaults()
2109         char directory[1024], text_path[1024];
2110 // set the default directory
2111         sprintf(directory, "%stitle.rc", BCASTDIR);
2113 // load the defaults
2114         defaults = new BC_Hash(directory);
2115         defaults->load();
2117         defaults->get("FONT", config.font);
2118         defaults->get("ENCODING", config.encoding);
2119         config.style = defaults->get("STYLE", (int64_t)config.style);
2120         config.size = defaults->get("SIZE", config.size);
2121         config.color = defaults->get("COLOR", config.color);
2122         config.color_stroke = defaults->get("COLOR_STROKE", config.color_stroke);
2123         config.stroke_width = defaults->get("STROKE_WIDTH", config.stroke_width);
2124         config.motion_strategy = defaults->get("MOTION_STRATEGY", config.motion_strategy);
2125         config.loop = defaults->get("LOOP", config.loop);
2126         config.pixels_per_second = defaults->get("PIXELS_PER_SECOND", config.pixels_per_second);
2127         config.hjustification = defaults->get("HJUSTIFICATION", config.hjustification);
2128         config.vjustification = defaults->get("VJUSTIFICATION", config.vjustification);
2129         config.fade_in = defaults->get("FADE_IN", config.fade_in);
2130         config.fade_out = defaults->get("FADE_OUT", config.fade_out);
2131         config.x = defaults->get("TITLE_X", config.x);
2132         config.y = defaults->get("TITLE_Y", config.y);
2133         config.dropshadow = defaults->get("DROPSHADOW", config.dropshadow);
2134         config.timecode = defaults->get("TIMECODE", config.timecode);
2135         defaults->get("TIMECODEFORMAT", config.timecodeformat);
2136         window_w = defaults->get("WINDOW_W", 660);
2137         window_h = defaults->get("WINDOW_H", 480);
2139 // Store text in separate path to isolate special characters
2140         FileSystem fs;
2141         sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2142         fs.complete_path(text_path);
2143         FILE *fd = fopen(text_path, "rb");
2144         if(fd)
2145         {
2146                 fseek(fd, 0, SEEK_END);
2147                 int64_t len = ftell(fd);
2148                 fseek(fd, 0, SEEK_SET);
2149                 fread(config.text, len, 1, fd);
2150                 config.text[len] = 0;
2151 //printf("TitleMain::load_defaults %s\n", config.text);
2152                 fclose(fd);
2153         }
2154         else
2155                 config.text[0] = 0;
2156         return 0;
2159 int TitleMain::save_defaults()
2161         char text_path[1024];
2163         defaults->update("FONT", config.font);
2164         defaults->update("ENCODING", config.encoding);
2165         defaults->update("STYLE", (int64_t)config.style);
2166         defaults->update("SIZE", config.size);
2167         defaults->update("COLOR", config.color);
2168         defaults->update("COLOR_STROKE", config.color_stroke);
2169         defaults->update("STROKE_WIDTH", config.stroke_width);
2170         defaults->update("MOTION_STRATEGY", config.motion_strategy);
2171         defaults->update("LOOP", config.loop);
2172         defaults->update("PIXELS_PER_SECOND", config.pixels_per_second);
2173         defaults->update("HJUSTIFICATION", config.hjustification);
2174         defaults->update("VJUSTIFICATION", config.vjustification);
2175         defaults->update("FADE_IN", config.fade_in);
2176         defaults->update("FADE_OUT", config.fade_out);
2177         defaults->update("TITLE_X", config.x);
2178         defaults->update("TITLE_Y", config.y);
2179         defaults->update("DROPSHADOW", config.dropshadow);
2180         defaults->update("TIMECODE", config.timecode);
2181         defaults->update("TIMECODEFORMAT", config.timecodeformat);
2182         defaults->update("WINDOW_W", window_w);
2183         defaults->update("WINDOW_H", window_h);
2184         defaults->save();
2186 // Store text in separate path to isolate special characters
2187         FileSystem fs;
2188         sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2189         fs.complete_path(text_path);
2190         FILE *fd = fopen(text_path, "wb");
2191         if(fd)
2192         {
2193                 fwrite(config.text, strlen(config.text), 1, fd);
2194                 fclose(fd);
2195         }
2196 //      else
2197 //              perror("TitleMain::save_defaults");
2198         return 0;
2204 int TitleMain::load_configuration()
2206         KeyFrame *prev_keyframe, *next_keyframe;
2207         prev_keyframe = get_prev_keyframe(get_source_position());
2208         next_keyframe = get_next_keyframe(get_source_position());
2210 // printf("TitleMain::load_configuration 1 %d %d\n", 
2211 // prev_keyframe->position,
2212 // next_keyframe->position);
2214         TitleConfig old_config, prev_config, next_config;
2215         old_config.copy_from(config);
2216         read_data(prev_keyframe);
2217         prev_config.copy_from(config);
2218         read_data(next_keyframe);
2219         next_config.copy_from(config);
2221         config.prev_keyframe_position = prev_keyframe->position;
2222         config.next_keyframe_position = next_keyframe->position;
2224         // if no previous keyframe exists, it should be start of the plugin, not start of the track
2225         if(config.next_keyframe_position == config.prev_keyframe_position)
2226                 config.next_keyframe_position = get_source_start() + get_total_len();
2227         if (config.prev_keyframe_position == 0) 
2228                 config.prev_keyframe_position = get_source_start();
2231 // printf("TitleMain::load_configuration 10 %d %d\n", 
2232 // config.prev_keyframe_position,
2233 // config.next_keyframe_position);
2235         config.interpolate(prev_config, 
2236                 next_config, 
2237                 (next_keyframe->position == prev_keyframe->position) ?
2238                         get_source_position() :
2239                         prev_keyframe->position,
2240                 (next_keyframe->position == prev_keyframe->position) ?
2241                         get_source_position() + 1 :
2242                         next_keyframe->position,
2243                 get_source_position());
2245         if(!config.equivalent(old_config))
2246                 return 1;
2247         else
2248                 return 0;
2262 void TitleMain::save_data(KeyFrame *keyframe)
2264         FileXML output;
2266 // cause data to be stored directly in text
2267         output.set_shared_string(keyframe->data, MESSAGESIZE);
2268         output.tag.set_title("TITLE");
2269         output.tag.set_property("FONT", config.font);
2270         output.tag.set_property("ENCODING", config.encoding);
2271         output.tag.set_property("STYLE", (int64_t)config.style);
2272         output.tag.set_property("SIZE", config.size);
2273         output.tag.set_property("COLOR", config.color);
2274         output.tag.set_property("COLOR_STROKE", config.color_stroke);
2275         output.tag.set_property("STROKE_WIDTH", config.stroke_width);
2276         output.tag.set_property("MOTION_STRATEGY", config.motion_strategy);
2277         output.tag.set_property("LOOP", config.loop);
2278         output.tag.set_property("PIXELS_PER_SECOND", config.pixels_per_second);
2279         output.tag.set_property("HJUSTIFICATION", config.hjustification);
2280         output.tag.set_property("VJUSTIFICATION", config.vjustification);
2281         output.tag.set_property("FADE_IN", config.fade_in);
2282         output.tag.set_property("FADE_OUT", config.fade_out);
2283         output.tag.set_property("TITLE_X", config.x);
2284         output.tag.set_property("TITLE_Y", config.y);
2285         output.tag.set_property("DROPSHADOW", config.dropshadow);
2286         output.tag.set_property("TIMECODE", config.timecode);
2287         output.tag.set_property("TIMECODEFORMAT", config.timecodeformat);
2288         output.append_tag();
2289         output.append_newline();
2290         
2291         output.encode_text(config.text);
2293         output.tag.set_title("/TITLE");
2294         output.append_tag();
2295         output.append_newline();
2296         output.terminate_string();
2297 //printf("TitleMain::save_data 1\n%s\n", output.string);
2298 //printf("TitleMain::save_data 2\n%s\n", config.text);
2301 void TitleMain::read_data(KeyFrame *keyframe)
2303         FileXML input;
2305         input.set_shared_string(keyframe->data, strlen(keyframe->data));
2307         int result = 0;
2308         int new_interlace = 0;
2309         int new_horizontal = 0;
2310         int new_luminance = 0;
2312         config.prev_keyframe_position = keyframe->position;
2313         while(!result)
2314         {
2315                 result = input.read_tag();
2317                 if(!result)
2318                 {
2319                         if(input.tag.title_is("TITLE"))
2320                         {
2321                                 input.tag.get_property("FONT", config.font);
2322                                 input.tag.get_property("ENCODING", config.encoding);
2323                                 config.style = input.tag.get_property("STYLE", (int64_t)config.style);
2324                                 config.size = input.tag.get_property("SIZE", config.size);
2325                                 config.color = input.tag.get_property("COLOR", config.color);
2326                                 config.color_stroke = input.tag.get_property("COLOR_STROKE", config.color_stroke);
2327                                 config.stroke_width = input.tag.get_property("STROKE_WIDTH", config.stroke_width);
2328                                 config.motion_strategy = input.tag.get_property("MOTION_STRATEGY", config.motion_strategy);
2329                                 config.loop = input.tag.get_property("LOOP", config.loop);
2330                                 config.pixels_per_second = input.tag.get_property("PIXELS_PER_SECOND", config.pixels_per_second);
2331                                 config.hjustification = input.tag.get_property("HJUSTIFICATION", config.hjustification);
2332                                 config.vjustification = input.tag.get_property("VJUSTIFICATION", config.vjustification);
2333                                 config.fade_in = input.tag.get_property("FADE_IN", config.fade_in);
2334                                 config.fade_out = input.tag.get_property("FADE_OUT", config.fade_out);
2335                                 config.x = input.tag.get_property("TITLE_X", config.x);
2336                                 config.y = input.tag.get_property("TITLE_Y", config.y);
2337                                 config.dropshadow = input.tag.get_property("DROPSHADOW", config.dropshadow);
2338                                 config.timecode = input.tag.get_property("TIMECODE", config.timecode);
2339                                 input.tag.get_property("TIMECODEFORMAT", config.timecodeformat);
2340                                 strcpy(config.text, input.read_text());
2341 //printf("TitleMain::read_data 1\n%s\n", input.string);
2342 //printf("TitleMain::read_data 2\n%s\n", config.text);
2343                         }
2344                         else
2345                         if(input.tag.title_is("/TITLE"))
2346                         {
2347                                 result = 1;
2348                         }
2349                 }
2350         }