diff libass/ass_font.c @ 31853:e64df5862cea

Import libass 0.9.10
author greg
date Fri, 06 Aug 2010 21:13:41 +0000
parents 48d020c5ceca
children ac6e48baa03d
line wrap: on
line diff
--- a/libass/ass_font.c	Fri Aug 06 10:48:16 2010 +0000
+++ b/libass/ass_font.c	Fri Aug 06 21:13:41 2010 +0000
@@ -36,13 +36,18 @@
 #include "ass_fontconfig.h"
 #include "ass_utils.h"
 
+#define VERTICAL_LOWER_BOUND 0x02f1
+
 /**
- * Select Microfost Unicode CharMap, if the font has one.
+ * Select a good charmap, prefer Microsoft Unicode charmaps.
  * Otherwise, let FreeType decide.
  */
 static void charmap_magic(ASS_Library *library, FT_Face face)
 {
     int i;
+    int ms_cmap = -1;
+
+    // Search for a Microsoft Unicode cmap
     for (i = 0; i < face->num_charmaps; ++i) {
         FT_CharMap cmap = face->charmaps[i];
         unsigned pid = cmap->platform_id;
@@ -52,7 +57,15 @@
                 || eid == 10 /*full unicode */ )) {
             FT_Set_Charmap(face, cmap);
             return;
-        }
+        } else if (pid == 3 && ms_cmap < 0)
+            ms_cmap = i;
+    }
+
+    // Try the first Microsoft cmap if no Microsoft Unicode cmap was found
+    if (ms_cmap >= 0) {
+        FT_CharMap cmap = face->charmaps[ms_cmap];
+        FT_Set_Charmap(face, cmap);
+        return;
     }
 
     if (!face->charmap) {
@@ -67,17 +80,6 @@
     }
 }
 
-static void update_transform(ASS_Font *font)
-{
-    int i;
-    FT_Matrix m;
-    m.xx = double_to_d16(font->scale_x);
-    m.yy = double_to_d16(font->scale_y);
-    m.xy = m.yx = 0;
-    for (i = 0; i < font->n_faces; ++i)
-        FT_Set_Transform(font->faces[i], &m, &font->v);
-}
-
 /**
  * \brief find a memory font by name
  */
@@ -139,7 +141,7 @@
             FT_New_Memory_Face(font->ftlibrary,
                                (unsigned char *) font->library->
                                fontdata[mem_idx].data,
-                               font->library->fontdata[mem_idx].size, 0,
+                               font->library->fontdata[mem_idx].size, index,
                                &face);
         if (error) {
             ass_msg(font->library, MSGL_WARN,
@@ -160,7 +162,6 @@
     buggy_font_workaround(face);
 
     font->faces[font->n_faces++] = face;
-    update_transform(font);
     face_set_size(face, font->size);
     free(path);
     return font->n_faces - 1;
@@ -188,6 +189,7 @@
     font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
     font.desc.bold = desc->bold;
     font.desc.italic = desc->italic;
+    font.desc.vertical = desc->vertical;
 
     font.scale_x = font.scale_y = 1.;
     font.v.x = font.v.y = 0;
@@ -213,7 +215,6 @@
         font->v.x = v->x;
         font->v.y = v->y;
     }
-    update_transform(font);
 }
 
 static void face_set_size(FT_Face face, double size)
@@ -276,6 +277,9 @@
                 *asc = FT_MulFix(face->ascender, y_scale);
                 *desc = FT_MulFix(-face->descender, y_scale);
             }
+            if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) {
+                *asc = FT_MulFix(face->max_advance_width, y_scale);
+            }
             return;
         }
     }
@@ -414,6 +418,7 @@
     FT_Glyph glyph;
     FT_Face face = 0;
     int flags = 0;
+    int vertical = font->desc.vertical;
 
     if (ch < 0x20)
         return 0;
@@ -441,6 +446,14 @@
         if (face_idx >= 0) {
             face = font->faces[face_idx];
             index = FT_Get_Char_Index(face, ch);
+            if (index == 0 && face->num_charmaps > 0) {
+                ass_msg(font->library, MSGL_WARN,
+                    "Glyph 0x%X not found, falling back to first charmap", ch);
+                FT_CharMap cur = face->charmap;
+                FT_Set_Charmap(face, face->charmaps[0]);
+                index = FT_Get_Char_Index(face, ch);
+                FT_Set_Charmap(face, cur);
+            }
             if (index == 0) {
                 ass_msg(font->library, MSGL_ERR,
                         "Glyph 0x%X not found in font for (%s, %d, %d)",
@@ -451,22 +464,23 @@
     }
 #endif
 
+    flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
+            | FT_LOAD_IGNORE_TRANSFORM;
     switch (hinting) {
     case ASS_HINTING_NONE:
-        flags = FT_LOAD_NO_HINTING;
+        flags |= FT_LOAD_NO_HINTING;
         break;
     case ASS_HINTING_LIGHT:
-        flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
+        flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
         break;
     case ASS_HINTING_NORMAL:
-        flags = FT_LOAD_FORCE_AUTOHINT;
+        flags |= FT_LOAD_FORCE_AUTOHINT;
         break;
     case ASS_HINTING_NATIVE:
-        flags = 0;
         break;
     }
 
-    error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags);
+    error = FT_Load_Glyph(face, index, flags);
     if (error) {
         ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d",
                 index);
@@ -488,6 +502,24 @@
         return 0;
     }
 
