Mercurial > mplayer.hg
changeset 31853:e64df5862cea
Import libass 0.9.10
author | greg |
---|---|
date | Fri, 06 Aug 2010 21:13:41 +0000 |
parents | 4ab8b78352ff |
children | c680009d6779 |
files | Makefile libass/ass.c libass/ass.h libass/ass_bitmap.c libass/ass_bitmap.h libass/ass_cache.c libass/ass_drawing.c libass/ass_drawing.h libass/ass_font.c libass/ass_font.h libass/ass_fontconfig.c libass/ass_parse.c libass/ass_render.c libass/ass_render.h libass/ass_render_api.c libass/ass_strtod.c |
diffstat | 16 files changed, 912 insertions(+), 684 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Fri Aug 06 10:48:16 2010 +0000 +++ b/Makefile Fri Aug 06 21:13:41 2010 +0000 @@ -119,6 +119,7 @@ libass/ass_library.c \ libass/ass_parse.c \ libass/ass_render.c \ + libass/ass_render_api.c \ libass/ass_strtod.c \ libass/ass_utils.c \
--- a/libass/ass.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass.c Fri Aug 06 21:13:41 2010 +0000 @@ -38,6 +38,8 @@ #include "ass_utils.h" #include "ass_library.h" +#define ass_atof(STR) (ass_strtod((STR),NULL)) + typedef enum { PST_UNKNOWN = 0, PST_INFO, @@ -250,7 +252,7 @@ ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); #define INTVAL(name) ANYVAL(name,atoi) -#define FPVAL(name) ANYVAL(name,atof) +#define FPVAL(name) ANYVAL(name,ass_atof) #define TIMEVAL(name) \ } else if (strcasecmp(tname, #name) == 0) { \ target->name = string2timecode(track->library, token); \ @@ -384,7 +386,7 @@ else if (!strcasecmp(*fs, "PlayResY")) track->PlayResY = atoi(token); else if (!strcasecmp(*fs, "Timer")) - track->Timer = atof(token); + track->Timer = ass_atof(token); else if (!strcasecmp(*fs, "WrapStyle")) track->WrapStyle = atoi(token); else if (!strcasecmp(*fs, "ScaledBorderAndShadow")) @@ -534,12 +536,6 @@ style->Name = strdup("Default"); if (!style->FontName) style->FontName = strdup("Arial"); - // skip '@' at the start of the font name - if (*style->FontName == '@') { - p = style->FontName; - style->FontName = strdup(p + 1); - free(p); - } free(format); return 0; @@ -568,7 +564,7 @@ } else if (!strncmp(str, "PlayResY:", 9)) { track->PlayResY = atoi(str + 9); } else if (!strncmp(str, "Timer:", 6)) { - track->Timer = atof(str + 6); + track->Timer = ass_atof(str + 6); } else if (!strncmp(str, "WrapStyle:", 10)) { track->WrapStyle = atoi(str + 10); } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) { @@ -618,7 +614,7 @@ process_event_tail(track, event, str, 0); } else { - ass_msg(track->library, MSGL_V, "Not understood: '%s'", str); + ass_msg(track->library, MSGL_V, "Not understood: '%.30s'", str); } return 0; } @@ -1033,14 +1029,6 @@ sz = ftell(fp); rewind(fp); - if (sz > 10 * 1024 * 1024) { - ass_msg(library, MSGL_INFO, - "ass_read_file(%s): Refusing to load subtitles " - "larger than 10MiB", fname); - fclose(fp); - return 0; - } - ass_msg(library, MSGL_V, "File size: %ld", sz); buf = malloc(sz + 1);
--- a/libass/ass.h Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass.h Fri Aug 06 21:13:41 2010 +0000 @@ -75,11 +75,13 @@ void ass_library_done(ASS_Library *priv); /** - * \brief Set private font directory. - * It is used for saving embedded fonts and also in font lookup. + * \brief Set additional fonts directory. + * Optional directory that will be scanned for fonts recursively. The fonts + * found are used for font lookup. + * NOTE: A valid font directory is not needed to support embedded fonts. * * \param priv library handle - * \param fonts_dir private directory for font extraction + * \param fonts_dir directory with additional fonts */ void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir); @@ -203,6 +205,8 @@ * if fontconfig is used. * \param update whether fontconfig cache should be built/updated now. Only * relevant if fontconfig is used. + * + * NOTE: font lookup must be configured before an ASS_Renderer can be used. */ void ass_set_fonts(ASS_Renderer *priv, const char *default_font, const char *default_family, int fc, const char *config, @@ -297,7 +301,8 @@ void ass_process_data(ASS_Track *track, char *data, int size); /** - * \brief Parse Codec Private section of subtitle stream. + * \brief Parse Codec Private section of the subtitle stream, in Matroska + * format. See the Matroska specification for details. * \param track target track * \param data string to parse * \param size length of data @@ -305,8 +310,8 @@ void ass_process_codec_private(ASS_Track *track, char *data, int size); /** - * \brief Parse a chunk of subtitle stream data. In Matroska, - * this contains exactly 1 event (or a commentary). + * \brief Parse a chunk of subtitle stream data. A chunk contains exactly one + * event in Matroska format. See the Matroska specification for details. * \param track track * \param data string to parse * \param size length of data
--- a/libass/ass_bitmap.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_bitmap.c Fri Aug 06 21:13:41 2010 +0000 @@ -139,8 +139,8 @@ static Bitmap *alloc_bitmap(int w, int h) { Bitmap *bm; - bm = calloc(1, sizeof(Bitmap)); - bm->buffer = malloc(w * h); + bm = malloc(sizeof(Bitmap)); + bm->buffer = calloc(w, h); bm->w = w; bm->h = h; bm->left = bm->top = 0; @@ -165,7 +165,7 @@ return dst; } -static int check_glyph_area(ASS_Library *library, FT_Glyph glyph) +int check_glyph_area(ASS_Library *library, FT_Glyph glyph) { FT_BBox bbox; long long dx, dy; @@ -213,7 +213,6 @@ w = bit->width; h = bit->rows; bm = alloc_bitmap(w + 2 * bord, h + 2 * bord); - memset(bm->buffer, 0, bm->w * bm->h); bm->left = bg->left - bord; bm->top = -bg->top - bord;
--- a/libass/ass_bitmap.h Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_bitmap.h Fri Aug 06 21:13:41 2010 +0000 @@ -53,5 +53,6 @@ int border_style); void ass_free_bitmap(Bitmap *bm); +int check_glyph_area(ASS_Library *library, FT_Glyph glyph); #endif /* LIBASS_BITMAP_H */
--- a/libass/ass_cache.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_cache.c Fri Aug 06 21:13:41 2010 +0000 @@ -156,6 +156,8 @@ return 0; if (a->treat_family_as_pattern != b->treat_family_as_pattern) return 0; + if (a->vertical != b->vertical) + return 0; return 1; } @@ -286,6 +288,11 @@ void *cache_add_glyph(Hashmap *glyph_cache, GlyphHashKey *key, GlyphHashValue *val) { + if (val->glyph && val->glyph->format == FT_GLYPH_FORMAT_BITMAP) { + FT_Bitmap *bitmap = &((FT_BitmapGlyph) val->glyph)->bitmap; + glyph_cache->cache_size += bitmap->rows * bitmap->pitch; + } + return hashmap_insert(glyph_cache, key, val); }
--- a/libass/ass_drawing.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_drawing.c Fri Aug 06 21:13:41 2010 +0000 @@ -112,24 +112,12 @@ static void drawing_finish(ASS_Drawing *drawing, int raw_mode) { int i, offset; - FT_BBox bbox; + FT_BBox bbox = drawing->cbox; FT_Outline *ol = &drawing->glyph->outline; // Close the last contour drawing_close_shape(drawing); -#if 0 - // Dump points - for (i = 0; i < ol->n_points; i++) { - printf("point (%d, %d)\n", (int) ol->points[i].x, - (int) ol->points[i].y); - } - - // Dump contours - for (i = 0; i < ol->n_contours; i++) - printf("contour %d\n", ol->contours[i]); -#endif - ass_msg(drawing->library, MSGL_V, "Parsed drawing with %d points and %d contours", ol->n_points, ol->n_contours); @@ -137,7 +125,6 @@ if (raw_mode) return; - FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox); drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin); drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y); @@ -231,15 +218,6 @@ p++; } -#if 0 - // Check tokens - ASS_DrawingToken *t = root; - while(t) { - printf("token %d point (%d, %d)\n", t->type, t->point.x, t->point.y); - t = t->next; - } -#endif - return root; } @@ -256,6 +234,19 @@ } /* + * \brief Update drawing cbox + */ +static inline void update_cbox(ASS_Drawing *drawing, FT_Vector *point) +{ + FT_BBox *box = &drawing->cbox; + + box->xMin = FFMIN(box->xMin, point->x); + box->xMax = FFMAX(box->xMax, point->x); + box->yMin = FFMIN(box->yMin, point->y); + box->yMax = FFMAX(box->yMax, point->y); +} + +/* * \brief Translate and scale a point coordinate according to baseline * offset and scale. */ @@ -263,6 +254,8 @@ { point->x = drawing->point_scale_x * point->x; point->y = drawing->point_scale_y * -point->y; + + update_cbox(drawing, point); } /* @@ -369,6 +362,8 @@ drawing = calloc(1, sizeof(*drawing)); drawing->text = calloc(1, DRAWING_INITIAL_SIZE); drawing->size = DRAWING_INITIAL_SIZE; + drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX; + drawing->cbox.xMax = drawing->cbox.yMax = INT_MIN; drawing->ftlibrary = lib; if (font) { @@ -390,8 +385,6 @@ void ass_drawing_free(ASS_Drawing* drawing) { if (drawing) { - if (drawing->glyph) - FT_Done_Glyph((FT_Glyph) drawing->glyph); free(drawing->text); } free(drawing);
--- a/libass/ass_drawing.h Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_drawing.h Fri Aug 06 21:13:41 2010 +0000 @@ -65,6 +65,7 @@ int max_contours; double point_scale_x; double point_scale_y; + FT_BBox cbox; // bounding box, or let's say... VSFilter's idea of it } ASS_Drawing; ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font,
--- 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); +} +
--- a/libass/ass_font.h Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_font.h Fri Aug 06 21:13:41 2010 +0000 @@ -36,6 +36,7 @@ unsigned bold; unsigned italic; int treat_family_as_pattern; + int vertical; // @font vertical layout } ASS_FontDesc; typedef struct { @@ -62,5 +63,6 @@ uint32_t ch, ASS_Hinting hinting, int flags); FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2); void ass_font_free(ASS_Font *font); +void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y); #endif /* LIBASS_FONT_H */
--- a/libass/ass_fontconfig.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_fontconfig.c Fri Aug 06 21:13:41 2010 +0000 @@ -52,6 +52,61 @@ #ifdef CONFIG_FONTCONFIG /** + * \brief Case-insensitive match ASS/SSA font family against full name. (also + * known as "name for humans") + * + * \param lib library instance + * \param priv fontconfig instance + * \param family font fullname + * \param bold weight attribute + * \param italic italic attribute + * \return font set + */ +static FcFontSet * +match_fullname(ASS_Library *lib, FCInstance *priv, const char *family, + unsigned bold, unsigned italic) +{ + FcFontSet *sets[2]; + FcFontSet *result = FcFontSetCreate(); + int nsets = 0; + int i, fi; + + if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetSystem))) + nsets++; + if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetApplication))) + nsets++; + + // Run over font sets and patterns and try to match against full name + for (i = 0; i < nsets; i++) { + FcFontSet *set = sets[i]; + for (fi = 0; fi < set->nfont; fi++) { + FcPattern *pat = set->fonts[fi]; + char *fullname; + int pi = 0, at; + FcBool ol; + while (FcPatternGetString(pat, FC_FULLNAME, pi++, + (FcChar8 **) &fullname) == FcResultMatch) { + if (FcPatternGetBool(pat, FC_OUTLINE, 0, &ol) != FcResultMatch + || ol != FcTrue) + continue; + if (FcPatternGetInteger(pat, FC_SLANT, 0, &at) != FcResultMatch + || at < italic) + continue; + if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &at) != FcResultMatch + || at < bold) + continue; + if (strcasecmp(fullname, family) == 0) { + FcFontSetAdd(result, FcPatternDuplicate(pat)); + break; + } + } + } + } + + return result; +} + +/** * \brief Low-level font selection. * \param priv private data * \param family font family @@ -62,7 +117,7 @@ * \param code: the character that should be present in the font, can be 0 * \return font file path */ -static char *_select_font(ASS_Library *library, FCInstance *priv, +static char *select_font(ASS_Library *library, FCInstance *priv, const char *family, int treat_family_as_pattern, unsigned bold, unsigned italic, int *index, uint32_t code) @@ -74,7 +129,7 @@ FcChar8 *r_family, *r_style, *r_file, *r_fullname; FcBool r_outline, r_embolden; FcCharSet *r_charset; - FcFontSet *fset = NULL; + FcFontSet *ffullname = NULL, *fsorted = NULL, *fset = NULL; int curf; char *retval = NULL; int family_cnt = 0; @@ -126,10 +181,23 @@ if (!rc) goto error; - fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result); - if (!fset) + fsorted = FcFontSort(priv->config, pat, FcTrue, NULL, &result); + ffullname = match_fullname(library, priv, family, bold, italic); + if (!fsorted || !ffullname) goto error; + fset = FcFontSetCreate(); + for (curf = 0; curf < ffullname->nfont; ++curf) { + FcPattern *curp = ffullname->fonts[curf]; + FcPatternReference(curp); + FcFontSetAdd(fset, curp); + } + for (curf = 0; curf < fsorted->nfont; ++curf) { + FcPattern *curp = fsorted->fonts[curf]; + FcPatternReference(curp); + FcFontSetAdd(fset, curp); + } + for (curf = 0; curf < fset->nfont; ++curf) { FcPattern *curp = fset->fonts[curf]; @@ -215,6 +283,10 @@ FcPatternDestroy(pat); if (rpat) FcPatternDestroy(rpat); + if (fsorted) + FcFontSetDestroy(fsorted); + if (ffullname) + FcFontSetDestroy(ffullname); if (fset) FcFontSetDestroy(fset); return retval; @@ -244,11 +316,11 @@ } if (family && *family) res = - _select_font(library, priv, family, treat_family_as_pattern, + select_font(library, priv, family, treat_family_as_pattern, bold, italic, index, code); if (!res && priv->family_default) { res = - _select_font(library, priv, priv->family_default, 0, bold, + select_font(library, priv, priv->family_default, 0, bold, italic, index, code); if (res) ass_msg(library, MSGL_WARN, "fontconfig_select: Using default " @@ -263,7 +335,7 @@ res, *index); } if (!res) { - res = _select_font(library, priv, "Arial", 0, bold, italic, + res = select_font(library, priv, "Arial", 0, bold, italic, index, code); if (res) ass_msg(library, MSGL_WARN, "fontconfig_select: Using 'Arial' " @@ -283,8 +355,8 @@ * \param library library object * \param ftlibrary freetype library object * \param idx index of the processed font in library->fontdata - * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace. - * With older FontConfig versions, save the font to ~/.mplayer/fonts. + * + * Builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace. */ static void process_fontdata(FCInstance *priv, ASS_Library *library, FT_Library ftlibrary, int idx) @@ -311,7 +383,7 @@ num_faces = face->num_faces; pattern = - FcFreeTypeQueryFace(face, (unsigned char *) name, 0, + FcFreeTypeQueryFace(face, (unsigned char *) name, face_index, FcConfigGetBlanks(priv->config)); if (!pattern) { ass_msg(library, MSGL_WARN, "%s failed", "FcFreeTypeQueryFace"); @@ -388,7 +460,7 @@ process_fontdata(priv, library, ftlibrary, i); if (dir) { - ass_msg(library, MSGL_INFO, "Updating font cache"); + ass_msg(library, MSGL_V, "Updating font cache"); rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir); if (!rc) {
--- a/libass/ass_parse.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_parse.c Fri Aug 06 21:13:41 2010 +0000 @@ -68,9 +68,15 @@ { unsigned val; ASS_FontDesc desc; - desc.family = strdup(render_priv->state.family); - desc.treat_family_as_pattern = - render_priv->state.treat_family_as_pattern; + desc.treat_family_as_pattern = render_priv->state.treat_family_as_pattern; + + if (render_priv->state.family[0] == '@') { + desc.vertical = 1; + desc.family = strdup(render_priv->state.family + 1); + } else { + desc.vertical = 0; + desc.family = strdup(render_priv->state.family); + } val = render_priv->state.bold; // 0 = normal, 1 = bold, >1 = exact weight @@ -210,6 +216,7 @@ int res = 0; ASS_Drawing *drawing; + ass_drawing_free(render_priv->state.clip_drawing); render_priv->state.clip_drawing = ass_drawing_new( render_priv->fontconfig_priv, render_priv->state.font, @@ -227,20 +234,6 @@ while (*p != ')' && *p != '}' && p != 0) ass_drawing_add_char(drawing, *p++); skipopt(')'); - if (ass_drawing_parse(drawing, 1)) { - // We need to translate the clip according to screen borders - if (render_priv->settings.left_margin != 0 || - render_priv->settings.top_margin != 0) { - FT_Vector trans = { - .x = int_to_d6(render_priv->settings.left_margin), - .y = -int_to_d6(render_priv->settings.top_margin), - }; - FT_Outline_Translate(&drawing->glyph->outline, trans.x, trans.y); - } - ass_msg(render_priv->library, MSGL_DBG2, - "Parsed vector clip: scale %d, scales (%f, %f) string [%s]\n", - scale, drawing->scale_x, drawing->scale_y, drawing->text); - } return p; } @@ -586,7 +579,7 @@ for (cnt = 0; cnt < 3; ++cnt) { if (*p == '\\') break; - v[cnt] = strtod(p, &p); + mystrtod(&p, &v[cnt]); skip(','); } if (cnt == 3) { @@ -836,7 +829,7 @@ } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) { render_priv->state.scroll_direction = SCROLL_TB; } else { - ass_msg(render_priv->library, MSGL_V, + ass_msg(render_priv->library, MSGL_DBG2, "Unknown transition effect: '%s'", event->Effect); return; } @@ -894,7 +887,7 @@ break; } else if (*p != '\\') ass_msg(render_priv->library, MSGL_V, - "Unable to parse: '%s'", p); + "Unable to parse: '%.30s'", p); if (*p == 0) break; } @@ -918,6 +911,14 @@ p += 2; *str = p; return NBSP; + } else if (p[1] == '{') { + p += 2; + *str = p; + return '{'; + } else if (p[1] == '}') { + p += 2; + *str = p; + return '}'; } } chr = ass_utf8_get_char((char **) &p);
--- a/libass/ass_render.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_render.c Fri Aug 06 21:13:41 2010 +0000 @@ -22,30 +22,14 @@ #include <assert.h> #include <math.h> -#include <inttypes.h> -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_STROKER_H -#include FT_GLYPH_H -#include FT_SYNTHESIS_H -#include "ass.h" -#include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" -#include "ass_utils.h" -#include "ass_fontconfig.h" -#include "ass_library.h" -#include "ass_drawing.h" #include "ass_render.h" #include "ass_parse.h" #define MAX_GLYPHS_INITIAL 1024 #define MAX_LINES_INITIAL 64 #define SUBPIXEL_MASK 63 -#define SUBPIXEL_ACCURACY 7 // d6 mask for subpixel accuracy adjustment -#define GLYPH_CACHE_MAX 1000 -#define BITMAP_CACHE_MAX_SIZE 50 * 1048576 +#define SUBPIXEL_ACCURACY 7 static void ass_lazy_track_init(ASS_Renderer *render_priv) { @@ -119,27 +103,20 @@ priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL; priv->text_info.max_lines = MAX_LINES_INITIAL; - priv->text_info.glyphs = - calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo)); + priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo)); priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo)); + priv->settings.font_size_coeff = 1.; + ass_init_exit: if (priv) - ass_msg(library, MSGL_INFO, "Init"); + ass_msg(library, MSGL_V, "Init"); else ass_msg(library, MSGL_ERR, "Init failed"); return priv; } -void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max, - int bitmap_max) -{ - render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX; - render_priv->cache.bitmap_max_size = bitmap_max ? 1048576 * bitmap_max : - BITMAP_CACHE_MAX_SIZE; -} - static void free_list_clear(ASS_Renderer *render_priv) { if (render_priv->free_head) { @@ -154,8 +131,6 @@ } } -static void ass_free_images(ASS_Image *img); - void ass_renderer_done(ASS_Renderer *render_priv) { ass_font_cache_done(render_priv->cache.font_cache); @@ -196,21 +171,85 @@ int bitmap_h, int stride, int dst_x, int dst_y, uint32_t color) { - ASS_Image *img = calloc(1, sizeof(ASS_Image)); + ASS_Image *img = malloc(sizeof(ASS_Image)); - img->w = bitmap_w; - img->h = bitmap_h; - img->stride = stride; - img->bitmap = bitmap; - img->color = color; - img->dst_x = dst_x; - img->dst_y = dst_y; + if (img) { + img->w = bitmap_w; + img->h = bitmap_h; + img->stride = stride; + img->bitmap = bitmap; + img->color = color; + img->dst_x = dst_x; + img->dst_y = dst_y; + } return img; } -static double x2scr_pos(ASS_Renderer *render_priv, double x); -static double y2scr_pos(ASS_Renderer *render_priv, double y); +/** + * \brief Mapping between script and screen coordinates + */ +static double x2scr(ASS_Renderer *render_priv, double x) +{ + return x * render_priv->orig_width_nocrop / render_priv->font_scale_x / + render_priv->track->PlayResX + + FFMAX(render_priv->settings.left_margin, 0); +} +static double x2scr_pos(ASS_Renderer *render_priv, double x) +{ + return x * render_priv->orig_width / render_priv->font_scale_x / render_priv->track->PlayResX + + render_priv->settings.left_margin; +} +static double x2scr_scaled(ASS_Renderer *render_priv, double x) +{ + return x * render_priv->orig_width_nocrop / + render_priv->track->PlayResX + + FFMAX(render_priv->settings.left_margin, 0); +} +static double x2scr_pos_scaled(ASS_Renderer *render_priv, double x) +{ + return x * render_priv->orig_width / render_priv->track->PlayResX + + render_priv->settings.left_margin; +} +/** + * \brief Mapping between script and screen coordinates + */ +static double y2scr(ASS_Renderer *render_priv, double y) +{ + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, 0); +} +static double y2scr_pos(ASS_Renderer *render_priv, double y) +{ + return y * render_priv->orig_height / render_priv->track->PlayResY + + render_priv->settings.top_margin; +} + +// the same for toptitles +static double y2scr_top(ASS_Renderer *render_priv, double y) +{ + if (render_priv->settings.use_margins) + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY; + else + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, 0); +} +// the same for subtitles +static double y2scr_sub(ASS_Renderer *render_priv, double y) +{ + if (render_priv->settings.use_margins) + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, 0) + + FFMAX(render_priv->settings.bottom_margin, 0); + else + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, 0); +} /* * \brief Convert bitmap glyphs into ASS_Image list with inverse clipping @@ -238,9 +277,9 @@ dst_y += bm->top; // we still need to clip against screen boundaries - zx = x2scr_pos(render_priv, 0); + zx = x2scr_pos_scaled(render_priv, 0); zy = y2scr_pos(render_priv, 0); - sx = x2scr_pos(render_priv, render_priv->track->PlayResX); + sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX); sy = y2scr_pos(render_priv, render_priv->track->PlayResY); x0 = 0; @@ -295,6 +334,7 @@ img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + r[j].x0, lbrk - r[j].x0, r[j].y1 - r[j].y0, bm->w, dst_x + r[j].x0, dst_y + r[j].y0, color); + if (!img) break; *tail = img; tail = &img->next; } @@ -303,6 +343,7 @@ img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + lbrk, r[j].x1 - lbrk, r[j].y1 - r[j].y0, bm->w, dst_x + lbrk, dst_y + r[j].y0, color2); + if (!img) break; *tail = img; tail = &img->next; } @@ -384,6 +425,7 @@ img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0, brk - b_x0, b_y1 - b_y0, bm->w, dst_x + b_x0, dst_y + b_y0, color); + if (!img) return tail; *tail = img; tail = &img->next; } @@ -393,6 +435,7 @@ img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk, b_x1 - brk, b_y1 - b_y0, bm->w, dst_x + brk, dst_y + b_y0, color2); + if (!img) return tail; *tail = img; tail = &img->next; } @@ -464,7 +507,6 @@ cur_top = top - by; // Query cache - memset(&hk, 0, sizeof(hk)); hk.a = (*last_tail)->bitmap; hk.b = (*tail)->bitmap; hk.aw = aw; @@ -529,23 +571,73 @@ FT_BitmapGlyph clip_bm; ASS_Image *cur; ASS_Drawing *drawing = render_priv->state.clip_drawing; + GlyphHashKey key; + GlyphHashValue *val; int error; if (!drawing) return; - // Rasterize it - FT_Glyph_Copy((FT_Glyph) drawing->glyph, &glyph); - error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); - if (error) { - ass_msg(render_priv->library, MSGL_V, - "Clip vector rasterization failed: %d. Skipping.", error); - goto blend_vector_exit; + // Try to get mask from cache + ass_drawing_hash(drawing); + memset(&key, 0, sizeof(key)); + key.ch = -2; + key.drawing_hash = drawing->hash; + val = cache_find_glyph(render_priv->cache.glyph_cache, &key); + + if (val) { + clip_bm = (FT_BitmapGlyph) val->glyph; + } else { + GlyphHashValue v; + + // Not found in cache, parse and rasterize it + glyph = (FT_Glyph) *ass_drawing_parse(drawing, 1); + if (!glyph) { + ass_msg(render_priv->library, MSGL_WARN, + "Clip vector parsing failed. Skipping."); + goto blend_vector_error; + } + + // We need to translate the clip according to screen borders + if (render_priv->settings.left_margin != 0 || + render_priv->settings.top_margin != 0) { + FT_Vector trans = { + .x = int_to_d6(render_priv->settings.left_margin), + .y = -int_to_d6(render_priv->settings.top_margin), + }; + FT_Outline_Translate(&drawing->glyph->outline, + trans.x, trans.y); + } + + // Check glyph bounding box size + if (check_glyph_area(render_priv->library, glyph)) { + FT_Done_Glyph(glyph); + glyph = 0; + goto blend_vector_error; + } + + ass_msg(render_priv->library, MSGL_DBG2, + "Parsed vector clip: scales (%f, %f) string [%s]\n", + drawing->scale_x, drawing->scale_y, drawing->text); + + error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); + if (error) { + ass_msg(render_priv->library, MSGL_WARN, + "Clip vector rasterization failed: %d. Skipping.", error); + FT_Done_Glyph(glyph); + glyph = 0; + } + +blend_vector_error: + clip_bm = (FT_BitmapGlyph) glyph; + + // Add to cache + memset(&v, 0, sizeof(v)); + v.glyph = glyph; + cache_add_glyph(render_priv->cache.glyph_cache, &key, &v); } - clip_bm = (FT_BitmapGlyph) glyph; - clip_bm->top = -clip_bm->top; - assert(clip_bm->bitmap.pitch >= 0); + if (!clip_bm) goto blend_vector_exit; // Iterate through bitmaps and blend/clip them for (cur = head; cur; cur = cur->next) { @@ -563,7 +655,7 @@ ah = cur->h; as = cur->stride; bx = clip_bm->left; - by = clip_bm->top; + by = -clip_bm->top; bw = clip_bm->bitmap.width; bh = clip_bm->bitmap.rows; bs = clip_bm->bitmap.pitch; @@ -589,6 +681,7 @@ // Allocate new buffer and add to free list nbuffer = malloc(as * ah); + if (!nbuffer) goto blend_vector_exit; free_list_add(render_priv, nbuffer); // Blend together @@ -609,6 +702,7 @@ // Allocate new buffer and add to free list nbuffer = calloc(as, ah); + if (!nbuffer) goto blend_vector_exit; free_list_add(render_priv, nbuffer); // Blend together @@ -622,8 +716,6 @@ cur->bitmap = nbuffer; } - // Free clip vector and its bitmap, we don't need it anymore - FT_Done_Glyph(glyph); blend_vector_exit: ass_drawing_free(render_priv->state.clip_drawing); render_priv->state.clip_drawing = 0; @@ -633,8 +725,7 @@ * \brief Convert TextInfo struct to ASS_Image list * Splits glyphs in halves when needed (for \kf karaoke). */ -static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, - int dst_y) +static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, int dst_y) { int pen_x, pen_y; int i; @@ -731,62 +822,6 @@ return head; } -/** - * \brief Mapping between script and screen coordinates - */ -static double x2scr(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width_nocrop / - render_priv->track->PlayResX + - FFMAX(render_priv->settings.left_margin, 0); -} -static double x2scr_pos(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width / render_priv->track->PlayResX + - render_priv->settings.left_margin; -} - -/** - * \brief Mapping between script and screen coordinates - */ -static double y2scr(ASS_Renderer *render_priv, double y) -{ - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} -static double y2scr_pos(ASS_Renderer *render_priv, double y) -{ - return y * render_priv->orig_height / render_priv->track->PlayResY + - render_priv->settings.top_margin; -} - -// the same for toptitles -static double y2scr_top(ASS_Renderer *render_priv, double y) -{ - if (render_priv->settings.use_margins) - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY; - else - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} - -// the same for subtitles -static double y2scr_sub(ASS_Renderer *render_priv, double y) -{ - if (render_priv->settings.use_margins) - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, - 0) + FFMAX(render_priv->settings.bottom_margin, 0); - else - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} - static void compute_string_bbox(TextInfo *info, DBBox *bbox) { int i; @@ -845,8 +880,6 @@ render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.; render_priv->state.fax = render_priv->state.fay = 0.; render_priv->state.wrap_style = render_priv->track->WrapStyle; - - // FIXME: does not reset unsupported attributes. } /** @@ -878,11 +911,10 @@ render_priv->state.effect_type = EF_NONE; render_priv->state.effect_timing = 0; render_priv->state.effect_skip_timing = 0; - render_priv->state.drawing = - ass_drawing_new(render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->settings.hinting, - render_priv->ftlibrary); + render_priv->state.drawing = ass_drawing_new(render_priv->fontconfig_priv, + render_priv->state.font, + render_priv->settings.hinting, + render_priv->ftlibrary); apply_transition_effects(render_priv, event); } @@ -896,88 +928,6 @@ render_priv->state.drawing = NULL; } -// 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 Fix-up stroker result for huge borders by removing the contours from - * the outline that are harmful. -*/ -static 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; - int start = 0; - int end = -1; - FT_BBox *boxes = calloc(nc, sizeof(FT_BBox)); - int i, j; - - // 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); - } - - // if a) contour's cbox is contained in another contours cbox - // b) contour's height or width is smaller than the border*2 - // the contour can be safely removed. - valid_cont = calloc(1, nc); - for (i = 0; i < nc; i++) { - valid_cont[i] = 1; - 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) { - int width = boxes[i].xMax - boxes[i].xMin; - int height = boxes[i].yMax - boxes[i].yMin; - if (width < border_x * 2 || height < border_y * 2) { - valid_cont[i] = 0; - modified = 1; - break; - } - } - } - } - - // 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); -} - /* * Replace the outline of a glyph by a contour which makes up a simple * opaque rectangle. @@ -989,8 +939,7 @@ int i; int adv = d16_to_d6(glyph->advance.x); double scale_y = render_priv->state.scale_y; - double scale_x = render_priv->state.scale_x - * render_priv->font_scale_x; + double scale_x = render_priv->state.scale_x; FT_OutlineGlyph og = (FT_OutlineGlyph) glyph; FT_Outline *ol; @@ -1080,6 +1029,38 @@ } /** + * \brief Prepare glyph hash + */ +static void +fill_glyph_hash(ASS_Renderer *priv, GlyphHashKey *key, + ASS_Drawing *drawing, uint32_t ch) +{ + if (drawing->hash) { + key->scale_x = double_to_d16(priv->state.scale_x); + key->scale_y = double_to_d16(priv->state.scale_y); + key->outline.x = priv->state.border_x * 0xFFFF; + key->outline.y = priv->state.border_y * 0xFFFF; + key->border_style = priv->state.style->BorderStyle; + key->drawing_hash = drawing->hash; + // not very clean, but works + key->size = drawing->scale; + key->ch = -1; + } else { + key->font = priv->state.font; + key->size = priv->state.font_size; + key->ch = ch; + key->bold = priv->state.bold; + key->italic = priv->state.italic; + key->scale_x = double_to_d16(priv->state.scale_x); + key->scale_y = double_to_d16(priv->state.scale_y); + key->outline.x = priv->state.border_x * 0xFFFF; + key->outline.y = priv->state.border_y * 0xFFFF; + key->flags = priv->state.flags; + key->border_style = priv->state.style->BorderStyle; + } +} + +/** * \brief Get normal and outline (border) glyphs * \param symbol ucs4 char * \param info out: struct filled with extracted data @@ -1094,35 +1075,15 @@ { GlyphHashValue *val; GlyphHashKey key; + memset(&key, 0, sizeof(key)); - - if (drawing->hash) { - key.scale_x = double_to_d16(render_priv->state.scale_x); - key.scale_y = double_to_d16(render_priv->state.scale_y); - key.outline.x = render_priv->state.border_x * 0xFFFF; - key.outline.y = render_priv->state.border_y * 0xFFFF; - key.border_style = render_priv->state.style->BorderStyle; - key.drawing_hash = drawing->hash; - } else { - key.font = render_priv->state.font; - key.size = render_priv->state.font_size; - key.ch = symbol; - key.bold = render_priv->state.bold; - key.italic = render_priv->state.italic; - key.scale_x = double_to_d16(render_priv->state.scale_x); - key.scale_y = double_to_d16(render_priv->state.scale_y); - key.outline.x = render_priv->state.border_x * 0xFFFF; - key.outline.y = render_priv->state.border_y * 0xFFFF; - key.flags = render_priv->state.flags; - key.border_style = render_priv->state.style->BorderStyle; - } memset(info, 0, sizeof(GlyphInfo)); + fill_glyph_hash(render_priv, &key, drawing, symbol); val = cache_find_glyph(render_priv->cache.glyph_cache, &key); if (val) { - FT_Glyph_Copy(val->glyph, &info->glyph); - if (val->outline_glyph) - FT_Glyph_Copy(val->outline_glyph, &info->outline_glyph); + info->glyph = val->glyph; + info->outline_glyph = val->outline_glyph; info->bbox = val->bbox_scaled; info->advance.x = val->advance.x; info->advance.y = val->advance.y; @@ -1135,7 +1096,7 @@ if (drawing->hash) { if(!ass_drawing_parse(drawing, 0)) return; - FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph); + info->glyph = (FT_Glyph) drawing->glyph; } else { info->glyph = ass_font_get_glyph(render_priv->fontconfig_priv, @@ -1145,6 +1106,7 @@ } 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_SUBPIXELS, &info->bbox); @@ -1158,8 +1120,9 @@ render_priv->border_scale), double_to_d6(render_priv->state.border_y * render_priv->border_scale)); - } else if (render_priv->state.border_x > 0 || - render_priv->state.border_y > 0) { + } else if ((render_priv->state.border_x > 0 + || render_priv->state.border_y > 0) + && key.scale_x && key.scale_y) { FT_Glyph_Copy(info->glyph, &info->outline_glyph); stroke_outline_glyph(render_priv, @@ -1171,9 +1134,8 @@ } 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.glyph = info->glyph; + v.outline_glyph = info->outline_glyph; v.advance = info->advance; v.bbox_scaled = info->bbox; if (drawing->hash) { @@ -1184,10 +1146,81 @@ } } -static void transform_3d(FT_Vector shift, FT_Glyph *glyph, - FT_Glyph *glyph2, double frx, double fry, - double frz, double fax, double fay, double scale, - int yshift); +/** + * \brief Apply transformation to outline points of a glyph + * Applies rotations given by frx, fry and frz and projects the points back + * onto the screen plane. + */ +static void +transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, + double frz, double fax, double fay, double scale, + int yshift) +{ + double sx = sin(frx); + double sy = sin(fry); + double sz = sin(frz); + double cx = cos(frx); + double cy = cos(fry); + double cz = cos(frz); + FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline; + FT_Vector *p = outline->points; + double x, y, z, xx, yy, zz; + int i, dist; + + dist = 20000 * scale; + for (i = 0; i < outline->n_points; i++) { + x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y)); + y = (double) p[i].y + shift.y + (-fay * p[i].x); + z = 0.; + + xx = x * cz + y * sz; + yy = -(x * sz - y * cz); + zz = z; + + x = xx; + y = yy * cx + zz * sx; + z = yy * sx - zz * cx; + + xx = x * cy + z * sy; + yy = y; + zz = x * sy - z * cy; + + zz = FFMAX(zz, 1000 - dist); + + x = (xx * dist) / (zz + dist); + y = (yy * dist) / (zz + dist); + p[i].x = x - shift.x + 0.5; + p[i].y = y - shift.y + 0.5; + } +} + +/** + * \brief Apply 3d transformation to several objects + * \param shift FreeType vector + * \param glyph FreeType glyph + * \param glyph2 FreeType glyph + * \param frx x-axis rotation angle + * \param fry y-axis rotation angle + * \param frz z-axis rotation angle + * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it. + */ +static void +transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2, + double frx, double fry, double frz, double fax, double fay, + double scale, int yshift) +{ + frx = -frx; + frz = -frz; + if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) { + if (glyph && *glyph) + transform_3d_points(shift, *glyph, frx, fry, frz, + fax, fay, scale, yshift); + + if (glyph2 && *glyph2) + transform_3d_points(shift, *glyph2, frx, fry, frz, + fax, fay, scale, yshift); + } +} /** * \brief Get bitmaps for a glyph @@ -1217,38 +1250,48 @@ info->bm = info->bm_o = info->bm_s = 0; if (info->glyph && info->symbol != '\n' && info->symbol != 0 && !info->skip) { + FT_Glyph glyph; + FT_Glyph outline; + double scale_x = render_priv->font_scale_x; + + FT_Glyph_Copy(info->glyph, &glyph); + FT_Glyph_Copy(info->outline_glyph, &outline); // calculating rotation shift vector (from rotation origin to the glyph basepoint) - shift.x = info->hash_key.shift_x; - shift.y = info->hash_key.shift_y; - fax_scaled = info->fax * render_priv->font_scale_x * + shift.x = key->shift_x; + shift.y = key->shift_y; + fax_scaled = info->fax * render_priv->state.scale_x; fay_scaled = info->fay * render_priv->state.scale_y; // apply rotation - transform_3d(shift, &info->glyph, &info->outline_glyph, + transform_3d(shift, &glyph, &outline, info->frx, info->fry, info->frz, fax_scaled, fay_scaled, render_priv->font_scale, info->asc); + // PAR correction scaling + FT_Matrix m = { double_to_d16(scale_x), 0, + 0, double_to_d16(1.0) }; + // subpixel shift - if (info->glyph) - FT_Outline_Translate( - &((FT_OutlineGlyph) info->glyph)->outline, - info->hash_key.advance.x, - -info->hash_key.advance.y); - if (info->outline_glyph) - FT_Outline_Translate( - &((FT_OutlineGlyph) info->outline_glyph)->outline, - info->hash_key.advance.x, - -info->hash_key.advance.y); - + if (glyph) { + FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; + if (scale_x != 1.0) + FT_Outline_Transform(outl, &m); + FT_Outline_Translate(outl, key->advance.x, -key->advance.y); + } + if (outline) { + FT_Outline *outl = &((FT_OutlineGlyph) outline)->outline; + if (scale_x != 1.0) + FT_Outline_Transform(outl, &m); + FT_Outline_Translate(outl, key->advance.x, -key->advance.y); + } // render glyph error = glyph_to_bitmap(render_priv->library, render_priv->synth_priv, - info->glyph, info->outline_glyph, + glyph, outline, &info->bm, &info->bm_o, &info->bm_s, info->be, info->blur * render_priv->border_scale, - info->hash_key.shadow_offset, - info->hash_key.border_style); + key->shadow_offset, key->border_style); if (error) info->symbol = 0; @@ -1256,15 +1299,12 @@ hash_val.bm_o = info->bm_o; hash_val.bm = info->bm; hash_val.bm_s = info->bm_s; - cache_add_bitmap(render_priv->cache.bitmap_cache, - &(info->hash_key), &hash_val); + cache_add_bitmap(render_priv->cache.bitmap_cache, key, &hash_val); + + FT_Done_Glyph(glyph); + FT_Done_Glyph(outline); } } - // deallocate glyphs - if (info->glyph) - FT_Done_Glyph(info->glyph); - if (info->outline_glyph) - FT_Done_Glyph(info->outline_glyph); } /** @@ -1377,7 +1417,7 @@ * the difference in lengths between this two lines. * The result may not be optimal, but usually is good enough. * - * FIXME: implement style 0 and 3 correctly, add support for style 1 + * FIXME: implement style 0 and 3 correctly */ static void wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width) @@ -1646,83 +1686,44 @@ } /** - * \brief Apply transformation to outline points of a glyph - * Applies rotations given by frx, fry and frz and projects the points back - * onto the screen plane. + * Prepare bitmap hash key of a glyph */ static void -transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, - double frz, double fax, double fay, double scale, - int yshift) +fill_bitmap_hash(ASS_Renderer *priv, BitmapHashKey *hash_key, + ASS_Drawing *drawing, FT_Vector pen, uint32_t code) { - double sx = sin(frx); - double sy = sin(fry); - double sz = sin(frz); - double cx = cos(frx); - double cy = cos(fry); - double cz = cos(frz); - FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline; - FT_Vector *p = outline->points; - double x, y, z, xx, yy, zz; - int i, dist; - - dist = 20000 * scale; - for (i = 0; i < outline->n_points; i++) { - x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y)); - y = (double) p[i].y + shift.y + (-fay * p[i].x); - z = 0.; - - xx = x * cz + y * sz; - yy = -(x * sz - y * cz); - zz = z; - - x = xx; - y = yy * cx + zz * sx; - z = yy * sx - zz * cx; - - xx = x * cy + z * sy; - yy = y; - zz = x * sy - z * cy; - - zz = FFMAX(zz, 1000 - dist); - - x = (xx * dist) / (zz + dist); - y = (yy * dist) / (zz + dist); - p[i].x = x - shift.x + 0.5; - p[i].y = y - shift.y + 0.5; + if (!drawing->hash) { + hash_key->font = priv->state.font; + hash_key->size = priv->state.font_size; + hash_key->bold = priv->state.bold; + hash_key->italic = priv->state.italic; + } else { + hash_key->drawing_hash = drawing->hash; + hash_key->size = drawing->scale; } + hash_key->ch = code; + hash_key->outline.x = double_to_d16(priv->state.border_x); + hash_key->outline.y = double_to_d16(priv->state.border_y); + hash_key->scale_x = double_to_d16(priv->state.scale_x); + hash_key->scale_y = double_to_d16(priv->state.scale_y); + hash_key->frx = rot_key(priv->state.frx); + hash_key->fry = rot_key(priv->state.fry); + hash_key->frz = rot_key(priv->state.frz); + hash_key->fax = double_to_d16(priv->state.fax); + hash_key->fay = double_to_d16(priv->state.fay); + hash_key->be = priv->state.be; + hash_key->blur = priv->state.blur; + hash_key->border_style = priv->state.style->BorderStyle; + hash_key->shadow_offset.x = double_to_d6( + priv->state.shadow_x * priv->border_scale - + (int) (priv->state.shadow_x * priv->border_scale)); + hash_key->shadow_offset.y = double_to_d6( + priv->state.shadow_y * priv->border_scale - + (int) (priv->state.shadow_y * priv->border_scale)); + hash_key->flags = priv->state.flags; } /** - * \brief Apply 3d transformation to several objects - * \param shift FreeType vector - * \param glyph FreeType glyph - * \param glyph2 FreeType glyph - * \param frx x-axis rotation angle - * \param fry y-axis rotation angle - * \param frz z-axis rotation angle - * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it. - */ -static void -transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2, - double frx, double fry, double frz, double fax, double fay, - double scale, int yshift) -{ - frx = -frx; - frz = -frz; - if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) { - if (glyph && *glyph) - transform_3d_points(shift, *glyph, frx, fry, frz, - fax, fay, scale, yshift); - - if (glyph2 && *glyph2) - transform_3d_points(shift, *glyph2, frx, fry, frz, - fax, fay, scale, yshift); - } -} - - -/** * \brief Main ass rendering function, glues everything together * \param event event to render * \param event_images struct containing resulting images, will also be initialized @@ -1746,6 +1747,7 @@ double device_x = 0; double device_y = 0; TextInfo *text_info = &render_priv->text_info; + GlyphInfo *glyphs = render_priv->text_info.glyphs; ASS_Drawing *drawing; if (event->Style >= render_priv->track->n_styles) { @@ -1779,7 +1781,6 @@ // Parse drawing if (drawing->i) { drawing->scale_x = render_priv->state.scale_x * - render_priv->font_scale_x * render_priv->font_scale; drawing->scale_y = render_priv->state.scale_y * render_priv->font_scale; @@ -1800,7 +1801,7 @@ if (text_info->length >= text_info->max_glyphs) { // Raise maximum number of glyphs text_info->max_glyphs *= 2; - text_info->glyphs = + text_info->glyphs = glyphs = realloc(text_info->glyphs, sizeof(GlyphInfo) * text_info->max_glyphs); } @@ -1811,139 +1812,82 @@ delta = ass_font_get_kerning(render_priv->state.font, previous, code); - pen.x += delta.x * render_priv->state.scale_x - * render_priv->font_scale_x; - pen.y += delta.y * render_priv->state.scale_y - * render_priv->font_scale_x; + pen.x += delta.x * render_priv->state.scale_x; + pen.y += delta.y * render_priv->state.scale_y; } ass_font_set_transform(render_priv->state.font, - render_priv->state.scale_x * - render_priv->font_scale_x, + render_priv->state.scale_x, render_priv->state.scale_y, NULL); get_outline_glyph(render_priv, code, - text_info->glyphs + text_info->length, drawing); + glyphs + text_info->length, drawing); // Add additional space after italic to non-italic style changes if (text_info->length && - text_info->glyphs[text_info->length - 1].hash_key.italic && + glyphs[text_info->length - 1].hash_key.italic && !render_priv->state.italic) { int back = text_info->length - 1; - GlyphInfo *og = &text_info->glyphs[back]; + GlyphInfo *og = &glyphs[back]; while (back && og->bbox.xMax - og->bbox.xMin == 0 && og->hash_key.italic) - og = &text_info->glyphs[--back]; + og = &glyphs[--back]; if (og->bbox.xMax > og->advance.x) { // The FreeType oblique slants by 6/16 pen.x += og->bbox.yMax * 0.375; } } - text_info->glyphs[text_info->length].pos.x = pen.x; - text_info->glyphs[text_info->length].pos.y = pen.y; + glyphs[text_info->length].pos.x = pen.x; + glyphs[text_info->length].pos.y = pen.y; - pen.x += text_info->glyphs[text_info->length].advance.x; + pen.x += glyphs[text_info->length].advance.x; pen.x += double_to_d6(render_priv->state.hspacing * render_priv->font_scale * render_priv->state.scale_x); - pen.y += text_info->glyphs[text_info->length].advance.y; + pen.y += glyphs[text_info->length].advance.y; pen.y += (render_priv->state.fay * render_priv->state.scale_y) * - text_info->glyphs[text_info->length].advance.x; + glyphs[text_info->length].advance.x; previous = code; - text_info->glyphs[text_info->length].symbol = code; - text_info->glyphs[text_info->length].linebreak = 0; + glyphs[text_info->length].symbol = code; + glyphs[text_info->length].linebreak = 0; for (i = 0; i < 4; ++i) { uint32_t clr = render_priv->state.c[i]; change_alpha(&clr, mult_alpha(_a(clr), render_priv->state.fade), 1.); - text_info->glyphs[text_info->length].c[i] = clr; + glyphs[text_info->length].c[i] = clr; } - text_info->glyphs[text_info->length].effect_type = - render_priv->state.effect_type; - text_info->glyphs[text_info->length].effect_timing = + glyphs[text_info->length].effect_type = render_priv->state.effect_type; + glyphs[text_info->length].effect_timing = render_priv->state.effect_timing; - text_info->glyphs[text_info->length].effect_skip_timing = + glyphs[text_info->length].effect_skip_timing = render_priv->state.effect_skip_timing; - text_info->glyphs[text_info->length].be = render_priv->state.be; - text_info->glyphs[text_info->length].blur = render_priv->state.blur; - text_info->glyphs[text_info->length].shadow_x = - render_priv->state.shadow_x; - text_info->glyphs[text_info->length].shadow_y = - render_priv->state.shadow_y; - text_info->glyphs[text_info->length].frx = render_priv->state.frx; - text_info->glyphs[text_info->length].fry = render_priv->state.fry; - text_info->glyphs[text_info->length].frz = render_priv->state.frz; - text_info->glyphs[text_info->length].fax = render_priv->state.fax; - text_info->glyphs[text_info->length].fay = render_priv->state.fay; + glyphs[text_info->length].be = render_priv->state.be; + glyphs[text_info->length].blur = render_priv->state.blur; + glyphs[text_info->length].shadow_x = render_priv->state.shadow_x; + glyphs[text_info->length].shadow_y = render_priv->state.shadow_y; + glyphs[text_info->length].frx = render_priv->state.frx; + glyphs[text_info->length].fry = render_priv->state.fry; + glyphs[text_info->length].frz = render_priv->state.frz; + glyphs[text_info->length].fax = render_priv->state.fax; + glyphs[text_info->length].fay = render_priv->state.fay; if (drawing->hash) { - text_info->glyphs[text_info->length].asc = drawing->asc; - text_info->glyphs[text_info->length].desc = drawing->desc; + glyphs[text_info->length].asc = drawing->asc; + glyphs[text_info->length].desc = drawing->desc; } else { ass_font_get_asc_desc(render_priv->state.font, code, - &text_info->glyphs[text_info->length].asc, - &text_info->glyphs[text_info->length].desc); + &glyphs[text_info->length].asc, + &glyphs[text_info->length].desc); - text_info->glyphs[text_info->length].asc *= - render_priv->state.scale_y; - text_info->glyphs[text_info->length].desc *= - render_priv->state.scale_y; + glyphs[text_info->length].asc *= render_priv->state.scale_y; + glyphs[text_info->length].desc *= render_priv->state.scale_y; } - // fill bitmap_hash_key - if (!drawing->hash) { - text_info->glyphs[text_info->length].hash_key.font = - render_priv->state.font; - text_info->glyphs[text_info->length].hash_key.size = - render_priv->state.font_size; - text_info->glyphs[text_info->length].hash_key.bold = - render_priv->state.bold; - text_info->glyphs[text_info->length].hash_key.italic = - render_priv->state.italic; - } else - text_info->glyphs[text_info->length].hash_key.drawing_hash = - drawing->hash; - text_info->glyphs[text_info->length].hash_key.ch = code; - text_info->glyphs[text_info->length].hash_key.outline.x = - double_to_d16(render_priv->state.border_x); - text_info->glyphs[text_info->length].hash_key.outline.y = - double_to_d16(render_priv->state.border_y); - text_info->glyphs[text_info->length].hash_key.scale_x = - double_to_d16(render_priv->state.scale_x); - text_info->glyphs[text_info->length].hash_key.scale_y = - double_to_d16(render_priv->state.scale_y); - text_info->glyphs[text_info->length].hash_key.frx = - rot_key(render_priv->state.frx); - text_info->glyphs[text_info->length].hash_key.fry = - rot_key(render_priv->state.fry); - text_info->glyphs[text_info->length].hash_key.frz = - rot_key(render_priv->state.frz); - text_info->glyphs[text_info->length].hash_key.fax = - double_to_d16(render_priv->state.fax); - text_info->glyphs[text_info->length].hash_key.fay = - double_to_d16(render_priv->state.fay); - text_info->glyphs[text_info->length].hash_key.advance.x = pen.x; - text_info->glyphs[text_info->length].hash_key.advance.y = pen.y; - text_info->glyphs[text_info->length].hash_key.be = - render_priv->state.be; - text_info->glyphs[text_info->length].hash_key.blur = - render_priv->state.blur; - text_info->glyphs[text_info->length].hash_key.border_style = - render_priv->state.style->BorderStyle; - text_info->glyphs[text_info->length].hash_key.shadow_offset.x = - double_to_d6( - render_priv->state.shadow_x * render_priv->border_scale - - (int) (render_priv->state.shadow_x * - render_priv->border_scale)); - text_info->glyphs[text_info->length].hash_key.shadow_offset.y = - double_to_d6( - render_priv->state.shadow_y * render_priv->border_scale - - (int) (render_priv->state.shadow_y * - render_priv->border_scale)); - text_info->glyphs[text_info->length].hash_key.flags = - render_priv->state.flags; + // fill bitmap hash + fill_bitmap_hash(render_priv, &glyphs[text_info->length].hash_key, + drawing, pen, code); text_info->length++; @@ -1967,6 +1911,7 @@ free_render_context(render_priv); return 1; } + // depends on glyph x coordinates being monotonous, so it should be done before line wrap process_karaoke_effects(render_priv); @@ -1976,14 +1921,11 @@ valign = alignment & 12; MarginL = - (event->MarginL) ? event->MarginL : render_priv->state.style-> - MarginL; + (event->MarginL) ? event->MarginL : render_priv->state.style->MarginL; MarginR = - (event->MarginR) ? event->MarginR : render_priv->state.style-> - MarginR; + (event->MarginR) ? event->MarginR : render_priv->state.style->MarginR; MarginV = - (event->MarginV) ? event->MarginV : render_priv->state.style-> - MarginV; + (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV; if (render_priv->state.evt_type != EVENT_HSCROLL) { double max_text_width; @@ -2001,11 +1943,11 @@ last_break = -1; for (i = 1; i < text_info->length + 1; ++i) { // (text_info->length + 1) is the end of the last line if ((i == text_info->length) - || text_info->glyphs[i].linebreak) { + || glyphs[i].linebreak) { double width, shift = 0; GlyphInfo *first_glyph = - text_info->glyphs + last_break + 1; - GlyphInfo *last_glyph = text_info->glyphs + i - 1; + glyphs + last_break + 1; + GlyphInfo *last_glyph = glyphs + i - 1; while (first_glyph < last_glyph && first_glyph->skip) first_glyph++; @@ -2027,7 +1969,7 @@ shift = (max_text_width - width) / 2.0; } for (j = last_break + 1; j < i; ++j) { - text_info->glyphs[j].pos.x += double_to_d6(shift); + glyphs[j].pos.x += double_to_d6(shift); } last_break = i - 1; } @@ -2057,6 +1999,7 @@ render_priv->state.scroll_shift) - (bbox.xMax - bbox.xMin); } + // y coordinate for everything except positioned events if (render_priv->state.evt_type == EVENT_NORMAL || render_priv->state.evt_type == EVENT_HSCROLL) { @@ -2072,7 +2015,7 @@ double scr_y; if (valign != VALIGN_SUB) ass_msg(render_priv->library, MSGL_V, - "Invalid valign, supposing 0 (subtitle)"); + "Invalid valign, assuming 0 (subtitle)"); scr_y = y2scr_sub(render_priv, render_priv->track->PlayResY - MarginV); @@ -2093,6 +2036,7 @@ render_priv->state.clip_y1 - render_priv->state.scroll_shift); } + // positioned events are totally different if (render_priv->state.evt_type == EVENT_POSITIONED) { double base_x = 0; @@ -2105,14 +2049,15 @@ device_y = y2scr_pos(render_priv, render_priv->state.pos_y) - base_y; } + // fix clip coordinates (they depend on alignment) if (render_priv->state.evt_type == EVENT_NORMAL || render_priv->state.evt_type == EVENT_HSCROLL || render_priv->state.evt_type == EVENT_VSCROLL) { render_priv->state.clip_x0 = - x2scr(render_priv, render_priv->state.clip_x0); + x2scr_scaled(render_priv, render_priv->state.clip_x0); render_priv->state.clip_x1 = - x2scr(render_priv, render_priv->state.clip_x1); + x2scr_scaled(render_priv, render_priv->state.clip_x1); if (valign == VALIGN_TOP) { render_priv->state.clip_y0 = y2scr_top(render_priv, render_priv->state.clip_y0); @@ -2131,14 +2076,15 @@ } } else if (render_priv->state.evt_type == EVENT_POSITIONED) { render_priv->state.clip_x0 = - x2scr_pos(render_priv, render_priv->state.clip_x0); + x2scr_pos_scaled(render_priv, render_priv->state.clip_x0); render_priv->state.clip_x1 = - x2scr_pos(render_priv, render_priv->state.clip_x1); + x2scr_pos_scaled(render_priv, render_priv->state.clip_x1); render_priv->state.clip_y0 = y2scr_pos(render_priv, render_priv->state.clip_y0); render_priv->state.clip_y1 = y2scr_pos(render_priv, render_priv->state.clip_y1); } + // calculate rotation parameters { DVector center; @@ -2154,7 +2100,7 @@ } for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; + GlyphInfo *info = glyphs + i; if (info->hash_key.frx || info->hash_key.fry || info->hash_key.frz || info->hash_key.fax @@ -2170,22 +2116,26 @@ } // convert glyphs to bitmaps + device_x *= render_priv->font_scale_x; for (i = 0; i < text_info->length; ++i) { - GlyphInfo *g = text_info->glyphs + i; + GlyphInfo *g = glyphs + i; + g->pos.x *= render_priv->font_scale_x; g->hash_key.advance.x = double_to_d6(device_x - (int) device_x + d6_to_double(g->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; g->hash_key.advance.y = double_to_d6(device_y - (int) device_y + d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; - get_bitmap_glyph(render_priv, text_info->glyphs + i); + get_bitmap_glyph(render_priv, glyphs + i); } memset(event_images, 0, sizeof(*event_images)); event_images->top = device_y - text_info->lines[0].asc; event_images->height = text_info->height; - event_images->left = device_x + bbox.xMin + 0.5; - event_images->width = bbox.xMax - bbox.xMin + 0.5; + event_images->left = + (device_x + bbox.xMin * render_priv->font_scale_x) + 0.5; + event_images->width = + (bbox.xMax - bbox.xMin) * render_priv->font_scale_x + 0.5; event_images->detect_collisions = render_priv->state.detect_collisions; event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1; event_images->event = event; @@ -2200,7 +2150,7 @@ * \brief deallocate image list * \param img list pointer */ -static void ass_free_images(ASS_Image *img) +void ass_free_images(ASS_Image *img) { while (img) { ASS_Image *next = img->next; @@ -2209,101 +2159,30 @@ } } -static void ass_reconfigure(ASS_Renderer *priv) -{ - priv->render_id++; - priv->cache.glyph_cache = - ass_glyph_cache_reset(priv->cache.glyph_cache); - priv->cache.bitmap_cache = - ass_bitmap_cache_reset(priv->cache.bitmap_cache); - priv->cache.composite_cache = - ass_composite_cache_reset(priv->cache.composite_cache); - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; -} - -void ass_set_frame_size(ASS_Renderer *priv, int w, int h) -{ - if (priv->settings.frame_width != w || priv->settings.frame_height != h) { - priv->settings.frame_width = w; - priv->settings.frame_height = h; - if (priv->settings.aspect == 0.) { - priv->settings.aspect = ((double) w) / h; - priv->settings.storage_aspect = ((double) w) / h; - } - ass_reconfigure(priv); - } -} - -void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r) -{ - if (priv->settings.left_margin != l || - priv->settings.right_margin != r || - priv->settings.top_margin != t - || priv->settings.bottom_margin != b) { - priv->settings.left_margin = l; - priv->settings.right_margin = r; - priv->settings.top_margin = t; - priv->settings.bottom_margin = b; - ass_reconfigure(priv); - } -} - -void ass_set_use_margins(ASS_Renderer *priv, int use) -{ - priv->settings.use_margins = use; -} - -void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar) +/** + * \brief Check cache limits and reset cache if they are exceeded + */ +static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache) { - if (priv->settings.aspect != dar || priv->settings.storage_aspect != sar) { - priv->settings.aspect = dar; - priv->settings.storage_aspect = sar; - ass_reconfigure(priv); + if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) { + ass_msg(priv->library, MSGL_V, + "Hitting hard bitmap cache limit (was: %ld bytes), " + "resetting.", (long) cache->bitmap_cache->cache_size); + cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache); + cache->composite_cache = ass_composite_cache_reset( + cache->composite_cache); + ass_free_images(priv->prev_images_root); + priv->prev_images_root = 0; } -} - -void ass_set_font_scale(ASS_Renderer *priv, double font_scale) -{ - if (priv->settings.font_size_coeff != font_scale) { - priv->settings.font_size_coeff = font_scale; - ass_reconfigure(priv); - } -} - -void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht) -{ - if (priv->settings.hinting != ht) { - priv->settings.hinting = ht; - ass_reconfigure(priv); - } -} -void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing) -{ - priv->settings.line_spacing = line_spacing; -} - -void ass_set_fonts(ASS_Renderer *priv, const char *default_font, - const char *default_family, int fc, const char *config, - int update) -{ - free(priv->settings.default_font); - free(priv->settings.default_family); - priv->settings.default_font = default_font ? strdup(default_font) : 0; - priv->settings.default_family = - default_family ? strdup(default_family) : 0; - - if (priv->fontconfig_priv) - fontconfig_done(priv->fontconfig_priv); - priv->fontconfig_priv = - fontconfig_init(priv->library, priv->ftlibrary, default_family, - default_font, fc, config, update); -} - -int ass_fonts_update(ASS_Renderer *render_priv) -{ - return fontconfig_update(render_priv->fontconfig_priv); + if (cache->glyph_cache->count > cache->glyph_max + || cache->glyph_cache->cache_size > cache->bitmap_max_size) { + ass_msg(priv->library, MSGL_V, + "Hitting hard glyph cache limit (was: %d glyphs, %ld bytes), " + "resetting.", + cache->glyph_cache->count, (long) cache->glyph_cache->cache_size); + cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache); + } } /** @@ -2314,7 +2193,6 @@ long long now) { ASS_Settings *settings_priv = &render_priv->settings; - CacheStore *cache = &render_priv->cache; if (!render_priv->settings.frame_width && !render_priv->settings.frame_height) @@ -2323,27 +2201,14 @@ if (render_priv->library != track->library) return 1; + if (!render_priv->fontconfig_priv) + return 1; + free_list_clear(render_priv); if (track->n_events == 0) return 1; // nothing to do - render_priv->width = settings_priv->frame_width; - render_priv->height = settings_priv->frame_height; - render_priv->orig_width = - settings_priv->frame_width - settings_priv->left_margin - - settings_priv->right_margin; - render_priv->orig_height = - settings_priv->frame_height - settings_priv->top_margin - - settings_priv->bottom_margin; - render_priv->orig_width_nocrop = - settings_priv->frame_width - FFMAX(settings_priv->left_margin, - 0) - - FFMAX(settings_priv->right_margin, 0); - render_priv->orig_height_nocrop = - settings_priv->frame_height - FFMAX(settings_priv->top_margin, - 0) - - FFMAX(settings_priv->bottom_margin, 0); render_priv->track = track; render_priv->time = now; @@ -2365,23 +2230,7 @@ render_priv->prev_images_root = render_priv->images_root; render_priv->images_root = 0; - if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) { - ass_msg(render_priv->library, MSGL_V, - "Hitting hard bitmap cache limit (was: %ld bytes), " - "resetting.", (long) cache->bitmap_cache->cache_size); - cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache); - cache->composite_cache = ass_composite_cache_reset( - cache->composite_cache); - ass_free_images(render_priv->prev_images_root); - render_priv->prev_images_root = 0; - } - - if (cache->glyph_cache->count > cache->glyph_max) { - ass_msg(render_priv->library, MSGL_V, - "Hitting hard glyph cache limit (was: %ld glyphs), resetting.", - (long) cache->glyph_cache->count); - cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache); - } + check_cache_limits(render_priv, &render_priv->cache); return 0; } @@ -2505,7 +2354,7 @@ s.hb = priv->left + priv->width; if (priv->height != imgs[i].height) { // no, it's not ass_msg(render_priv->library, MSGL_WARN, - "Warning! Event height has changed"); + "Event height has changed"); priv->top = 0; priv->height = 0; priv->left = 0;
--- a/libass/ass_render.h Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_render.h Fri Aug 06 21:13:41 2010 +0000 @@ -38,6 +38,9 @@ #include "ass_library.h" #include "ass_drawing.h" +#define GLYPH_CACHE_MAX 1000 +#define BITMAP_CACHE_MAX_SIZE 30 * 1048576 + typedef struct { double xMin; double xMax; @@ -258,5 +261,6 @@ } Segment; void reset_render_context(ASS_Renderer *render_priv); +void ass_free_images(ASS_Image *img); #endif /* LIBASS_RENDER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libass/ass_render_api.c Fri Aug 06 21:13:41 2010 +0000 @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> + * Copyright (C) 2010 Grigori Goronzy <greg@geekmind.org> + * + * This file is part of libass. + * + * libass is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libass is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with libass; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ass_render.h" + +static void ass_reconfigure(ASS_Renderer *priv) +{ + ASS_Settings *settings = &priv->settings; + + priv->render_id++; + priv->cache.glyph_cache = + ass_glyph_cache_reset(priv->cache.glyph_cache); + priv->cache.bitmap_cache = + ass_bitmap_cache_reset(priv->cache.bitmap_cache); + priv->cache.composite_cache = + ass_composite_cache_reset(priv->cache.composite_cache); + ass_free_images(priv->prev_images_root); + priv->prev_images_root = 0; + + priv->width = settings->frame_width; + priv->height = settings->frame_height; + priv->orig_width = settings->frame_width - settings->left_margin - + settings->right_margin; + priv->orig_height = settings->frame_height - settings->top_margin - + settings->bottom_margin; + priv->orig_width_nocrop = + settings->frame_width - FFMAX(settings->left_margin, 0) - + FFMAX(settings->right_margin, 0); + priv->orig_height_nocrop = + settings->frame_height - FFMAX(settings->top_margin, 0) - + FFMAX(settings->bottom_margin, 0); +} + +void ass_set_frame_size(ASS_Renderer *priv, int w, int h) +{ + if (priv->settings.frame_width != w || priv->settings.frame_height != h) { + priv->settings.frame_width = w; + priv->settings.frame_height = h; + if (priv->settings.aspect == 0.) { + priv->settings.aspect = ((double) w) / h; + priv->settings.storage_aspect = ((double) w) / h; + } + ass_reconfigure(priv); + } +} + +void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r) +{ + if (priv->settings.left_margin != l || priv->settings.right_margin != r || + priv->settings.top_margin != t || priv->settings.bottom_margin != b) { + priv->settings.left_margin = l; + priv->settings.right_margin = r; + priv->settings.top_margin = t; + priv->settings.bottom_margin = b; + ass_reconfigure(priv); + } +} + +void ass_set_use_margins(ASS_Renderer *priv, int use) +{ + priv->settings.use_margins = use; +} + +void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar) +{ + if (priv->settings.aspect != dar || priv->settings.storage_aspect != sar) { + priv->settings.aspect = dar; + priv->settings.storage_aspect = sar; + ass_reconfigure(priv); + } +} + +void ass_set_font_scale(ASS_Renderer *priv, double font_scale) +{ + if (priv->settings.font_size_coeff != font_scale) { + priv->settings.font_size_coeff = font_scale; + ass_reconfigure(priv); + } +} + +void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht) +{ + if (priv->settings.hinting != ht) { + priv->settings.hinting = ht; + ass_reconfigure(priv); + } +} + +void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing) +{ + priv->settings.line_spacing = line_spacing; +} + +void ass_set_fonts(ASS_Renderer *priv, const char *default_font, + const char *default_family, int fc, const char *config, + int update) +{ + free(priv->settings.default_font); + free(priv->settings.default_family); + priv->settings.default_font = default_font ? strdup(default_font) : 0; + priv->settings.default_family = + default_family ? strdup(default_family) : 0; + + if (priv->fontconfig_priv) + fontconfig_done(priv->fontconfig_priv); + priv->fontconfig_priv = + fontconfig_init(priv->library, priv->ftlibrary, default_family, + default_font, fc, config, update); +} + +int ass_fonts_update(ASS_Renderer *render_priv) +{ + return fontconfig_update(render_priv->fontconfig_priv); +} + +void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max, + int bitmap_max) +{ + render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX; + render_priv->cache.bitmap_max_size = bitmap_max ? 1048576 * bitmap_max : + BITMAP_CACHE_MAX_SIZE; +}
--- a/libass/ass_strtod.c Fri Aug 06 10:48:16 2010 +0000 +++ b/libass/ass_strtod.c Fri Aug 06 21:13:41 2010 +0000 @@ -16,12 +16,14 @@ #include <ctype.h> #include <errno.h> +const static int maxExponent = 511; /* Largest possible base 10 exponent. Any * exponent larger than this will already * produce underflow or overflow, so there's * no need to worry about additional digits. */ +const static double powersOf10[] = { /* Table giving binary powers of 10. Entry */ 10., /* is 10^2^i. Used to convert decimal */ 100., /* exponents into floating-point numbers. */ @@ -224,7 +226,7 @@ errno = ERANGE; } dblExp = 1.0; - for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { + for (d = (double *) powersOf10; exp != 0; exp >>= 1, d += 1) { if (exp & 01) { dblExp *= *d; }