Mercurial > mplayer.hg
comparison 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 |
comparison
equal
deleted
inserted
replaced
31852:4ab8b78352ff | 31853:e64df5862cea |
---|---|
34 #include "ass_bitmap.h" | 34 #include "ass_bitmap.h" |
35 #include "ass_cache.h" | 35 #include "ass_cache.h" |
36 #include "ass_fontconfig.h" | 36 #include "ass_fontconfig.h" |
37 #include "ass_utils.h" | 37 #include "ass_utils.h" |
38 | 38 |
39 /** | 39 #define VERTICAL_LOWER_BOUND 0x02f1 |
40 * Select Microfost Unicode CharMap, if the font has one. | 40 |
41 /** | |
42 * Select a good charmap, prefer Microsoft Unicode charmaps. | |
41 * Otherwise, let FreeType decide. | 43 * Otherwise, let FreeType decide. |
42 */ | 44 */ |
43 static void charmap_magic(ASS_Library *library, FT_Face face) | 45 static void charmap_magic(ASS_Library *library, FT_Face face) |
44 { | 46 { |
45 int i; | 47 int i; |
48 int ms_cmap = -1; | |
49 | |
50 // Search for a Microsoft Unicode cmap | |
46 for (i = 0; i < face->num_charmaps; ++i) { | 51 for (i = 0; i < face->num_charmaps; ++i) { |
47 FT_CharMap cmap = face->charmaps[i]; | 52 FT_CharMap cmap = face->charmaps[i]; |
48 unsigned pid = cmap->platform_id; | 53 unsigned pid = cmap->platform_id; |
49 unsigned eid = cmap->encoding_id; | 54 unsigned eid = cmap->encoding_id; |
50 if (pid == 3 /*microsoft */ | 55 if (pid == 3 /*microsoft */ |
51 && (eid == 1 /*unicode bmp */ | 56 && (eid == 1 /*unicode bmp */ |
52 || eid == 10 /*full unicode */ )) { | 57 || eid == 10 /*full unicode */ )) { |
53 FT_Set_Charmap(face, cmap); | 58 FT_Set_Charmap(face, cmap); |
54 return; | 59 return; |
55 } | 60 } else if (pid == 3 && ms_cmap < 0) |
61 ms_cmap = i; | |
62 } | |
63 | |
64 // Try the first Microsoft cmap if no Microsoft Unicode cmap was found | |
65 if (ms_cmap >= 0) { | |
66 FT_CharMap cmap = face->charmaps[ms_cmap]; | |
67 FT_Set_Charmap(face, cmap); | |
68 return; | |
56 } | 69 } |
57 | 70 |
58 if (!face->charmap) { | 71 if (!face->charmap) { |
59 if (face->num_charmaps == 0) { | 72 if (face->num_charmaps == 0) { |
60 ass_msg(library, MSGL_WARN, "Font face with no charmaps"); | 73 ass_msg(library, MSGL_WARN, "Font face with no charmaps"); |
63 ass_msg(library, MSGL_WARN, | 76 ass_msg(library, MSGL_WARN, |
64 "No charmap autodetected, trying the first one"); | 77 "No charmap autodetected, trying the first one"); |
65 FT_Set_Charmap(face, face->charmaps[0]); | 78 FT_Set_Charmap(face, face->charmaps[0]); |
66 return; | 79 return; |
67 } | 80 } |
68 } | |
69 | |
70 static void update_transform(ASS_Font *font) | |
71 { | |
72 int i; | |
73 FT_Matrix m; | |
74 m.xx = double_to_d16(font->scale_x); | |
75 m.yy = double_to_d16(font->scale_y); | |
76 m.xy = m.yx = 0; | |
77 for (i = 0; i < font->n_faces; ++i) | |
78 FT_Set_Transform(font->faces[i], &m, &font->v); | |
79 } | 81 } |
80 | 82 |
81 /** | 83 /** |
82 * \brief find a memory font by name | 84 * \brief find a memory font by name |
83 */ | 85 */ |
137 if (mem_idx >= 0) { | 139 if (mem_idx >= 0) { |
138 error = | 140 error = |
139 FT_New_Memory_Face(font->ftlibrary, | 141 FT_New_Memory_Face(font->ftlibrary, |
140 (unsigned char *) font->library-> | 142 (unsigned char *) font->library-> |
141 fontdata[mem_idx].data, | 143 fontdata[mem_idx].data, |
142 font->library->fontdata[mem_idx].size, 0, | 144 font->library->fontdata[mem_idx].size, index, |
143 &face); | 145 &face); |
144 if (error) { | 146 if (error) { |
145 ass_msg(font->library, MSGL_WARN, | 147 ass_msg(font->library, MSGL_WARN, |
146 "Error opening memory font: '%s'", path); | 148 "Error opening memory font: '%s'", path); |
147 free(path); | 149 free(path); |
158 } | 160 } |
159 charmap_magic(font->library, face); | 161 charmap_magic(font->library, face); |
160 buggy_font_workaround(face); | 162 buggy_font_workaround(face); |
161 | 163 |
162 font->faces[font->n_faces++] = face; | 164 font->faces[font->n_faces++] = face; |
163 update_transform(font); | |
164 face_set_size(face, font->size); | 165 face_set_size(face, font->size); |
165 free(path); | 166 free(path); |
166 return font->n_faces - 1; | 167 return font->n_faces - 1; |
167 } | 168 } |
168 | 169 |
186 font.n_faces = 0; | 187 font.n_faces = 0; |
187 font.desc.family = strdup(desc->family); | 188 font.desc.family = strdup(desc->family); |
188 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; | 189 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; |
189 font.desc.bold = desc->bold; | 190 font.desc.bold = desc->bold; |
190 font.desc.italic = desc->italic; | 191 font.desc.italic = desc->italic; |
192 font.desc.vertical = desc->vertical; | |
191 | 193 |
192 font.scale_x = font.scale_y = 1.; | 194 font.scale_x = font.scale_y = 1.; |
193 font.v.x = font.v.y = 0; | 195 font.v.x = font.v.y = 0; |
194 font.size = 0.; | 196 font.size = 0.; |
195 | 197 |
211 font->scale_y = scale_y; | 213 font->scale_y = scale_y; |
212 if (v) { | 214 if (v) { |
213 font->v.x = v->x; | 215 font->v.x = v->x; |
214 font->v.y = v->y; | 216 font->v.y = v->y; |
215 } | 217 } |
216 update_transform(font); | |
217 } | 218 } |
218 | 219 |
219 static void face_set_size(FT_Face face, double size) | 220 static void face_set_size(FT_Face face, double size) |
220 { | 221 { |
221 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); | 222 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); |
274 *desc = FT_MulFix(os2->usWinDescent, y_scale); | 275 *desc = FT_MulFix(os2->usWinDescent, y_scale); |
275 } else { | 276 } else { |
276 *asc = FT_MulFix(face->ascender, y_scale); | 277 *asc = FT_MulFix(face->ascender, y_scale); |
277 *desc = FT_MulFix(-face->descender, y_scale); | 278 *desc = FT_MulFix(-face->descender, y_scale); |
278 } | 279 } |
280 if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) { | |
281 *asc = FT_MulFix(face->max_advance_width, y_scale); | |
282 } | |
279 return; | 283 return; |
280 } | 284 } |
281 } | 285 } |
282 | 286 |
283 *asc = *desc = 0; | 287 *asc = *desc = 0; |
412 int index = 0; | 416 int index = 0; |
413 int i; | 417 int i; |
414 FT_Glyph glyph; | 418 FT_Glyph glyph; |
415 FT_Face face = 0; | 419 FT_Face face = 0; |
416 int flags = 0; | 420 int flags = 0; |
421 int vertical = font->desc.vertical; | |
417 | 422 |
418 if (ch < 0x20) | 423 if (ch < 0x20) |
419 return 0; | 424 return 0; |
420 // Handle NBSP like a regular space when rendering the glyph | 425 // Handle NBSP like a regular space when rendering the glyph |
421 if (ch == 0xa0) | 426 if (ch == 0xa0) |
439 font->desc.bold, font->desc.italic); | 444 font->desc.bold, font->desc.italic); |
440 face_idx = add_face(fontconfig_priv, font, ch); | 445 face_idx = add_face(fontconfig_priv, font, ch); |
441 if (face_idx >= 0) { | 446 if (face_idx >= 0) { |
442 face = font->faces[face_idx]; | 447 face = font->faces[face_idx]; |
443 index = FT_Get_Char_Index(face, ch); | 448 index = FT_Get_Char_Index(face, ch); |
449 if (index == 0 && face->num_charmaps > 0) { | |
450 ass_msg(font->library, MSGL_WARN, | |
451 "Glyph 0x%X not found, falling back to first charmap", ch); | |
452 FT_CharMap cur = face->charmap; | |
453 FT_Set_Charmap(face, face->charmaps[0]); | |
454 index = FT_Get_Char_Index(face, ch); | |
455 FT_Set_Charmap(face, cur); | |
456 } | |
444 if (index == 0) { | 457 if (index == 0) { |
445 ass_msg(font->library, MSGL_ERR, | 458 ass_msg(font->library, MSGL_ERR, |
446 "Glyph 0x%X not found in font for (%s, %d, %d)", | 459 "Glyph 0x%X not found in font for (%s, %d, %d)", |
447 ch, font->desc.family, font->desc.bold, | 460 ch, font->desc.family, font->desc.bold, |
448 font->desc.italic); | 461 font->desc.italic); |
449 } | 462 } |
450 } | 463 } |
451 } | 464 } |
452 #endif | 465 #endif |
453 | 466 |
467 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | |
468 | FT_LOAD_IGNORE_TRANSFORM; | |
454 switch (hinting) { | 469 switch (hinting) { |
455 case ASS_HINTING_NONE: | 470 case ASS_HINTING_NONE: |
456 flags = FT_LOAD_NO_HINTING; | 471 flags |= FT_LOAD_NO_HINTING; |
457 break; | 472 break; |
458 case ASS_HINTING_LIGHT: | 473 case ASS_HINTING_LIGHT: |
459 flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; | 474 flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; |
460 break; | 475 break; |
461 case ASS_HINTING_NORMAL: | 476 case ASS_HINTING_NORMAL: |
462 flags = FT_LOAD_FORCE_AUTOHINT; | 477 flags |= FT_LOAD_FORCE_AUTOHINT; |
463 break; | 478 break; |
464 case ASS_HINTING_NATIVE: | 479 case ASS_HINTING_NATIVE: |
465 flags = 0; | |
466 break; | 480 break; |
467 } | 481 } |
468 | 482 |
469 error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags); | 483 error = FT_Load_Glyph(face, index, flags); |
470 if (error) { | 484 if (error) { |
471 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | 485 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", |
472 index); | 486 index); |
473 return 0; | 487 return 0; |
474 } | 488 } |
486 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | 500 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", |
487 index); | 501 index); |
488 return 0; | 502 return 0; |
489 } | 503 } |
490 | 504 |
505 // Rotate glyph, if needed | |
506 if (vertical && ch >= VERTICAL_LOWER_BOUND) { | |
507 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; | |
508 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); | |
509 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, | |
510 face->glyph->metrics.vertAdvance, | |
511 0); | |
512 glyph->advance.x = face->glyph->linearVertAdvance; | |
513 } | |
514 | |
515 // Apply scaling and shift | |
516 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0, | |
517 double_to_d16(font->scale_y) }; | |
518 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; | |
519 FT_Outline_Transform(outl, &scale); | |
520 FT_Outline_Translate(outl, font->v.x, font->v.y); | |
521 glyph->advance.x *= font->scale_x; | |
522 | |
491 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, | 523 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, |
492 deco & DECO_STRIKETHROUGH); | 524 deco & DECO_STRIKETHROUGH); |
493 | 525 |
494 return glyph; | 526 return glyph; |
495 } | 527 } |
499 **/ | 531 **/ |
500 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2) | 532 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2) |
501 { | 533 { |
502 FT_Vector v = { 0, 0 }; | 534 FT_Vector v = { 0, 0 }; |
503 int i; | 535 int i; |
536 | |
537 if (font->desc.vertical) | |
538 return v; | |
504 | 539 |
505 for (i = 0; i < font->n_faces; ++i) { | 540 for (i = 0; i < font->n_faces; ++i) { |
506 FT_Face face = font->faces[i]; | 541 FT_Face face = font->faces[i]; |
507 int i1 = FT_Get_Char_Index(face, c1); | 542 int i1 = FT_Get_Char_Index(face, c1); |
508 int i2 = FT_Get_Char_Index(face, c2); | 543 int i2 = FT_Get_Char_Index(face, c2); |
528 FT_Done_Face(font->faces[i]); | 563 FT_Done_Face(font->faces[i]); |
529 if (font->desc.family) | 564 if (font->desc.family) |
530 free(font->desc.family); | 565 free(font->desc.family); |
531 free(font); | 566 free(font); |
532 } | 567 } |
568 | |
569 /** | |
570 * \brief Calculate the cbox of a series of points | |
571 */ | |
572 static void | |
573 get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) | |
574 { | |
575 box->xMin = box->yMin = INT_MAX; | |
576 box->xMax = box->yMax = INT_MIN; | |
577 int i; | |
578 | |
579 for (i = start; i <= end; i++) { | |
580 box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin; | |
581 box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax; | |
582 box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin; | |
583 box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax; | |
584 } | |
585 } | |
586 | |
587 /** | |
588 * \brief Determine winding direction of a contour | |
589 * \return direction; 0 = clockwise | |
590 */ | |
591 static int get_contour_direction(FT_Vector *points, int start, int end) | |
592 { | |
593 int i; | |
594 long long sum = 0; | |
595 int x = points[start].x; | |
596 int y = points[start].y; | |
597 for (i = start + 1; i <= end; i++) { | |
598 sum += x * (points[i].y - y) - y * (points[i].x - x); | |
599 x = points[i].x; | |
600 y = points[i].y; | |
601 } | |
602 sum += x * (points[start].y - y) - y * (points[start].x - x); | |
603 return sum > 0; | |
604 } | |
605 | |
606 /** | |
607 * \brief Fix-up stroker result for huge borders by removing inside contours | |
608 * that would reverse in size | |
609 */ | |
610 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y) | |
611 { | |
612 int nc = glyph->outline.n_contours; | |
613 int begin, stop; | |
614 char modified = 0; | |
615 char *valid_cont = malloc(nc); | |
616 int start = 0; | |
617 int end = -1; | |
618 FT_BBox *boxes = malloc(nc * sizeof(FT_BBox)); | |
619 int i, j; | |
620 int inside_direction; | |
621 | |
622 inside_direction = FT_Outline_Get_Orientation(&glyph->outline) == | |
623 FT_ORIENTATION_TRUETYPE; | |
624 | |
625 // create a list of cboxes of the contours | |
626 for (i = 0; i < nc; i++) { | |
627 start = end + 1; | |
628 end = glyph->outline.contours[i]; | |
629 get_contour_cbox(&boxes[i], glyph->outline.points, start, end); | |
630 } | |
631 | |
632 // for each contour, check direction and whether it's "outside" | |
633 // or contained in another contour | |
634 end = -1; | |
635 for (i = 0; i < nc; i++) { | |
636 start = end + 1; | |
637 end = glyph->outline.contours[i]; | |
638 int dir = get_contour_direction(glyph->outline.points, start, end); | |
639 valid_cont[i] = 1; | |
640 if (dir == inside_direction) { | |
641 for (j = 0; j < nc; j++) { | |
642 if (i == j) | |
643 continue; | |
644 if (boxes[i].xMin >= boxes[j].xMin && | |
645 boxes[i].xMax <= boxes[j].xMax && | |
646 boxes[i].yMin >= boxes[j].yMin && | |
647 boxes[i].yMax <= boxes[j].yMax) | |
648 goto check_inside; | |
649 } | |
650 /* "inside" contour but we can't find anything it could be | |
651 * inside of - assume the font is buggy and it should be | |
652 * an "outside" contour, and reverse it */ | |
653 for (j = 0; j < (end + 1 - start) / 2; j++) { | |
654 FT_Vector temp = glyph->outline.points[start + j]; | |
655 char temp2 = glyph->outline.tags[start + j]; | |
656 glyph->outline.points[start + j] = glyph->outline.points[end - j]; | |
657 glyph->outline.points[end - j] = temp; | |
658 glyph->outline.tags[start + j] = glyph->outline.tags[end - j]; | |
659 glyph->outline.tags[end - j] = temp2; | |
660 } | |
661 dir ^= 1; | |
662 } | |
663 check_inside: | |
664 if (dir == inside_direction) { | |
665 FT_BBox box; | |
666 get_contour_cbox(&box, glyph->outline.points, start, end); | |
667 int width = box.xMax - box.xMin; | |
668 int height = box.yMax - box.yMin; | |
669 if (width < border_x * 2 || height < border_y * 2) { | |
670 valid_cont[i] = 0; | |
671 modified = 1; | |
672 } | |
673 } | |
674 } | |
675 | |
676 // zero-out contours that can be removed; much simpler than copying | |
677 if (modified) { | |
678 for (i = 0; i < nc; i++) { | |
679 if (valid_cont[i]) | |
680 continue; | |
681 begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1; | |
682 stop = glyph->outline.contours[i]; | |
683 for (j = begin; j <= stop; j++) { | |
684 glyph->outline.points[j].x = 0; | |
685 glyph->outline.points[j].y = 0; | |
686 glyph->outline.tags[j] = 0; | |
687 } | |
688 } | |
689 } | |
690 | |
691 free(boxes); | |
692 free(valid_cont); | |
693 } | |
694 |