add binfolder path relative filters, fix gbrp color model, vwdw timebar tweaks, title...
[goodguy/history.git] / cinelerra-5.1 / plugins / titler / titler.C
index b7d8e7275ac374018140353560d102eca867db5b..5086fea00d029d6101dae87de2b60ae5229449ed 100644 (file)
@@ -25,7 +25,6 @@
 // Additional support for UTF-8 by
 // Paolo Rampino aka Akirad <info at tuttoainternet.it>
 
-
 #include "asset.h"
 #include "bccmodels.h"
 #include "bcsignals.h"
@@ -52,7 +51,7 @@
 #include "titlerwindow.h"
 #include "transportque.h"
 #include "vrender.h"
-
+#include "workarounds.h"
 
 #include <errno.h>
 #include <stdint.h>
 #include <endian.h>
 #include <byteswap.h>
 #include <iconv.h>
-#include <wctype.h>
 #include <sys/stat.h>
 #include <fontconfig/fontconfig.h>
 
-#define FIXED_FONT "Bitstream Vera Sans Mono (Bits)"
+#define FIXED_FONT "bitstream vera sans mono (bitstream)"
 #define SMALL (1.0 / 64.0)
 
 #define MAX_FLT  3.40282347e+38
@@ -80,11 +78,15 @@ REGISTER_PLUGIN(TitleMain)
 #endif
 #define DEFAULT_TIMECODEFORMAT TIME_HMS
 
