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"
16 #include "picon_png.h"
17 #include "plugincolors.h"
19 #include "titlewindow.h"
32 #define _(String) gettext(String)
33 #define gettext_noop(String) String
34 #define N_(String) gettext_noop (String)
37 #define ZERO (1.0 / 64.0)
39 #define FONT_SEARCHPATH "fonts"
40 //#define FONT_SEARCHPATH "/usr/X11R6/lib/X11/fonts"
43 REGISTER_PLUGIN(TitleMain)
46 TitleConfig::TitleConfig()
50 color_stroke = 0xff0000;
52 motion_strategy = NO_MOTION;
54 hjustification = JUSTIFY_CENTER;
55 vjustification = JUSTIFY_MID;
61 sprintf(font, "fixed");
62 sprintf(text, _("hello world"));
63 #define DEFAULT_ENCODING "ISO8859-1"
64 sprintf(encoding, DEFAULT_ENCODING);
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 hjustification == that.hjustification &&
81 vjustification == that.vjustification &&
82 EQUIV(pixels_per_second, that.pixels_per_second) &&
83 !strcasecmp(font, that.font) &&
84 !strcasecmp(encoding, that.encoding) &&
85 !strcmp(text, that.text);
88 void TitleConfig::copy_from(TitleConfig &that)
90 strcpy(font, that.font);
94 color_stroke = that.color_stroke;
95 stroke_width = that.stroke_width;
96 pixels_per_second = that.pixels_per_second;
97 motion_strategy = that.motion_strategy;
99 hjustification = that.hjustification;
100 vjustification = that.vjustification;
101 fade_in = that.fade_in;
102 fade_out = that.fade_out;
105 dropshadow = that.dropshadow;
106 timecode = that.timecode;
107 strcpy(text, that.text);
108 strcpy(encoding, that.encoding);
111 void TitleConfig::interpolate(TitleConfig &prev,
115 int64_t current_frame)
117 strcpy(font, prev.font);
118 strcpy(encoding, prev.encoding);
122 color_stroke = prev.color_stroke;
123 stroke_width = prev.stroke_width;
124 motion_strategy = prev.motion_strategy;
126 hjustification = prev.hjustification;
127 vjustification = prev.vjustification;
128 fade_in = prev.fade_in;
129 fade_out = prev.fade_out;
130 pixels_per_second = prev.pixels_per_second;
131 strcpy(text, prev.text);
133 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
134 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
137 // this->x = prev.x * prev_scale + next.x * next_scale;
138 // this->y = prev.y * prev_scale + next.y * next_scale;
141 timecode = prev.timecode;
142 // this->dropshadow = (int)(prev.dropshadow * prev_scale + next.dropshadow * next_scale);
143 this->dropshadow = prev.dropshadow;
162 FontEntry::FontEntry()
178 FontEntry::~FontEntry()
180 if(path) delete [] path;
181 if(foundary) delete [] foundary;
182 if(family) delete [] family;
183 if(weight) delete [] weight;
184 if(slant) delete [] slant;
185 if(swidth) delete [] swidth;
186 if(adstyle) delete [] adstyle;
187 if(spacing) delete [] spacing;
188 if(registry) delete [] registry;
189 if(encoding) delete [] encoding;
190 if(fixed_title) delete [] fixed_title;
193 void FontEntry::dump()
195 printf("%s: %s %s %s %s %s %s %d %d %d %d %s %d %s %s\n",
213 TitleGlyph::TitleGlyph()
222 TitleGlyph::~TitleGlyph()
224 //printf("TitleGlyph::~TitleGlyph 1\n");
225 if(data) delete data;
226 if(data_stroke) delete data_stroke;
239 GlyphPackage::GlyphPackage() : LoadPackage()
243 GlyphUnit::GlyphUnit(TitleMain *plugin, GlyphEngine *server)
246 this->plugin = plugin;
248 freetype_library = 0;
252 GlyphUnit::~GlyphUnit()
254 if(freetype_library) FT_Done_FreeType(freetype_library);
257 void GlyphUnit::process_package(LoadPackage *package)
259 GlyphPackage *pkg = (GlyphPackage*)package;
260 TitleGlyph *glyph = pkg->glyph;
263 if(!freetype_library)
265 current_font = plugin->get_font();
267 if(plugin->load_freetype_face(freetype_library,
271 printf(_("GlyphUnit::process_package FT_New_Face failed.\n"));
276 FT_Set_Pixel_Sizes(freetype_face, plugin->config.size, 0);
282 int gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
284 //printf("GlyphUnit::process_package 1 %c\n", glyph->char_code);
289 if (glyph->char_code != 10)
290 printf(_("GlyphUnit::process_package FT_Load_Char failed - char: %i.\n"),
292 // Prevent a crash here
298 glyph->freetype_index = 0;
299 glyph->advance_w = 8;
300 glyph->data = new VFrame(0,
305 glyph->data->clear_frame();
306 glyph->data_stroke = 0;
310 // create outline glyph
311 if (plugin->config.stroke_width >= ZERO &&
312 (plugin->config.style & FONT_OUTLINE))
314 glyph->data_stroke = new VFrame(0,
319 glyph->data_stroke->clear_frame();
326 // char found and no outline desired
327 if (plugin->config.stroke_width < ZERO ||
328 !(plugin->config.style & FONT_OUTLINE))
330 FT_Glyph glyph_image;
333 FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
334 FT_Get_Glyph(freetype_face->glyph, &glyph_image);
335 FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
336 // printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
337 // bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax);
339 FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
342 glyph->width = bm.width = ((bbox.xMax - bbox.xMin + 63) >> 6);
343 glyph->height = bm.rows = ((bbox.yMax - bbox.yMin + 63) >> 6);
344 glyph->pitch = bm.pitch = bm.width;
345 bm.pixel_mode = FT_PIXEL_MODE_GRAY;
347 glyph->left = (bbox.xMin + 31) >> 6;
348 if (glyph->left < 0) glyph->left = 0;
349 glyph->top = (bbox.yMax + 31) >> 6;
350 glyph->freetype_index = gindex;
351 glyph->advance_w = ((freetype_face->glyph->advance.x + 31) >> 6);
352 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n",
353 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
355 glyph->data = new VFrame(0,
360 glyph->data->clear_frame();
361 bm.buffer = glyph->data->get_data();
362 FT_Outline_Get_Bitmap( freetype_library,
363 &((FT_OutlineGlyph) glyph_image)->outline,
365 FT_Done_Glyph(glyph_image);
368 // Outline desired and glyph found
370 FT_Glyph glyph_image;
376 FT_UInt npoints, ncontours;
378 typedef struct FT_LibraryRec_
383 FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
384 FT_Get_Glyph(freetype_face->glyph, &glyph_image);
386 // check if the outline is ok (non-empty);
387 FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
388 if (bbox.xMin == 0 && bbox.xMax == 0 && bbox.yMin ==0 && bbox.yMax == 0)
390 FT_Done_Glyph(glyph_image);
391 glyph->data = new VFrame(0, 0, BC_A8,0);
392 glyph->data_stroke = new VFrame(0, 0, BC_A8,0);;
397 glyph->advance_w =((int)(freetype_face->glyph->advance.x +
398 plugin->config.stroke_width * 64)) >> 6;
401 FT_Stroker_New(((FT_LibraryRec *)freetype_library)->memory, &stroker);
402 FT_Stroker_Set(stroker, (int)(plugin->config.stroke_width * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
403 FT_Stroker_ParseOutline(stroker, &((FT_OutlineGlyph) glyph_image)->outline,1);
404 FT_Stroker_GetCounts(stroker,&npoints, &ncontours);
405 if (npoints ==0 && ncontours == 0)
407 // this never happens, but FreeType has a bug regarding Linotype's Palatino font
408 FT_Stroker_Done(stroker);
409 FT_Done_Glyph(glyph_image);
410 glyph->data = new VFrame(0, 0, BC_A8,0);
411 glyph->data_stroke = new VFrame(0, 0, BC_A8,0);;
416 glyph->advance_w =((int)(freetype_face->glyph->advance.x +
417 plugin->config.stroke_width * 64)) >> 6;
421 FT_Outline_New(freetype_library, npoints, ncontours, &outline);
423 outline.n_contours=0;
424 FT_Stroker_Export (stroker, &outline);
425 FT_Outline_Get_BBox(&outline, &bbox);
427 FT_Outline_Translate(&outline,
431 FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
433 - bbox.yMin + (int)(plugin->config.stroke_width*32));
434 // printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\nFill Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
435 // bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax,
436 // bbox_fill.xMin,bbox_fill.xMax, bbox_fill.yMin, bbox_fill.yMax);
438 glyph->width = bm.width = ((bbox.xMax - bbox.xMin) >> 6)+1;
439 glyph->height = bm.rows = ((bbox.yMax - bbox.yMin) >> 6) +1;
440 glyph->pitch = bm.pitch = bm.width;
441 bm.pixel_mode = FT_PIXEL_MODE_GRAY;
443 glyph->left = (bbox.xMin + 31) >> 6;
444 if (glyph->left < 0) glyph->left = 0;
445 glyph->top = (bbox.yMax + 31) >> 6;
446 glyph->freetype_index = gindex;
447 int real_advance = ((int)ceil((float)freetype_face->glyph->advance.x +
448 plugin->config.stroke_width * 64) >> 6);
449 glyph->advance_w = glyph->width + glyph->left;
450 if (real_advance > glyph->advance_w)
451 glyph->advance_w = real_advance;
452 //printf("GlyphUnit::process_package 1 width=%d height=%d pitch=%d left=%d top=%d advance_w=%d freetype_index=%d\n",
453 //glyph->width, glyph->height, glyph->pitch, glyph->left, glyph->top, glyph->advance_w, glyph->freetype_index);
456 //printf("GlyphUnit::process_package 1\n");
457 glyph->data = new VFrame(0,
462 glyph->data_stroke = new VFrame(0,
467 glyph->data->clear_frame();
468 glyph->data_stroke->clear_frame();
469 // for debugging memset( glyph->data_stroke->get_data(), 60, glyph->pitch * glyph->height);
470 bm.buffer=glyph->data->get_data();
471 FT_Outline_Get_Bitmap( freetype_library,
472 &((FT_OutlineGlyph) glyph_image)->outline,
474 bm.buffer=glyph->data_stroke->get_data();
475 FT_Outline_Get_Bitmap( freetype_library,
478 FT_Outline_Done(freetype_library,&outline);
479 FT_Stroker_Done(stroker);
480 FT_Done_Glyph(glyph_image);
482 //printf("GlyphUnit::process_package 2\n");
487 GlyphEngine::GlyphEngine(TitleMain *plugin, int cpus)
488 : LoadServer(cpus, cpus)
490 this->plugin = plugin;
492 void GlyphEngine::init_packages()
494 int current_package = 0;
495 for(int i = 0; i < plugin->glyphs.total; i++)
497 if(!plugin->glyphs.values[i]->data)
499 GlyphPackage *pkg = (GlyphPackage*)packages[current_package++];
500 pkg->glyph = plugin->glyphs.values[i];
504 LoadClient* GlyphEngine::new_client()
506 return new GlyphUnit(plugin, this);
508 LoadPackage* GlyphEngine::new_package()
510 return new GlyphPackage;
525 // Copy a single character to the text mask
526 TitlePackage::TitlePackage()
532 TitleUnit::TitleUnit(TitleMain *plugin, TitleEngine *server)
535 this->plugin = plugin;
538 void TitleUnit::draw_glyph(VFrame *output, TitleGlyph *glyph, int x, int y)
540 int glyph_w = glyph->data->get_w();
541 int glyph_h = glyph->data->get_h();
542 int output_w = output->get_w();
543 int output_h = output->get_h();
544 unsigned char **in_rows = glyph->data->get_rows();
545 unsigned char **out_rows = output->get_rows();
547 //printf("TitleUnit::draw_glyph 1 %c %d %d\n", glyph->c, x, y);
548 for(int in_y = 0; in_y < glyph_h; in_y++)
550 // int y_out = y + plugin->ascent + in_y - glyph->top;
551 int y_out = y + plugin->get_char_height() + in_y - glyph->top;
553 //printf("TitleUnit::draw_glyph 1 %d\n", y_out);
554 if(y_out >= 0 && y_out < output_h)
556 unsigned char *out_row = out_rows[y_out];
557 unsigned char *in_row = in_rows[in_y];
558 for(int in_x = 0; in_x < glyph_w; in_x++)
560 int x_out = x + glyph->left + in_x;
561 if(x_out >= 0 && x_out < output_w)
564 out_row[x_out] = in_row[in_x];
565 //out_row[x_out] = 0xff;
573 void TitleUnit::process_package(LoadPackage *package)
575 TitlePackage *pkg = (TitlePackage*)package;
579 for(int i = 0; i < plugin->glyphs.total; i++)
581 TitleGlyph *glyph = plugin->glyphs.values[i];
582 if(glyph->c == pkg->c)
584 draw_glyph(plugin->text_mask, glyph, pkg->x, pkg->y);
585 if(plugin->config.stroke_width >= ZERO &&
586 (plugin->config.style & FONT_OUTLINE))
588 VFrame *tmp = glyph->data;
589 glyph->data = glyph->data_stroke;
590 draw_glyph(plugin->text_mask_stroke, glyph, pkg->x, pkg->y);
599 TitleEngine::TitleEngine(TitleMain *plugin, int cpus)
600 : LoadServer(cpus, cpus)
602 this->plugin = plugin;
605 void TitleEngine::init_packages()
607 //printf("TitleEngine::init_packages 1\n");
608 int visible_y1 = plugin->visible_row1 * plugin->get_char_height();
609 //printf("TitleEngine::init_packages 1\n");
610 int current_package = 0;
611 for(int i = plugin->visible_char1; i < plugin->visible_char2; i++)
613 title_char_position_t *char_position = plugin->char_positions + i;
614 TitlePackage *pkg = (TitlePackage*)get_package(current_package);
615 //printf("TitleEngine::init_packages 1\n");
616 pkg->x = char_position->x;
617 //printf("TitleEngine::init_packages 1\n");
618 pkg->y = char_position->y - visible_y1;
619 //printf("TitleEngine::init_packages 1\n");
620 pkg->c = plugin->config.text[i];
621 //printf("TitleEngine::init_packages 2\n");
626 LoadClient* TitleEngine::new_client()
628 return new TitleUnit(plugin, this);
631 LoadPackage* TitleEngine::new_package()
633 return new TitlePackage;
646 TitleTranslatePackage::TitleTranslatePackage()
652 TitleTranslateUnit::TitleTranslateUnit(TitleMain *plugin, TitleTranslate *server)
655 this->plugin = plugin;
660 #define TRANSLATE(type, max, components, r, g, b) \
662 unsigned char **in_rows = plugin->text_mask->get_rows(); \
663 type **out_rows = (type**)plugin->output->get_rows(); \
665 for(int i = pkg->y1; i < pkg->y2; i++) \
667 if(i + server->out_y1_int >= 0 && \
668 i + server->out_y1_int < server->output_h) \
671 float y_fraction1, y_fraction2; \
672 float y_output_fraction; \
673 in_y1 = server->y_table[i].in_x1; \
674 in_y2 = server->y_table[i].in_x2; \
675 y_fraction1 = server->y_table[i].in_fraction1; \
676 y_fraction2 = server->y_table[i].in_fraction2; \
677 y_output_fraction = server->y_table[i].output_fraction; \
678 unsigned char *in_row1 = in_rows[in_y1]; \
679 unsigned char *in_row2 = in_rows[in_y2]; \
680 type *out_row = out_rows[i + server->out_y1_int]; \
682 for(int j = server->out_x1_int; j < server->out_x2_int; j++) \
684 if(j >= 0 && j < server->output_w) \
690 float x_output_fraction; \
692 server->x_table[j - server->out_x1_int].in_x1; \
694 server->x_table[j - server->out_x1_int].in_x2; \
696 server->x_table[j - server->out_x1_int].in_fraction1; \
698 server->x_table[j - server->out_x1_int].in_fraction2; \
699 x_output_fraction = \
700 server->x_table[j - server->out_x1_int].output_fraction; \
702 float fraction1 = x_fraction1 * y_fraction1; \
703 float fraction2 = x_fraction2 * y_fraction1; \
704 float fraction3 = x_fraction1 * y_fraction2; \
705 float fraction4 = x_fraction2 * y_fraction2; \
706 int input = (int)(in_row1[in_x1] * fraction1 + \
707 in_row1[in_x2] * fraction2 + \
708 in_row2[in_x1] * fraction3 + \
709 in_row2[in_x2] * fraction4 + 0.5); \
710 input *= plugin->alpha; \
711 /* Alpha is 0 - 256 */ \
714 int anti_input = 0xff - input; \
715 if(components == 4) \
717 out_row[j * components + 0] = \
718 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
719 out_row[j * components + 1] = \
720 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
721 out_row[j * components + 2] = \
722 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
724 out_row[j * components + 3] = \
725 MAX((input << 8) | input, out_row[j * components + 3]); \
727 out_row[j * components + 3] = \
728 MAX(input, out_row[j * components + 3]); \
732 out_row[j * components + 0] = \
733 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
734 out_row[j * components + 1] = \
735 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
736 out_row[j * components + 2] = \
737 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
746 #define TRANSLATEA(type, max, components, r, g, b) \
748 unsigned char **in_rows = plugin->text_mask->get_rows(); \
749 type **out_rows = (type**)plugin->output->get_rows(); \
751 for(int i = pkg->y1; i < pkg->y2; i++) \
753 if(i + server->out_y1_int >= 0 && \
754 i + server->out_y1_int < server->output_h) \
756 unsigned char *in_row = in_rows[i]; \
757 type *out_row = out_rows[i + server->out_y1_int]; \
759 for(int j = server->out_x1; j < server->out_x2_int; j++) \
762 j < server->output_w) \
764 int input = (int)(in_row[j - server->out_x1]); \
766 input *= plugin->alpha; \
767 /* Alpha is 0 - 256 */ \
770 int anti_input = 0xff - input; \
771 if(components == 4) \
773 out_row[j * components + 0] = \
774 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
775 out_row[j * components + 1] = \
776 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
777 out_row[j * components + 2] = \
778 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
780 out_row[j * components + 3] = \
781 MAX((input << 8) | input, out_row[j * components + 3]); \
783 out_row[j * components + 3] = \
784 MAX(input, out_row[j * components + 3]); \
788 out_row[j * components + 0] = \
789 (r * input + out_row[j * components + 0] * anti_input) / 0xff; \
790 out_row[j * components + 1] = \
791 (g * input + out_row[j * components + 1] * anti_input) / 0xff; \
792 out_row[j * components + 2] = \
793 (b * input + out_row[j * components + 2] * anti_input) / 0xff; \
803 void TitleTranslateUnit::process_package(LoadPackage *package)
805 TitleTranslatePackage *pkg = (TitleTranslatePackage*)package;
806 TitleTranslate *server = (TitleTranslate*)this->server;
807 int r_in, g_in, b_in;
809 //printf("TitleTranslateUnit::process_package 1 %d %d\n", pkg->y1, pkg->y2);
811 r_in = (plugin->config.color & 0xff0000) >> 16;
812 g_in = (plugin->config.color & 0xff00) >> 8;
813 b_in = plugin->config.color & 0xff;
815 switch(plugin->output->get_color_model())
819 TRANSLATE(unsigned char, 0xff, 3, r_in, g_in, b_in);
824 unsigned char y, u, v;
825 yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
826 TRANSLATE(unsigned char, 0xff, 3, y, u, v);
832 r = (r_in << 8) | r_in;
833 g = (g_in << 8) | g_in;
834 b = (b_in << 8) | b_in;
835 TRANSLATE(uint16_t, 0xffff, 3, r, g, b);
848 TRANSLATE(uint16_t, 0xffff, 3, y, u, v);
853 TRANSLATE(unsigned char, 0xff, 4, r_in, g_in, b_in);
858 unsigned char y, u, v;
859 yuv.rgb_to_yuv_8(r_in, g_in, b_in, y, u, v);
860 TRANSLATE(unsigned char, 0xff, 4, y, u, v);
863 case BC_RGBA16161616:
866 r = (r_in << 8) | r_in;
867 g = (g_in << 8) | g_in;
868 b = (b_in << 8) | b_in;
869 TRANSLATE(uint16_t, 0xffff, 4, r, g, b);
872 case BC_YUVA16161616:
882 TRANSLATE(uint16_t, 0xffff, 4, y, u, v);
887 //printf("TitleTranslateUnit::process_package 5\n");
893 TitleTranslate::TitleTranslate(TitleMain *plugin, int cpus)
896 this->plugin = plugin;
897 x_table = y_table = 0;
900 TitleTranslate::~TitleTranslate()
902 if(x_table) delete [] x_table;
903 if(y_table) delete [] y_table;
906 void TitleTranslate::init_packages()
908 //printf("TitleTranslate::init_packages 1\n");
909 // Generate scaling tables
910 if(x_table) delete [] x_table;
911 if(y_table) delete [] y_table;
912 //printf("TitleTranslate::init_packages 1\n");
914 output_w = plugin->output->get_w();
915 output_h = plugin->output->get_h();
916 //printf("TitleTranslate::init_packages 1 %f %d\n", plugin->text_x1, plugin->text_w);
919 TranslateUnit::translation_array_f(x_table,
921 plugin->text_x1 + plugin->text_w,
928 //printf("TitleTranslate::init_packages 1 %f %f\n", plugin->mask_y1, plugin->mask_y2);
930 TranslateUnit::translation_array_f(y_table,
932 plugin->mask_y1 + plugin->text_mask->get_h(),
934 plugin->text_mask->get_h(),
935 plugin->text_mask->get_h(),
940 //printf("TitleTranslate::init_packages 1\n");
947 int increment = (out_y2 - out_y1) / get_total_packages() + 1;
949 //printf("TitleTranslate::init_packages 1 %d %d %d %d\n",
950 // out_y1, out_y2, out_y1_int, out_y2_int);
951 for(int i = 0; i < get_total_packages(); i++)
953 TitleTranslatePackage *pkg = (TitleTranslatePackage*)get_package(i);
954 pkg->y1 = i * increment;
955 pkg->y2 = i * increment + increment;
956 if(pkg->y1 > out_y2 - out_y1)
957 pkg->y1 = out_y2 - out_y1;
958 if(pkg->y2 > out_y2 - out_y1)
959 pkg->y2 = out_y2 - out_y1;
961 //printf("TitleTranslate::init_packages 2\n");
964 LoadClient* TitleTranslate::new_client()
966 return new TitleTranslateUnit(plugin, this);
969 LoadPackage* TitleTranslate::new_package()
971 return new TitleTranslatePackage;
988 ArrayList<FontEntry*>* TitleMain::fonts = 0;
992 TitleMain::TitleMain(PluginServer *server)
993 : PluginVClient(server)
995 PLUGIN_CONSTRUCTOR_MACRO
997 // Build font database
1000 text_mask_stroke = 0;
1003 freetype_library = 0;
1008 need_reconfigure = 1;
1011 TitleMain::~TitleMain()
1013 //printf("TitleMain::~TitleMain 1\n");
1014 PLUGIN_DESTRUCTOR_MACRO
1015 if(text_mask) delete text_mask;
1016 if(text_mask_stroke) delete text_mask_stroke;
1017 if(char_positions) delete [] char_positions;
1018 if(rows_bottom) delete [] rows_bottom;
1020 if(glyph_engine) delete glyph_engine;
1021 if(title_engine) delete title_engine;
1022 if(freetype_library) FT_Done_FreeType(freetype_library);
1023 if(translate) delete translate;
1026 char* TitleMain::plugin_title() { return _("Title"); }
1027 int TitleMain::is_realtime() { return 1; }
1028 int TitleMain::is_synthesis() { return 1; }
1030 VFrame* TitleMain::new_picon()
1032 return new VFrame(picon_png);
1036 void TitleMain::build_fonts()
1040 fonts = new ArrayList<FontEntry*>;
1041 // Construct path from location of the plugin
1042 char search_path[BCTEXTLEN];
1043 strcpy(search_path, PluginClient::get_path());
1044 char *ptr = strrchr(search_path, '/');
1045 strcpy(ptr + 1, FONT_SEARCHPATH);
1046 char command_line[BCTEXTLEN];
1048 sprintf(command_line,
1049 "find %s -name 'fonts.dir' -print -exec cat {} \\;",
1051 //printf("TitleMain::build_fonts %s\n", command_line);
1053 FILE *in = popen(command_line, "r");
1054 char current_dir[BCTEXTLEN];
1055 FT_Library freetype_library = 0; // Freetype library
1056 FT_Face freetype_face = 0;
1058 // FT_Init_FreeType(&freetype_library);
1063 char string[BCTEXTLEN], string2[BCTEXTLEN];
1064 fgets(string, BCTEXTLEN, in);
1065 if(!strlen(string)) break;
1067 char *in_ptr = string;
1070 // Get current directory
1072 if(string[0] == '/')
1074 out_ptr = current_dir;
1075 while(*in_ptr != 0 && *in_ptr != 0xa)
1076 *out_ptr++ = *in_ptr++;
1078 while(*out_ptr != '/')
1085 //printf("TitleMain::build_fonts %s\n", string);
1086 FontEntry *entry = new FontEntry;
1090 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa)
1092 *out_ptr++ = *in_ptr++;
1095 if(string2[0] == '/')
1097 entry->path = new char[strlen(string2) + 1];
1098 sprintf(entry->path, "%s", string2);
1102 entry->path = new char[strlen(current_dir) + strlen(string2) + 1];
1103 sprintf(entry->path, "%s%s", current_dir, string2);
1107 while(*in_ptr != 0 && *in_ptr != 0xa && (*in_ptr == ' ' || *in_ptr == '-'))
1111 while(*in_ptr != 0 && *in_ptr != ' ' && *in_ptr != 0xa && *in_ptr != '-')
1113 *out_ptr++ = *in_ptr++;
1116 entry->foundary = new char[strlen(string2) + 1];
1117 strcpy(entry->foundary, string2);
1118 if(*in_ptr == '-') in_ptr++;
1123 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1125 *out_ptr++ = *in_ptr++;
1128 entry->family = new char[strlen(string2) + 1];
1129 strcpy(entry->family, string2);
1130 if(*in_ptr == '-') in_ptr++;
1134 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1136 *out_ptr++ = *in_ptr++;
1139 entry->weight = new char[strlen(string2) + 1];
1140 strcpy(entry->weight, string2);
1141 if(*in_ptr == '-') in_ptr++;
1145 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1147 *out_ptr++ = *in_ptr++;
1150 entry->slant = new char[strlen(string2) + 1];
1151 strcpy(entry->slant, string2);
1152 if(*in_ptr == '-') in_ptr++;
1156 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1158 *out_ptr++ = *in_ptr++;
1161 entry->swidth = new char[strlen(string2) + 1];
1162 strcpy(entry->swidth, string2);
1163 if(*in_ptr == '-') in_ptr++;
1167 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1169 *out_ptr++ = *in_ptr++;
1172 entry->adstyle = new char[strlen(string2) + 1];
1173 strcpy(entry->adstyle, string2);
1174 if(*in_ptr == '-') in_ptr++;
1178 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1180 *out_ptr++ = *in_ptr++;
1183 entry->pixelsize = atol(string2);
1184 if(*in_ptr == '-') in_ptr++;
1188 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1190 *out_ptr++ = *in_ptr++;
1193 entry->pointsize = atol(string2);
1194 if(*in_ptr == '-') in_ptr++;
1198 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1200 *out_ptr++ = *in_ptr++;
1203 entry->xres = atol(string2);
1204 if(*in_ptr == '-') in_ptr++;
1208 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1210 *out_ptr++ = *in_ptr++;
1213 entry->yres = atol(string2);
1214 if(*in_ptr == '-') in_ptr++;
1218 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1220 *out_ptr++ = *in_ptr++;
1223 entry->spacing = new char[strlen(string2) + 1];
1224 strcpy(entry->spacing, string2);
1225 if(*in_ptr == '-') in_ptr++;
1229 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1231 *out_ptr++ = *in_ptr++;
1234 entry->avg_width = atol(string2);
1235 if(*in_ptr == '-') in_ptr++;
1239 while(*in_ptr != 0 && *in_ptr != 0xa && *in_ptr != '-')
1241 *out_ptr++ = *in_ptr++;
1244 entry->registry = new char[strlen(string2) + 1];
1245 strcpy(entry->registry, string2);
1246 if(*in_ptr == '-') in_ptr++;
1250 while(*in_ptr != 0 && *in_ptr != 0xa)
1252 *out_ptr++ = *in_ptr++;
1255 entry->encoding = new char[strlen(string2) + 1];
1256 strcpy(entry->encoding, string2);
1261 if(strlen(entry->foundary))
1263 //printf("TitleMain::build_fonts 1 %s\n", entry->path);
1264 // This takes a real long time to do. Instead just take all fonts
1265 // if(!load_freetype_face(freetype_library,
1269 if(entry->family[0])
1272 sprintf(string, "%s (%s)", entry->family, entry->foundary);
1273 entry->fixed_title = new char[strlen(string) + 1];
1274 strcpy(entry->fixed_title, string);
1276 if(!strcasecmp(entry->weight, "demibold") ||
1277 !strcasecmp(entry->weight, "bold"))
1278 entry->fixed_style |= FONT_BOLD;
1279 if(!strcasecmp(entry->slant, "i") ||
1280 !strcasecmp(entry->slant, "o"))
1281 entry->fixed_style |= FONT_ITALIC;
1282 fonts->append(entry);
1283 // printf("TitleMain::build_fonts %s: success\n",
1285 //printf("TitleMain::build_fonts 2\n");
1289 // printf("TitleMain::build_fonts %s: FT_New_Face failed\n",
1291 //printf("TitleMain::build_fonts 3\n");
1303 if(freetype_library) FT_Done_FreeType(freetype_library);
1307 // for(int i = 0; i < fonts->total; i++)
1308 // fonts->values[i]->dump();
1313 int TitleMain::load_freetype_face(FT_Library &freetype_library,
1314 FT_Face &freetype_face,
1317 //printf("TitleMain::load_freetype_face 1\n");
1318 if(!freetype_library) FT_Init_FreeType(&freetype_library);
1319 if(freetype_face) FT_Done_Face(freetype_face);
1321 //printf("TitleMain::load_freetype_face 2\n");
1323 // Use freetype's internal function for loading font
1324 if(FT_New_Face(freetype_library,
1329 fprintf(stderr, _("TitleMain::load_freetype_face %s failed.\n"));
1330 FT_Done_FreeType(freetype_library);
1332 freetype_library = 0;
1339 //printf("TitleMain::load_freetype_face 4\n");
1342 FontEntry* TitleMain::get_font_entry(char *title,
1346 //printf("TitleMain::get_font_entry %s %d %d\n", title, style, size);
1347 FontEntry *result = 0;
1350 for(int i = 0; i < fonts->total; i++)
1352 FontEntry *entry = fonts->values[i];
1354 if(!result) result = entry;
1356 if(!strcmp(title, entry->fixed_title))
1358 if(!got_title) result = entry;
1361 // Not every font has a size but every font has a style
1362 if(entry->fixed_style == style)
1365 if(entry->fixed_style == style && entry->pointsize == size)
1375 FontEntry* TitleMain::get_font()
1377 return get_font_entry(config.font,
1387 int TitleMain::get_char_height()
1389 // this is height above the zero line, but does not include characters that go below
1390 int result = config.size;
1391 if((config.style & FONT_OUTLINE)) result += (int)ceil(config.stroke_width * 2);
1395 int TitleMain::get_char_advance(int current, int next)
1399 TitleGlyph *current_glyph = 0;
1400 TitleGlyph *next_glyph = 0;
1402 if(current == 0xa) return 0;
1404 for(int i = 0; i < glyphs.total; i++)
1406 if(glyphs.values[i]->c == current)
1408 current_glyph = glyphs.values[i];
1413 for(int i = 0; i < glyphs.total; i++)
1415 if(glyphs.values[i]->c == next)
1417 next_glyph = glyphs.values[i];
1423 result = current_glyph->advance_w;
1425 //printf("TitleMain::get_char_advance 1 %c %c %p %p\n", current, next, current_glyph, next_glyph);
1427 FT_Get_Kerning(freetype_face,
1428 current_glyph->freetype_index,
1429 next_glyph->freetype_index,
1434 //printf("TitleMain::get_char_advance 2 %d %d\n", result, kerning.x);
1436 return result + (kerning.x >> 6);
1440 void TitleMain::draw_glyphs()
1442 // Build table of all glyphs needed
1443 int text_len = strlen(config.text);
1444 int total_packages = 0;
1446 cd = iconv_open ("UCS-4", config.encoding);
1449 if (cd == (iconv_t) -1)
1451 /* Something went wrong. */
1452 fprintf (stderr, _("Iconv conversion from %s to Unicode UCS-4 not available\n"),config.encoding);
1455 for(int i = 0; i < text_len; i++)
1458 int c = config.text[i];
1461 /* if iconv is working ok for current encoding */
1462 if (cd != (iconv_t) -1)
1465 size_t inbytes,outbytes;
1467 char *inp = (char*)&inbuf, *outp = (char *)&char_code;
1473 iconv (cd, &inp, &inbytes, &outp, &outbytes);
1474 #if __BYTE_ORDER == __LITTLE_ENDIAN
1475 char_code = bswap_32(char_code);
1476 #endif /* Big endian. */
1484 for(int j = 0; j < glyphs.total; j++)
1486 if(glyphs.values[j]->char_code == char_code)
1496 //printf("TitleMain::draw_glyphs 1\n");
1497 TitleGlyph *glyph = new TitleGlyph;
1498 //printf("TitleMain::draw_glyphs 2\n");
1499 glyphs.append(glyph);
1501 glyph->char_code = char_code;
1507 glyph_engine = new GlyphEngine(this, PluginClient::smp + 1);
1509 glyph_engine->set_package_count(total_packages);
1510 //printf("TitleMain::draw_glyphs 3 %d\n", glyphs.total);
1511 glyph_engine->process_packages();
1512 //printf("TitleMain::draw_glyphs 4\n");
1515 void TitleMain::get_total_extents()
1517 // Determine extents of total text
1520 text_len = strlen(config.text);
1521 if(!char_positions) char_positions = new title_char_position_t[text_len];
1527 for(int i = 0; i < glyphs.total; i++)
1528 if(glyphs.values[i]->top > ascent) ascent = glyphs.values[i]->top;
1529 //printf("TitleMain::get_total_extents %d\n", ascent);
1531 // get the number of rows first
1532 for(int i = 0; i < text_len; i++)
1534 if(config.text[i] == 0xa || i == text_len - 1)
1539 if (!rows_bottom) rows_bottom = new int[text_rows+1];
1543 for(int i = 0; i < text_len; i++)
1545 char_positions[i].x = current_w;
1546 char_positions[i].y = text_rows * get_char_height();
1547 char_positions[i].w = get_char_advance(config.text[i], config.text[i + 1]);
1548 TitleGlyph *current_glyph = 0;
1549 for(int j = 0; j < glyphs.total; j++)
1551 if(glyphs.values[j]->c == config.text[i])
1553 current_glyph = glyphs.values[j];
1557 int current_bottom = current_glyph->top - current_glyph->height;
1558 if (current_bottom < rows_bottom[text_rows])
1559 rows_bottom[text_rows] = current_bottom ;
1561 // printf("TitleMain::get_total_extents 1 %c %d %d %d\n",
1563 // char_positions[i].x,
1564 // char_positions[i].y,
1565 // char_positions[i].w);
1566 current_w += char_positions[i].w;
1568 if(config.text[i] == 0xa || i == text_len - 1)
1571 rows_bottom[text_rows] = 0;
1572 if(current_w > text_w) text_w = current_w;
1576 text_w += config.dropshadow;
1577 text_h = text_rows * get_char_height();
1578 text_h += config.dropshadow;
1580 // Now that text_w is known
1581 // Justify rows based on configuration
1583 for(int i = 0; i < text_len; i++)
1585 if(config.text[i] == 0xa || i == text_len - 1)
1587 for(int j = row_start; j <= i; j++)
1589 switch(config.hjustification)
1595 char_positions[j].x += (text_w -
1596 char_positions[i].x -
1597 char_positions[i].w) /
1602 char_positions[j].x += (text_w -
1603 char_positions[i].x -
1604 char_positions[i].w);
1613 //printf("TitleMain::get_total_extents 2 %d %d\n", text_w, text_h);
1616 int TitleMain::draw_mask()
1618 int old_visible_row1 = visible_row1;
1619 int old_visible_row2 = visible_row2;
1622 // Determine y of visible text
1623 if(config.motion_strategy == BOTTOM_TO_TOP)
1625 // printf("TitleMain::draw_mask 1 %d %d %d %d\n",
1626 // config.motion_strategy,
1627 // get_source_position(),
1628 // get_source_start(),
1629 // config.prev_keyframe_position);
1630 float magnitude = config.pixels_per_second *
1631 ((get_source_position() - get_source_start()) -
1632 (config.prev_keyframe_position - get_source_start())) /
1633 PluginVClient::project_frame_rate;
1636 int loop_size = text_h + input->get_h();
1637 magnitude -= (int)(magnitude / loop_size) * loop_size;
1639 text_y1 = config.y + input->get_h() - magnitude;
1642 if(config.motion_strategy == TOP_TO_BOTTOM)
1644 float magnitude = config.pixels_per_second *
1645 (get_source_position() -
1646 get_source_start() -
1647 config.prev_keyframe_position) /
1648 PluginVClient::project_frame_rate;
1651 int loop_size = text_h + input->get_h();
1652 magnitude -= (int)(magnitude / loop_size) * loop_size;
1654 text_y1 = config.y + magnitude;
1658 if(config.vjustification == JUSTIFY_TOP)
1663 if(config.vjustification == JUSTIFY_MID)
1665 text_y1 = config.y + input->get_h() / 2 - text_h / 2;
1668 if(config.vjustification == JUSTIFY_BOTTOM)
1670 text_y1 = config.y + input->get_h() - text_h;
1673 text_y2 = text_y1 + text_h + 0.5;
1675 // Determine x of visible text
1676 if(config.motion_strategy == RIGHT_TO_LEFT)
1678 float magnitude = config.pixels_per_second *
1679 (get_source_position() -
1680 get_source_start() -
1681 config.prev_keyframe_position) /
1682 PluginVClient::project_frame_rate;
1685 int loop_size = text_w + input->get_w();
1686 magnitude -= (int)(magnitude / loop_size) * loop_size;
1688 text_x1 = config.x + (float)input->get_w() - magnitude;
1691 if(config.motion_strategy == LEFT_TO_RIGHT)
1693 float magnitude = config.pixels_per_second *
1694 (get_source_position() -
1695 get_source_start() -
1696 config.prev_keyframe_position) /
1697 PluginVClient::project_frame_rate;
1700 int loop_size = text_w + input->get_w();
1701 magnitude -= (int)(magnitude / loop_size) * loop_size;
1703 text_x1 = config.x + -(float)text_w + magnitude;
1706 if(config.hjustification == JUSTIFY_LEFT)
1711 if(config.hjustification == JUSTIFY_MID)
1713 text_x1 = config.x + input->get_w() / 2 - text_w / 2;
1716 if(config.hjustification == JUSTIFY_RIGHT)
1718 text_x1 = config.x + input->get_w() - text_w;
1725 // Determine y extents just of visible text
1726 visible_row1 = (int)(-text_y1 / get_char_height());
1727 if(visible_row1 < 0) visible_row1 = 0;
1729 visible_row2 = (int)((float)text_rows - (text_y2 - input->get_h()) / get_char_height() + 1);
1730 if(visible_row2 > text_rows) visible_row2 = text_rows;
1733 if(visible_row2 <= visible_row1) return 1;
1736 mask_y1 = text_y1 + visible_row1 * get_char_height();
1737 mask_y2 = text_y1 + visible_row2 * get_char_height();
1738 text_x1 += config.x;
1741 visible_char1 = visible_char2 = 0;
1743 for(int i = 0; i < text_len; i++)
1745 title_char_position_t *char_position = char_positions + i;
1746 int char_row = char_position->y / get_char_height();
1747 if(char_row >= visible_row1 &&
1748 char_row < visible_row2)
1763 int visible_rows = visible_row2 - visible_row1;
1764 int need_redraw = 0;
1766 (text_mask->get_w() != text_w ||
1767 text_mask->get_h() != visible_rows * get_char_height() - rows_bottom[visible_row2 - 1]))
1770 delete text_mask_stroke;
1772 text_mask_stroke = 0;
1777 text_mask = new VFrame(0,
1779 visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1781 text_mask_stroke = new VFrame(0,
1783 visible_rows * get_char_height() - rows_bottom[visible_row2-1],
1791 // Draw on text mask if different
1792 if(old_visible_row1 != visible_row1 ||
1793 old_visible_row2 != visible_row2 ||
1796 text_mask->clear_frame();
1797 text_mask_stroke->clear_frame();
1801 title_engine = new TitleEngine(this, PluginClient::smp + 1);
1803 title_engine->set_package_count(visible_char2 - visible_char1);
1804 title_engine->process_packages();
1811 void TitleMain::overlay_mask()
1815 if(!EQUIV(config.fade_in, 0))
1817 int fade_len = (int)(config.fade_in * PluginVClient::project_frame_rate);
1818 int fade_position = get_source_position() -
1819 /* get_source_start() - */
1820 config.prev_keyframe_position;
1823 if(fade_position >= 0 && fade_position < fade_len)
1825 alpha = (int)((float)0x100 *
1831 if(!EQUIV(config.fade_out, 0))
1833 int fade_len = (int)(config.fade_out *
1834 PluginVClient::project_frame_rate);
1835 int fade_position = config.next_keyframe_position -
1836 get_source_position();
1839 if(fade_position > 0 && fade_position < fade_len)
1841 alpha = (int)((float)0x100 *
1847 if(config.dropshadow)
1849 text_x1 += config.dropshadow;
1850 text_x2 += config.dropshadow;
1851 mask_y1 += config.dropshadow;
1852 mask_y2 += config.dropshadow;
1853 if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1854 mask_y1 < input->get_h() && mask_y2 > 0)
1856 if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1857 // Do 2 passes if dropshadow.
1858 int temp_color = config.color;
1860 translate->process_packages();
1861 config.color = temp_color;
1863 text_x1 -= config.dropshadow;
1864 text_x2 -= config.dropshadow;
1865 mask_y1 -= config.dropshadow;
1866 mask_y2 -= config.dropshadow;
1869 if(text_x1 < input->get_w() && text_x1 + text_w > 0 &&
1870 mask_y1 < input->get_h() && mask_y2 > 0)
1872 if(!translate) translate = new TitleTranslate(this, PluginClient::smp + 1);
1873 translate->process_packages();
1874 if (config.stroke_width >= ZERO &&
1875 (config.style & FONT_OUTLINE))
1877 int temp_color = config.color;
1878 VFrame *tmp_text_mask = this->text_mask;
1879 config.color = config.color_stroke;
1880 this->text_mask = this->text_mask_stroke;
1882 translate->process_packages();
1883 config.color = temp_color;
1884 this->text_mask = tmp_text_mask;
1889 void TitleMain::clear_glyphs()
1891 //printf("TitleMain::clear_glyphs 1\n");
1892 glyphs.remove_all_objects();
1895 char* TitleMain::motion_to_text(int motion)
1899 case NO_MOTION: return _("No motion"); break;
1900 case BOTTOM_TO_TOP: return _("Bottom to top"); break;
1901 case TOP_TO_BOTTOM: return _("Top to bottom"); break;
1902 case RIGHT_TO_LEFT: return _("Right to left"); break;
1903 case LEFT_TO_RIGHT: return _("Left to right"); break;
1907 int TitleMain::text_to_motion(char *text)
1909 for(int i = 0; i < TOTAL_PATHS; i++)
1911 if(!strcasecmp(motion_to_text(i), text)) return i;
1922 int TitleMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
1926 output = output_ptr;
1928 need_reconfigure |= load_configuration();
1931 // Always synthesize text and redraw it for timecode
1934 Units::totext(config.text,
1935 (double)get_source_position() / PluginVClient::project_frame_rate,
1938 PluginVClient::project_frame_rate,
1940 need_reconfigure = 1;
1944 if(config.size <= 0 || config.size >= 2048) config.size = 72;
1945 if(config.stroke_width < 0 ||
1946 config.stroke_width >= 512) config.stroke_width = 0.0;
1947 if(!strlen(config.text)) return 0;
1948 if(!strlen(config.encoding)) strcpy(config.encoding, DEFAULT_ENCODING);
1950 //printf("TitleMain::process_realtime 4\n");
1952 // Handle reconfiguration
1953 if(need_reconfigure)
1955 //printf("TitleMain::process_realtime 2\n");
1956 if(text_mask) delete text_mask;
1957 if(text_mask_stroke) delete text_mask_stroke;
1959 text_mask_stroke = 0;
1960 //printf("TitleMain::process_realtime 2\n");
1961 if(freetype_face) FT_Done_Face(freetype_face);
1963 //printf("TitleMain::process_realtime 2\n");
1964 if(glyph_engine) delete glyph_engine;
1966 //printf("TitleMain::process_realtime 2\n");
1967 if(char_positions) delete [] char_positions;
1969 if(rows_bottom) delete [] rows_bottom;
1971 //printf("TitleMain::process_realtime 2\n");
1973 //printf("TitleMain::process_realtime 2\n");
1978 if(!freetype_library)
1979 FT_Init_FreeType(&freetype_library);
1981 //printf("TitleMain::process_realtime 2\n");
1984 FontEntry *font = get_font();
1985 //printf("TitleMain::process_realtime 2.1 %s\n", font->path);
1986 if(load_freetype_face(freetype_library,
1990 printf("TitleMain::process_realtime %s: FT_New_Face failed.\n",
1994 //printf("TitleMain::process_realtime 2.2\n");
1996 if(!result) FT_Set_Pixel_Sizes(freetype_face, config.size, 0);
1997 //printf("TitleMain::process_realtime 2.3\n");
2000 //printf("TitleMain::process_realtime 3\n");
2005 //printf("TitleMain::process_realtime 4\n");
2006 get_total_extents();
2007 //printf("TitleMain::process_realtime 5\n");
2008 need_reconfigure = 0;
2014 //printf("TitleMain::process_realtime 4\n");
2015 // Determine region of text visible on the output and draw mask
2016 result = draw_mask();
2018 //printf("TitleMain::process_realtime 50\n");
2021 // Overlay mask on output
2026 //printf("TitleMain::process_realtime 60 %d\n", glyphs.total);
2031 int TitleMain::show_gui()
2033 load_configuration();
2034 thread = new TitleThread(this);
2039 int TitleMain::set_string()
2041 if(thread) thread->window->set_title(gui_string);
2045 void TitleMain::raise_window()
2049 thread->window->raise_window();
2050 thread->window->flush();
2054 void TitleMain::update_gui()
2058 int reconfigure = load_configuration();
2061 thread->window->lock_window();
2062 thread->window->update();
2063 thread->window->unlock_window();
2069 int TitleMain::load_defaults()
2071 char directory[1024], text_path[1024];
2072 // set the default directory
2073 sprintf(directory, "%stitle.rc", BCASTDIR);
2075 // load the defaults
2076 defaults = new Defaults(directory);
2079 defaults->get("FONT", config.font);
2080 defaults->get("ENCODING", config.encoding);
2081 config.style = defaults->get("STYLE", (int64_t)config.style);
2082 config.size = defaults->get("SIZE", config.size);
2083 config.color = defaults->get("COLOR", config.color);
2084 config.color_stroke = defaults->get("COLOR_STROKE", config.color_stroke);
2085 config.stroke_width = defaults->get("STROKE_WIDTH", config.stroke_width);
2086 config.motion_strategy = defaults->get("MOTION_STRATEGY", config.motion_strategy);
2087 config.loop = defaults->get("LOOP", config.loop);
2088 config.pixels_per_second = defaults->get("PIXELS_PER_SECOND", config.pixels_per_second);
2089 config.hjustification = defaults->get("HJUSTIFICATION", config.hjustification);
2090 config.vjustification = defaults->get("VJUSTIFICATION", config.vjustification);
2091 config.fade_in = defaults->get("FADE_IN", config.fade_in);
2092 config.fade_out = defaults->get("FADE_OUT", config.fade_out);
2093 config.x = defaults->get("TITLE_X", config.x);
2094 config.y = defaults->get("TITLE_Y", config.y);
2095 config.dropshadow = defaults->get("DROPSHADOW", config.dropshadow);
2096 config.timecode = defaults->get("TIMECODE", config.timecode);
2097 window_w = defaults->get("WINDOW_W", 660);
2098 window_h = defaults->get("WINDOW_H", 480);
2100 // Store text in separate path to isolate special characters
2102 sprintf(text_path, "%stitle_text.rc", BCASTDIR);
2103 fs.complete_path(text_path);
2104 FILE *fd = fopen(text_path, "rb");
2107 fseek(fd, 0, SEEK_END);
2108 int64_t len = ftell(fd);
2109 fseek(fd, 0, SEEK_SET);
2110 fread(config.text, len, 1, fd);
2111 config.text[len] = 0;
2112 //printf("TitleMain::load_defaults %s\n", config.text);
2120 int TitleMain::save_defaults()
2122 char text_path[1024];
2124 defaults->update("FONT", config.font);
2125 defaults->update("ENCODING", config.encoding);
2126 defaults->update("STYLE", (int64_t)config.style);
2127 defaults->update("SIZE", config.size);
2128 defaults->update("COLOR", config.color);
2129 defaults->update("COLOR_STROKE", config.color_stroke);
2130 defaults->update("STROKE_WIDTH", config.stroke_width);
2131 defaults->update("MOTION_STRATEGY", config.motion_strategy);
2132 defaults->update("LOOP", config.loop);
2133 defaults->update("PIXELS_PER_SECOND", config.pixels_per_second);
2134 defaults->update("HJUSTIFICATION", config.hjustification);
2135 defaults->update("VJUSTIFICATION", config.vjustification);
2136 defaults->update("FADE_IN", config.fade_in);
2137 defaults->update("FADE_OUT", config.fade_out);
2138 defaults->update("TITLE_X", config.x);
2139 defaults->update("TITLE_Y", config.y);
2140 defaults->update("DROPSHADOW", config.dropshadow);
2141 defaults->update("TIMECODE", config.timecode);
2142 defaults->update("WINDOW_W", window_w);
2143 defaults->update("WINDOW_H", window_h);
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, "wb");
2153 fwrite(config.text, strlen(config.text), 1, fd);
2157 // perror("TitleMain::save_defaults");
2164 int TitleMain::load_configuration()
2166 KeyFrame *prev_keyframe, *next_keyframe;
2167 prev_keyframe = get_prev_keyframe(get_source_position());
2168 next_keyframe = get_next_keyframe(get_source_position());
2170 // printf("TitleMain::load_configuration 1 %d %d\n",
2171 // prev_keyframe->position,
2172 // next_keyframe->position);
2174 TitleConfig old_config, prev_config, next_config;
2175 old_config.copy_from(config);
2176 read_data(prev_keyframe);
2177 prev_config.copy_from(config);
2178 read_data(next_keyframe);
2179 next_config.copy_from(config);
2181 config.prev_keyframe_position = prev_keyframe->position;
2182 config.next_keyframe_position = next_keyframe->position;
2183 if(config.next_keyframe_position == config.prev_keyframe_position)
2184 config.next_keyframe_position = get_source_start() + get_total_len();
2187 // printf("TitleMain::load_configuration 10 %d %d\n",
2188 // config.prev_keyframe_position,
2189 // config.next_keyframe_position);
2191 config.interpolate(prev_config,
2193 (next_keyframe->position == prev_keyframe->position) ?
2194 get_source_position() :
2195 prev_keyframe->position,
2196 (next_keyframe->position == prev_keyframe->position) ?
2197 get_source_position() + 1 :
2198 next_keyframe->position,
2199 get_source_position());
2201 if(!config.equivalent(old_config))
2218 void TitleMain::save_data(KeyFrame *keyframe)
2222 // cause data to be stored directly in text
2223 output.set_shared_string(keyframe->data, MESSAGESIZE);
2224 output.tag.set_title("TITLE");
2225 output.tag.set_property("FONT", config.font);
2226 output.tag.set_property("ENCODING", config.encoding);
2227 output.tag.set_property("STYLE", (int64_t)config.style);
2228 output.tag.set_property("SIZE", config.size);
2229 output.tag.set_property("COLOR", config.color);
2230 output.tag.set_property("COLOR_STROKE", config.color_stroke);
2231 output.tag.set_property("STROKE_WIDTH", config.stroke_width);
2232 output.tag.set_property("MOTION_STRATEGY", config.motion_strategy);
2233 output.tag.set_property("LOOP", config.loop);
2234 output.tag.set_property("PIXELS_PER_SECOND", config.pixels_per_second);
2235 output.tag.set_property("HJUSTIFICATION", config.hjustification);
2236 output.tag.set_property("VJUSTIFICATION", config.vjustification);
2237 output.tag.set_property("FADE_IN", config.fade_in);
2238 output.tag.set_property("FADE_OUT", config.fade_out);
2239 output.tag.set_property("TITLE_X", config.x);
2240 output.tag.set_property("TITLE_Y", config.y);
2241 output.tag.set_property("DROPSHADOW", config.dropshadow);
2242 output.tag.set_property("TIMECODE", config.timecode);
2243 output.append_tag();
2244 output.append_newline();
2246 output.append_text(config.text);
2248 output.tag.set_title("/TITLE");
2249 output.append_tag();
2250 output.append_newline();
2251 output.terminate_string();
2252 //printf("TitleMain::save_data 1\n%s\n", output.string);
2253 //printf("TitleMain::save_data 2\n%s\n", config.text);
2256 void TitleMain::read_data(KeyFrame *keyframe)
2260 input.set_shared_string(keyframe->data, strlen(keyframe->data));
2263 int new_interlace = 0;
2264 int new_horizontal = 0;
2265 int new_luminance = 0;
2267 config.prev_keyframe_position = keyframe->position;
2270 result = input.read_tag();
2274 if(input.tag.title_is("TITLE"))
2276 input.tag.get_property("FONT", config.font);
2277 input.tag.get_property("ENCODING", config.encoding);
2278 config.style = input.tag.get_property("STYLE", (int64_t)config.style);
2279 config.size = input.tag.get_property("SIZE", config.size);
2280 config.color = input.tag.get_property("COLOR", config.color);
2281 config.color_stroke = input.tag.get_property("COLOR_STROKE", config.color_stroke);
2282 config.stroke_width = input.tag.get_property("STROKE_WIDTH", config.stroke_width);
2283 config.motion_strategy = input.tag.get_property("MOTION_STRATEGY", config.motion_strategy);
2284 config.loop = input.tag.get_property("LOOP", config.loop);
2285 config.pixels_per_second = input.tag.get_property("PIXELS_PER_SECOND", config.pixels_per_second);
2286 config.hjustification = input.tag.get_property("HJUSTIFICATION", config.hjustification);
2287 config.vjustification = input.tag.get_property("VJUSTIFICATION", config.vjustification);
2288 config.fade_in = input.tag.get_property("FADE_IN", config.fade_in);
2289 config.fade_out = input.tag.get_property("FADE_OUT", config.fade_out);
2290 config.x = input.tag.get_property("TITLE_X", config.x);
2291 config.y = input.tag.get_property("TITLE_Y", config.y);
2292 config.dropshadow = input.tag.get_property("DROPSHADOW", config.dropshadow);
2293 config.timecode = input.tag.get_property("TIMECODE", config.timecode);
2294 strcpy(config.text, input.read_text());
2295 //printf("TitleMain::read_data 1\n%s\n", input.string);
2296 //printf("TitleMain::read_data 2\n%s\n", config.text);
2299 if(input.tag.title_is("/TITLE"))