Fix division by zero in "\t" parsing.
[mplayer.git] / libass / ass_render.c
index 1287c9d..f69c0c9 100644 (file)
@@ -112,7 +112,7 @@ typedef struct glyph_info_s {
        int shadow;
        double frx, fry, frz; // rotation
        
-       glyph_hash_key_t hash_key;
+       bitmap_hash_key_t hash_key;
 } glyph_info_t;
 
 typedef struct line_info_s {
@@ -252,6 +252,7 @@ ass_renderer_t* ass_renderer_init(ass_library_t* library)
        // images_root and related stuff is zero-filled in calloc
        
        ass_font_cache_init();
+       ass_bitmap_cache_init();
        ass_glyph_cache_init();
 
        text_info.glyphs = calloc(MAX_GLYPHS, sizeof(glyph_info_t));
@@ -266,6 +267,7 @@ ass_init_exit:
 void ass_renderer_done(ass_renderer_t* priv)
 {
        ass_font_cache_done();
+       ass_bitmap_cache_done();
        ass_glyph_cache_done();
        if (render_context.stroker) {
                FT_Stroker_Done(render_context.stroker);
@@ -386,12 +388,12 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
        int pen_x, pen_y;
        int i, error;
        bitmap_t* bm;
-       glyph_hash_val_t hash_val;
+       bitmap_hash_val_t hash_val;
        ass_image_t* head;
        ass_image_t** tail = &head;
 
        for (i = 0; i < text_info->length; ++i) {
-               if (text_info->glyphs[i].glyph) {
+               if (text_info->glyphs[i].glyph && text_info->glyphs[i].bm == 0) {
                        if ((text_info->glyphs[i].symbol == '\n') || (text_info->glyphs[i].symbol == 0))
                                continue;
                        error = glyph_to_bitmap(ass_renderer->synth_priv,
@@ -400,27 +402,23 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
                                        &text_info->glyphs[i].bm_s, text_info->glyphs[i].be);
                        if (error)
                                text_info->glyphs[i].symbol = 0;
-                       FT_Done_Glyph(text_info->glyphs[i].glyph);
-                       if (text_info->glyphs[i].outline_glyph)
-                               FT_Done_Glyph(text_info->glyphs[i].outline_glyph);
 
                        // cache
-                       if (text_info->glyphs[i].hash_key.frx == 0 &&
-                           text_info->glyphs[i].hash_key.fry == 0 &&
-                           text_info->glyphs[i].hash_key.frz == 0) {
-                               hash_val.bbox_scaled = text_info->glyphs[i].bbox;
-                               hash_val.bm_o = text_info->glyphs[i].bm_o;
-                               hash_val.bm = text_info->glyphs[i].bm;
-                               hash_val.bm_s = text_info->glyphs[i].bm_s;
-                               hash_val.advance.x = text_info->glyphs[i].advance.x;
-                               hash_val.advance.y = text_info->glyphs[i].advance.y;
-                               cache_add_glyph(&(text_info->glyphs[i].hash_key), &hash_val);
-                       }
-
+                       hash_val.bm_o = text_info->glyphs[i].bm_o;
+                       hash_val.bm = text_info->glyphs[i].bm;
+                       hash_val.bm_s = text_info->glyphs[i].bm_s;
+                       cache_add_bitmap(&(text_info->glyphs[i].hash_key), &hash_val);
                }
        }
 
        for (i = 0; i < text_info->length; ++i) {
+               if (text_info->glyphs[i].glyph)
+                       FT_Done_Glyph(text_info->glyphs[i].glyph);
+               if (text_info->glyphs[i].outline_glyph)
+                       FT_Done_Glyph(text_info->glyphs[i].outline_glyph);
+       }
+
+       for (i = 0; i < text_info->length; ++i) {
                glyph_info_t* info = text_info->glyphs + i;
                if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s || (info->shadow == 0))
                        continue;
@@ -931,11 +929,14 @@ static char* parse_tag(char* p, double pwr) {
                if (v3 < 0.)
                        v3 = 0.;
                t = frame_context.time - render_context.event->Start; // FIXME: move to render_context
-               if (t < t1)
+               if (t <= t1)
                        k = 0.;
-               else if (t > t2)
+               else if (t >= t2)
                        k = 1.;
-               else k = pow(((double)(t - t1)) / delta_t, v3);
+               else {
+                       assert(delta_t != 0.);
+                       k = pow(((double)(t - t1)) / delta_t, v3);
+               }
                while (*p == '\\')
                        p = parse_tag(p, k); // maybe k*pwr ? no, specs forbid nested \t's 
                skip_all(')'); // FIXME: better skip(')'), but much more tags support required
@@ -1222,6 +1223,57 @@ static void free_render_context(void)
 {
 }
 
+static void get_outline_glyph(int symbol, glyph_info_t* info, FT_Vector* advance)
+{
+       int error;
+       glyph_hash_val_t* val;
+       glyph_hash_key_t key;
+       key.font = render_context.font;
+       key.size = render_context.font_size;
+       key.ch = symbol;
+       key.scale_x = (render_context.scale_x * 0xFFFF);
+       key.scale_y = (render_context.scale_y * 0xFFFF);
+       key.advance = *advance;
+       key.bold = render_context.bold;
+       key.italic = render_context.italic;
+
+       info->glyph = info->outline_glyph = 0;
+
+       val = cache_find_glyph(&key);
+       if (val) {
+               FT_Glyph_Copy(val->glyph, &info->glyph);
+               if (val->outline_glyph)
+                       FT_Glyph_Copy(val->outline_glyph, &info->outline_glyph);
+               info->bbox = val->bbox_scaled;
+               info->advance.x = val->advance.x;
+               info->advance.y = val->advance.y;
+       } else {
+               glyph_hash_val_t v;
+               info->glyph = ass_font_get_glyph(frame_context.ass_priv->fontconfig_priv, render_context.font, symbol);
+               if (!info->glyph)
+                       return;
+               info->advance.x = d16_to_d6(info->glyph->advance.x);
+               info->advance.y = d16_to_d6(info->glyph->advance.y);
+               FT_Glyph_Get_CBox( info->glyph, FT_GLYPH_BBOX_PIXELS, &info->bbox);
+
+               if (render_context.stroker) {
+                       info->outline_glyph = info->glyph;
+                       error = FT_Glyph_StrokeBorder( &(info->outline_glyph), render_context.stroker, 0 , 0 ); // don't destroy original
+                       if (error) {
+                               mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FT_Glyph_Stroke_Error, error);
+                       }
+               }
+
+               memset(&v, 0, sizeof(v));
+               FT_Glyph_Copy(info->glyph, &v.glyph);
+               if (info->outline_glyph)
+                       FT_Glyph_Copy(info->outline_glyph, &v.outline_glyph);
+               v.advance = info->advance;
+               v.bbox_scaled = info->bbox;
+               cache_add_glyph(&key, &v);
+       }
+}
+
 /**
  * \brief Get normal and outline glyphs from cache (if possible) or font face
  * \param index face glyph index
@@ -1230,63 +1282,20 @@ static void free_render_context(void)
  * \param advance advance vector of the extracted glyph
  * \return 0 on success
  */
-static void get_glyph(int symbol, glyph_info_t* info, FT_Vector* advance)
+static void get_bitmap_glyph(glyph_info_t* info)
 {
-       int error;
-       glyph_hash_val_t* val;
-       glyph_hash_key_t* key = &(info->hash_key);
+       bitmap_hash_val_t* val;
+       bitmap_hash_key_t* key = &info->hash_key;
        
-       key->font = render_context.font;
-       key->size = render_context.font_size;
-       key->ch = symbol;
-       key->outline = (render_context.border * 0xFFFF); // convert to 16.16
-       key->scale_x = (render_context.scale_x * 0xFFFF);
-       key->scale_y = (render_context.scale_y * 0xFFFF);
-       key->frx = (render_context.frx * 0xFFFF);
-       key->fry = (render_context.fry * 0xFFFF);
-       key->frz = (render_context.frz * 0xFFFF);
-       key->advance = *advance;
-       key->bold = render_context.bold;
-       key->italic = render_context.italic;
-       key->be = render_context.be;
-
-       val = cache_find_glyph(key);
+       val = cache_find_bitmap(key);
 /*     val = 0; */
        
        if (val) {
-               info->glyph = info->outline_glyph = 0;
                info->bm = val->bm;
                info->bm_o = val->bm_o;
                info->bm_s = val->bm_s;
-               info->bbox = val->bbox_scaled;
-               info->advance.x = val->advance.x;
-               info->advance.y = val->advance.y;
-
-               return;
-       }
-
-       // not found, get a new outline glyph from face
-//     mp_msg(MSGT_ASS, MSGL_INFO, "miss, index = %d, symbol = %c, adv = (%d, %d)\n", index, symbol, advance->x, advance->y);
-
-       info->outline_glyph = 0;
-       info->bm = info->bm_o = info->bm_s = 0;
-       info->bbox.xMin = info->bbox.xMax = info->bbox.yMin = info->bbox.yMax = 0;
-       info->advance.x = info->advance.y = 0;
-       
-       info->glyph = ass_font_get_glyph(frame_context.ass_priv->fontconfig_priv, render_context.font, symbol);
-       if (!info->glyph)
-               return;
-
-       info->advance.x = d16_to_d6(info->glyph->advance.x);
-       info->advance.y = d16_to_d6(info->glyph->advance.y);
-
-       if (render_context.stroker) {
-               info->outline_glyph = info->glyph;
-               error = FT_Glyph_StrokeBorder( &(info->outline_glyph), render_context.stroker, 0 , 0 ); // don't destroy original
-               if (error) {
-                       mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FT_Glyph_Stroke_Error, error);
-               }
-       }
+       } else
+               info->bm = info->bm_o = info->bm_s = 0;
 }
 
 /**
@@ -1583,6 +1592,7 @@ static inline void transform_point_3d(double *a, double *m, double *b)
  * Result is returned in v.
  */
 static inline void transform_vector_3d(FT_Vector* v, double *m) {
+       const double camera = 2500 * frame_context.border_scale; // camera distance
        double a[4], b[4];
        a[0] = d6_to_double(v->x);
        a[1] = d6_to_double(v->y);
@@ -1598,9 +1608,9 @@ static inline void transform_vector_3d(FT_Vector* v, double *m) {
           Camera is always located in (org_x, org_y, -2500). This means
           that different subtitle events can be displayed at the same time
           using different cameras. */
-       b[0] *= 2500;
-       b[1] *= 2500;
-       b[3] = 8 * b[2] + 2500;
+       b[0] *= camera;
+       b[1] *= camera;
+       b[3] = 8 * b[2] + camera;
        if (b[3] < 0.001 && b[3] > -0.001)
                b[3] = b[3] < 0. ? -0.001 : 0.001;
        v->x = double_to_d6(b[0] / b[3]);
@@ -1740,8 +1750,8 @@ static int ass_render_event(ass_event_t* event, event_images_t* event_images)
 
                        ass_font_set_transform(render_context.font, &matrix, &shift );
                }
-               
-               get_glyph(code, text_info.glyphs + text_info.length, &shift);
+
+               get_outline_glyph(code, text_info.glyphs + text_info.length, &shift);
                
                text_info.glyphs[text_info.length].pos.x = pen.x >> 6;
                text_info.glyphs[text_info.length].pos.y = pen.y >> 6;
@@ -1750,11 +1760,6 @@ static int ass_render_event(ass_event_t* event, event_images_t* event_images)
                pen.x += double_to_d6(render_context.hspacing);
                pen.y += text_info.glyphs[text_info.length].advance.y;
                
-               // if it's an outline glyph, we still need to fill the bbox
-               if (text_info.glyphs[text_info.length].glyph) {
-                       FT_Glyph_Get_CBox( text_info.glyphs[text_info.length].glyph, FT_GLYPH_BBOX_PIXELS, &(text_info.glyphs[text_info.length].bbox) );
-               }
-
                previous = code;
 
                text_info.glyphs[text_info.length].symbol = code;
@@ -1778,6 +1783,21 @@ static int ass_render_event(ass_event_t* event, event_images_t* event_images)
                text_info.glyphs[text_info.length].asc *= render_context.scale_y;
                text_info.glyphs[text_info.length].desc *= render_context.scale_y;
 
+               // fill bitmap_hash_key
+               text_info.glyphs[text_info.length].hash_key.font = render_context.font;
+               text_info.glyphs[text_info.length].hash_key.size = render_context.font_size;
+               text_info.glyphs[text_info.length].hash_key.outline = render_context.border * 0xFFFF;
+               text_info.glyphs[text_info.length].hash_key.scale_x = render_context.scale_x * 0xFFFF;
+               text_info.glyphs[text_info.length].hash_key.scale_y = render_context.scale_y * 0xFFFF;
+               text_info.glyphs[text_info.length].hash_key.frx = render_context.frx * 0xFFFF;
+               text_info.glyphs[text_info.length].hash_key.fry = render_context.fry * 0xFFFF;
+               text_info.glyphs[text_info.length].hash_key.frz = render_context.frz * 0xFFFF;
+               text_info.glyphs[text_info.length].hash_key.bold = render_context.bold;
+               text_info.glyphs[text_info.length].hash_key.italic = render_context.italic;
+               text_info.glyphs[text_info.length].hash_key.ch = code;
+               text_info.glyphs[text_info.length].hash_key.advance = shift;
+               text_info.glyphs[text_info.length].hash_key.be = render_context.be;
+
                text_info.length++;
 
                render_context.effect_type = EF_NONE;
@@ -1927,16 +1947,25 @@ static int ass_render_event(ass_event_t* event, event_images_t* event_images)
                }
 
                for (i = 0; i < text_info.length; ++i) {
-                       FT_Vector start;
-                       FT_Vector start_old;
                        FT_Vector shift;
                        glyph_info_t* info = text_info.glyphs + i;
 
-                       // calculating shift vector
-                       shift.x = int_to_d6(info->pos.x + device_x - center.x);
-                       shift.y = - int_to_d6(info->pos.y + device_y - center.y);
+                       if (info->hash_key.frx || info->hash_key.fry || info->hash_key.frz) {
+                               info->hash_key.shift_x = info->pos.x + device_x - center.x;
+                               info->hash_key.shift_y = - (info->pos.y + device_y - center.y);
+                       } else {
+                               info->hash_key.shift_x = 0;
+                               info->hash_key.shift_y = 0;
+                       }
+                       get_bitmap_glyph(info);
+
+                       if (info->bm == 0) {
+                               // calculating shift vector
+                               shift.x = int_to_d6(info->pos.x + device_x - center.x);
+                               shift.y = - int_to_d6(info->pos.y + device_y - center.y);
 
-                       transform_3d(shift, &info->glyph, &info->outline_glyph, info->frx, info->fry, info->frz);
+                               transform_3d(shift, &info->glyph, &info->outline_glyph, info->frx, info->fry, info->frz);
+                       }
                }
        }
 
@@ -1969,6 +1998,7 @@ static void ass_reconfigure(ass_renderer_t* priv)
 {
        priv->render_id = ++last_render_id;
        ass_glyph_cache_reset();
+       ass_bitmap_cache_reset();
        ass_free_images(priv->prev_images_root);
        priv->prev_images_root = 0;
 }