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