+static inline int kw_strcmp(const char *ap, const char *bp) {
+       return !strcmp(ap, bp) ? 0 : strcmp(ap,_(bp));
+}
+
 TitleConfig::TitleConfig()
 {
        strcpy(font, "fixed");
        strcpy(encoding, DEFAULT_ENCODING);
-       style = 0;
+       style = FONT_ALIAS;
        size = 24;
        color = BLACK;
        alpha = 0xff;
@@ -92,7 +94,7 @@ TitleConfig::TitleConfig()
        outline_color = WHITE;
        outline_alpha = 0xff;
        color_stroke = 0xff0000;
-       stroke_width = 1.0;
+       stroke_width = 0.0;
        motion_strategy = NO_MOTION;
        line_pitch = 0;
        loop = 0;
@@ -103,7 +105,7 @@ TitleConfig::TitleConfig()
        wtext[0] = 0;  wlen = 0;
        title_x = title_y = 0.0;
        title_w = title_h = 0;
-       window_w = 800;
+       window_w = 860;
        window_h = 460;
        next_keyframe_position = 0;
        prev_keyframe_position = 0;
@@ -288,13 +290,28 @@ GlyphUnit::GlyphUnit(TitleMain *plugin, GlyphEngine *server)
 GlyphUnit::~GlyphUnit()
 {
        if( freetype_library )
-               FT_Done_FreeType(freetype_library);
+               ft_Done_FreeType(freetype_library);
+}
+
+static inline void to_mono(VFrame *data)
+{
+       if( !data ) return;
+       int w = data->get_w(), h = data->get_h();
+       uint8_t **rows = data->get_rows();
+       for( int y=0; y<h; ++y ) {
+               uint8_t *dp = rows[y];
+               for( int x=0; x<w; ++x,++dp ) *dp = *dp >= 0x80 ? 0xff : 0;
+       }
 }
 
 void GlyphUnit::process_package(LoadPackage *package)
 {
        GlyphPackage *pkg = (GlyphPackage*)package;
        TitleGlyph *glyph = pkg->glyph;
+       BC_Resources *resources =  BC_WindowBase::get_resources();
+       if( resources->font_debug )
+               printf("GlyphUnit load glyph (%s) %04x, '%c'\n", glyph->font->displayname,
+                       (unsigned)glyph->char_code, (unsigned)glyph->char_code);
        int result = 0;
        char new_path[BCTEXTLEN];
        if( plugin->load_freetype_face(freetype_library, freetype_face, glyph->font->path) ) {
@@ -302,22 +319,24 @@ void GlyphUnit::process_package(LoadPackage *package)
                        glyph->font->path);
                result = 1;
        }
-
        if( !result ) {
-               int gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
-
-//printf("GlyphUnit::process_package 1 %c\n", glyph->char_code);
-// Char not found
+               int gindex = ft_Get_Char_Index(freetype_face, glyph->char_code);
+               if( !gindex && !freetype_face->charmap && // if no default charmap
+                   freetype_face->charmaps && freetype_face->charmaps[0] &&
+                   !ft_Select_Charmap(freetype_face, freetype_face->charmaps[0]->encoding) ) {
+                       gindex = ft_Get_Char_Index(freetype_face, glyph->char_code);
+               }
                if( gindex == 0 ) {
-                       BC_Resources *resources =  BC_WindowBase::get_resources();
+printf("GlyphUnit::process_package 1 glyph not found (%s) %04x, '%c'\n",
+ glyph->font->displayname, (unsigned)glyph->char_code, (unsigned)glyph->char_code);
                        // Search replacement font
                        if( resources->find_font_by_char(glyph->char_code, new_path, freetype_face) ) {
                                plugin->load_freetype_face(freetype_library,
                                        freetype_face, new_path);
-                               gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
+                               gindex = ft_Get_Char_Index(freetype_face, glyph->char_code);
                        }
                }
-               FT_Set_Pixel_Sizes(freetype_face, glyph->size, 0);
+               ft_Set_Pixel_Sizes(freetype_face, glyph->size, 0);
 
                if( gindex == 0 ) {
 // carrige return
@@ -347,9 +366,9 @@ void GlyphUnit::process_package(LoadPackage *package)
                        FT_Glyph glyph_image;
                        FT_BBox bbox;
                        FT_Bitmap bm;
-                       FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
-                       FT_Get_Glyph(freetype_face->glyph, &glyph_image);
-                       FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
+                       ft_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
+                       ft_Get_Glyph(freetype_face->glyph, &glyph_image);
+                       ft_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
 //                     printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n",
 //                                     bbox.xMin,bbox.xMax, bbox.yMin, bbox.yMax);
 
@@ -370,12 +389,12 @@ void GlyphUnit::process_package(LoadPackage *package)
                        glyph->data = new VFrame(glyph->width, glyph->height, BC_A8, glyph->pitch);
                        glyph->data->clear_frame();
                        bm.buffer = glyph->data->get_data();
-                       FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
+                       ft_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
                                - bbox.xMin, - bbox.yMin);
-                       FT_Outline_Get_Bitmap( freetype_library,
+                       ft_Outline_Get_Bitmap( freetype_library,
                                &((FT_OutlineGlyph) glyph_image)->outline,
                                &bm);
-                       FT_Done_Glyph(glyph_image);
+                       ft_Done_Glyph(glyph_image);
                }
                else {
 // Outline desired and glyph found
@@ -386,14 +405,14 @@ void GlyphUnit::process_package(LoadPackage *package)
                        FT_BBox bbox;
                        FT_UInt npoints, ncontours;
 
-                       FT_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
-                       FT_Get_Glyph(freetype_face->glyph, &glyph_image);
+                       ft_Load_Glyph(freetype_face, gindex, FT_LOAD_DEFAULT);
+                       ft_Get_Glyph(freetype_face->glyph, &glyph_image);
 
 // check if the outline is ok (non-empty);
-                       FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
+                       ft_Outline_Get_BBox(&((FT_OutlineGlyph) glyph_image)->outline, &bbox);
                        if( bbox.xMin == 0 && bbox.xMax == 0 &&
                            bbox.yMin == 0 && bbox.yMax == 0 ) {
-                               FT_Done_Glyph(glyph_image);
+                               ft_Done_Glyph(glyph_image);
                                glyph->width = 0;   glyph->height = 0;
                                glyph->left = 0;    glyph->top = 0;
                                glyph->right = 0;   glyph->bottom = 0;
@@ -407,18 +426,18 @@ void GlyphUnit::process_package(LoadPackage *package)
                                return;
                        }
 #if FREETYPE_MAJOR > 2 || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 2)
-                       FT_Stroker_New(freetype_library, &stroker);
+                       ft_Stroker_New(freetype_library, &stroker);
 #else
-                       FT_Stroker_New(((FT_LibraryRec *)freetype_library)->memory, &stroker);
+                       ft_Stroker_New(((FT_LibraryRec *)freetype_library)->memory, &stroker);
 #endif
-                       FT_Stroker_Set(stroker, (int)(plugin->config.stroke_width * 64),
+                       ft_Stroker_Set(stroker, (int)(plugin->config.stroke_width * 64),
                                FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
-                       FT_Stroker_ParseOutline(stroker, &((FT_OutlineGlyph) glyph_image)->outline,1);
-                       FT_Stroker_GetCounts(stroker,&npoints, &ncontours);
+                       ft_Stroker_ParseOutline(stroker, &((FT_OutlineGlyph) glyph_image)->outline,1);
+                       ft_Stroker_GetCounts(stroker,&npoints, &ncontours);
                        if( npoints == 0 && ncontours == 0 ) {
 // this never happens, but FreeType has a bug regarding Linotype's Palatino font
-                               FT_Stroker_Done(stroker);
-                               FT_Done_Glyph(glyph_image);
+                               ft_Stroker_Done(stroker);
+                               ft_Done_Glyph(glyph_image);
                                glyph->width = 0;   glyph->height = 0;
                                glyph->left = 0;    glyph->top = 0;
                                glyph->right = 0;   glyph->bottom = 0;
@@ -432,13 +451,13 @@ void GlyphUnit::process_package(LoadPackage *package)
                                return;
                        };
 
-                       FT_Outline_New(freetype_library, npoints, ncontours, &outline);
+                       ft_Outline_New(freetype_library, npoints, ncontours, &outline);
                        outline.n_points=0;
                        outline.n_contours=0;
-                       FT_Stroker_Export (stroker, &outline);
-                       FT_Outline_Get_BBox(&outline, &bbox);
-                       FT_Outline_Translate(&outline, - bbox.xMin, - bbox.yMin);
-                       FT_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
+                       ft_Stroker_Export(stroker, &outline);
+                       ft_Outline_Get_BBox(&outline, &bbox);
+                       ft_Outline_Translate(&outline, - bbox.xMin, - bbox.yMin);
+                       ft_Outline_Translate(&((FT_OutlineGlyph) glyph_image)->outline,
                                        - bbox.xMin,
                                        - bbox.yMin + (int)(plugin->config.stroke_width*32));
 //                     printf("Stroke: Xmin: %ld, Xmax: %ld, Ymin: %ld, yMax: %ld\n"
@@ -474,19 +493,24 @@ void GlyphUnit::process_package(LoadPackage *package)
                        glyph->data_stroke->clear_frame();
 // for debugging       memset( glyph->data_stroke->get_data(), 60, glyph->pitch * glyph->height);
                        bm.buffer=glyph->data->get_data();
-                       FT_Outline_Get_Bitmap( freetype_library,
+                       ft_Outline_Get_Bitmap( freetype_library,
                                &((FT_OutlineGlyph) glyph_image)->outline,
                                &bm);
                        bm.buffer=glyph->data_stroke->get_data();
-                       FT_Outline_Get_Bitmap( freetype_library,
+                       ft_Outline_Get_Bitmap( freetype_library,
                        &outline,
                                &bm);
-                       FT_Outline_Done(freetype_library,&outline);
-                       FT_Stroker_Done(stroker);
-                       FT_Done_Glyph(glyph_image);
+                       ft_Outline_Done(freetype_library,&outline);
+                       ft_Stroker_Done(stroker);
+                       ft_Done_Glyph(glyph_image);
 
 //printf("GlyphUnit::process_package 2\n");
                }
+
+               if( !(glyph->style & FONT_ALIAS) ) {
+                       to_mono(glyph->data);
+                       to_mono(glyph->data_stroke);
+               }
        }
 }
 
@@ -539,7 +563,7 @@ TitleUnit::TitleUnit(TitleMain *plugin, TitleEngine *server)
 static void get_mask_colors(int rgb, int color_model, int &rr, int &gg, int &bb)
 {
        int r = 0xff & (rgb>>16), g = 0xff & (rgb>>8), b = 0xff & (rgb>>0);
-       if( BC_CModels::is_yuv(color_model) ) bc_rgb2yuv(r,g,b, r,g,b);
+       if( BC_CModels::is_yuv(color_model) ) YUV::yuv.rgb_to_yuv_8(r,g,b);
        rr = r;  gg = g; bb = b;
 }
 
@@ -568,7 +592,7 @@ void TitleUnit::draw_frame(int mode, VFrame *dst, VFrame *src, int x, int y)
                while( y_inp < inp_h && y_out < out_h ) {
                        uint8_t *inp = inp_rows[y_inp], *out = out_rows[y_out];
                        for( int xin=x_inp,xout=x_out*4+3; xin<inp_w; ++xin,xout+=4 ) {
-                               out[xout] = inp[xin];
+                               out[xout] = STD_ALPHA(max, inp[xin], out[xout]);
                        }
                        ++y_inp;  ++y_out;
                }
@@ -581,11 +605,18 @@ void TitleUnit::draw_frame(int mode, VFrame *dst, VFrame *src, int x, int y)
                        for( int xin=x_inp,xout=x_out*4+0; xin<inp_w; ++xin,xout+=4 ) {
                                int in_a = inp[xin], out_a = out[xout+3];
                                if( in_a + out_a == 0 ) continue;
-                               int opacity = (in_a * alpha)/max, transp = out_a * (max - opacity)/max;
-                               out[xout+0] = (opacity * r + transp * out[xout+0]) / max;
-                               out[xout+1] = (opacity * (g-ofs) + transp * (out[xout+1]-ofs)) / max + ofs;
-                               out[xout+2] = (opacity * (b-ofs) + transp * (out[xout+2]-ofs)) / max + ofs;
-                               out[xout+3] = opacity + transp - (opacity * transp) / max;
+                               if( in_a >= out_a ) { // crayola for top draw
+                                       out[xout+0] = r;  out[xout+1] = g;  out[xout+2] = b;
+                                       out[xout+3] = alpha * in_a / max;
+                               }
+                               else {
+                                       int i_r = r, i_g = g-ofs, i_b = b-ofs;
+                                       int o_r = out[xout+0], o_g = out[xout+1]-ofs, o_b = out[xout+2]-ofs;
+                                       out[xout+0] = COLOR_NORMAL(max, i_r, in_a, o_r, out_a);
+                                       out[xout+1] = COLOR_NORMAL(max, i_g, in_a, o_g, out_a) + ofs;
+                                       out[xout+2] = COLOR_NORMAL(max, i_b, in_a, o_b, out_a) + ofs;
+                                       out[xout+3] = alpha * STD_ALPHA(max, in_a, out_a) / max;
+                               }
                        }
                        ++y_inp;  ++y_out;
                }
@@ -594,13 +625,22 @@ void TitleUnit::draw_frame(int mode, VFrame *dst, VFrame *src, int x, int y)
                while( y_inp < inp_h && y_out < out_h ) {
                        uint8_t *inp = inp_rows[y_inp], *out = out_rows[y_out];
                        for( int xin=x_inp,xout=x_out*4+0; xin<inp_w; ++xin,xout+=4 ) {
-                               int xinp = xin*4, in_a = inp[xinp+3], out_a = out[xout+3];
+                               int xinp = xin*4;
+                               int in_a = inp[xinp+3], out_a = out[xout+3];
                                if( in_a + out_a == 0 ) continue;
-                               int opacity = (in_a * alpha)/max, transp = out_a * (max - opacity)/max;
-                               out[xout+0] = (opacity * inp[xinp+0] + transp * out[xout+0]) / max;
-                               out[xout+1] = (opacity * (inp[xinp+1]-ofs) + transp * (out[xout+1]-ofs)) / max + ofs;
-                               out[xout+2] = (opacity * (inp[xinp+2]-ofs) + transp * (out[xout+2]-ofs)) / max + ofs;
-                               out[xout+3] = opacity + transp - (opacity * transp) / max;
+                               if( in_a >= out_a ) {
+                                       int i_r = inp[xinp+0], i_g = inp[xinp+1], i_b = inp[xinp+2];
+                                       out[xout+0] = i_r;  out[xout+1] = i_g;  out[xout+2] = i_b;
+                                       out[xout+3] = alpha * in_a / max;
+                               }
+                               else {
+                                       int i_r = inp[xinp+0], i_g = inp[xinp+1]-ofs, i_b = inp[xinp+2]-ofs;
+                                       int o_r = out[xout+0], o_g = out[xout+1]-ofs, o_b = out[xout+2]-ofs;
+                                       out[xout+0] = COLOR_NORMAL(max, i_r, in_a, o_r, out_a);
+                                       out[xout+1] = COLOR_NORMAL(max, i_g, in_a, o_g, out_a) + ofs;
+                                       out[xout+2] = COLOR_NORMAL(max, i_b, in_a, o_b, out_a) + ofs;
+                                       out[xout+3] = alpha * STD_ALPHA(max, in_a, out_a) / max;
+                               }
                        }
                        ++y_inp;  ++y_out;
                }
@@ -670,69 +710,6 @@ LoadPackage* TitleEngine::new_package()
        return new TitlePackage;
 }
 
-void TitleTranslateUnit::translation_array_f(transfer_table_f* &table,
-       float in_x1, float in_x2, int in_total,
-       float out_x1, float out_x2, int out_total,
-       int &x1_out, int &x2_out)
-{
-       int out_w;
-       //float offset = out_x1 - in_x1;
-
-       x1_out = (int)out_x1;
-       x2_out = MIN((int)ceil(out_x2), out_total);
-       if( x1_out >= x2_out ) return;
-
-       out_w = x2_out - x1_out;
-       table = new transfer_table_f[out_w];
-       bzero(table, sizeof(transfer_table_f) * out_w);
-
-       float in_x = in_x1;
-       for( int out_x=x1_out; out_x<x2_out; ++out_x ) {
-               transfer_table_f *entry = &table[out_x - x1_out];
-
-               entry->in_x1 = (int)in_x;
-               entry->in_x2 = (int)in_x + 1;
-
-// Get fraction of output pixel to fill
-               entry->output_fraction = 1;
-
-               if( out_x1 > out_x ) {
-                       entry->output_fraction -= out_x1 - out_x;
-               }
-
-               if( out_x2 < out_x + 1 ) {
-                       entry->output_fraction = (out_x2 - out_x);
-               }
-
-// Advance in_x until out_x_fraction is filled
-               float out_x_fraction = entry->output_fraction;
-               float in_x_fraction = floor(in_x + 1) - in_x;
-
-               if( out_x_fraction <= in_x_fraction ) {
-                       entry->in_fraction1 = out_x_fraction;
-                       entry->in_fraction2 = 0.0;
-                       in_x += out_x_fraction;
-               }
-               else {
-                       entry->in_fraction1 = in_x_fraction;
-                       in_x += out_x_fraction;
-                       entry->in_fraction2 = in_x - floor(in_x);
-               }
-
-// Clip in_x and zero out fraction.  This doesn't work for YUV.
-               if( entry->in_x2 >= in_total ) {
-                       entry->in_x2 = in_total - 1;
-                       entry->in_fraction2 = 0.0;
-               }
-
-               if( entry->in_x1 >= in_total ) {
-                       entry->in_x1 = in_total - 1;
-                       entry->in_fraction1 = 0.0;
-               }
-       }
-}
-
-
 
 // Copy a single character to the text mask
 TitleOutlinePackage::TitleOutlinePackage()
@@ -758,16 +735,16 @@ void TitleOutlineUnit::process_package(LoadPackage *package)
        unsigned char **text_rows = plugin->text_mask->get_rows();
        int mask_w1 = plugin->text_mask->get_w()-1;
        int mask_h1 = plugin->text_mask->get_h()-1;
-       int ofs = plugin->config.outline_size;
+       int oln_sz = plugin->config.outline_size;
 
        if( engine->pass == 0 ) {
 // get max alpha under outline size macropixel
                for( int y=pkg->y1, my=pkg->y2; y<my; ++y ) {
                        unsigned char *out_row = outline_rows[y];
-                       int y1 = y - ofs, y2 = y + ofs;
+                       int y1 = y - oln_sz, y2 = y + oln_sz;
                        CLAMP(y1, 0, mask_h1);  CLAMP(y2, 0, mask_h1);
                        for( int x=0, mx=plugin->text_mask->get_w(); x<mx; ++x ) {
-                               int x1 = x - ofs, x2 = x + ofs;
+                               int x1 = x - oln_sz, x2 = x + oln_sz;
                                CLAMP(x1, 0, mask_w1);  CLAMP(x2, 0, mask_w1);
                                int max_a = 0;
                                for( int yy=y1; yy<=y2; ++yy ) {
@@ -840,179 +817,6 @@ LoadPackage* TitleOutlineEngine::new_package()
 }
 
 
-
-TitleTranslatePackage::TitleTranslatePackage()
- : LoadPackage()
-{
-       y1 = y2 = 0;
-}
-
-
-TitleTranslateUnit::TitleTranslateUnit(TitleMain *plugin, TitleTranslate *server)
- : LoadClient(server)
-{
-       this->plugin = plugin;
-}
-
-void TitleTranslate::run_packages()
-{
-// Generate scaling tables
-       delete [] x_table;  x_table = 0;
-       delete [] y_table;  y_table = 0;
-
-       float in_w = xlat_mask->get_w();
-       float in_h = xlat_mask->get_h();
-       float ix1 = 0, ix2 = ix1 + in_w;
-       float iy1 = 0, iy2 = iy1 + in_h;
-
-       float out_w = plugin->output->get_w();
-       float out_h = plugin->output->get_h();
-       float x1 = plugin->title_x, x2 = x1 + plugin->title_w;
-       float y1 = plugin->title_y, y2 = y1 + plugin->title_h;
-       bclamp(x1, 0, out_w);  bclamp(y1, 0, out_h);
-       bclamp(x2, 0, out_w);  bclamp(y2, 0, out_h);
-
-       float ox1 = plugin->title_x + plugin->text_x - plugin->text_x1 + plugin->mask_x1, ox2 = ox1 + in_w;
-       float oy1 = plugin->title_y + plugin->text_y - plugin->text_y1 + plugin->mask_y1, oy2 = oy1 + in_h;
-       if( ox1 < x1 ) { ix1 -= (ox1-x1);  ox1 = x1; }
-       if( oy1 < y1 ) { iy1 -= (oy1-y1);  oy1 = y1; }
-       if( ox2 > x2 ) { ix2 -= (ox2-x2);  ox2 = x2; }
-       if( oy2 > y2 ) { iy2 -= (oy2-x2);  oy2 = y2; }
-#if 0
-printf("TitleTranslate text  txy=%7.2f,%-7.2f\n"
-  "  mxy1=%7d,%-7d mxy2=%7d,%-7d\n"
-  "   xy1=%7.2f,%-7.2f  xy2=%7.2f,%-7.2f\n"
-  "  ixy1=%7.2f,%-7.2f ixy2=%7.2f,%-7.2f\n"
-  "  oxy1=%7.2f,%-7.2f oxy2=%7.2f,%-7.2f\n",
-   plugin->text_x, plugin->text_y,
-   plugin->mask_x1, plugin->mask_y1, plugin->mask_x2, plugin->mask_y2,
-   x1,y1, x2,y2, ix1,iy1, ix2,iy2, ox1,oy1, ox2,oy2);
-#endif
-       out_x1 = out_x2 = out_y1 = out_y2 = 0;
-       TitleTranslateUnit::translation_array_f(x_table,
-               ix1, ix2, in_w, ox1, ox2, out_w, out_x1, out_x2);
-       TitleTranslateUnit::translation_array_f(y_table,
-               iy1, iy2, in_h, oy1, oy2, out_h, out_y1, out_y2);
-
-       process_packages();
-}
-
-
-
-#define TRANSLATE(type, max, components, ofs) { \
-       unsigned char **in_rows = server->xlat_mask->get_rows(); \
-       type **out_rows = (type**)plugin->output->get_rows(); \
- \
-       for( int y=pkg->y1; y<pkg->y2; ++y ) { \
-               int in_y1 = server->y_table[y].in_x1; \
-               int in_y2 = server->y_table[y].in_x2; \
-               float y_f1 = server->y_table[y].in_fraction1; \
-               float y_f2 = server->y_table[y].in_fraction2; \
-               unsigned char *in_row1 = in_rows[in_y1]; \
-               unsigned char *in_row2 = in_rows[in_y2]; \
-               type *out_row = out_rows[y + server->out_y1]; \
-               for( int i=0,x=server->out_x1; x<server->out_x2; ++i,++x ) { \
-                       int in_x1 = 4*server->x_table[i].in_x1; \
-                       int in_x2 = 4*server->x_table[i].in_x2; \
-                       float x_f1 = server->x_table[i].in_fraction1; \
-                       float x_f2 = server->x_table[i].in_fraction2; \
-                       float f11 = x_f1 * y_f1 / (256.f-max); \
-                       float f12 = x_f2 * y_f1 / (256.f-max); \
-                       float f21 = x_f1 * y_f2 / (256.f-max); \
-                       float f22 = x_f2 * y_f2 / (256.f-max); \
-                       type input_r = (type)( \
-                               in_row1[in_x1 + 0] * f11 + in_row1[in_x2 + 0] * f12 +  \
-                               in_row2[in_x1 + 0] * f21 + in_row2[in_x2 + 0] * f22 ); \
-                       type input_g = (type)( \
-                               in_row1[in_x1 + 1] * f11 + in_row1[in_x2 + 1] * f12 +  \
-                               in_row2[in_x1 + 1] * f21 + in_row2[in_x2 + 1] * f22 ); \
-                       type input_b = (type)( \
-                               in_row1[in_x1 + 2] * f11 + in_row1[in_x2 + 2] * f12 +  \
-                               in_row2[in_x1 + 2] * f21 + in_row2[in_x2 + 2] * f22 ); \
-                       type input_a = (type)( \
-                               in_row1[in_x1 + 3] * f11 + in_row1[in_x2 + 3] * f12 +  \
-                               in_row2[in_x1 + 3] * f21 + in_row2[in_x2 + 3] * f22 ); \
-                       input_a = input_a * plugin->fade; \
-                       if( components == 4 ) { \
-                               type transparency = out_row[x * components + 3] * (max - input_a) / max; \
-                               out_row[x * components + 0] =  (input_r * input_a + \
-                                       out_row[x * components + 0] * transparency) / max; \
-                               out_row[x * components + 1] =  ((input_g-ofs) * input_a + \
-                                       (out_row[x * components + 1]-ofs) * transparency) / max + ofs; \
-                               out_row[x * components + 2] =  ((input_b-ofs) * input_a + \
-                                       (out_row[x * components + 2]-ofs) * transparency) / max + ofs; \
-                               out_row[x * components + 3] =  MAX(input_a, out_row[x * components + 3]); \
-                       } \
-                       else { \
-                               type transparency = max - input_a; \
-                               out_row[x * components + 0] = (input_r * input_a + \
-                                       out_row[x * components + 0] * transparency) / max; \
-                               out_row[x * components + 1] =  ((input_g-ofs) * input_a + \
-                                       (out_row[x * components + 1]-ofs) * transparency) / max + ofs; \
-                               out_row[x * components + 2] =  ((input_b-ofs) * input_a + \
-                                       (out_row[x * components + 2]-ofs) * transparency) / max + ofs; \
-                       } \
-               } \
-       } \
-}
-
-
-void TitleTranslateUnit::process_package(LoadPackage *package)
-{
-       TitleTranslatePackage *pkg = (TitleTranslatePackage*)package;
-       TitleTranslate *server = (TitleTranslate*)this->server;
-
-       switch( plugin->output->get_color_model() ) {
-       case BC_RGB888:     TRANSLATE(unsigned char, 0xff, 3, 0);    break;
-       case BC_RGB_FLOAT:  TRANSLATE(float, 1.0, 3, 0);             break;
-       case BC_YUV888:     TRANSLATE(unsigned char, 0xff, 3, 0x80); break;
-       case BC_RGBA_FLOAT: TRANSLATE(float, 1.0, 4, 0);             break;
-       case BC_RGBA8888:   TRANSLATE(unsigned char, 0xff, 4, 0);    break;
-       case BC_YUVA8888:   TRANSLATE(unsigned char, 0xff, 4, 0x80); break;
-       }
-//printf("TitleTranslateUnit::process_package 5\n");
-}
-
-
-TitleTranslate::TitleTranslate(TitleMain *plugin, int cpus)
- : LoadServer(cpus, cpus)
-{
-       this->plugin = plugin;
-       x_table = 0;
-       y_table = 0;
-       out_x1 = out_x2 = 0;
-       out_y1 = out_y2 = 0;
-}
-
-TitleTranslate::~TitleTranslate()
-{
-       delete [] x_table;
-       delete [] y_table;
-}
-
-void TitleTranslate::init_packages()
-{
-       int out_h = out_y2 - out_y1;
-       int py1 = 0, py2 = 0;
-       int pkgs = get_total_packages();
-       for( int i=0; i<pkgs; py1=py2 ) {
-               TitleTranslatePackage *pkg = (TitleTranslatePackage*)get_package(i);
-               py2 = (++i*out_h) / pkgs;
-               pkg->y1 = py1;  pkg->y2 = py2;
-       }
-//printf("TitleTranslate::init_packages 2\n");
-}
-
-LoadClient* TitleTranslate::new_client()
-{
-       return new TitleTranslateUnit(plugin, this);
-}
-
-LoadPackage* TitleTranslate::new_package()
-{
-       return new TitleTranslatePackage;
-}
-
 TitleCurNudge::TitleCurNudge(TitleParser *parser, TitleMain *plugin)
  : TitleStack<int>(parser, 0)
 {
@@ -1057,6 +861,10 @@ TitleCurFixed::TitleCurFixed(TitleParser *parser, TitleMain *plugin)
  : TitleStack<int>(parser, 0)
 {
 }
+TitleCurAlias::TitleCurAlias(TitleParser *parser, TitleMain *plugin)
+ : TitleStack<int>(parser, (plugin->config.style & FONT_ALIAS) ? 1 : 0)
+{
+}
 TitleCurSuper::TitleCurSuper(TitleParser *parser, TitleMain *plugin)
  : TitleStack<int>(parser, 0)
 {
@@ -1075,6 +883,7 @@ TitleParser::TitleParser(TitleMain *plugin)
    cur_under(this, plugin),
    cur_blink(this, plugin),
    cur_fixed(this, plugin),
+   cur_alias(this, plugin),
    cur_super(this, plugin)
 {
        bfr = out = plugin->config.wtext;
@@ -1133,14 +942,14 @@ TitleMain::~TitleMain()
        delete glyph_engine;
        delete title_engine;
        if( freetype_face )
-               FT_Done_Face(freetype_face);
+               ft_Done_Face(freetype_face);
        if( freetype_library )
-               FT_Done_FreeType(freetype_library);
+               ft_Done_FreeType(freetype_library);
        delete translate;
        delete outline_engine;
 }
 
-const char* TitleMain::plugin_title() { return _("Title"); }
+const char* TitleMain::plugin_title() { return N_("Title"); }
 int TitleMain::is_realtime() { return 1; }
 int TitleMain::is_synthesis() { return 1; }
 
@@ -1149,7 +958,8 @@ NEW_WINDOW_MACRO(TitleMain, TitleWindow);
 
 void TitleMain::build_previews(TitleWindow *gui)
 {
-       ArrayList<BC_FontEntry*>&fonts = *gui->get_resources()->fontlist;
+       BC_Resources *resources =  BC_WindowBase::get_resources();
+       ArrayList<BC_FontEntry*>&fonts = *resources->fontlist;
 
        for( int font_number=0; font_number<fonts.size(); ++font_number ) {
                BC_FontEntry *font = fonts.get(font_number);
@@ -1164,7 +974,7 @@ void TitleMain::build_previews(TitleWindow *gui)
        char new_path[BCTEXTLEN];
        int text_height = gui->get_text_height(LARGEFONT);
        int max_height = 3*text_height/2, max_width = 2 * max_height;
-       int text_color = BC_WindowBase::get_resources()->default_text_color;
+       int text_color = resources->default_text_color;
        int r = (text_color >> 16) & 0xff;
        int g = (text_color >> 8) & 0xff;
        int b = text_color & 0xff;
@@ -1177,6 +987,8 @@ void TitleMain::build_previews(TitleWindow *gui)
        int total_w = 0;
        int total_h = 0;
        for( int pass=0; pass<2; ++pass ) {
+               if( resources->font_debug )
+                       printf("Titler: build previews pass %d\n",pass);
 //printf("TitleMain::build_previews %d %d %d\n",
 //__LINE__, text_height, total_h);
                for( int font_number=0; font_number<fonts.size(); ++font_number ) {
@@ -1186,7 +998,7 @@ void TitleMain::build_previews(TitleWindow *gui)
                        for( int i=0; i<font_number; ++i ) {
                                if( !strcasecmp(fonts[i]->displayname, font->displayname) ) {
                                        if( pass == 1 ) {
-                                               font->image = fonts[i]->image;
+                                               font->image = new VFrame(*fonts[i]->image);
                                        }
                                        skip = 1;
                                        break;
@@ -1194,6 +1006,8 @@ void TitleMain::build_previews(TitleWindow *gui)
                        }
 
                        if( skip ) continue;
+                       if( resources->font_debug )
+                               printf("Titler: preview %s = %s\n",font->displayname, font->path);
                        if( pass > 0 ) {
                                font->image = new VFrame;
                                font->image->set_use_shm(0);
@@ -1215,8 +1029,8 @@ void TitleMain::build_previews(TitleWindow *gui)
 //                                     (char *)new_path);
                                strcpy(new_path, font->path);
                                if( load_freetype_face(freetype_library, freetype_face, new_path)) continue;
-                               FT_Set_Pixel_Sizes(freetype_face, text_height, 0);
-                               if( FT_Load_Char(freetype_face, c, FT_LOAD_RENDER) ) continue;
+                               ft_Set_Pixel_Sizes(freetype_face, text_height, 0);
+                               if( ft_Load_Char(freetype_face, c, FT_LOAD_RENDER) ) continue;
                                int glyph_w = freetype_face->glyph->bitmap.width;
                                int glyph_h = freetype_face->glyph->bitmap.rows;
                                if( glyph_h > max_height ) glyph_h = max_height;
@@ -1260,7 +1074,7 @@ void TitleMain::build_previews(TitleWindow *gui)
                }
        }
 
-       if( freetype_library ) FT_Done_FreeType(freetype_library);
+       if( freetype_library ) ft_Done_FreeType(freetype_library);
 }
 
 
@@ -1276,20 +1090,20 @@ int TitleMain::check_char_code_path(FT_Library &freetype_library,
        FcConfig *config;
        FcPattern *font;
 
-       FcInit();
-       config = FcConfigGetCurrent();
-       FcConfigSetRescanInterval(config, 0);
+       fcInit();
+       config = fcConfigGetCurrent();
+       fcConfigSetRescanInterval(config, 0);
 
-       pat = FcPatternCreate();
-       os = FcObjectSetBuild ( FC_FILE, FC_FONTFORMAT, (char *) 0);
-       fs = FcFontList(config, pat, os);
+       pat = fcPatternCreate();
+       os = fcObjectSetBuild( FC_FILE, FC_FONTFORMAT, (char *) 0);
+       fs = fcFontList(config, pat, os);
        int found = 0;
        char tmpstring[BCTEXTLEN];
        int limit_to_truetype = 0; //if you want to limit search to truetype put 1
-       if( !freetype_library ) FT_Init_FreeType(&freetype_library);
-       if( !FT_New_Face(freetype_library, path_old, 0, &temp_freetype_face) ) {
-               FT_Set_Pixel_Sizes(temp_freetype_face, 128, 0);
-               int gindex = FT_Get_Char_Index(temp_freetype_face, char_code);
+       if( !freetype_library ) ft_Init_FreeType(&freetype_library);
+       if( !ft_New_Face(freetype_library, path_old, 0, &temp_freetype_face) ) {
+               ft_Set_Pixel_Sizes(temp_freetype_face, 128, 0);
+               int gindex = ft_Get_Char_Index(temp_freetype_face, char_code);
                if( gindex != 0 && char_code == 10 ) {
                        strcpy(path_new, path_old);
                        found = 1;
@@ -1299,13 +1113,13 @@ int TitleMain::check_char_code_path(FT_Library &freetype_library,
        if( !found ) {
                for( int i=0; fs && i<fs->nfont; ++i ) {
                        font = fs->fonts[i];
-                       FcPatternGetString(font, FC_FONTFORMAT, 0, &format);
+                       fcPatternGetString(font, FC_FONTFORMAT, 0, &format);
                        if( strcmp((char *)format, "TrueType") && !limit_to_truetype ) continue;
-                       if( FcPatternGetString(font, FC_FILE, 0, &file) != FcResultMatch ) continue;
+                       if( fcPatternGetString(font, FC_FILE, 0, &file) != FcResultMatch ) continue;
                        sprintf(tmpstring, "%s", file);
-                       if( !FT_New_Face(freetype_library, tmpstring, 0, &temp_freetype_face) ) continue;
-                       FT_Set_Pixel_Sizes(temp_freetype_face, 128, 0);
-                       int gindex = FT_Get_Char_Index(temp_freetype_face, char_code);
+                       if( !ft_New_Face(freetype_library, tmpstring, 0, &temp_freetype_face) ) continue;
+                       ft_Set_Pixel_Sizes(temp_freetype_face, 128, 0);
+                       int gindex = ft_Get_Char_Index(temp_freetype_face, char_code);
                        if( gindex != 0 && char_code == 10 ) {
                                sprintf(path_new, "%s", tmpstring);
                                found = 1;
@@ -1315,8 +1129,8 @@ int TitleMain::check_char_code_path(FT_Library &freetype_library,
        }
 
 done:
-       if( fs ) FcFontSetDestroy(fs);
-       if( temp_freetype_face ) FT_Done_Face(temp_freetype_face);
+       if( fs ) fcFontSetDestroy(fs);
+       if( temp_freetype_face ) ft_Done_Face(temp_freetype_face);
        temp_freetype_face = 0;
 
        if( !found ) {
@@ -1333,14 +1147,14 @@ int TitleMain::load_freetype_face(FT_Library &freetype_library,
 {
 //printf("TitleMain::load_freetype_face 1\n");
        if( !freetype_library )
-               FT_Init_FreeType(&freetype_library);
+               ft_Init_FreeType(&freetype_library);
        if( freetype_face )
-               FT_Done_Face(freetype_face);
+               ft_Done_Face(freetype_face);
        freetype_face = 0;
 //printf("TitleMain::load_freetype_face 2\n");
 
 // Use freetype's internal function for loading font
-       if( FT_New_Face(freetype_library, path, 0, &freetype_face) ) {
+       if( ft_New_Face(freetype_library, path, 0, &freetype_face) ) {
                fprintf(stderr, _("TitleMain::load_freetype_face %s failed.\n"), path);
                freetype_face = 0;
                freetype_library = 0;
@@ -1351,7 +1165,7 @@ int TitleMain::load_freetype_face(FT_Library &freetype_library,
 
 int TitleMain::load_font(BC_FontEntry *font)
 {
-       if( load_freetype_face(freetype_library,freetype_face, font->path) ) return 1;
+       if( !font || load_freetype_face(freetype_library,freetype_face, font->path) ) return 1;
        strcpy(text_font, font->displayname);
        return 0;
 }
@@ -1455,7 +1269,7 @@ void TitleMain::draw_background()
                        delete bg_frame;  bg_frame = 0;
                }
                if( !bg_frame )
-                       bg_frame = new VFrame(0, -1, bw, bh, output_model, -1);
+                       bg_frame = new VFrame(bw, bh, output_model);
                int64_t position = get_source_position() - get_source_start();
                if( !read_background(bg_frame, position, output_model) ) {
                        if( !overlay_frame )
@@ -1476,37 +1290,41 @@ BC_FontEntry* TitleMain::get_font(const char *font_name, int style)
 {
        if( !strcmp("fixed", font_name) )
                font_name = FIXED_FONT;
-       int flavor =
+       int flavor = FL_WIDTH_MASK |
            ((style & BC_FONT_ITALIC) != 0 ? FL_SLANT_ITALIC | FL_SLANT_OBLIQUE : FL_SLANT_ROMAN) |
            ((style & BC_FONT_BOLD) != 0 ? FL_WEIGHT_BOLD | FL_WEIGHT_DEMIBOLD |
                        FL_WEIGHT_EXTRABOLD| FL_WEIGHT_BLACK | FL_WEIGHT_EXTRABLACK :
                        FL_WEIGHT_BOOK | FL_WEIGHT_NORMAL | FL_WEIGHT_MEDIUM |
                        FL_WEIGHT_LIGHT | FL_WEIGHT_EXTRALIGHT | FL_WEIGHT_THIN);
 
-       int mask = FL_WEIGHT_MASK | FL_SLANT_MASK;
+       int mask = FL_WEIGHT_MASK | FL_SLANT_MASK | FL_WIDTH_MASK;
 
        BC_Resources *resources =  BC_WindowBase::get_resources();
-       return resources->find_fontentry(font_name, flavor, mask, style);
+       BC_FontEntry *font = resources->find_fontentry(font_name, flavor, mask, style);
+       if( font && strcmp(font_name, font->displayname) ) font = 0;
+       return font;
 }
 BC_FontEntry* TitleMain::config_font()
 {
        BC_FontEntry *font = get_font(config.font, config.style);
-       if( font && load_font(font) )
-               font = get_font(FIXED_FONT,0);
+       if( !font || load_font(font) )
+               load_font(font = get_font(FIXED_FONT,0));
        return font;
 }
 
 
-static inline bool is_ltr(wchar_t wch) { return (wch>='a' && wch<='z') || (wch>='A' && wch<='Z'); }
-static inline bool is_nbr(wchar_t wch) { return (wch>='0' && wch<='9'); }
+static inline bool is_ltr(wchar_t wch) { return iswalpha(wch); }
+static inline bool is_nbr(wchar_t wch) { return iswdigit(wch); }
 static inline bool is_ws(wchar_t wch) { return wch==' ' || wch=='\t'; }
 static inline bool is_idch(wchar_t wch) { return is_ltr(wch) || is_nbr(wch) || wch=='_'; }
 
 // return eof=-1, chr=0, opener=1, closer=2
 int TitleParser::wget(wchar_t &wch)
 {
-       char *ip = id, *tp = text;  *ip = 0;  *tp = 0;
-       int ilen = sizeof(id), tlen = sizeof(text), ich;
+       wchar_t *wip = wid, *wtp = wtxt;  *wip = 0;  *wtp = 0;
+       int ilen = sizeof(wid)/sizeof(wid[0]);
+       int tlen = sizeof(wtxt)/sizeof(wtxt[0]);
+       int ich;
        while( (ich=wnext()) >= 0 ) {
                if( ich == '\\' ) {
                        if( (ich=wnext()) == '\n' ) continue;
@@ -1522,23 +1340,38 @@ int TitleParser::wget(wchar_t &wch)
        int ret = 1;  long pos = tell();
        if( (ich=wnext()) == '/' ) { ret = 2; ich = wnext(); }
        if( is_ltr(ich) ) {
-               *ip++ = ich;
+               *wip++ = ich;
                while( is_idch(ich=wnext()) )
-                       if( --ilen > 0 ) *ip++ = ich;
+                       if( --ilen > 0 ) *wip++ = ich;
        }
-       *ip = 0;
+       *wip = 0;
        while( is_ws(ich) ) ich = wnext();
        while( ich >= 0 && ich != '>' ) {
                if( ich == '\n' || ich == '<' ) { ich = -1;  break; }
                if( ich == '\\' && (ich=wnext()) < 0 ) break;
-               if( --tlen > 0 ) *tp++ = ich;
+               if( --tlen > 0 ) *wtp++ = ich;
                ich = wnext();
        }
-       *tp = 0;
+       *wtp = 0;
        if( ich < 0 ) { ich = '<';  seek(pos);  ret = 0; }
        wch = ich;
        return ret;
 }
+int TitleParser::tget(wchar_t &wch)
+{
+       int ret = wget(wch);
+       if( ret > 0 ) {
+               int wid_len = wcslen(wid)+1;
+               BC_Resources::encode(
+                       BC_Resources::wide_encoding, plugin->config.encoding,
+                       (char*)wid,wid_len*sizeof(wid[0]), (char *)id,sizeof(id));
+               int wtxt_len = wcslen(wtxt)+1;
+               BC_Resources::encode(
+                       BC_Resources::wide_encoding, plugin->config.encoding,
+                       (char*)wtxt,wtxt_len*sizeof(wtxt[0]), (char *)text,sizeof(text));
+       }
+       return ret;
+}
 
 TitleGlyph *TitleMain::get_glyph(FT_ULong char_code, BC_FontEntry *font, int size, int style)
 {
@@ -1557,7 +1390,7 @@ int TitleMain::get_width(TitleGlyph *cur, TitleGlyph *nxt)
        int result = cur->advance_x;
        if( !nxt ) return result;
        FT_Vector kerning;
-       if( !FT_Get_Kerning(freetype_face,
+       if( !ft_Get_Kerning(freetype_face,
            cur->freetype_index, nxt->freetype_index,
            ft_kerning_default, &kerning) )
                result += (kerning.x >> 6);
@@ -1579,7 +1412,8 @@ VFrame *TitleMain::add_image(const char *path)
        VFrame *vframe = get_image(path);
        if( !vframe && (vframe=VFramePng::vframe_png(path)) != 0 ) {
                if( vframe->get_color_model() != text_model ) {
-                       VFrame *frame = new VFrame(vframe->get_w(), vframe->get_h(), text_model);
+                       VFrame *frame = new VFrame(vframe->get_w(), vframe->get_h(),
+                               text_model, 0);
                        frame->transfer_from(vframe);  delete vframe;
                        vframe = frame;
                }
@@ -1769,6 +1603,14 @@ int TitleCurFixed::set(const char *txt)
        return 0;
 }
 
+int TitleCurAlias::set(const char *txt)
+{
+       int alias = !*txt ? 1 : strtol(txt,(char **)&txt,0);
+       if( *txt ) return 1;
+       push(alias);
+       return 0;
+}
+
 int TitleCurSuper::set(const char *txt)
 {
        int super = !*txt ? 1 : strtol(txt,(char **)&txt,0);
@@ -1791,18 +1633,19 @@ int TitleCurNudge::set(const char *txt)
 
 int TitleParser::set_attributes(int ret)
 {
-        if( !strcmp(id,_("nudge")) )  return ret>1 ? cur_nudge.unset(text)  : cur_nudge.set(text);
-        if( !strcmp(id,_("color")) )  return ret>1 ? cur_color.unset(text)  : cur_color.set(text);
-        if( !strcmp(id,_("alpha")) )  return ret>1 ? cur_alpha.unset(text)  : cur_alpha.set(text);
-        if( !strcmp(id,_("font")) )   return ret>1 ? cur_font.unset(text)   : cur_font.set(text);
-        if( !strcmp(id,_("size")) )   return ret>1 ? cur_size.unset(text)   : cur_size.set(text);
-        if( !strcmp(id,_("bold")) )   return ret>1 ? cur_bold.unset(text)   : cur_bold.set(text);
-        if( !strcmp(id,_("italic")) ) return ret>1 ? cur_italic.unset(text) : cur_italic.set(text);
-        if( !strcmp(id,_("caps")) )   return ret>1 ? cur_caps.unset(text)   : cur_caps.set(text);
-        if( !strcmp(id,_("ul")) )     return ret>1 ? cur_under.unset(text)  : cur_under.set(text);
-        if( !strcmp(id,_("blink")) )  return ret>1 ? cur_blink.unset(text)  : cur_blink.set(text);
-        if( !strcmp(id,_("fixed")) )  return ret>1 ? cur_fixed.unset(text)  : cur_fixed.set(text);
-        if( !strcmp(id,_("sup")) )    return ret>1 ? cur_super.unset(text)  : cur_super.set(text);
+        if( !kw_strcmp(id,KW_NUDGE) )  return ret>1 ? cur_nudge.unset(text)  : cur_nudge.set(text);
+        if( !kw_strcmp(id,KW_COLOR) )  return ret>1 ? cur_color.unset(text)  : cur_color.set(text);
+        if( !kw_strcmp(id,KW_ALPHA) )  return ret>1 ? cur_alpha.unset(text)  : cur_alpha.set(text);
+        if( !kw_strcmp(id,KW_FONT) )   return ret>1 ? cur_font.unset(text)   : cur_font.set(text);
+        if( !kw_strcmp(id,KW_SIZE) )   return ret>1 ? cur_size.unset(text)   : cur_size.set(text);
+        if( !kw_strcmp(id,KW_BOLD) )   return ret>1 ? cur_bold.unset(text)   : cur_bold.set(text);
+        if( !kw_strcmp(id,KW_ITALIC) ) return ret>1 ? cur_italic.unset(text) : cur_italic.set(text);
+        if( !kw_strcmp(id,KW_CAPS) )   return ret>1 ? cur_caps.unset(text)   : cur_caps.set(text);
+        if( !kw_strcmp(id,KW_UL) )     return ret>1 ? cur_under.unset(text)  : cur_under.set(text);
+        if( !kw_strcmp(id,KW_BLINK) )  return ret>1 ? cur_blink.unset(text)  : cur_blink.set(text);
+        if( !kw_strcmp(id,KW_FIXED) )  return ret>1 ? cur_fixed.unset(text)  : cur_fixed.set(text);
+        if( !kw_strcmp(id,KW_ALIAS) )  return ret>1 ? cur_alias.unset(text)  : cur_alias.set(text);
+        if( !kw_strcmp(id,KW_SUP) )    return ret>1 ? cur_super.unset(text)  : cur_super.set(text);
        return 1;
 }
 
@@ -1816,45 +1659,47 @@ void TitleMain::load_glyphs()
        while( !wchrs.eof() ) {
                wchar_t wch1 = wchrs.wcur(), wch;
                long ipos = wchrs.tell();
-               int ret = wchrs.wget(wch);
+               int ret = wchrs.tget(wch);
                if( ret > 0 ) {
                        if( !wchrs.set_attributes(ret) ) continue;
-                       if( !strcmp(wchrs.id,"png") && add_image(wchrs.text) ) continue;
+                       if( !kw_strcmp(wchrs.id,KW_PNG) && add_image(wchrs.text) ) continue;
                        wch = wch1;  wchrs.seek(ipos+1);
                        ret = 0;
                }
-               if( !ret ) {
-                       int cur_caps = wchrs.cur_caps;
-                       if( cur_caps > 0 ) wch = towupper(wch);
-                       else if( cur_caps < 0 ) wch = towlower(wch);
-                       BC_FontEntry *cur_font = wchrs.cur_font;
-                       int cur_size = wchrs.cur_size;
-                       int cur_style = 0;
-                       int cur_bold  = wchrs.cur_bold;
-                       if( cur_bold ) cur_style |= BC_FONT_BOLD;
-                       int cur_italic  = wchrs.cur_italic;
-                       if( cur_italic ) cur_style |= BC_FONT_ITALIC;
-                       int cur_super = wchrs.cur_super;
-                       if( cur_super ) cur_size /= 2;
-                       int exists = 0;
-                       for( int j=0; j<title_glyphs.count(); ++j ) {
-                               TitleGlyph *glyph = title_glyphs[j];
-                               if( glyph->char_code == (FT_ULong)wch && glyph->font == cur_font &&
-                                   glyph->size == cur_size && glyph->style == cur_style ) {
-                                       exists = 1;   break;
-                               }
-                       }
+               if( ret || wch == '\n' ) continue;
 
-                       if( !exists && cur_font ) {
-                               total_packages++;
-                               TitleGlyph *glyph = new TitleGlyph;
-                               glyph->char_code = (FT_ULong)wch;
-                               glyph->font = cur_font;
-                               glyph->size = cur_size;
-                               glyph->style = cur_style;
-                               title_glyphs.append(glyph);
+               int cur_caps = wchrs.cur_caps;
+               if( cur_caps > 0 ) wch = towupper(wch);
+               else if( cur_caps < 0 ) wch = towlower(wch);
+               BC_FontEntry *cur_font = wchrs.cur_font;
+               int cur_size = wchrs.cur_size;
+               int cur_style = 0;
+               int cur_bold  = wchrs.cur_bold;
+               if( cur_bold ) cur_style |= BC_FONT_BOLD;
+               int cur_italic  = wchrs.cur_italic;
+               if( cur_italic ) cur_style |= BC_FONT_ITALIC;
+               int cur_alias  = wchrs.cur_alias;
+               if( cur_alias ) cur_style |= FONT_ALIAS;
+               int cur_super = wchrs.cur_super;
+               if( cur_super ) cur_size /= 2;
+               int exists = 0;
+               for( int j=0; j<title_glyphs.count(); ++j ) {
+                       TitleGlyph *glyph = title_glyphs[j];
+                       if( glyph->char_code == (FT_ULong)wch && glyph->font == cur_font &&
+                           glyph->size == cur_size && glyph->style == cur_style ) {
+                               exists = 1;   break;
                        }
                }
+
+               if( !exists && cur_font ) {
+                       total_packages++;
+                       TitleGlyph *glyph = new TitleGlyph;
+                       glyph->char_code = (FT_ULong)wch;
+                       glyph->font = cur_font;
+                       glyph->size = cur_size;
+                       glyph->style = cur_style;
+                       title_glyphs.append(glyph);
+               }
        }
 
        if( !glyph_engine )
@@ -1921,7 +1766,7 @@ int TitleMain::get_text()
                TitleChar *chr = 0;
                long ipos = wchrs.tell();
                wchar_t wch1 = wchrs.wcur(), wch;
-               int ret = wchrs.wget(wch);
+               int ret = wchrs.tget(wch);
                if( ret < 0 || wch == '\n' ) {
                        if( row->x1 > row->x2 ) row->x1 = row->x2 = 0;
                        if( row->y2 > row->y1 ) row->y1 = row->y2 = 0;
@@ -1949,6 +1794,8 @@ int TitleMain::get_text()
                int cur_style = 0;
                int cur_bold  = wchrs.cur_bold;
                if( cur_bold ) cur_style |= BC_FONT_BOLD;
+               int cur_alias   = wchrs.cur_alias;
+               if( cur_alias ) cur_style |= FONT_ALIAS;
                int cur_italic  = wchrs.cur_italic;
                if( cur_italic ) cur_style |= BC_FONT_ITALIC;
                short nx = cur_nudge >> 16, ny = cur_nudge;
@@ -1956,7 +1803,7 @@ int TitleMain::get_text()
                if( ret > 0 ) {
                        if( !wchrs.set_attributes(ret) ) continue;
                        ret = -1;
-                       if( !strcmp(wchrs.id,"png") ) {
+                       if( !kw_strcmp(wchrs.id,KW_PNG) ) {
                                VFrame *png_image = get_image(wchrs.text);
                                if( png_image ) {
                                        chr = title_chars.add(CHAR_IMAGE, png_image);
@@ -2308,16 +2155,162 @@ void TitleMain::draw_overlay()
 
        if( !translate )
                translate = new TitleTranslate(this, cpus);
-       if( text_x+mask_w > 0 && text_x < title_w ) {
-               translate->xlat_mask = text_mask;
-               translate->run_packages();
+
+       int tx = text_x - text_x1 + mask_x1;
+       if( tx < title_w && tx+mask_w > 0 ) {
+               translate->copy(text_mask);
                if( config.stroke_width >= SMALL && (config.style & BC_FONT_OUTLINE) ) {
-                       translate->xlat_mask = stroke_mask;
-                       translate->run_packages();
+                       translate->copy(stroke_mask);
                }
        }
 }
 
+
+TitleTranslate::TitleTranslate(TitleMain *plugin, int cpus)
+ : LoadServer(cpus, cpus)
+{
+       this->plugin = plugin;
+}
+
+TitleTranslate::~TitleTranslate()
+{
+}
+
+void TitleTranslate::copy(VFrame *input)
+{
+       this->input = input;
+       in_w = input->get_w();
+       in_h = input->get_h();
+       ix1 = 0, ix2 = ix1 + in_w;
+       iy1 = 0, iy2 = iy1 + in_h;
+
+       out_w = plugin->output->get_w();
+       out_h = plugin->output->get_h();
+       float x1 = plugin->title_x, x2 = x1 + plugin->title_w;
+       float y1 = plugin->title_y, y2 = y1 + plugin->title_h;
+       bclamp(x1, 0, out_w);  bclamp(y1, 0, out_h);
+       bclamp(x2, 0, out_w);  bclamp(y2, 0, out_h);
+
+       ox1 = plugin->title_x + plugin->text_x - plugin->text_x1 + plugin->mask_x1;
+       ox2 = ox1 + in_w;
+       oy1 = plugin->title_y + plugin->text_y - plugin->text_y1 + plugin->mask_y1;
+       oy2 = oy1 + in_h;
+       if( ox1 < x1 ) { ix1 -= (ox1-x1);  ox1 = x1; }
+       if( oy1 < y1 ) { iy1 -= (oy1-y1);  oy1 = y1; }
+       if( ox2 > x2 ) { ix2 -= (ox2-x2);  ox2 = x2; }
+       if( oy2 > y2 ) { iy2 -= (oy2-x2);  oy2 = y2; }
+#if 0
+printf("TitleTranslate text  txy=%7.2f,%-7.2f\n"
+  "  mxy1=%7d,%-7d mxy2=%7d,%-7d\n"
+  "   xy1=%7.2f,%-7.2f  xy2=%7.2f,%-7.2f\n"
+  "  ixy1=%7.2f,%-7.2f ixy2=%7.2f,%-7.2f\n"
+  "  oxy1=%7.2f,%-7.2f oxy2=%7.2f,%-7.2f\n",
+   plugin->text_x, plugin->text_y,
+   plugin->mask_x1, plugin->mask_y1, plugin->mask_x2, plugin->mask_y2,
+   x1,y1, x2,y2, ix1,iy1, ix2,iy2, ox1,oy1, ox2,oy2);
+#endif
+       process_packages();
+}
+
+
+TitleTranslatePackage::TitleTranslatePackage()
+ : LoadPackage()
+{
+       y1 = y2 = 0;
+}
+
+TitleTranslateUnit::TitleTranslateUnit(TitleMain *plugin, TitleTranslate *server)
+ : LoadClient(server)
+{
+}
+
+#define TRANSLATE(type, max, comps, ofs) { \
+       type **out_rows = (type**)output->get_rows(); \
+       float fr = 1./(256.-max), fs = max/255.; \
+       float r = max > 1 ? 0.5 : 0; \
+       int ix1= x1, iy1 = y1, ix2= x2, iy2 = y2; \
+       float fy = y1 + yofs; \
+       for( int y=iy1; y<iy2; ++y,++fy ) { \
+               int iy = fy;  float yf1 = fy - iy; \
+               if( yf1 < 0 ) ++yf1; \
+               float yf0 = 1. - yf1; \
+               unsigned char *in_row0 = in_rows[iy<0 ? 0 : iy]; \
+               unsigned char *in_row1 = in_rows[iy<ih1 ? iy+1 : ih1]; \
+               float fx = x1 + xofs; \
+               for( int x=ix1; x<ix2; ++x,++fx ) { \
+                       type *op = out_rows[y] + x*comps; \
+                       int ix = fx;  float xf1 = fx - ix; \
+                       if( xf1 < 0 ) ++xf1; \
+                       float xf0 = 1. - xf1; \
+                       int i0 = (ix<0 ? 0 : ix)*4, i1 = (ix<iw1 ? ix+1 : iw1)*4; \
+                       uint8_t *cp00 = in_row0 + i0, *cp01 = in_row0 + i1; \
+                       uint8_t *cp10 = in_row1 + i0, *cp11 = in_row1 + i1; \
+                       float a00 = yf0 * xf0 * cp00[3], a01 = yf0 * xf1 * cp01[3]; \
+                       float a10 = yf1 * xf0 * cp10[3], a11 = yf1 * xf1 * cp11[3]; \
+                       float fa = a00 + a01 + a10 + a11;  if( !fa ) continue;  \
+                       type in_a = fa*fr + r;  float s = fs/fa; \
+                       type in_r = (cp00[0]*a00 + cp01[0]*a01 + cp10[0]*a10 + cp11[0]*a11)*s + r; \
+                       type in_g = (cp00[1]*a00 + cp01[1]*a01 + cp10[1]*a10 + cp11[1]*a11)*s + r; \
+                       type in_b = (cp00[2]*a00 + cp01[2]*a01 + cp10[2]*a10 + cp11[2]*a11)*s + r; \
+                       type a = in_a*plugin->fade, b = max - a, px; \
+                       /*if( comps == 4 ) { b = (b * op[3]) / max; }*/ \
+                       px = *op;  *op++ = (a*in_r + b*px) / max; \
+                       px = *op;  *op++ = (a*(in_g-ofs) + b*(px-ofs)) / max + ofs; \
+                       px = *op;  *op++ = (a*(in_b-ofs) + b*(px-ofs)) / max + ofs; \
+                       if( comps == 4 ) { b = *op;  *op++ = a + b - a*b / max; } \
+               } \
+       } \
+}
+
+void TitleTranslateUnit::process_package(LoadPackage *package)
+{
+       TitleTranslatePackage *pkg = (TitleTranslatePackage*)package;
+       TitleTranslate *server = (TitleTranslate*)this->server;
+       TitleMain *plugin = server->plugin;
+       VFrame *input = server->input, *output = plugin->output;
+       int iw = input->get_w(),  ih = input->get_h();
+       int iw1 = iw-1, ih1 = ih-1;
+       float x1 = server->ox1, x2 = server->ox2;
+       float y1 = pkg->y1, y2 = pkg->y2;
+       float xofs = server->ix1 - server->ox1;
+       float yofs = server->iy1 - server->oy1;
+       unsigned char **in_rows = input->get_rows();
+
+       switch( output->get_color_model() ) {
+       case BC_RGB888:     TRANSLATE(unsigned char, 0xff, 3, 0);    break;
+       case BC_RGB_FLOAT:  TRANSLATE(float, 1.0, 3, 0);             break;
+       case BC_YUV888:     TRANSLATE(unsigned char, 0xff, 3, 0x80); break;
+       case BC_RGBA_FLOAT: TRANSLATE(float, 1.0, 4, 0);             break;
+       case BC_RGBA8888:   TRANSLATE(unsigned char, 0xff, 4, 0);    break;
+       case BC_YUVA8888:   TRANSLATE(unsigned char, 0xff, 4, 0x80); break;
+       }
+}
+
+void TitleTranslate::init_packages()
+{
+       int oh = oy2 - oy1;
+       int py = oy1;
+       int i = 0, pkgs = get_total_packages();
+       while( i < pkgs ) {
+               TitleTranslatePackage *pkg = (TitleTranslatePackage*)get_package(i++);
+               pkg->y1 = py;
+               py = oy1 + i*oh / pkgs;
+               pkg->y2 = py;
+       }
+}
+
+LoadClient* TitleTranslate::new_client()
+{
+       return new TitleTranslateUnit(plugin, this);
+}
+
+LoadPackage* TitleTranslate::new_package()
+{
+       return new TitleTranslatePackage;
+}
+
+
+
 const char* TitleMain::motion_to_text(int motion)
 {
        switch( motion ) {
@@ -2349,7 +2342,7 @@ void TitleMain::reset_render()
        title_glyphs.clear();
        title_images.clear();
        if( freetype_face ) {
-               FT_Done_Face(freetype_face);
+               ft_Done_Face(freetype_face);
                freetype_face = 0;
        }
 }
@@ -2357,52 +2350,15 @@ void TitleMain::reset_render()
 int TitleMain::init_freetype()
 {
        if( !freetype_library )
-               FT_Init_FreeType(&freetype_library);
+               ft_Init_FreeType(&freetype_library);
        return 0;
 }
 
 void TitleMain::draw_boundry()
 {
-       VFrame &out = *output;
-       int iw = output->get_w(), ih = output->get_h();
-       int mr = MIN(iw, ih)/200 + 2, rr = 2*mr;
-       int x = title_x, y = title_y, w = title_w, h = title_h;
-       int r2 = (rr+1)/2;
-       int x0 = x-r2, x1 = x+(w+1)/2, x2 = x+w+r2;
-       int y0 = y-r2, y1 = y+(h+1)/2, y2 = y+h+r2;
-       int st = 1;
-       for( int r=2; r<mr; r<<=1 ) st = r;
-       out.set_stiple(st);
-
-       for( int r=mr/2; --r>=0; ) { // bbox
-               int lft = x+r, rgt = x+w-1-r;
-               int top = y+r, bot = y+h-1-r;
-               out.draw_line(lft,top, rgt,top);
-               out.draw_line(rgt,top, rgt,bot);
-               out.draw_line(rgt,bot, lft,bot);
-               out.draw_line(lft,bot, lft,top);
-       }
-
-       for( int r=mr; r<rr; ++r ) { // center
-               out.draw_smooth(x1-r,y1, x1-r,y1+r, x1,y1+r);
-               out.draw_smooth(x1,y1+r, x1+r,y1+r, x1+r,y1);
-               out.draw_smooth(x1+r,y1, x1+r,y1-r, x1,y1-r);
-               out.draw_smooth(x1,y1-r, x1-r,y1-r, x1-r,y1);
-       }
-
-       for( int r=rr; --r>=0; ) { // edge arrows
-               out.draw_line(x1-r,y0+r, x1+r,y0+r);
-               out.draw_line(x2-r,y1-r, x2-r,y1+r);
-               out.draw_line(x1-r,y2-r, x1+r,y2-r);
-               out.draw_line(x0+r,y1+r, x0+r,y1-r);
-       }
-       x0 += r2;  y0 += r2;  x2 -= r2;  y2 -= r2;
-       for( int r=2*mr; --r>=0; ) { // corner arrows
-               out.draw_line(x0,y0+r, x0+r,y0);
-               out.draw_line(x2,y0+r, x2-r,y0);
-               out.draw_line(x2,y2-r, x2-r,y2);
-               out.draw_line(x0,y2-r, x0+r,y2);
-       }
+       if( !gui_open() ) return;
+       DragCheckBox::draw_boundary(output,
+               title_x, title_y, title_w, title_h);
 }
 
 
@@ -2583,11 +2539,19 @@ void TitleMain::save_data(KeyFrame *keyframe)
        output.tag.set_property("LOOP_PLAYBACK", config.loop_playback);
        output.append_tag();
        output.append_newline();
-       char text[BCTEXTLEN];
+       char text[2*sizeof(config.wtext)];
        int text_len = BC_Resources::encode(
                BC_Resources::wide_encoding, DEFAULT_ENCODING,
                (char*)config.wtext, config.wlen*sizeof(wchar_t),
                text, sizeof(text));
+       int len = output.length(), avail = MESSAGESIZE-16 - len;
+       if( text_len >= avail ) { // back off last utf8 char
+               text_len = avail;
+               while( text_len > 0 && (text[text_len-1] & 0xc0) == 0x80 )
+                       text[--text_len] = 0;
+               if( text_len > 0 )
+                       text[--text_len] = 0;
+       }
        output.append_text(text, text_len);
        output.tag.set_title("/TITLE");
        output.append_tag();
@@ -2653,25 +2617,25 @@ void TitleMain::read_data(KeyFrame *keyframe)
        }
 }
 
-void TitleMain::insert_text(const char *txt, int pos)
+void TitleMain::insert_text(const wchar_t *wtxt, int pos)
 {
-       int ilen = strlen(txt);
+       int len = wcslen(wtxt);
        wchar_t *wtext = config.wtext;
        int wsize = sizeof(config.wtext)-1;
        int wlen = config.wlen;
        if( pos < 0 ) pos = 0;
        if( pos > wlen ) pos = wlen;
 
-       for( int i=wlen-1, j=wlen+ilen-1; i>=pos; --i,--j ) {
+       for( int i=wlen-1, j=wlen+len-1; i>=pos; --i,--j ) {
                if( j >= wsize ) continue;
                wtext[j] = wtext[i];
        }
-       for( int i=pos, j=0; j<ilen; ++i,++j ) {
+       for( int i=pos, j=0; j<len; ++i,++j ) {
                if( i >= wsize ) break;
-               wtext[i] = txt[j];
+               wtext[i] = wtxt[j];
        }
 
-       if( (wlen+=ilen) > wsize ) wlen = wsize;
+       if( (wlen+=len) > wsize ) wlen = wsize;
        wtext[wlen] = 0;
        config.wlen = wlen;
 }