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