+    // Rotate glyph, if needed
+    if (vertical && ch >= VERTICAL_LOWER_BOUND) {
+        FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 };
+        FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m);
+        FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline,
+                             face->glyph->metrics.vertAdvance,
+                             0);
+        glyph->advance.x = face->glyph->linearVertAdvance;
+    }
+
+    // Apply scaling and shift
+    FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0,
+                        double_to_d16(font->scale_y) };
+    FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline;
+    FT_Outline_Transform(outl, &scale);
+    FT_Outline_Translate(outl, font->v.x, font->v.y);
+    glyph->advance.x *= font->scale_x;
+
     ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
                              deco & DECO_STRIKETHROUGH);
 
@@ -502,6 +534,9 @@
     FT_Vector v = { 0, 0 };
     int i;
 
+    if (font->desc.vertical)
+        return v;
+
     for (i = 0; i < font->n_faces; ++i) {
         FT_Face face = font->faces[i];
         int i1 = FT_Get_Char_Index(face, c1);
@@ -530,3 +565,130 @@
         free(font->desc.family);
     free(font);
 }
+
+/**
+ * \brief Calculate the cbox of a series of points
+ */
+static void
+get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end)
+{
+    box->xMin = box->yMin = INT_MAX;
+    box->xMax = box->yMax = INT_MIN;
+    int i;
+
+    for (i = start; i <= end; i++) {
+        box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
+        box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
+        box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
+        box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
+    }
+}
+
+/**
+ * \brief Determine winding direction of a contour
+ * \return direction; 0 = clockwise
+ */
+static int get_contour_direction(FT_Vector *points, int start, int end)
+{
+    int i;
+    long long sum = 0;
+    int x = points[start].x;
+    int y = points[start].y;
+    for (i = start + 1; i <= end; i++) {
+        sum += x * (points[i].y - y) - y * (points[i].x - x);
+        x = points[i].x;
+        y = points[i].y;
+    }
+    sum += x * (points[start].y - y) - y * (points[start].x - x);
+    return sum > 0;
+}
+
+/**
+ * \brief Fix-up stroker result for huge borders by removing inside contours
+ * that would reverse in size
+ */
+void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y)
+{
+    int nc = glyph->outline.n_contours;
+    int begin, stop;
+    char modified = 0;
+    char *valid_cont = malloc(nc);
+    int start = 0;
+    int end = -1;
+    FT_BBox *boxes = malloc(nc * sizeof(FT_BBox));
+    int i, j;
+    int inside_direction;
+
+    inside_direction = FT_Outline_Get_Orientation(&glyph->outline) ==
+        FT_ORIENTATION_TRUETYPE;
+
+    // create a list of cboxes of the contours
+    for (i = 0; i < nc; i++) {
+        start = end + 1;
+        end = glyph->outline.contours[i];
+        get_contour_cbox(&boxes[i], glyph->outline.points, start, end);
+    }
+
+    // for each contour, check direction and whether it's "outside"
+    // or contained in another contour
+    end = -1;
+    for (i = 0; i < nc; i++) {
+        start = end + 1;
+        end = glyph->outline.contours[i];
+        int dir = get_contour_direction(glyph->outline.points, start, end);
+        valid_cont[i] = 1;
+        if (dir == inside_direction) {
+            for (j = 0; j < nc; j++) {
+                if (i == j)
+                    continue;
+                if (boxes[i].xMin >= boxes[j].xMin &&
+                    boxes[i].xMax <= boxes[j].xMax &&
+                    boxes[i].yMin >= boxes[j].yMin &&
+                    boxes[i].yMax <= boxes[j].yMax)
+                    goto check_inside;
+            }
+            /* "inside" contour but we can't find anything it could be
+             * inside of - assume the font is buggy and it should be
+             * an "outside" contour, and reverse it */
+            for (j = 0; j < (end + 1 - start) / 2; j++) {
+                FT_Vector temp = glyph->outline.points[start + j];
+                char temp2 = glyph->outline.tags[start + j];
+                glyph->outline.points[start + j] = glyph->outline.points[end - j];
+                glyph->outline.points[end - j] = temp;
+                glyph->outline.tags[start + j] = glyph->outline.tags[end - j];
+                glyph->outline.tags[end - j] = temp2;
+            }
+            dir ^= 1;
+        }
+        check_inside:
+        if (dir == inside_direction) {
+            FT_BBox box;
+            get_contour_cbox(&box, glyph->outline.points, start, end);
+            int width = box.xMax - box.xMin;
+            int height = box.yMax - box.yMin;
+            if (width < border_x * 2 || height < border_y * 2) {
+                valid_cont[i] = 0;
+                modified = 1;
+            }
+        }
+    }
+
+    // zero-out contours that can be removed; much simpler than copying
+    if (modified) {
+        for (i = 0; i < nc; i++) {
+            if (valid_cont[i])
+                continue;
+            begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1;
+            stop = glyph->outline.contours[i];
+            for (j = begin; j <= stop; j++) {
+                glyph->outline.points[j].x = 0;
+                glyph->outline.points[j].y = 0;
+                glyph->outline.tags[j] = 0;
+            }
+        }
+    }
+
+    free(boxes);
+    free(valid_cont);
+}
+