1 // Originally developed by Heroine Virtual Ltd.
2 // Support for multiple encodings, outline (stroke) by
3 // Andraz Tori <Andraz.tori1@guest.arnes.si>
7 #include "colormodels.h"
9 #include "filesystem.h"
10 #include "transportque.inc"
18 #include "mwindow.inc"
19 #include "picon_png.h"
20 #include "plugincolors.h"
22 #include "titlewindow.h"
23 #include "transportque.inc"
35 #define ZERO (1.0 / 64.0)
37 #define FONT_SEARCHPATH "fonts"
38 //#define FONT_SEARCHPATH "/usr/X11R6/lib/X11/fonts"
41 REGISTER_PLUGIN(TitleMain)
44 TitleConfig::TitleConfig()
48 color_stroke = 0xff0000;
50 motion_strategy = NO_MOTION;
52 hjustification = JUSTIFY_CENTER;
53 vjustification = JUSTIFY_MID;
59 sprintf(font, "fixed");
60 sprintf(text, _("hello world"));
61 #define DEFAULT_ENCODING "ISO8859-1"
62 sprintf(encoding, DEFAULT_ENCODING);
63 #define DEFAULT_TIMECODEFORMAT "h:mm:ss:ff"
64 sprintf(timecodeformat, DEFAULT_TIMECODEFORMAT);
65 pixels_per_second = 1.0;
70 // Does not test equivalency but determines if redrawing text is necessary.
71 int TitleConfig::equivalent(TitleConfig &that)
73 return dropshadow == that.dropshadow &&
74 style == that.style &&
76 color == that.color &&
77 color_stroke == that.color_stroke &&
78 stroke_width == that.stroke_width &&
79 timecode == that.timecode &&
80 !strcasecmp(timecodeformat, that.timecodeformat) &&
81 hjustification == that.hjustification &&
82 vjustification == that.vjustification &&
83 EQUIV(pixels_per_second, that.pixels_per_second) &&
84 !strcasecmp(font, that.font) &&
85 !strcasecmp(encoding, that.encoding) &&
86 !strcmp(text, that.text);
89 void TitleConfig::copy_from(TitleConfig &that)
91 strcpy(font, that.font);
95 color_stroke = that.color_stroke;
96 stroke_width = that.stroke_width;
97 pixels_per_second = that.pixels_per_second;
98 motion_strategy = that.motion_strategy;
100 hjustification = that.hjustification;
101 vjustification = that.vjustification;
102 fade_in = that.fade_in;
103 fade_out = that.fade_out;
106 dropshadow = that.dropshadow;
107 timecode = that.timecode;
108 strcpy(timecodeformat, that.timecodeformat);
109 strcpy(text, that.text);
110 strcpy(encoding, that.encoding);
113 void TitleConfig::interpolate(TitleConfig &prev,
117 int64_t current_frame)
119 strcpy(font, prev.font);
120 strcpy(encoding, prev.encoding);
124 color_stroke = prev.color_stroke;
125 stroke_width = prev.stroke_width;
126 motion_strategy = prev.motion_strategy;
128 hjustification = prev.hjustification;
129 vjustification = prev.vjustification;
130 fade_in = prev.fade_in;
131 fade_out = prev.fade_out;
132 pixels_per_second = prev.pixels_per_second;
133 strcpy(text, prev.text);
135 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
136 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
139 // this->x = prev.x * prev_scale + next.x * next_scale;
140 // this->y = prev.y * prev_scale + next.y * next_scale;
143 timecode = prev.timecode;
144 strcpy(timecodeformat, prev.timecodeformat);
145 // this->dropshadow = (int)(prev.dropshadow * prev_scale + next.dropshadow * next_scale);
146 this->dropshadow = prev.dropshadow;
165 FontEntry::FontEntry()
181 FontEntry::~FontEntry()
183 if(path) delete [] path;
184 if(foundary) delete [] foundary;
185 if(family) delete [] family;
186 if(weight) delete [] weight;
187 if(slant) delete [] slant;
188 if(swidth) delete [] swidth;
189 if(adstyle) delete [] adstyle;
190 if(spacing) delete [] spacing;
191 if(registry) delete [] registry;
192 if(encoding) delete [] encoding;
193 if(fixed_title) delete [] fixed_title;
196 void FontEntry::dump()
198 printf("%s: %s %s %s %s %s %s %d %d %d %d %s %d %s %s\n",
216 TitleGlyph::TitleGlyph()
225 TitleGlyph::~TitleGlyph()
227 //printf("TitleGlyph::~TitleGlyph 1\n");
228 if(data) delete data;
229 if(data_stroke) delete data_stroke;
242 GlyphPackage::GlyphPackage() : LoadPackage()
246 GlyphUnit::GlyphUnit(TitleMain *plugin, GlyphEngine *server)
249 this->plugin = plugin;
251 freetype_library = 0;
255 GlyphUnit::~GlyphUnit()
257 if(freetype_library) FT_Done_FreeType(freetype_library);
260 void GlyphUnit::process_package(LoadPackage *package)
262 GlyphPackage *pkg = (GlyphPackage*)package;
263 TitleGlyph *glyph = pkg->glyph;
266 if(!freetype_library)
268 current_font = plugin->get_font();
270 if(plugin->load_freetype_face(freetype_library,
274 printf(_("GlyphUnit::process_package FT_New_Face failed.\n"));
279 FT_Set_Pixel_Sizes(freetype_face, plugin->config.size, 0);
285 int gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
287 //printf("GlyphUnit::process_package 1 %c\n", glyph->char_code);
292 if (glyph->char_code != 10)
293 printf(_("GlyphUnit::process_package FT_Load_Char failed - char: %i.\n"),
295 // Prevent a crash here
301 glyph->freetype_index = 0;
302 glyph->advance_w = 8;
303 glyph->data = new VFrame(0,
308 glyph->data->clear_frame();
309 glyph->data_stroke = 0;
313 // create outline glyph
314 if (plugin->config.stroke_width >= ZERO &&
315 (plugin->config.style & FONT_OUTLINE))
317 glyph->data_stroke = new VFrame(0,
322 glyph->data_stroke->clear_frame();
329 // char found and no outline desired
330 if (plugin->config.stroke_width < ZERO ||
331 !(plugin->config.style & FONT_OUTLINE))
333 FT_Glyph glyph_image;
336 FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
337 FT_Get_Glyph(freetype_face->glyph, &glyph_image);
338 FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
339 // printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
340 // bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax);
342 FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
345 glyph->width = bm.width = ((bbox.xMax - bbox.xMin + 63) >> 6);
346 glyph->height = bm.rows = ((bbox.yMax - bbox.yMin + 63) >> 6);
347 glyph->pitch = bm.pitch = bm.width;
348 bm.pixel_mode = FT_PIXEL_MODE_GRAY;
350 glyph->left = (bbox.xMin + 31) >> 6;
351 if (glyph->left < 0) glyph->left = 0;
352 glyph->top = (bbox.yMax + 31) >> 6;
353 glyph->freetype_index = gindex;
354 glyph->advance_w = ((freetype_face->glyph->advance.x + 31) >> 6);
355 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n",
356 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
358 glyph->data = new VFrame(0,
363 glyph->data->clear_frame();
364 bm.buffer = glyph->data->get_data();
365 FT_Outline_Get_Bitmap( freetype_library,
366 &((FT_OutlineGlyph) glyph_image)->outline,
368 FT_Done_Glyph(glyph_image);
371 // Outline desired and glyph found
373 FT_Glyph glyph_image;
379 FT_UInt npoints, ncontours;
381 typedef struct FT_LibraryRec_
386 FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
387 FT_Get_Glyph(freetype_face->glyph, &glyph_image);
389 // check if the outline is ok (non-empty);
390 FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
391 if (bbox.xMin == 0 && bbox.xMax == 0 && bbox.yMin ==0 && bbox.yMax == 0)
393 FT_Done_Glyph(glyph_image);
394 glyph->data = new VFrame(0, 0, BC_A8,0);
395 glyph->data_stroke = new VFrame(0, 0, BC_A8,0);;
400 glyph->advance_w =((int)(freetype_face->glyph->advance.x +
401 plugin->config.stroke_width * 64)) >> 6;
404 #if FREETYPE_MAJOR > 2 || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 2)
405 FT_Stroker_New(freetype_library, &stroker);
407 FT_Stroker_New(((FT_LibraryRec *)freetype_library)->memory, &stroker);
409 FT_Stroker_Set(stroker, (int)(plugin->config.stroke_width * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
410 FT_Stroker_ParseOutline(stroker, &((FT_OutlineGlyph) glyph_image)->outline,1);
411 FT_Stroker_GetCounts(stroker,&npoints, &ncontours);
412 if (npoints ==0 && ncontours == 0)
414 // this never happens, but FreeType has a bug regarding Linotype's Palatino font
415 FT_Stroker_Done(stroker);
416 FT_Done_Glyph(glyph_image);
417 glyph->data = new VFrame(0, 0, BC_A8,0);
418 glyph->data_stroke = new VFrame(0, 0, BC_A8,0);;
423 glyph->advance_w =((int)(freetype_face->glyph->advance.x +
424 plugin->config.stroke_width * 64)) >> 6;
428 FT_Outline_New(freetype_library, npoints, ncontours, &outline);
430 outline.n_contours=0;
431 FT_Stroker_Export (stroker, &outline);
432 FT_Outline_Get_BBox(&outline, &bbox);
434 FT_Outline_Translate(&outline,
438 FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
440 - bbox.yMin + (int)(plugin->config.stroke_width*32));
441 // printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\nFill Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
442 // bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax,
443 // bbox_fill.xMin,bbox_fill.xMax, bbox_fill.yMin, bbox_fill.yMax);
445 glyph->width = bm.width = ((bbox.xMax - bbox.xMin) >> 6)+1;
446 glyph->height = bm.rows = ((bbox.yMax - bbox.yMin) >> 6) +1;
447 glyph->pitch = bm.pitch = bm.width;
448 bm.pixel_mode = FT_PIXEL_MODE_GRAY;
450 glyph->left = (bbox.xMin + 31) >> 6;
451 if (glyph->left < 0) glyph->left = 0;
452 glyph->top = (bbox.yMax + 31) >> 6;
453 glyph->freetype_index = gindex;
454 int real_advance = ((int)ceil((float)freetype_face->glyph->advance.x +
455 plugin->config.stroke_width * 64) >> 6);
456 glyph->advance_w = glyph->width + glyph->left;
457 if (real_advance > glyph->advance_w)
458 glyph->advance_w = real_advance;
459 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n",
460 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
463 //printf("GlyphUnit::process_package 1\n");
464 glyph->data = new VFrame(0,
469 glyph->data_stroke = new VFrame(0,
474 glyph->data->clear_frame();
475 glyph->data_stroke->clear_frame();
476 // for debugging memset( glyph->data_stroke->get_data(), 60, glyph->pitch * glyph->height);
477 bm.buffer=glyph->data->get_data();
478 FT_Outline_Get_Bitmap( freetype_library,
479 &((FT_OutlineGlyph) glyph_image)->outline,
481 bm.buffer=glyph->data_stroke->get_data();
482 FT_Outline_Get_Bitmap( freetype_library,
485 FT_Outline_Done(freetype_library,&outline);
486 FT_Stroker_Done(stroker);
487 FT_Done_Glyph(glyph_image);
489 //printf("GlyphUnit::process_package 2\n");
494 GlyphEngine::GlyphEngine(TitleMain *plugin, int cpus)
495 : LoadServer(cpus, cpus)
497 this->plugin = plugin;
499 void GlyphEngine::init_packages()
501 int current_package = 0;
502 for(int i = 0; i < plugin->glyphs.total; i++)
504 if(!plugin->glyphs.values[i]->data)
506 GlyphPackage *pkg = (GlyphPackage*)get_package(current_package++);
507 pkg->glyph = plugin->glyphs.values[i];
511 LoadClient* GlyphEngine::new_client()
513 return new GlyphUnit(plugin, this);
515 LoadPackage* GlyphEngine::new_package()
517 return new GlyphPackage;
532 // Copy a single character to the text mask
533 TitlePackage::TitlePackage()
539 TitleUnit::TitleUnit(TitleMain *plugin, TitleEngine *server)
542 this->plugin = plugin;
545 void TitleUnit::draw_glyph(VFrame *output, TitleGlyph *glyph, int x, int y)
547 int glyph_w = glyph->data->get_w();
548 int glyph_h = glyph->data->get_h();
549 int output_w = output->get_w();
550 int output_h = output->get_h();
551 unsigned char **in_rows = glyph->data->get_rows();
552 unsigned char **out_rows = output->get_rows();
554 //printf("TitleUnit::draw_glyph 1 %c %d %d\n", glyph->c, x, y);
555 for(int in_y = 0; in_y < glyph_h; in_y++)
557 // int y_out = y + plugin->ascent + in_y - glyph->top;
558 int y_out = y + plugin->get_char_height() + in_y - glyph->top;
560 //printf("TitleUnit::draw_glyph 1 %d\n", y_out);
561 if(y_out >= 0 && y_out < output_h)
563 unsigned char *out_row = out_rows[y_out];
564 unsigned char *in_row = in_rows[in_y];
565 for(int in_x = 0; in_x < glyph_w; in_x++)
567 int x_out = x + glyph->left + in_x;
568 if(x_out >= 0 && x_out < output_w)
571 out_row[x_out] = in_row[in_x];
572 //out_row[x_out] = 0xff;
580 void TitleUnit::process_package(LoadPackage *package)
582 TitlePackage *pkg = (TitlePackage*)package;
586 for(int i = 0; i < plugin->glyphs.total; i++)
588 TitleGlyph *glyph = plugin->glyphs.values[i];
589 if(glyph->c == pkg->c)
591 draw_glyph(plugin->text_mask, glyph, pkg->x, pkg->y);
592 if(plugin->config.stroke_width >= ZERO &&
593 (plugin->config.style & FONT_OUTLINE))
595 VFrame *tmp = glyph->data;
596 glyph->data = glyph->data_stroke;
597 draw_glyph(plugin->text_mask_stroke, glyph, pkg->x, pkg->y);
606 TitleEngine::TitleEngine(TitleMain *plugin, int cpus)
607 : LoadServer(cpus, cpus)
609 this->plugin = plugin;
612 void TitleEngine::init_packages()
614 //printf("TitleEngine::init_packages 1\n");
615 int visible_y1 = plugin->visible_row1 * plugin->get_char_height();
616 //printf("TitleEngine::init_packages 1\n");
617 int current_package = 0;
618 for(int i = plugin->visible_char1; i < plugin->visible_char2; i++)
620 title_char_position_t *char_position = plugin->char_positions + i;
621 TitlePackage *pkg = (TitlePackage*)get_package(current_package);
622 //printf("TitleEngine::init_packages 1\n");
623 pkg->x = char_position->x;
624 //printf("TitleEngine::init_packages 1\n");
625 pkg->y = char_position->y - visible_y1;
626 //printf("TitleEngine::init_packages 1\n");
627 pkg->c = plugin->config.text[i];
628 //printf("TitleEngine::init_packages 2\n");
633 LoadClient* TitleEngine::new_client()
635 return new TitleUnit(plugin, this);
638 LoadPackage* TitleEngine::new_package()
640 return new TitlePackage;
653 TitleTranslatePackage::TitleTranslatePackage()
659 TitleTranslateUnit::TitleTranslateUnit(TitleMain *plugin, TitleTranslate *server)
662 this->plugin = plugin;
667 #define TRANSLATE(type, max, components, r, g, b) \
669 unsigned char **in_rows = plugin->text_mask->get_rows(); \
670 type **out_rows = (type**)plugin->output->get_rows(); \
672 for(int i = pkg->y1; i < pkg->y2; i++) \
674 if(i + server->out_y1_int >= 0 && \
675 i + server->out_y1_int < server->output_h) \
678 float y_fraction1, y_fraction2; \
679 float y_output_fraction; \
680 in_y1 = server->y_table[i].in_x1; \
681 in_y2 = server->y_table[i].in_x2; \
682 y_fraction1 = server->y_table[i].in_fraction1; \
683 y_fraction2 = server->y_table[i].in_fraction2; \
684 y_output_fraction = server->y_table[i].output_fraction; \
685 unsigned char *in_row1 = in_rows[in_y1]; \
686 unsigned char *in_row2 = in_rows[in_y2]; \
687 type *out_row = out_rows[i + server->out_y1_int]; \
689 for(int j = server->out_x1_int; j < server->out_x2_int; j++) \
691 if(j >= 0 && j < server->output_w) \
697 float x_output_fraction; \
699 server->x_table[j - server->out_x1_int].in_x1; \
701 server->x_table[j - server->out_x1_int].in_x2; \
703 server->x_table[j - server->out_x1_int].in_fraction1; \
705 server->x_table[j - server->out_x1_int].in_fraction2; \
706 x_output_fraction = \
707 server->x_table[j - server->out_x1_int].output_fraction; \
709 float fraction1 = x_fraction1 * y_fraction1; \
710 float fraction2 = x_fraction2 * y_fraction1; \
711 float fraction3 = x_fraction1 * y_fraction2; \
712 float fraction4 = x_fraction2 * y_fraction2; \
713 int input = (int)(in_row1[in_x1] * fraction1 + \
714 in_row1[in_x2] * fraction2 + \
715 in_row2[in_x1] * fraction3 + \
716 in_row2[in_x2] * fraction4 + 0.5); \
717 input *= plugin->alpha; \
718 /* Alpha is 0 - 256 */ \
721 int anti_input = 0xff - input; \
722 if(components == 4) \
724 out_row[j * components + 0] = \
725 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
726 out_row[j * components + 1] = \
727 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
728 out_row[j * components + 2] = \
729 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
731 out_row[j * components + 3] = \
732 MAX((input << 8) | input, out_row[j * components + 3]); \
734 out_row[j * components + 3] = \
735 MAX(input, out_row[j * components + 3]); \
739 out_row[j * components + 0] = \
740 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
741 out_row[j * components + 1] = \
742 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
743 out_row[j * components + 2] = \
744 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
753 #define TRANSLATEA(type, max, components, r, g, b) \
755 unsigned char **in_rows = plugin->text_mask->get_rows(); \
756 type **out_rows = (type**)plugin->output->get_rows(); \
758 for(int i = pkg->y1; i < pkg->y2; i++) \
760 if(i + server->out_y1_int >= 0 && \
761 i + server->out_y1_int < server->output_h) \
763 unsigned char *in_row = in_rows[i]; \
764 type *out_row = out_rows[i + server->out_y1_int]; \
766 for(int j = server->out_x1; j < server->out_x2_int; j++) \
769 j < server->output_w) \
771 int input = (int)(in_row[j - server->out_x1]); \
773 input *= plugin->alpha; \
774 /* Alpha is 0 - 256 */ \
777 int anti_input = 0xff - input; \
778 if(components == 4) \
780 out_row[j * components + 0] = \
781 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
782 out_row[j * components + 1] = \
783 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
784 out_row[j * components + 2] = \
785 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
787 out_row[j * components + 3] = \
788 MAX((input << 8) | input, out_row[j * components + 3]); \
790 out_row[j * components + 3] = \
791 MAX(input, out_row[j * components + 3]); \
795 out_row[j * components + 0] = \
796 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
797 out_row[j * components + 1] = \
798 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
799 out_row[j * components + 2] = \
800 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
810 void TitleTranslateUnit::process_package(LoadPackage *package)
812 TitleTranslatePackage *pkg = (TitleTranslatePackage*)package;
813 TitleTranslate *server = (TitleTranslate*)this->server;
814 int r_in, g_in, b_in;
816 //printf("TitleTranslateUnit::process_package 1 %d %d\n", pkg->y1, pkg->y2);
818 r_in = (plugin->config.color & 0xff0000) >> 16;
819 g_in = (plugin->config.color & 0xff00) >> 8;
820 b_in = plugin->config.color & 0xff;
821 switch(plugin->output->get_color_model())
825 TRANSLATE(unsigned char, 0xff, 3, r_in, g_in, b_in);
831 r = (float)r_in / 0xff;
832 g = (float)g_in / 0xff;
833 b = (float)b_in / 0xff;
834 TRANSLATE(float, 0xff, 3, r, g, b);
839 unsigned char y, u, v;
840 yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
841 TRANSLATE(unsigned char, 0xff, 3, y, u, v);
847 r = (r_in << 8) | r_in;
848 g = (g_in << 8) | g_in;
849 b = (b_in << 8) | b_in;
850 TRANSLATE(uint16_t, 0xffff, 3, r, g, b);
863 TRANSLATE(uint16_t, 0xffff, 3, y, u, v);
869 r = (float)r_in / 0xff;
870 g = (float)g_in / 0xff;
871 b = (float)b_in / 0xff;
872 TRANSLATE(float, 0xff, 4, r, g, b);
877 TRANSLATE(unsigned char, 0xff, 4, r_in, g_in, b_in);
882 unsigned char y, u, v;
883 yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
884 TRANSLATE(unsigned char, 0xff, 4, y, u, v);
887 case BC_RGBA16161616:
890 r = (r_in << 8) | r_in;
891 g = (g_in << 8) | g_in;
892 b = (b_in << 8) | b_in;
893 TRANSLATE(uint16_t, 0xffff, 4, r, g, b);
896 case BC_YUVA16161616:
906 TRANSLATE(uint16_t, 0xffff, 4, y, u, v);
911 //printf("TitleTranslateUnit::process_package 5\n");
917 TitleTranslate::TitleTranslate(TitleMain *plugin, int cpus)
920 this->plugin = plugin;
921 x_table = y_table = 0;
924 TitleTranslate::~TitleTranslate()
926 if(x_table) delete [] x_table;
927 if(y_table) delete [] y_table;
930 void TitleTranslate::init_packages()
932 //printf("TitleTranslate::init_packages 1\n");
933 // Generate scaling tables
934 if(x_table) delete [] x_table;
935 if(y_table) delete [] y_table;
936 //printf("TitleTranslate::init_packages 1\n");
938 output_w = plugin->output->get_w();
939 output_h = plugin->output->get_h();
940 //printf("TitleTranslate::init_packages 1 %f %d\n", plugin->text_x1, plugin->text_w);
943 TranslateUnit::translation_array_f(x_table,
945 plugin->text_x1 + plugin->text_w,
952 //printf("TitleTranslate::init_packages 1 %f %f\n", plugin->mask_y1, plugin->mask_y2);
954 TranslateUnit::translation_array_f(y_table,
956 plugin->mask_y1 + plugin->text_mask->get_h(),
958 plugin->text_mask->get_h(),
959 plugin->text_mask->get_h(),
964 //printf("TitleTranslate::init_packages 1\n");
971 int increment = (out_y2 - out_y1) / get_total_packages() + 1;
973 //printf("TitleTranslate::init_packages 1 %d %d %d %d\n",
974 // out_y1, out_y2, out_y1_int, out_y2_int);
975 for(int i = 0; i < get_total_packages(); i++)
977 TitleTranslatePackage *pkg = (TitleTranslatePackage*)get_package(i);
978 pkg->y1 = i * increment;
979 pkg->y2 = i * increment + increment;
980 if(pkg->y1 > out_y2 - out_y1)
981 pkg->y1 = out_y2 - out_y1;
982 if(pkg->y2 > out_y2 - out_y1)
983 pkg->y2 = out_y2 - out_y1;
985 //printf("TitleTranslate::init_packages 2\n");
988 LoadClient* TitleTranslate::new_client()
990 return new TitleTranslateUnit(plugin, this);
993 LoadPackage* TitleTranslate::new_package()
995 return new TitleTranslatePackage;
1012 ArrayList<FontEntry*>* TitleMain::fonts = 0;
1016 TitleMain::TitleMain(PluginServer *server)
1017 : PluginVClient(server)
1019 PLUGIN_CONSTRUCTOR_MACRO
1021 // Build font database
1024 text_mask_stroke = 0;
1027 freetype_library = 0;
1032 need_reconfigure = 1;
1035 TitleMain::~TitleMain()
1037 PLUGIN_DESTRUCTOR_MACRO
1038 if(text_mask) delete text_mask;
1039 if(text_mask_stroke) delete text_mask_stroke;
1040 if(char_positions) delete [] char_positions;
1041 if(rows_bottom) delete [] rows_bottom;
1043 if(glyph_engine) delete glyph_engine;
1044 if(title_engine) delete title_engine;
1045 if(freetype_library) FT_Done_FreeType(freetype_library);
1046 if(translate) delete translate;
1049 char* TitleMain::plugin_title() { return N_("Title"); }
1050 int TitleMain::is_realtime() { return 1; }
1051 int TitleMain::is_synthesis() { return 1; }
1053 VFrame* TitleMain::new_picon()
1055 return new VFrame(picon_png);
1059 void TitleMain::build_fonts()
1063 fonts = new ArrayList<FontEntry*>;
1064 // Construct path from location of the plugin
1065 char search_path[BCTEXTLEN];
1066 strcpy(search_path, PluginClient::get_path());
1067 char *ptr = strrchr(search_path, '/');
1068 strcpy(ptr + 1, FONT_SEARCHPATH);
1069 char command_line[BCTEXTLEN];
1071 sprintf(command_line,
1072 "find %s -name 'fonts.dir' -print -exec cat {} \\;",
1074 //printf("TitleMain::build_fonts %s\n", command_line);
1076 FILE *in = popen(command_line, "r");
1079 char current_dir[BCTEXTLEN];
1080 FT_Library freetype_library = 0; // Freetype library
1081 FT_Face freetype_face = 0;
1083 // FT_Init_FreeType(&freetype_library);
1088 char string[BCTEXTLEN], string2[BCTEXTLEN];
1089 fgets(string, BCTEXTLEN, in);
1090 if(!strlen(string)) break;
1092 char *in_ptr = string;
1095 // Get current directory
1097 if(string[0] == '/')
1099 out_ptr = current_dir;
1100 while(*in_ptr != 0 && *in_ptr != 0xa)
1101 *out_ptr++ = *in_ptr++;
1103 while(*out_ptr != '/')
1110 //printf("TitleMain::build_fonts %s\n", string);
1111 FontEntry *entry = new FontEntry;
1116 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa)
1118 *out_ptr++ = *in_ptr++;
1121 if(string2[0] == '/')
1123 entry->path = new char[strlen(string2) + 1];
1124 sprintf(entry->path, "%s", string2);
1128 entry->path = new char[strlen(current_dir) + strlen(string2) + 1];
1129 sprintf(entry->path, "%s%s", current_dir, string2);
1133 // Test path existence
1134 struct stat test_stat;
1135 if(stat(entry->path, &test_stat))
1139 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1142 while(*in_ptr != 0 && *in_ptr != 0xa && (*in_ptr == ' ' || *in_ptr == '-'))
1146 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa && *in_ptr != '-')
1148 *out_ptr++ = *in_ptr++;
1151 entry->foundary = new char[strlen(string2) + 1];
1152 strcpy(entry->foundary, string2);
1153 if(*in_ptr == '-') in_ptr++;
1158 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1160 *out_ptr++ = *in_ptr++;
1163 entry->family = new char[strlen(string2) + 1];
1164 strcpy(entry->family, string2);
1165 if(*in_ptr == '-') in_ptr++;
1169 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1171 *out_ptr++ = *in_ptr++;
1174 entry->weight = new char[strlen(string2) + 1];
1175 strcpy(entry->weight, string2);
1176 if(*in_ptr == '-') in_ptr++;
1180 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1182 *out_ptr++ = *in_ptr++;
1185 entry->slant = new char[strlen(string2) + 1];
1186 strcpy(entry->slant, string2);
1187 if(*in_ptr == '-') in_ptr++;
1191 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1193 *out_ptr++ = *in_ptr++;
1196 entry->swidth = new char[strlen(string2) + 1];
1197 strcpy(entry->swidth, string2);
1198 if(*in_ptr == '-') in_ptr++;
1202 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1204 *out_ptr++ = *in_ptr++;
1207 entry->adstyle = new char[strlen(string2) + 1];
1208 strcpy(entry->adstyle, string2);
1209 if(*in_ptr == '-') in_ptr++;
1213 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1215 *out_ptr++ = *in_ptr++;
1218 entry->pixelsize = atol(string2);
1219 if(*in_ptr == '-') in_ptr++;
1223 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1225 *out_ptr++ = *in_ptr++;
1228 entry->pointsize = atol(string2);
1229 if(*in_ptr == '-') in_ptr++;
1233 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1235 *out_ptr++ = *in_ptr++;
1238 entry->xres = atol(string2);
1239 if(*in_ptr == '-') in_ptr++;
1243 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1245 *out_ptr++ = *in_ptr++;
1248 entry->yres = atol(string2);
1249 if(*in_ptr == '-') in_ptr++;
1253 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1255 *out_ptr++ = *in_ptr++;
1258 entry->spacing = new char[strlen(string2) + 1];
1259 strcpy(entry->spacing, string2);
1260 if(*in_ptr == '-') in_ptr++;
1264 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1266 *out_ptr++ = *in_ptr++;
1269 entry->avg_width = atol(string2);
1270 if(*in_ptr == '-') in_ptr++;
1274 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1276 *out_ptr++ = *in_ptr++;
1279 entry->registry = new char[strlen(string2) + 1];
1280 strcpy(entry->registry, string2);
1281 if(*in_ptr == '-') in_ptr++;
1285 while(*in_ptr != 0 && *in_ptr != 0xa)
1287 *out_ptr++ = *in_ptr++;
1290 entry->encoding = new char[strlen(string2) + 1];
1291 strcpy(entry->encoding, string2);
1296 if(strlen(entry->foundary) && !result)
1298 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1299 // This takes a real long time to do. Instead just take all fonts
1300 // if(!load_freetype_face(freetype_library,
1304 if(entry->family[0])
1307 sprintf(string, "%s (%s)", entry->family, entry->foundary);
1308 entry->fixed_title = new char[strlen(string) + 1];
1309 strcpy(entry->fixed_title, string);
1311 if(!strcasecmp(entry->weight, "demibold") ||
1312 !strcasecmp(entry->weight, "bold"))
1313 entry->fixed_style |= FONT_BOLD;
1314 if(!strcasecmp(entry->slant, "i") ||
1315 !strcasecmp(entry->slant, "o"))
1316 entry->fixed_style |= FONT_ITALIC;
1317 fonts->append(entry);
1318 // printf("TitleMain::build_fonts %s: success\n",
1320 //printf("TitleMain::build_fonts 2\n");
1324 // printf("TitleMain::build_fonts %s: FT_New_Face failed\n",
1326 //printf("TitleMain::build_fonts 3\n");
1338 if(freetype_library) FT_Done_FreeType(freetype_library);
1342 // for(int i = 0; i < fonts->total; i++)
1343 // fonts->values[i]->dump();
1348 int TitleMain::load_freetype_face(FT_Library &freetype_library,
1349 FT_Face &freetype_face,
1352 //printf("TitleMain::load_freetype_face 1\n");
1353 if(!freetype_library) FT_Init_FreeType(&freetype_library);
1354 if(freetype_face) FT_Done_Face(freetype_face);
1356 //printf("TitleMain::load_freetype_face 2\n");
1358 // Use freetype's internal function for loading font
1359 if(FT_New_Face(freetype_library,
1364 fprintf(stderr, _("TitleMain::load_freetype_face %s failed.\n"));
1365 FT_Done_FreeType(freetype_library);
1367 freetype_library = 0;
1374 //printf("TitleMain::load_freetype_face 4\n");
1377 FontEntry* TitleMain::get_font_entry(char *title,
1381 //printf("TitleMain::get_font_entry %s %d %d\n", title, style, size);
1382 FontEntry *result = 0;
1385 for(int i = 0; i < fonts->total; i++)
1387 FontEntry *entry = fonts->values[i];
1389 if(!result) result = entry;
1391 if(!strcmp(title, entry->fixed_title))
1393 if(!got_title) result = entry;
1396 // Not every font has a size but every font has a style
1397 if(entry->fixed_style == style)
1400 if(entry->fixed_style == style && entry->pointsize == size)
1410 FontEntry* TitleMain::get_font()
1412 return get_font_entry(config.font,
1422 int TitleMain::get_char_height()
1424 // this is height above the zero line, but does not include characters that go below
1425 int result = config.size;
1426 if((config.style & FONT_OUTLINE)) result += (int)ceil(config.stroke_width * 2);
1430 int TitleMain::get_char_advance(int current, int next)
1434 TitleGlyph *current_glyph = 0;
1435 TitleGlyph *next_glyph = 0;
1437 if(current == 0xa) return 0;
1439 for(int i = 0; i < glyphs.total; i++)
1441 if(glyphs.values[i]->c == current)
1443 current_glyph = glyphs.values[i];
1448 for(int i = 0; i < glyphs.total; i++)
1450 if(glyphs.values[i]->c == next)
1452 next_glyph = glyphs.values[i];
1458 result = current_glyph->advance_w;
1460 //printf("TitleMain::get_char_advance 1 %c %c %p %p\n", current, next, current_glyph, next_glyph);
1462 FT_Get_Kerning(freetype_face,
1463 current_glyph->freetype_index,
1464 next_glyph->freetype_index,
1469 //printf("TitleMain::get_char_advance 2 %d %d\n", result, kerning.x);
1471 return result + (kerning.x >> 6);
1475 void TitleMain::draw_glyphs()
1477 // Build table of all glyphs needed
1478 int text_len = strlen(config.text);
1479 int total_packages = 0;
1481 cd = iconv_open ("UCS-4", config.encoding);
1482 if (cd == (iconv_t) -1)
1484 /* Something went wrong. */
1485 fprintf (stderr, _("Iconv conversion from %s to Unicode UCS-4 not available\n"),config.encoding);
1488 for(int i = 0; i < text_len; i++)
1491 int c = config.text[i];
1493 /* if iconv is working ok for current encoding */
1494 if (cd != (iconv_t) -1)
1497 size_t inbytes,outbytes;
1499 char *inp = (char*)&inbuf, *outp = (char *)&char_code;
1505 iconv (cd, &inp, &inbytes, &outp, &outbytes);
1506 #if __BYTE_ORDER == __LITTLE_ENDIAN
1507 char_code = bswap_32(char_code);
1508 #endif /* Big endian. */
1514 for(int j = 0; j < glyphs.total; j++)
1516 if(glyphs.values[j]->char_code == char_code)
1526 //printf("TitleMain::draw_glyphs 1\n");
1527 TitleGlyph *glyph = new TitleGlyph;
1528 //printf("TitleMain::draw_glyphs 2\n");
1529 glyphs.append(glyph);
1531 glyph->char_code = char_code;
1537 glyph_engine = new GlyphEngine(this, PluginClient::smp + 1);
1539 glyph_engine->set_package_count(total_packages);
1540 //printf("TitleMain::draw_glyphs 3 %d\n", glyphs.total);
1541 glyph_engine->process_packages();
1542 //printf("TitleMain::draw_glyphs 4\n");
1545 void TitleMain::get_total_extents()
1547 // Determine extents of total text
1550 text_len = strlen(config.text);
1551 if(!char_positions) char_positions = new title_char_position_t[text_len];
1556 for(int i = 0; i < glyphs.total; i++)
1557 if(glyphs.values[i]->top > ascent) ascent = glyphs.values[i]->top;
1558 //printf("TitleMain::get_total_extents %d\n", ascent);
1560 // get the number of rows first
1561 for(int i = 0; i < text_len; i++)
1563 if(config.text[i] == 0xa || i == text_len - 1)
1568 if (!rows_bottom) rows_bottom = new int[text_rows+1];
1572 for(int i = 0; i < text_len; i++)
1574 char_positions[i].x = current_w;
1575 char_positions[i].y = text_rows * get_char_height();
1576 char_positions[i].w = get_char_advance(config.text[i], config.text[i + 1]);
1577 TitleGlyph *current_glyph = 0;
1578 for(int j = 0; j < glyphs.total; j++)
1580 if(glyphs.values[j]->c == config.text[i])
1582 current_glyph = glyphs.values[j];
1586 int current_bottom = current_glyph->top - current_glyph->height;
1587 if (current_bottom < rows_bottom[text_rows])
1588 rows_bottom[text_rows] = current_bottom ;
1590 // printf("TitleMain::get_total_extents 1 %c %d %d %d\n",
1592 // char_positions[i].x,
1593 // char_positions[i].y,
1594 // char_positions[i].w);
1595 current_w += char_positions[i].w;
1597 if(config.text[i] == 0xa || i == text_len - 1)
1600 rows_bottom[text_rows] = 0;
1601 if(current_w > text_w) text_w = current_w;
1605 text_w += config.dropshadow;
1606 text_h = text_rows * get_char_height();
1607 text_h += config.dropshadow;
1609 // Now that text_w is known
1610 // Justify rows based on configuration
1612 for(int i = 0; i < text_len; i++)
1614 if(config.text[i] == 0xa || i == text_len - 1)
1616 for(int j = row_start; j <= i; j++)
1618 switch(config.hjustification)
1624 char_positions[j].x += (text_w -
1625 char_positions[i].x -
1626 char_positions[i].w) /
1631 char_positions[j].x += (text_w -
1632 char_positions[i].x -
1633 char_positions[i].w);
1642 //printf("TitleMain::get_total_extents 2 %d %d\n", text_w, text_h);
1645 int TitleMain::draw_mask()
1647 int old_visible_row1 = visible_row1;
1648 int old_visible_row2 = visible_row2;
1651 // Determine y of visible text
1652 if(config.motion_strategy == BOTTOM_TO_TOP)
1654 // printf("TitleMain::draw_mask 1 %d %lld %lld %lld\n",
1655 // config.motion_strategy,
1656 // get_source_position(),
1657 // get_source_start(),
1658 // config.prev_keyframe_position);
1659 float magnitude = config.pixels_per_second *
1660 (get_source_position() - config.prev_keyframe_position) /
1661 PluginVClient::project_frame_rate;
1664 int loop_size = text_h + input->get_h();
1665 magnitude -= (int)(magnitude / loop_size) * loop_size;
1667 text_y1 = config.y + input->get_h() - magnitude;
1670 if(config.motion_strategy == TOP_TO_BOTTOM)
1672 float magnitude = config.pixels_per_second *
1673 (get_source_position() - config.prev_keyframe_position) /
1674 PluginVClient::project_frame_rate;
1677 int loop_size = text_h + input->get_h();
1678 magnitude -= (int)(magnitude / loop_size) * loop_size;
1680 text_y1 = config.y + magnitude;
1684 if(config.vjustification == JUSTIFY_TOP)
1689 if(config.vjustification == JUSTIFY_MID)
1691 text_y1 = config.y + input->get_h() / 2 - text_h / 2;
1694 if(config.vjustification == JUSTIFY_BOTTOM)
1696 text_y1 = config.y + input->get_h() - text_h;
1699 text_y2 = text_y1 + text_h + 0.5;
1701 // Determine x of visible text
1702 if(config.motion_strategy == RIGHT_TO_LEFT)
1704 float magnitude = config.pixels_per_second *
1705 (get_source_position() - config.prev_keyframe_position) /
1706 PluginVClient::project_frame_rate;
1709 int loop_size = text_w + input->get_w();
1710 magnitude -= (int)(magnitude / loop_size) * loop_size;
1712 text_x1 = config.x + (float)input->get_w() - magnitude;
1715 if(config.motion_strategy == LEFT_TO_RIGHT)
1717 float magnitude = config.pixels_per_second *
1718 (get_source_position() - config.prev_keyframe_position) /
1719 PluginVClient::project_frame_rate;
1722 int loop_size = text_w + input->get_w();
1723 magnitude -= (int)(magnitude / loop_size) * loop_size;
1725 text_x1 = config.x + -(float)text_w + magnitude;
1728 if(config.hjustification == JUSTIFY_LEFT)
1733 if(config.hjustification == JUSTIFY_MID)
1735 text_x1 = config.x + input->get_w() / 2 - text_w / 2;
1738 if(config.hjustification == JUSTIFY_RIGHT)
1740 text_x1 = config.x + input->get_w() - text_w;
1747 // Determine y extents just of visible text
1748 visible_row1 = (int)(-text_y1 / get_char_height());
1749 if(visible_row1 < 0) visible_row1 = 0;
1751 visible_row2 = (int)((float)text_rows - (text_y2 - input->get_h()) / get_char_height() + 1);
1752 if(visible_row2 > text_rows) visible_row2 = text_rows;
1755 if(visible_row2 <= visible_row1) return 1;
1758 mask_y1 = text_y1 + visible_row1 * get_char_height();
1759 mask_y2 = text_y1 + visible_row2 * get_char_height();
1760 text_x1 += config.x;
1763 //printf("TitleMain::draw_mask %d %d\n", visible_row1, visible_row2);
1764 visible_char1 = visible_char2 = 0;
1766 for(int i = 0; i < text_len; i++)
1768 title_char_position_t *char_position = char_positions + i;
1769 int char_row = char_position->y / get_char_height();
1770 if(char_row >= visible_row1 &&
1771 char_row < visible_row2)
1786 int visible_rows = visible_row2 - visible_row1;
1787 int need_redraw = 0;
1789 (text_mask->get_w() != text_w ||
1790 text_mask->get_h() != visible_rows * get_char_height() - rows_bottom[visible_row2 - 1]))
1793 delete text_mask_stroke;
1795 text_mask_stroke = 0;
1800 text_mask = new VFrame(0,
1802 visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1804 text_mask_stroke = new VFrame(0,
1806 visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1812 //printf("TitleMain::draw_mask %d %d\n", text_w, visible_rows * get_char_height());
1815 //printf("TitleMain::draw_mask 1\n");
1816 // Draw on text mask if different
1817 if(old_visible_row1 != visible_row1 ||
1818 old_visible_row2 != visible_row2 ||
1821 //printf("TitleMain::draw_mask 2\n");
1822 text_mask->clear_frame();
1823 text_mask_stroke->clear_frame();
1826 //printf("TitleMain::draw_mask 2\n");
1828 title_engine = new TitleEngine(this, PluginClient::smp + 1);
1829 //printf("TitleMain::draw_mask 2\n");
1831 title_engine->set_package_count(visible_char2 - visible_char1);
1832 //printf("TitleMain::draw_mask 2\n");
1833 title_engine->process_packages();
1834 //printf("TitleMain::draw_mask 3\n");
1841 void TitleMain::overlay_mask()
1844 //printf("TitleMain::overlay_mask 1\n");
1846 if(!EQUIV(config.fade_in, 0))
1848 int fade_len = (int)(config.fade_in * PluginVClient::project_frame_rate);
1849 int fade_position = get_source_position() -
1850 config.prev_keyframe_position;
1853 if(fade_position >= 0 && fade_position < fade_len)
1855 alpha = (int)((float)0x100 *
1860 //printf("TitleMain::overlay_mask 1\n");
1862 if(!EQUIV(config.fade_out, 0))
1864 int fade_len = (int)(config.fade_out *
1865 PluginVClient::project_frame_rate);
1866 int fade_position = config.next_keyframe_position -
1867 get_source_position();
1870 if(fade_position > 0 && fade_position < fade_len)
1872 alpha = (int)((float)0x100 *
1877 //printf("TitleMain::overlay_mask 1\n");
1879 if(config.dropshadow)
1881 text_x1 += config.dropshadow;
1882 text_x2 += config.dropshadow;
1883 mask_y1 += config.dropshadow;
1884 mask_y2 += config.dropshadow;
1885 if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1886 mask_y1 < input->get_h() && mask_y2 > 0)
1888 if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1889 // Do 2 passes if dropshadow.
1890 int temp_color = config.color;
1892 translate->process_packages();
1893 config.color = temp_color;
1895 text_x1 -= config.dropshadow;
1896 text_x2 -= config.dropshadow;
1897 mask_y1 -= config.dropshadow;
1898 mask_y2 -= config.dropshadow;
1900 //printf("TitleMain::overlay_mask 1\n");
1902 if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1903 mask_y1 < input->get_h() && mask_y2 > 0)
1905 if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1906 translate->process_packages();
1907 if (config.stroke_width >= ZERO &&
1908 (config.style & FONT_OUTLINE))
1910 int temp_color = config.color;
1911 VFrame *tmp_text_mask = this->text_mask;
1912 config.color = config.color_stroke;
1913 this->text_mask = this->text_mask_stroke;
1915 translate->process_packages();
1916 config.color = temp_color;
1917 this->text_mask = tmp_text_mask;
1920 //printf("TitleMain::overlay_mask 200\n");
1923 void TitleMain::clear_glyphs()
1925 //printf("TitleMain::clear_glyphs 1\n");
1926 glyphs.remove_all_objects();
1929 char* TitleMain::motion_to_text(int motion)
1933 case NO_MOTION: return _("No motion"); break;
1934 case BOTTOM_TO_TOP: return _("Bottom to top"); break;
1935 case TOP_TO_BOTTOM: return _("Top to bottom"); break;
1936 case RIGHT_TO_LEFT: return _("Right to left"); break;
1937 case LEFT_TO_RIGHT: return _("Left to right"); break;
1941 int TitleMain::text_to_motion(char *text)
1943 for(int i = 0; i < TOTAL_PATHS; i++)
1945 if(!strcasecmp(motion_to_text(i), text)) return i;
1956 int TitleMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
1960 output = output_ptr;
1962 need_reconfigure |= load_configuration();
1963 //printf("TitleMain::process_realtime 1\n");
1966 // Always synthesize text and redraw it for timecode
1969 int64_t rendered_frame = get_source_position();
1970 if (get_direction() == PLAY_REVERSE)
1971 rendered_frame -= 1;
1973 int tcf = Units::timeformat_totype(config.timecodeformat);
1976 strcpy(config.timecodeformat, DEFAULT_TIMECODEFORMAT);
1978 Units::totext(config.text,
1979 (double)rendered_frame / PluginVClient::project_frame_rate,
1982 PluginVClient::project_frame_rate,
1984 need_reconfigure = 1;
1988 if(config.size <= 0 || config.size >= 2048) config.size = 72;
1989 if(config.stroke_width < 0 ||
1990 config.stroke_width >= 512) config.stroke_width = 0.0;
1991 if(!strlen(config.text)) return 0;
1992 if(!strlen(config.encoding)) strcpy(config.encoding, DEFAULT_ENCODING);
1994 //printf("TitleMain::process_realtime 10\n");
1996 // Handle reconfiguration
1997 if(need_reconfigure)
1999 //printf("TitleMain::process_realtime 2\n");
2000 if(text_mask) delete text_mask;
2001 if(text_mask_stroke) delete text_mask_stroke;
2003 text_mask_stroke = 0;
2004 //printf("TitleMain::process_realtime 2\n");
2005 if(freetype_face) FT_Done_Face(freetype_face);
2007 //printf("TitleMain::process_realtime 2\n");
2008 if(glyph_engine) delete glyph_engine;
2010 //printf("TitleMain::process_realtime 2\n");
2011 if(char_positions) delete [] char_positions;
2013 if(rows_bottom) delete [] rows_bottom;
2015 //printf("TitleMain::process_realtime 2\n");
2017 //printf("TitleMain::process_realtime 2\n");
2022 if(!freetype_library)
2023 FT_Init_FreeType(&freetype_library);
2025 //printf("TitleMain::process_realtime 2\n");
2028 FontEntry *font = get_font();
2029 //printf("TitleMain::process_realtime 2.1 %s\n", font->path);
2030 if(load_freetype_face(freetype_library,
2034 printf("TitleMain::process_realtime %s: FT_New_Face failed.\n",
2038 //printf("TitleMain::process_realtime 2.2\n");
2040 if(!result) FT_Set_Pixel_Sizes(freetype_face, config.size, 0);
2041 //printf("TitleMain::process_realtime 2.3\n");
2044 //printf("TitleMain::process_realtime 3\n");
2049 //printf("TitleMain::process_realtime 4\n");
2050 get_total_extents();
2051 //printf("TitleMain::process_realtime 5\n");
2052 need_reconfigure = 0;
2058 //printf("TitleMain::process_realtime 4\n");
2059 // Determine region of text visible on the output and draw mask
2060 result = draw_mask();
2062 //printf("TitleMain::process_realtime 50\n");
2065 // Overlay mask on output
2070 //printf("TitleMain::process_realtime 60 %d\n", glyphs.total);
2075 int TitleMain::show_gui()
2077 load_configuration();
2078 thread = new TitleThread(this);
2083 int TitleMain::set_string()
2085 if(thread) thread->window->set_title(gui_string);
2089 void TitleMain::raise_window()
2093 thread->window->raise_window();
2094 thread->window->flush();
2098 void TitleMain::update_gui()
2102 int reconfigure = load_configuration();
2105 thread->window->lock_window();
2106 thread->window->update();
2107 thread->window->unlock_window();
2108 thread->window->color_thread->update_gui(config.color, 0);
2114 int TitleMain::load_defaults()
2116 char directory[1024], text_path[1024];
2117 // set the default directory
2118 sprintf(directory, "%stitle.rc", BCASTDIR);
2120 // load the defaults
2121 defaults = new BC_Hash(directory);
2124 defaults->get("FONT", config.font);
2125 defaults->get("ENCODING", config.encoding);
2126 config.style = defaults->get("STYLE", (int64_t)config.style);
2127 config.size = defaults->get("SIZE", config.size);
2128 config.color = defaults->get("COLOR", config.color);
2129 config.color_stroke = defaults->get("COLOR_STROKE", config.color_stroke);
2130 config.stroke_width = defaults->get("STROKE_WIDTH", config.stroke_width);
2131 config.motion_strategy = defaults->get("MOTION_STRATEGY", config.motion_strategy);
2132 config.loop = defaults->get("LOOP", config.loop);
2133 config.pixels_per_second = defaults->get("PIXELS_PER_SECOND", config.pixels_per_second);
2134 config.hjustification = defaults->get("HJUSTIFICATION", config.hjustification);
2135 config.vjustification = defaults->get("VJUSTIFICATION", config.vjustification);
2136 config.fade_in = defaults->get("FADE_IN", config.fade_in);
2137 config.fade_out = defaults->get("FADE_OUT", config.fade_out);
2138 config.x = defaults->get("TITLE_X", config.x);
2139 config.y = defaults->get("TITLE_Y", config.y);
2140 config.dropshadow = defaults->get("DROPSHADOW", config.dropshadow);
2141 config.timecode = defaults->get("TIMECODE", config.timecode);
2142 defaults->get("TIMECODEFORMAT", config.timecodeformat);
2143 window_w = defaults->get("WINDOW_W", 660);
2144 window_h = defaults->get("WINDOW_H", 480);
2146 // Store text in separate path to isolate special characters
2148 sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2149 fs.complete_path(text_path);
2150 FILE *fd = fopen(text_path, "rb");
2153 fseek(fd, 0, SEEK_END);
2154 int64_t len = ftell(fd);
2155 fseek(fd, 0, SEEK_SET);
2156 fread(config.text, len, 1, fd);
2157 config.text[len] = 0;
2158 //printf("TitleMain::load_defaults %s\n", config.text);
2166 int TitleMain::save_defaults()
2168 char text_path[1024];
2170 defaults->update("FONT", config.font);
2171 defaults->update("ENCODING", config.encoding);
2172 defaults->update("STYLE", (int64_t)config.style);
2173 defaults->update("SIZE", config.size);
2174 defaults->update("COLOR", config.color);
2175 defaults->update("COLOR_STROKE", config.color_stroke);
2176 defaults->update("STROKE_WIDTH", config.stroke_width);
2177 defaults->update("MOTION_STRATEGY", config.motion_strategy);
2178 defaults->update("LOOP", config.loop);
2179 defaults->update("PIXELS_PER_SECOND", config.pixels_per_second);
2180 defaults->update("HJUSTIFICATION", config.hjustification);
2181 defaults->update("VJUSTIFICATION", config.vjustification);
2182 defaults->update("FADE_IN", config.fade_in);
2183 defaults->update("FADE_OUT", config.fade_out);
2184 defaults->update("TITLE_X", config.x);
2185 defaults->update("TITLE_Y", config.y);
2186 defaults->update("DROPSHADOW", config.dropshadow);
2187 defaults->update("TIMECODE", config.timecode);
2188 defaults->update("TIMECODEFORMAT", config.timecodeformat);
2189 defaults->update("WINDOW_W", window_w);
2190 defaults->update("WINDOW_H", window_h);
2193 // Store text in separate path to isolate special characters
2195 sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2196 fs.complete_path(text_path);
2197 FILE *fd = fopen(text_path, "wb");
2200 fwrite(config.text, strlen(config.text), 1, fd);
2204 // perror("TitleMain::save_defaults");
2211 int TitleMain::load_configuration()
2213 KeyFrame *prev_keyframe, *next_keyframe;
2214 prev_keyframe = get_prev_keyframe(get_source_position());
2215 next_keyframe = get_next_keyframe(get_source_position());
2217 // printf("TitleMain::load_configuration 1 %d %d\n",
2218 // prev_keyframe->position,
2219 // next_keyframe->position);
2221 TitleConfig old_config, prev_config, next_config;
2222 old_config.copy_from(config);
2223 read_data(prev_keyframe);
2224 prev_config.copy_from(config);
2225 read_data(next_keyframe);
2226 next_config.copy_from(config);
2228 config.prev_keyframe_position = prev_keyframe->position;
2229 config.next_keyframe_position = next_keyframe->position;
2231 // if no previous keyframe exists, it should be start of the plugin, not start of the track
2232 if(config.next_keyframe_position == config.prev_keyframe_position)
2233 config.next_keyframe_position = get_source_start() + get_total_len();
2234 if (config.prev_keyframe_position == 0)
2235 config.prev_keyframe_position = get_source_start();
2238 // printf("TitleMain::load_configuration 10 %d %d\n",
2239 // config.prev_keyframe_position,
2240 // config.next_keyframe_position);
2242 config.interpolate(prev_config,
2244 (next_keyframe->position == prev_keyframe->position) ?
2245 get_source_position() :
2246 prev_keyframe->position,
2247 (next_keyframe->position == prev_keyframe->position) ?
2248 get_source_position() + 1 :
2249 next_keyframe->position,
2250 get_source_position());
2252 if(!config.equivalent(old_config))
2269 void TitleMain::save_data(KeyFrame *keyframe)
2273 // cause data to be stored directly in text
2274 output.set_shared_string(keyframe->data, MESSAGESIZE);
2275 output.tag.set_title("TITLE");
2276 output.tag.set_property("FONT", config.font);
2277 output.tag.set_property("ENCODING", config.encoding);
2278 output.tag.set_property("STYLE", (int64_t)config.style);
2279 output.tag.set_property("SIZE", config.size);
2280 output.tag.set_property("COLOR", config.color);
2281 output.tag.set_property("COLOR_STROKE", config.color_stroke);
2282 output.tag.set_property("STROKE_WIDTH", config.stroke_width);
2283 output.tag.set_property("MOTION_STRATEGY", config.motion_strategy);
2284 output.tag.set_property("LOOP", config.loop);
2285 output.tag.set_property("PIXELS_PER_SECOND", config.pixels_per_second);
2286 output.tag.set_property("HJUSTIFICATION", config.hjustification);
2287 output.tag.set_property("VJUSTIFICATION", config.vjustification);
2288 output.tag.set_property("FADE_IN", config.fade_in);
2289 output.tag.set_property("FADE_OUT", config.fade_out);
2290 output.tag.set_property("TITLE_X", config.x);
2291 output.tag.set_property("TITLE_Y", config.y);
2292 output.tag.set_property("DROPSHADOW", config.dropshadow);
2293 output.tag.set_property("TIMECODE", config.timecode);
2294 output.tag.set_property("TIMECODEFORMAT", config.timecodeformat);
2295 output.append_tag();
2296 output.append_newline();
2298 output.encode_text(config.text);
2300 output.tag.set_title("/TITLE");
2301 output.append_tag();
2302 output.append_newline();
2303 output.terminate_string();
2304 //printf("TitleMain::save_data 1\n%s\n", output.string);
2305 //printf("TitleMain::save_data 2\n%s\n", config.text);
2308 void TitleMain::read_data(KeyFrame *keyframe)
2312 input.set_shared_string(keyframe->data, strlen(keyframe->data));
2315 int new_interlace = 0;
2316 int new_horizontal = 0;
2317 int new_luminance = 0;
2319 config.prev_keyframe_position = keyframe->position;
2322 result = input.read_tag();
2326 if(input.tag.title_is("TITLE"))
2328 input.tag.get_property("FONT", config.font);
2329 input.tag.get_property("ENCODING", config.encoding);
2330 config.style = input.tag.get_property("STYLE", (int64_t)config.style);
2331 config.size = input.tag.get_property("SIZE", config.size);
2332 config.color = input.tag.get_property("COLOR", config.color);
2333 config.color_stroke = input.tag.get_property("COLOR_STROKE", config.color_stroke);
2334 config.stroke_width = input.tag.get_property("STROKE_WIDTH", config.stroke_width);
2335 config.motion_strategy = input.tag.get_property("MOTION_STRATEGY", config.motion_strategy);
2336 config.loop = input.tag.get_property("LOOP", config.loop);
2337 config.pixels_per_second = input.tag.get_property("PIXELS_PER_SECOND", config.pixels_per_second);
2338 config.hjustification = input.tag.get_property("HJUSTIFICATION", config.hjustification);
2339 config.vjustification = input.tag.get_property("VJUSTIFICATION", config.vjustification);
2340 config.fade_in = input.tag.get_property("FADE_IN", config.fade_in);
2341 config.fade_out = input.tag.get_property("FADE_OUT", config.fade_out);
2342 config.x = input.tag.get_property("TITLE_X", config.x);
2343 config.y = input.tag.get_property("TITLE_Y", config.y);
2344 config.dropshadow = input.tag.get_property("DROPSHADOW", config.dropshadow);
2345 config.timecode = input.tag.get_property("TIMECODE", config.timecode);
2346 input.tag.get_property("TIMECODEFORMAT", config.timecodeformat);
2347 strcpy(config.text, input.read_text());
2348 //printf("TitleMain::read_data 1\n%s\n", input.string);
2349 //printf("TitleMain::read_data 2\n%s\n", config.text);
2352 if(input.tag.title_is("/TITLE"))