Mercurial > mplayer.hg
changeset 34295:6e7f60f6f9d4
Update libass to 0.10 release.
Patch by [subjunk gmail com], see bug #2008.
author | reimar |
---|---|
date | Sat, 03 Dec 2011 21:35:56 +0000 |
parents | d30ea496ed2a |
children | d9dfe3689e14 |
files | Makefile libass/ass.c libass/ass.h libass/ass_bitmap.c libass/ass_bitmap.h libass/ass_cache.c libass/ass_cache.h libass/ass_cache_template.h libass/ass_drawing.c libass/ass_drawing.h libass/ass_font.c libass/ass_font.h libass/ass_library.c libass/ass_parse.c libass/ass_parse.h libass/ass_render.c libass/ass_render.h libass/ass_render_api.c libass/ass_types.h |
diffstat | 19 files changed, 1391 insertions(+), 1198 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Sat Dec 03 21:33:28 2011 +0000 +++ b/Makefile Sat Dec 03 21:35:56 2011 +0000 @@ -115,6 +115,7 @@ libass/ass_parse.c \ libass/ass_render.c \ libass/ass_render_api.c \ + libass/ass_shaper.c \ libass/ass_strtod.c \ libass/ass_utils.c \
--- a/libass/ass.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass.c Sat Dec 03 21:35:56 2011 +0000 @@ -28,6 +28,7 @@ #include <sys/stat.h> #include <unistd.h> #include <inttypes.h> +#include <ctype.h> #ifdef CONFIG_ICONV #include <iconv.h> @@ -69,6 +70,7 @@ } free(track->style_format); free(track->event_format); + free(track->Language); if (track->styles) { for (i = 0; i < track->n_styles; ++i) ass_free_style(track, i); @@ -595,6 +597,12 @@ track->ScaledBorderAndShadow = parse_bool(str + 22); } else if (!strncmp(str, "Kerning:", 8)) { track->Kerning = parse_bool(str + 8); + } else if (!strncmp(str, "Language:", 9)) { + char *p = str + 9; + while (*p && isspace(*p)) p++; + track->Language = malloc(3); + strncpy(track->Language, p, 2); + track->Language[2] = 0; } return 0; } @@ -1269,3 +1277,36 @@ track->parser_priv = calloc(1, sizeof(ASS_ParserPriv)); return track; } + +/** + * \brief Prepare track for rendering + */ +void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track) +{ + if (track->PlayResX && track->PlayResY) + return; + if (!track->PlayResX && !track->PlayResY) { + ass_msg(lib, MSGL_WARN, + "Neither PlayResX nor PlayResY defined. Assuming 384x288"); + track->PlayResX = 384; + track->PlayResY = 288; + } else { + if (!track->PlayResY && track->PlayResX == 1280) { + track->PlayResY = 1024; + ass_msg(lib, MSGL_WARN, + "PlayResY undefined, setting to %d", track->PlayResY); + } else if (!track->PlayResY) { + track->PlayResY = track->PlayResX * 3 / 4; + ass_msg(lib, MSGL_WARN, + "PlayResY undefined, setting to %d", track->PlayResY); + } else if (!track->PlayResX && track->PlayResY == 1024) { + track->PlayResX = 1280; + ass_msg(lib, MSGL_WARN, + "PlayResX undefined, setting to %d", track->PlayResX); + } else if (!track->PlayResX) { + track->PlayResX = track->PlayResY * 4 / 3; + ass_msg(lib, MSGL_WARN, + "PlayResX undefined, setting to %d", track->PlayResX); + } + } +}
--- a/libass/ass.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass.h Sat Dec 03 21:35:56 2011 +0000 @@ -23,7 +23,7 @@ #include <stdarg.h> #include "ass_types.h" -#define LIBASS_VERSION 0x00913000 +#define LIBASS_VERSION 0x01000000 /* * A linked list of images produced by an ass renderer. @@ -61,6 +61,19 @@ } ASS_Hinting; /** + * \brief Text shaping levels. + * + * SIMPLE is a fast, font-agnostic shaper that can do only substitutions. + * COMPLEX is a slower shaper using OpenType for substitutions and positioning. + * + * libass uses the best shaper available by default. + */ +typedef enum { + ASS_SHAPING_SIMPLE = 0, + ASS_SHAPING_COMPLEX +} ASS_ShapingLevel; + +/** * \brief Initialize the library. * \return library handle or NULL if failed */ @@ -147,6 +160,13 @@ void ass_set_frame_size(ASS_Renderer *priv, int w, int h); /** + * \brief Set shaping level. This is merely a hint, the renderer will use + * whatever is available if the request cannot be fulfilled. + * \param level shaping level + */ +void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level); + +/** * \brief Set frame margins. These values may be negative if pan-and-scan * is used. * \param priv renderer handle
--- a/libass/ass_bitmap.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_bitmap.c Sat Dec 03 21:35:56 2011 +0000 @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> + * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> * * This file is part of libass. * @@ -22,6 +23,7 @@ #include <assert.h> #include <ft2build.h> #include FT_GLYPH_H +#include FT_OUTLINE_H #include "ass_utils.h" #include "ass_bitmap.h" @@ -133,10 +135,12 @@ static Bitmap *alloc_bitmap(int w, int h) { Bitmap *bm; + unsigned s = w; // XXX: alignment bm = malloc(sizeof(Bitmap)); - bm->buffer = calloc(w, h); + bm->buffer = calloc(s, h); bm->w = w; bm->h = h; + bm->stride = s; bm->left = bm->top = 0; return bm; } @@ -153,70 +157,57 @@ Bitmap *dst = alloc_bitmap(src->w, src->h); dst->left = src->left; dst->top = src->top; - memcpy(dst->buffer, src->buffer, src->w * src->h); + memcpy(dst->buffer, src->buffer, src->stride * src->h); return dst; } -int check_glyph_area(ASS_Library *library, FT_Glyph glyph) +Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib, + FT_Outline *outline, int bord) { - FT_BBox bbox; - long long dx, dy; - FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); - dx = bbox.xMax - bbox.xMin; - dy = bbox.yMax - bbox.yMin; - if (dx * dy > 8000000) { - ass_msg(library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx", - (int) dx, (int) dy); - return 1; - } else - return 0; -} - -static Bitmap *glyph_to_bitmap_internal(ASS_Library *library, - FT_Glyph glyph, int bord) -{ - FT_BitmapGlyph bg; - FT_Bitmap *bit; Bitmap *bm; int w, h; - unsigned char *src; - unsigned char *dst; - int i; int error; + FT_BBox bbox; + FT_Bitmap bitmap; - if (check_glyph_area(library, glyph)) - return 0; - error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0); - if (error) { - ass_msg(library, MSGL_WARN, "FT_Glyph_To_Bitmap error %d", - error); - return 0; + FT_Outline_Get_CBox(outline, &bbox); + // move glyph to origin (0, 0) + bbox.xMin &= ~63; + bbox.yMin &= ~63; + FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin); + // bitmap size + bbox.xMax = (bbox.xMax + 63) & ~63; + bbox.yMax = (bbox.yMax + 63) & ~63; + w = (bbox.xMax - bbox.xMin) >> 6; + h = (bbox.yMax - bbox.yMin) >> 6; + // pen offset + bbox.xMin >>= 6; + bbox.yMax >>= 6; + + if (w * h > 8000000) { + ass_msg(library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx", + w, h); + return NULL; } - bg = (FT_BitmapGlyph) glyph; - bit = &(bg->bitmap); - if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) { - ass_msg(library, MSGL_WARN, "Unsupported pixel mode: %d", - (int) (bit->pixel_mode)); - FT_Done_Glyph(glyph); - return 0; + // allocate and set up bitmap + bm = alloc_bitmap(w + 2 * bord, h + 2 * bord); + bm->left = bbox.xMin - bord; + bm->top = -bbox.yMax - bord; + bitmap.width = w; + bitmap.rows = h; + bitmap.pitch = bm->stride; + bitmap.buffer = bm->buffer + bord + bm->stride * bord; + bitmap.num_grays = 256; + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; + + // render into target bitmap + if ((error = FT_Outline_Get_Bitmap(ftlib, outline, &bitmap))) { + ass_msg(library, MSGL_WARN, "Failed to rasterize glyph: %d\n", error); + ass_free_bitmap(bm); + return NULL; } - w = bit->width; - h = bit->rows; - bm = alloc_bitmap(w + 2 * bord, h + 2 * bord); - bm->left = bg->left - bord; - bm->top = -bg->top - bord; - - src = bit->buffer; - dst = bm->buffer + bord + bm->w * bord; - for (i = 0; i < h; ++i) { - memcpy(dst, src, w); - src += bit->pitch; - dst += bm->w; - } - - FT_Done_Glyph(glyph); return bm; } @@ -232,16 +223,16 @@ const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top; const int r = - bm_o->left + bm_o->w < - bm_g->left + bm_g->w ? bm_o->left + bm_o->w : bm_g->left + bm_g->w; + bm_o->left + bm_o->stride < + bm_g->left + bm_g->stride ? bm_o->left + bm_o->stride : bm_g->left + bm_g->stride; const int b = bm_o->top + bm_o->h < bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h; unsigned char *g = - bm_g->buffer + (t - bm_g->top) * bm_g->w + (l - bm_g->left); + bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left); unsigned char *o = - bm_o->buffer + (t - bm_o->top) * bm_o->w + (l - bm_o->left); + bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left); for (y = 0; y < b - t; ++y) { for (x = 0; x < r - l; ++x) { @@ -250,8 +241,8 @@ c_o = o[x]; o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0; } - g += bm_g->w; - o += bm_o->w; + g += bm_g->stride; + o += bm_o->stride; } } @@ -259,27 +250,30 @@ * \brief Shift a bitmap by the fraction of a pixel in x and y direction * expressed in 26.6 fixed point */ -static void shift_bitmap(unsigned char *buf, int w, int h, int shift_x, - int shift_y) +static void shift_bitmap(Bitmap *bm, int shift_x, int shift_y) { int x, y, b; + int w = bm->w; + int h = bm->h; + int s = bm->stride; + unsigned char *buf = bm->buffer; // Shift in x direction if (shift_x > 0) { for (y = 0; y < h; y++) { for (x = w - 1; x > 0; x--) { - b = (buf[x + y * w - 1] * shift_x) >> 6; - buf[x + y * w - 1] -= b; - buf[x + y * w] += b; + b = (buf[x + y * s - 1] * shift_x) >> 6; + buf[x + y * s - 1] -= b; + buf[x + y * s] += b; } } } else if (shift_x < 0) { shift_x = -shift_x; for (y = 0; y < h; y++) { for (x = 0; x < w - 1; x++) { - b = (buf[x + y * w + 1] * shift_x) >> 6; - buf[x + y * w + 1] -= b; - buf[x + y * w] += b; + b = (buf[x + y * s + 1] * shift_x) >> 6; + buf[x + y * s + 1] -= b; + buf[x + y * s] += b; } } } @@ -288,18 +282,18 @@ if (shift_y > 0) { for (x = 0; x < w; x++) { for (y = h - 1; y > 0; y--) { - b = (buf[x + (y - 1) * w] * shift_y) >> 6; - buf[x + (y - 1) * w] -= b; - buf[x + y * w] += b; + b = (buf[x + (y - 1) * s] * shift_y) >> 6; + buf[x + (y - 1) * s] -= b; + buf[x + y * s] += b; } } } else if (shift_y < 0) { shift_y = -shift_y; for (x = 0; x < w; x++) { for (y = 0; y < h - 1; y++) { - b = (buf[x + (y + 1) * w] * shift_y) >> 6; - buf[x + (y + 1) * w] -= b; - buf[x + y * w] += b; + b = (buf[x + (y + 1) * s] * shift_y) >> 6; + buf[x + (y + 1) * s] -= b; + buf[x + y * s] += b; } } } @@ -430,16 +424,20 @@ * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel * This blur is the same as the one employed by vsfilter. */ -static void be_blur(unsigned char *buf, int w, int h) +static void be_blur(Bitmap *bm) { + int w = bm->w; + int h = bm->h; + int s = bm->stride; + unsigned char *buf = bm->buffer; unsigned int x, y; unsigned int old_sum, new_sum; for (y = 0; y < h; y++) { - old_sum = 2 * buf[y * w]; + old_sum = 2 * buf[y * s]; for (x = 0; x < w - 1; x++) { - new_sum = buf[y * w + x] + buf[y * w + x + 1]; - buf[y * w + x] = (old_sum + new_sum) >> 2; + new_sum = buf[y * s + x] + buf[y * s + x + 1]; + buf[y * s + x] = (old_sum + new_sum) >> 2; old_sum = new_sum; } } @@ -447,18 +445,18 @@ for (x = 0; x < w; x++) { old_sum = 2 * buf[x]; for (y = 0; y < h - 1; y++) { - new_sum = buf[y * w + x] + buf[(y + 1) * w + x]; - buf[y * w + x] = (old_sum + new_sum) >> 2; + new_sum = buf[y * s + x] + buf[(y + 1) * s + x]; + buf[y * s + x] = (old_sum + new_sum) >> 2; old_sum = new_sum; } } } -int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *priv_blur, - FT_Glyph glyph, FT_Glyph outline_glyph, - Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, - int be, double blur_radius, FT_Vector shadow_offset, - int border_style) +int outline_to_bitmap3(ASS_Library *library, ASS_SynthPriv *priv_blur, + FT_Library ftlib, FT_Outline *outline, FT_Outline *border, + Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, + int be, double blur_radius, FT_Vector shadow_offset, + int border_style) { blur_radius *= 2; int bbord = be > 0 ? sqrt(2 * be) : 0; @@ -471,13 +469,13 @@ *bm_g = *bm_o = *bm_s = 0; - if (glyph) - *bm_g = glyph_to_bitmap_internal(library, glyph, bord); + if (outline) + *bm_g = outline_to_bitmap(library, ftlib, outline, bord); if (!*bm_g) return 1; - if (outline_glyph) { - *bm_o = glyph_to_bitmap_internal(library, outline_glyph, bord); + if (border) { + *bm_o = outline_to_bitmap(library, ftlib, border, bord); if (!*bm_o) { return 1; } @@ -486,9 +484,9 @@ // Apply box blur (multiple passes, if requested) while (be--) { if (*bm_o) - be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h); + be_blur(*bm_o); else - be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h); + be_blur(*bm_g); } // Apply gaussian blur @@ -500,12 +498,12 @@ generate_tables(priv_blur, blur_radius); if (*bm_o) ass_gauss_blur((*bm_o)->buffer, priv_blur->tmp, - (*bm_o)->w, (*bm_o)->h, (*bm_o)->w, + (*bm_o)->w, (*bm_o)->h, (*bm_o)->stride, (int *) priv_blur->gt2, priv_blur->g_r, priv_blur->g_w); else ass_gauss_blur((*bm_g)->buffer, priv_blur->tmp, - (*bm_g)->w, (*bm_g)->h, (*bm_g)->w, + (*bm_g)->w, (*bm_g)->h, (*bm_g)->stride, (int *) priv_blur->gt2, priv_blur->g_r, priv_blur->g_w); } @@ -521,8 +519,7 @@ assert(bm_s); - shift_bitmap((*bm_s)->buffer, (*bm_s)->w,(*bm_s)->h, - shadow_offset.x, shadow_offset.y); + shift_bitmap(*bm_s, shadow_offset.x, shadow_offset.y); return 0; }
--- a/libass/ass_bitmap.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_bitmap.h Sat Dec 03 21:35:56 2011 +0000 @@ -32,9 +32,12 @@ typedef struct { int left, top; int w, h; // width, height + int stride; unsigned char *buffer; // w x h buffer } Bitmap; +Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib, + FT_Outline *outline, int bord); /** * \brief perform glyph rendering * \param glyph original glyph @@ -44,13 +47,12 @@ * \param bm_g out: pointer to the bitmap of glyph shadow is returned here * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps */ -int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *priv_blur, - FT_Glyph glyph, FT_Glyph outline_glyph, - Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, - int be, double blur_radius, FT_Vector shadow_offset, - int border_style); +int outline_to_bitmap3(ASS_Library *library, ASS_SynthPriv *priv_blur, + FT_Library ftlib, FT_Outline *outline, FT_Outline *border, + Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, + int be, double blur_radius, FT_Vector shadow_offset, + 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 Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_cache.c Sat Dec 03 21:35:56 2011 +0000 @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> + * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> * * This file is part of libass. * @@ -20,129 +21,35 @@ #include <inttypes.h> #include <ft2build.h> -#include FT_FREETYPE_H -#include FT_GLYPH_H - +#include FT_OUTLINE_H #include <assert.h> #include "ass_utils.h" -#include "ass.h" -#include "ass_fontconfig.h" #include "ass_font.h" -#include "ass_bitmap.h" #include "ass_cache.h" -static unsigned hashmap_hash(void *buf, size_t len) -{ - return fnv_32a_buf(buf, len, FNV1_32A_INIT); -} - -static int hashmap_key_compare(void *a, void *b, size_t size) -{ - return memcmp(a, b, size) == 0; -} - -static void hashmap_item_dtor(void *key, size_t key_size, void *value, - size_t value_size) -{ - free(key); - free(value); -} - -Hashmap *hashmap_init(ASS_Library *library, size_t key_size, - size_t value_size, int nbuckets, - HashmapItemDtor item_dtor, - HashmapKeyCompare key_compare, - HashmapHash hash) -{ - Hashmap *map = calloc(1, sizeof(Hashmap)); - map->library = library; - map->nbuckets = nbuckets; - map->key_size = key_size; - map->value_size = value_size; - map->root = calloc(nbuckets, sizeof(hashmap_item_p)); - map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor; - map->key_compare = key_compare ? key_compare : hashmap_key_compare; - map->hash = hash ? hash : hashmap_hash; - return map; -} - -void hashmap_done(Hashmap *map) -{ - int i; - // print stats - if (map->count > 0 || map->hit_count + map->miss_count > 0) - ass_msg(map->library, MSGL_V, - "cache statistics: \n total accesses: %d\n hits: %d\n " - "misses: %d\n object count: %d", - map->hit_count + map->miss_count, map->hit_count, - map->miss_count, map->count); +// type-specific functions +// create hash/compare functions for bitmap, outline and composite cache +#define CREATE_HASH_FUNCTIONS +#include "ass_cache_template.h" +#define CREATE_COMPARISON_FUNCTIONS +#include "ass_cache_template.h" - for (i = 0; i < map->nbuckets; ++i) { - HashmapItem *item = map->root[i]; - while (item) { - HashmapItem *next = item->next; - map->item_dtor(item->key, map->key_size, item->value, - map->value_size); - free(item); - item = next; - } - } - free(map->root); - free(map); -} - -// does nothing if key already exists -void *hashmap_insert(Hashmap *map, void *key, void *value) -{ - unsigned hash = map->hash(key, map->key_size); - HashmapItem **next = map->root + (hash % map->nbuckets); - while (*next) { - if (map->key_compare(key, (*next)->key, map->key_size)) - return (*next)->value; - next = &((*next)->next); - assert(next); - } - (*next) = malloc(sizeof(HashmapItem)); - (*next)->key = malloc(map->key_size); - (*next)->value = malloc(map->value_size); - memcpy((*next)->key, key, map->key_size); - memcpy((*next)->value, value, map->value_size); - (*next)->next = 0; - - map->count++; - return (*next)->value; -} - -void *hashmap_find(Hashmap *map, void *key) -{ - unsigned hash = map->hash(key, map->key_size); - HashmapItem *item = map->root[hash % map->nbuckets]; - while (item) { - if (map->key_compare(key, item->key, map->key_size)) { - map->hit_count++; - return item->value; - } - item = item->next; - } - map->miss_count++; - return 0; -} - -//--------------------------------- // font cache - -static unsigned font_desc_hash(void *buf, size_t len) +static unsigned font_hash(void *buf, size_t len) { ASS_FontDesc *desc = buf; unsigned hval; hval = fnv_32a_str(desc->family, FNV1_32A_INIT); hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval); + hval = fnv_32a_buf(&desc->treat_family_as_pattern, + sizeof(desc->treat_family_as_pattern), hval); + hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval); return hval; } -static int font_compare(void *key1, void *key2, size_t key_size) +static unsigned font_compare(void *key1, void *key2, size_t key_size) { ASS_FontDesc *a = key1; ASS_FontDesc *b = key2; @@ -159,181 +66,63 @@ return 1; } -static void font_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) +static void font_destruct(void *key, void *value) { ass_font_free(value); free(key); } -ASS_Font *ass_font_cache_find(Hashmap *font_cache, - ASS_FontDesc *desc) -{ - return hashmap_find(font_cache, desc); -} - -/** - * \brief Add a face struct to cache. - * \param font font struct -*/ -void *ass_font_cache_add(Hashmap *font_cache, ASS_Font *font) -{ - return hashmap_insert(font_cache, &(font->desc), font); -} - -Hashmap *ass_font_cache_init(ASS_Library *library) -{ - Hashmap *font_cache; - font_cache = hashmap_init(library, sizeof(ASS_FontDesc), - sizeof(ASS_Font), - 1000, - font_hash_dtor, font_compare, font_desc_hash); - return font_cache; -} - -void ass_font_cache_done(Hashmap *font_cache) -{ - hashmap_done(font_cache); -} - - -// Create hash/compare functions for bitmap and glyph -#define CREATE_HASH_FUNCTIONS -#include "ass_cache_template.h" -#define CREATE_COMPARISON_FUNCTIONS -#include "ass_cache_template.h" - -//--------------------------------- // bitmap cache - -static void bitmap_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) +static void bitmap_destruct(void *key, void *value) { BitmapHashValue *v = value; + BitmapHashKey *k = key; if (v->bm) ass_free_bitmap(v->bm); if (v->bm_o) ass_free_bitmap(v->bm_o); if (v->bm_s) ass_free_bitmap(v->bm_s); + if (k->type == BITMAP_CLIP) + free(k->u.clip.text); free(key); free(value); } -void *cache_add_bitmap(Hashmap *bitmap_cache, BitmapHashKey *key, - BitmapHashValue *val) -{ - // Note: this is only an approximation - if (val->bm_o) - bitmap_cache->cache_size += val->bm_o->w * val->bm_o->h * 3; - else if (val->bm) - bitmap_cache->cache_size += val->bm->w * val->bm->h * 3; - - return hashmap_insert(bitmap_cache, key, val); -} - -/** - * \brief Get a bitmap from bitmap cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache, - BitmapHashKey *key) +static size_t bitmap_size(void *value, size_t value_size) { - return hashmap_find(bitmap_cache, key); -} - -Hashmap *ass_bitmap_cache_init(ASS_Library *library) -{ - Hashmap *bitmap_cache; - bitmap_cache = hashmap_init(library, - sizeof(BitmapHashKey), - sizeof(BitmapHashValue), - 0xFFFF + 13, - bitmap_hash_dtor, bitmap_compare, - bitmap_hash); - return bitmap_cache; -} - -void ass_bitmap_cache_done(Hashmap *bitmap_cache) -{ - hashmap_done(bitmap_cache); -} - -Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache) -{ - ASS_Library *lib = bitmap_cache->library; - - ass_bitmap_cache_done(bitmap_cache); - return ass_bitmap_cache_init(lib); + BitmapHashValue *val = value; + if (val->bm_o) + return val->bm_o->w * val->bm_o->h * 3; + else if (val->bm) + return val->bm->w * val->bm->h * 3; + return 0; } -//--------------------------------- -// glyph cache - -static void glyph_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) +static unsigned bitmap_hash(void *key, size_t key_size) { - GlyphHashValue *v = value; - if (v->glyph) - FT_Done_Glyph(v->glyph); - if (v->outline_glyph) - FT_Done_Glyph(v->outline_glyph); - free(key); - free(value); -} - -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); + BitmapHashKey *k = key; + switch (k->type) { + case BITMAP_OUTLINE: return outline_bitmap_hash(&k->u, key_size); + case BITMAP_CLIP: return clip_bitmap_hash(&k->u, key_size); + default: return 0; + } } -/** - * \brief Get a glyph from glyph cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, - GlyphHashKey *key) +static unsigned bitmap_compare (void *a, void *b, size_t key_size) { - return hashmap_find(glyph_cache, key); + BitmapHashKey *ak = a; + BitmapHashKey *bk = b; + if (ak->type != bk->type) return 0; + switch (ak->type) { + case BITMAP_OUTLINE: return outline_bitmap_compare(&ak->u, &bk->u, key_size); + case BITMAP_CLIP: return clip_bitmap_compare(&ak->u, &bk->u, key_size); + default: return 0; + } } -Hashmap *ass_glyph_cache_init(ASS_Library *library) -{ - Hashmap *glyph_cache; - glyph_cache = hashmap_init(library, sizeof(GlyphHashKey), - sizeof(GlyphHashValue), - 0xFFFF + 13, - glyph_hash_dtor, glyph_compare, glyph_hash); - return glyph_cache; -} - -void ass_glyph_cache_done(Hashmap *glyph_cache) -{ - hashmap_done(glyph_cache); -} - -Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache) -{ - ASS_Library *lib = glyph_cache->library; - - ass_glyph_cache_done(glyph_cache); - return ass_glyph_cache_init(lib); -} - - -//--------------------------------- // composite cache - -static void composite_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) +static void composite_destruct(void *key, void *value) { CompositeHashValue *v = value; free(v->a); @@ -342,44 +131,222 @@ free(value); } -void *cache_add_composite(Hashmap *composite_cache, - CompositeHashKey *key, - CompositeHashValue *val) +// outline cache + +static unsigned outline_hash(void *key, size_t key_size) +{ + OutlineHashKey *k = key; + switch (k->type) { + case OUTLINE_GLYPH: return glyph_hash(&k->u, key_size); + case OUTLINE_DRAWING: return drawing_hash(&k->u, key_size); + default: return 0; + } +} + +static unsigned outline_compare(void *a, void *b, size_t key_size) { - return hashmap_insert(composite_cache, key, val); + OutlineHashKey *ak = a; + OutlineHashKey *bk = b; + if (ak->type != bk->type) return 0; + switch (ak->type) { + case OUTLINE_GLYPH: return glyph_compare(&ak->u, &bk->u, key_size); + case OUTLINE_DRAWING: return drawing_compare(&ak->u, &bk->u, key_size); + default: return 0; + } +} + +static void outline_destruct(void *key, void *value) +{ + OutlineHashValue *v = value; + OutlineHashKey *k = key; + if (v->outline) + outline_free(v->lib, v->outline); + if (v->border) + outline_free(v->lib, v->border); + if (k->type == OUTLINE_DRAWING) + free(k->u.drawing.text); + free(key); + free(value); } -/** - * \brief Get a composite bitmap from composite cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -CompositeHashValue *cache_find_composite(Hashmap *composite_cache, - CompositeHashKey *key) + + +// Cache data +typedef struct cache_item { + void *key; + void *value; + struct cache_item *next; +} CacheItem; + +struct cache { + unsigned buckets; + CacheItem **map; + + HashFunction hash_func; + ItemSize size_func; + HashCompare compare_func; + CacheItemDestructor destruct_func; + size_t key_size; + size_t value_size; + + size_t cache_size; + unsigned hits; + unsigned misses; + unsigned items; +}; + +// Hash for a simple (single value or array) type +static unsigned hash_simple(void *key, size_t key_size) { - return hashmap_find(composite_cache, key); + return fnv_32a_buf(key, key_size, FNV1_32A_INIT); +} + +// Comparison of a simple type +static unsigned compare_simple(void *a, void *b, size_t key_size) +{ + return memcmp(a, b, key_size) == 0; +} + +// Default destructor +static void destruct_simple(void *key, void *value) +{ + free(key); + free(value); +} + + +// Create a cache with type-specific hash/compare/destruct/size functions +Cache *ass_cache_create(HashFunction hash_func, HashCompare compare_func, + CacheItemDestructor destruct_func, ItemSize size_func, + size_t key_size, size_t value_size) +{ + Cache *cache = calloc(1, sizeof(*cache)); + cache->buckets = 0xFFFF; + cache->hash_func = hash_simple; + cache->compare_func = compare_simple; + cache->destruct_func = destruct_simple; + cache->size_func = size_func; + if (hash_func) + cache->hash_func = hash_func; + if (compare_func) + cache->compare_func = compare_func; + if (destruct_func) + cache->destruct_func = destruct_func; + cache->key_size = key_size; + cache->value_size = value_size; + cache->map = calloc(cache->buckets, sizeof(CacheItem *)); + + return cache; } -Hashmap *ass_composite_cache_init(ASS_Library *library) +void *ass_cache_put(Cache *cache, void *key, void *value) { - Hashmap *composite_cache; - composite_cache = hashmap_init(library, sizeof(CompositeHashKey), - sizeof(CompositeHashValue), - 0xFFFF + 13, - composite_hash_dtor, composite_compare, - composite_hash); - return composite_cache; + unsigned bucket = cache->hash_func(key, cache->key_size) % cache->buckets; + CacheItem **item = &cache->map[bucket]; + while (*item) + item = &(*item)->next; + (*item) = calloc(1, sizeof(CacheItem)); + (*item)->key = malloc(cache->key_size); + (*item)->value = malloc(cache->value_size); + memcpy((*item)->key, key, cache->key_size); + memcpy((*item)->value, value, cache->value_size); + + cache->items++; + if (cache->size_func) + cache->cache_size += cache->size_func(value, cache->value_size); + else + cache->cache_size++; + + return (*item)->value; +} + +void *ass_cache_get(Cache *cache, void *key) +{ + unsigned bucket = cache->hash_func(key, cache->key_size) % cache->buckets; + CacheItem *item = cache->map[bucket]; + while (item) { + if (cache->compare_func(key, item->key, cache->key_size)) { + cache->hits++; + return item->value; + } + item = item->next; + } + cache->misses++; + return NULL; } -void ass_composite_cache_done(Hashmap *composite_cache) +int ass_cache_empty(Cache *cache, size_t max_size) { - hashmap_done(composite_cache); + int i; + + if (cache->cache_size < max_size) + return 0; + + for (i = 0; i < cache->buckets; i++) { + CacheItem *item = cache->map[i]; + while (item) { + CacheItem *next = item->next; + cache->destruct_func(item->key, item->value); + free(item); + item = next; + } + cache->map[i] = NULL; + } + + cache->items = cache->hits = cache->misses = cache->cache_size = 0; + + return 1; +} + +void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits, + unsigned *misses, unsigned *count) +{ + if (size) + *size = cache->cache_size; + if (hits) + *hits = cache->hits; + if (misses) + *misses = cache->misses; + if (count) + *count = cache->items; } -Hashmap *ass_composite_cache_reset(Hashmap *composite_cache) +void ass_cache_done(Cache *cache) +{ + ass_cache_empty(cache, 0); + free(cache->map); + free(cache); +} + +// Type-specific creation function +Cache *ass_font_cache_create(void) { - ASS_Library *lib = composite_cache->library; + return ass_cache_create(font_hash, font_compare, font_destruct, + (ItemSize)NULL, sizeof(ASS_FontDesc), sizeof(ASS_Font)); +} + +Cache *ass_outline_cache_create(void) +{ + return ass_cache_create(outline_hash, outline_compare, outline_destruct, + NULL, sizeof(OutlineHashKey), sizeof(OutlineHashValue)); +} - ass_composite_cache_done(composite_cache); - return ass_composite_cache_init(lib); +Cache *ass_glyph_metrics_cache_create(void) +{ + return ass_cache_create(glyph_metrics_hash, glyph_metrics_compare, NULL, + (ItemSize) NULL, sizeof(GlyphMetricsHashKey), + sizeof(GlyphMetricsHashValue)); } + +Cache *ass_bitmap_cache_create(void) +{ + return ass_cache_create(bitmap_hash, bitmap_compare, bitmap_destruct, + bitmap_size, sizeof(BitmapHashKey), sizeof(BitmapHashValue)); +} + +Cache *ass_composite_cache_create(void) +{ + return ass_cache_create(composite_hash, composite_compare, + composite_destruct, (ItemSize)NULL, sizeof(CompositeHashKey), + sizeof(CompositeHashValue)); +}
--- a/libass/ass_cache.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_cache.h Sat Dec 03 21:35:56 2011 +0000 @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> + * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> * * This file is part of libass. * @@ -23,51 +24,9 @@ #include "ass_font.h" #include "ass_bitmap.h" -typedef void (*HashmapItemDtor) (void *key, size_t key_size, - void *value, size_t value_size); -typedef int (*HashmapKeyCompare) (void *key1, void *key2, - size_t key_size); -typedef unsigned (*HashmapHash) (void *key, size_t key_size); - -typedef struct hashmap_item { - void *key; - void *value; - struct hashmap_item *next; -} HashmapItem; -typedef HashmapItem *hashmap_item_p; +typedef struct cache Cache; -typedef struct { - int nbuckets; - size_t key_size, value_size; - hashmap_item_p *root; - HashmapItemDtor item_dtor; // a destructor for hashmap key/value pairs - HashmapKeyCompare key_compare; - HashmapHash hash; - size_t cache_size; - // stats - int hit_count; - int miss_count; - int count; - ASS_Library *library; -} Hashmap; - -Hashmap *hashmap_init(ASS_Library *library, size_t key_size, - size_t value_size, int nbuckets, - HashmapItemDtor item_dtor, - HashmapKeyCompare key_compare, - HashmapHash hash); -void hashmap_done(Hashmap *map); -void *hashmap_insert(Hashmap *map, void *key, void *value); -void *hashmap_find(Hashmap *map, void *key); - -Hashmap *ass_font_cache_init(ASS_Library *library); -ASS_Font *ass_font_cache_find(Hashmap *, ASS_FontDesc *desc); -void *ass_font_cache_add(Hashmap *, ASS_Font *font); -void ass_font_cache_done(Hashmap *); - -// Create definitions for bitmap_hash_key and glyph_hash_key -#define CREATE_STRUCT_DEFINITIONS -#include "ass_cache_template.h" +// cache values typedef struct { Bitmap *bm; // the actual bitmaps @@ -75,43 +34,71 @@ Bitmap *bm_s; } BitmapHashValue; -Hashmap *ass_bitmap_cache_init(ASS_Library *library); -void *cache_add_bitmap(Hashmap *, BitmapHashKey *key, - BitmapHashValue *val); -BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache, - BitmapHashKey *key); -Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache); -void ass_bitmap_cache_done(Hashmap *bitmap_cache); - - typedef struct { unsigned char *a; unsigned char *b; } CompositeHashValue; -Hashmap *ass_composite_cache_init(ASS_Library *library); -void *cache_add_composite(Hashmap *, CompositeHashKey *key, - CompositeHashValue *val); -CompositeHashValue *cache_find_composite(Hashmap *composite_cache, - CompositeHashKey *key); -Hashmap *ass_composite_cache_reset(Hashmap *composite_cache); -void ass_composite_cache_done(Hashmap *composite_cache); - +typedef struct { + FT_Library lib; + FT_Outline *outline; + FT_Outline *border; + FT_BBox bbox_scaled; // bbox after scaling, but before rotation + FT_Vector advance; // 26.6, advance distance to the next outline in line + int asc, desc; // ascender/descender +} OutlineHashValue; typedef struct { - FT_Glyph glyph; - FT_Glyph outline_glyph; - FT_BBox bbox_scaled; // bbox after scaling, but before rotation - FT_Vector advance; // 26.6, advance distance to the next bitmap in line - int asc, desc; // ascender/descender of a drawing -} GlyphHashValue; + FT_Glyph_Metrics metrics; +} GlyphMetricsHashValue; + +// Create definitions for bitmap, outline and composite hash keys +#define CREATE_STRUCT_DEFINITIONS +#include "ass_cache_template.h" + +// Type-specific function pointers +typedef unsigned(*HashFunction)(void *key, size_t key_size); +typedef size_t(*ItemSize)(void *value, size_t value_size); +typedef unsigned(*HashCompare)(void *a, void *b, size_t key_size); +typedef void(*CacheItemDestructor)(void *key, void *value); + +// cache hash keys + +typedef struct outline_hash_key { + enum { + OUTLINE_GLYPH, + OUTLINE_DRAWING, + } type; + union { + GlyphHashKey glyph; + DrawingHashKey drawing; + } u; +} OutlineHashKey; -Hashmap *ass_glyph_cache_init(ASS_Library *library); -void *cache_add_glyph(Hashmap *, GlyphHashKey *key, - GlyphHashValue *val); -GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, - GlyphHashKey *key); -Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache); -void ass_glyph_cache_done(Hashmap *glyph_cache); +typedef struct bitmap_hash_key { + enum { + BITMAP_OUTLINE, + BITMAP_CLIP, + } type; + union { + OutlineBitmapHashKey outline; + ClipMaskHashKey clip; + } u; +} BitmapHashKey; + +Cache *ass_cache_create(HashFunction hash_func, HashCompare compare_func, + CacheItemDestructor destruct_func, ItemSize size_func, + size_t key_size, size_t value_size); +void *ass_cache_put(Cache *cache, void *key, void *value); +void *ass_cache_get(Cache *cache, void *key); +int ass_cache_empty(Cache *cache, size_t max_size); +void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits, + unsigned *misses, unsigned *count); +void ass_cache_done(Cache *cache); +Cache *ass_font_cache_create(void); +Cache *ass_outline_cache_create(void); +Cache *ass_glyph_metrics_cache_create(void); +Cache *ass_bitmap_cache_create(void); +Cache *ass_composite_cache_create(void); #endif /* LIBASS_CACHE_H */
--- a/libass/ass_cache_template.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_cache_template.h Sat Dec 03 21:35:56 2011 +0000 @@ -4,6 +4,8 @@ typedef struct structname { #define GENERIC(type, member) \ type member; +#define STRING(member) \ + char *member; #define FTVECTOR(member) \ FT_Vector member; #define BITMAPHASHKEY(member) \ @@ -14,13 +16,15 @@ #elif defined(CREATE_COMPARISON_FUNCTIONS) #undef CREATE_COMPARISON_FUNCTIONS #define START(funcname, structname) \ - static int funcname##_compare(void *key1, void *key2, size_t key_size) \ + static unsigned funcname##_compare(void *key1, void *key2, size_t key_size) \ { \ struct structname *a = key1; \ struct structname *b = key2; \ return // conditions follow #define GENERIC(type, member) \ a->member == b->member && +#define STRING(member) \ + strcmp(a->member, b->member) == 0 && #define FTVECTOR(member) \ a->member.x == b->member.x && a->member.y == b->member.y && #define BITMAPHASHKEY(member) \ @@ -38,6 +42,8 @@ unsigned hval = FNV1_32A_INIT; #define GENERIC(type, member) \ hval = fnv_32a_buf(&p->member, sizeof(p->member), hval); +#define STRING(member) \ + hval = fnv_32a_str(p->member, hval); #define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y); #define BITMAPHASHKEY(member) { \ unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \ @@ -53,19 +59,11 @@ -// describes a bitmap; bitmaps with equivalents structs are considered identical -START(bitmap, bitmap_hash_key) - GENERIC(char, bitmap) // bool : true = bitmap, false = outline - GENERIC(ASS_Font *, font) - GENERIC(double, size) // font size - GENERIC(uint32_t, ch) // character code - FTVECTOR(outline) // border width, 16.16 fixed point value - GENERIC(int, bold) - GENERIC(int, italic) +// describes an outline bitmap +START(outline_bitmap, outline_bitmap_hash_key) + GENERIC(OutlineHashValue *, outline) GENERIC(char, be) // blur edges GENERIC(double, blur) // gaussian blur - GENERIC(unsigned, scale_x) // 16.16 - GENERIC(unsigned, scale_y) // 16.16 GENERIC(int, frx) // signed 16.16 GENERIC(int, fry) // signed 16.16 GENERIC(int, frz) // signed 16.16 @@ -78,26 +76,49 @@ GENERIC(int, shift_y) FTVECTOR(advance) // subpixel shift vector FTVECTOR(shadow_offset) // shadow subpixel shift - GENERIC(unsigned, drawing_hash) // hashcode of a drawing - GENERIC(unsigned, flags) // glyph decoration - GENERIC(unsigned, border_style) -END(BitmapHashKey) +END(OutlineBitmapHashKey) + +// describe a clip mask bitmap +START(clip_bitmap, clip_bitmap_hash_key) + STRING(text) +END(ClipMaskHashKey) // describes an outline glyph START(glyph, glyph_hash_key) GENERIC(ASS_Font *, font) GENERIC(double, size) // font size - GENERIC(uint32_t, ch) // character code + GENERIC(int, face_index) + GENERIC(int, glyph_index) GENERIC(int, bold) GENERIC(int, italic) GENERIC(unsigned, scale_x) // 16.16 GENERIC(unsigned, scale_y) // 16.16 FTVECTOR(outline) // border width, 16.16 - GENERIC(unsigned, drawing_hash) // hashcode of a drawing GENERIC(unsigned, flags) // glyph decoration flags GENERIC(unsigned, border_style) END(GlyphHashKey) +START(glyph_metrics, glyph_metrics_hash_key) + GENERIC(ASS_Font *, font) + GENERIC(double, size) + GENERIC(int, face_index) + GENERIC(int, glyph_index) + GENERIC(unsigned, scale_x) + GENERIC(unsigned, scale_y) +END(GlyphMetricsHashKey) + +// describes an outline drawing +START(drawing, drawing_hash_key) + GENERIC(unsigned, scale_x) + GENERIC(unsigned, scale_y) + GENERIC(int, pbo) + FTVECTOR(outline) + GENERIC(unsigned, border_style) + GENERIC(int, scale) + GENERIC(unsigned, hash) + STRING(text) +END(DrawingHashKey) + // Cache for composited bitmaps START(composite, composite_hash_key) GENERIC(int, aw) @@ -117,6 +138,7 @@ #undef START #undef GENERIC +#undef STRING #undef FTVECTOR #undef BITMAPHASHKEY #undef END
--- a/libass/ass_drawing.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_drawing.c Sat Dec 03 21:35:56 2011 +0000 @@ -17,13 +17,11 @@ */ #include <ft2build.h> -#include FT_GLYPH_H #include FT_OUTLINE_H #include FT_BBOX_H #include <math.h> #include "ass_utils.h" -#include "ass_font.h" #include "ass_drawing.h" #define CURVE_ACCURACY 64.0 @@ -31,35 +29,12 @@ #define GLYPH_INITIAL_CONTOURS 5 /* - * \brief Get and prepare a FreeType glyph - */ -static void drawing_make_glyph(ASS_Drawing *drawing, void *fontconfig_priv, - ASS_Font *font) -{ - FT_OutlineGlyph glyph; - - // This is hacky... - glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font, - (uint32_t) ' ', 0, 0); - if (glyph) { - FT_Outline_Done(drawing->ftlibrary, &glyph->outline); - FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS, - GLYPH_INITIAL_CONTOURS, &glyph->outline); - - glyph->outline.n_contours = 0; - glyph->outline.n_points = 0; - glyph->root.advance.x = glyph->root.advance.y = 0; - } - drawing->glyph = glyph; -} - -/* * \brief Add a single point to a contour. */ static inline void drawing_add_point(ASS_Drawing *drawing, FT_Vector *point) { - FT_Outline *ol = &drawing->glyph->outline; + FT_Outline *ol = &drawing->outline; if (ol->n_points >= drawing->max_points) { drawing->max_points *= 2; @@ -75,11 +50,11 @@ } /* - * \brief Close a contour and check glyph size overflow. + * \brief Close a contour and check outline size overflow. */ static inline void drawing_close_shape(ASS_Drawing *drawing) { - FT_Outline *ol = &drawing->glyph->outline; + FT_Outline *ol = &drawing->outline; if (ol->n_contours >= drawing->max_contours) { drawing->max_contours *= 2; @@ -107,13 +82,13 @@ /* * \brief Finish a drawing. This only sets the horizontal advance according - * to the glyph's bbox at the moment. + * to the outline's bbox at the moment. */ static void drawing_finish(ASS_Drawing *drawing, int raw_mode) { int i, offset; FT_BBox bbox = drawing->cbox; - FT_Outline *ol = &drawing->glyph->outline; + FT_Outline *ol = &drawing->outline; // Close the last contour drawing_close_shape(drawing); @@ -126,7 +101,7 @@ if (raw_mode) return; - drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin); + drawing->advance.x = bbox.xMax - bbox.xMin; drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y); drawing->asc = bbox.yMax - bbox.yMin + drawing->desc; @@ -355,8 +330,7 @@ /* * \brief Create and initialize a new drawing and return it */ -ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font, - FT_Library lib) +ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib) { ASS_Drawing *drawing; @@ -365,17 +339,18 @@ drawing->size = DRAWING_INITIAL_SIZE; drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX; drawing->cbox.xMax = drawing->cbox.yMax = INT_MIN; - drawing->fontconfig_priv = fontconfig_priv; - drawing->font = font; - drawing->ftlibrary = lib; - if (font) - drawing->library = font->library; - + drawing->ftlibrary = ftlib; + drawing->library = lib; drawing->scale_x = 1.; drawing->scale_y = 1.; drawing->max_contours = GLYPH_INITIAL_CONTOURS; drawing->max_points = GLYPH_INITIAL_POINTS; + FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS, + GLYPH_INITIAL_CONTOURS, &drawing->outline); + drawing->outline.n_contours = 0; + drawing->outline.n_points = 0; + return drawing; } @@ -386,6 +361,7 @@ { if (drawing) { free(drawing->text); + FT_Outline_Done(drawing->ftlibrary, &drawing->outline); } free(drawing); } @@ -416,17 +392,12 @@ /* * \brief Convert token list to outline. Calls the line and curve evaluators. */ -FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) +FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) { int started = 0; ASS_DrawingToken *token; FT_Vector pen = {0, 0}; - if (drawing->font) - drawing_make_glyph(drawing, drawing->fontconfig_priv, drawing->font); - if (!drawing->glyph) - return NULL; - drawing->tokens = drawing_tokenize(drawing->text); drawing_prepare(drawing); @@ -486,5 +457,5 @@ drawing_finish(drawing, raw_mode); drawing_free_tokens(drawing->tokens); - return &drawing->glyph; + return &drawing->outline; }
--- a/libass/ass_drawing.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_drawing.h Sat Dec 03 21:35:56 2011 +0000 @@ -20,7 +20,7 @@ #define LIBASS_DRAWING_H #include <ft2build.h> -#include FT_GLYPH_H +#include FT_OUTLINE_H #include "ass.h" @@ -53,13 +53,12 @@ double scale_y; // FontScaleY int asc; // ascender int desc; // descender - FT_OutlineGlyph glyph; // the "fake" glyph created for later rendering + FT_Outline outline; // target outline + FT_Vector advance; // advance (from cbox) int hash; // hash value (for caching) // private FT_Library ftlibrary; // needed for font ops - ASS_Font *font; // dito - void *fontconfig_priv; // dito ASS_Library *library; int size; // current buffer size ASS_DrawingToken *tokens; // tokenized drawing @@ -70,11 +69,10 @@ 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, - FT_Library lib); +ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib); void ass_drawing_free(ASS_Drawing* drawing); void ass_drawing_add_char(ASS_Drawing* drawing, char symbol); void ass_drawing_hash(ASS_Drawing* drawing); -FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode); +FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode); #endif /* LIBASS_DRAWING_H */
--- a/libass/ass_font.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_font.c Sat Dec 03 21:35:56 2011 +0000 @@ -30,12 +30,9 @@ #include "ass.h" #include "ass_library.h" #include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" #include "ass_fontconfig.h" #include "ass_utils.h" - -#define VERTICAL_LOWER_BOUND 0x02f1 +#include "ass_shaper.h" /** * Select a good charmap, prefer Microsoft Unicode charmaps. @@ -91,8 +88,6 @@ return -1; } -static void face_set_size(FT_Face face, double size); - static void buggy_font_workaround(FT_Face face) { // Some fonts have zero Ascender/Descender fields in 'hhea' table. @@ -161,7 +156,7 @@ buggy_font_workaround(face); font->faces[font->n_faces++] = face; - face_set_size(face, font->size); + ass_face_set_size(face, font->size); free(path); return font->n_faces - 1; } @@ -169,7 +164,7 @@ /** * \brief Create a new ASS_Font according to "desc" argument */ -ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, +ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, FT_Library ftlibrary, void *fc_priv, ASS_FontDesc *desc) { @@ -177,12 +172,13 @@ ASS_Font *fontp; ASS_Font font; - fontp = ass_font_cache_find((Hashmap *) font_cache, desc); + fontp = ass_cache_get(font_cache, desc); if (fontp) return fontp; font.library = library; font.ftlibrary = ftlibrary; + font.shaper_priv = NULL; font.n_faces = 0; font.desc.family = strdup(desc->family); font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; @@ -199,7 +195,7 @@ free(font.desc.family); return 0; } else - return ass_font_cache_add((Hashmap *) font_cache, &font); + return ass_cache_put(font_cache, &font.desc, &font); } /** @@ -216,7 +212,7 @@ } } -static void face_set_size(FT_Face face, double size) +void ass_face_set_size(FT_Face face, double size) { TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); @@ -251,7 +247,7 @@ if (font->size != size) { font->size = size; for (i = 0; i < font->n_faces; ++i) - face_set_size(font->faces[i], size); + ass_face_set_size(font->faces[i], size); } } @@ -276,9 +272,6 @@ *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; } } @@ -388,6 +381,25 @@ return 0; } +void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest) +{ + if (source == NULL) { + *dest = NULL; + return; + } + *dest = calloc(1, sizeof(**dest)); + + FT_Outline_New(lib, source->n_points, source->n_contours, *dest); + FT_Outline_Copy(source, *dest); +} + +void outline_free(FT_Library lib, FT_Outline *outline) +{ + if (outline) + FT_Outline_Done(lib, outline); + free(outline); +} + /** * Slightly embold a glyph without touching its metrics */ @@ -405,33 +417,43 @@ } /** - * \brief Get a glyph - * \param ch character code - **/ -FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, - uint32_t ch, ASS_Hinting hinting, int deco) + * \brief Get glyph and face index + * Finds a face that has the requested codepoint and returns both face + * and glyph index. + */ +int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol, + int *face_index, int *glyph_index) { - int error; int index = 0; int i; - FT_Glyph glyph; FT_Face face = 0; - int flags = 0; - int vertical = font->desc.vertical; + + *glyph_index = 0; - if (ch < 0x20) + if (symbol < 0x20) { + *face_index = 0; return 0; + } // Handle NBSP like a regular space when rendering the glyph - if (ch == 0xa0) - ch = ' '; - if (font->n_faces == 0) + if (symbol == 0xa0) + symbol = ' '; + if (font->n_faces == 0) { + *face_index = 0; return 0; + } - for (i = 0; i < font->n_faces; ++i) { + // try with the requested face + if (*face_index < font->n_faces) { + face = font->faces[*face_index]; + index = FT_Get_Char_Index(face, symbol); + } + + // not found in requested face, try all others + for (i = 0; i < font->n_faces && index == 0; ++i) { face = font->faces[i]; - index = FT_Get_Char_Index(face, ch); + index = FT_Get_Char_Index(face, symbol); if (index) - break; + *face_index = i; } #ifdef CONFIG_FONTCONFIG @@ -439,30 +461,50 @@ int face_idx; ass_msg(font->library, MSGL_INFO, "Glyph 0x%X not found, selecting one more " - "font for (%s, %d, %d)", ch, font->desc.family, + "font for (%s, %d, %d)", symbol, font->desc.family, font->desc.bold, font->desc.italic); - face_idx = add_face(fontconfig_priv, font, ch); + face_idx = *face_index = add_face(fcpriv, font, symbol); if (face_idx >= 0) { face = font->faces[face_idx]; - index = FT_Get_Char_Index(face, ch); + index = FT_Get_Char_Index(face, symbol); if (index == 0 && face->num_charmaps > 0) { int i; ass_msg(font->library, MSGL_WARN, - "Glyph 0x%X not found, broken font? Trying all charmaps", ch); + "Glyph 0x%X not found, broken font? Trying all charmaps", symbol); for (i = 0; i < face->num_charmaps; i++) { FT_Set_Charmap(face, face->charmaps[i]); - if ((index = FT_Get_Char_Index(face, ch)) != 0) break; + if ((index = FT_Get_Char_Index(face, symbol)) != 0) break; } } if (index == 0) { ass_msg(font->library, MSGL_ERR, "Glyph 0x%X not found in font for (%s, %d, %d)", - ch, font->desc.family, font->desc.bold, + symbol, font->desc.family, font->desc.bold, font->desc.italic); } } } #endif + // FIXME: make sure we have a valid face_index. this is a HACK. + *face_index = FFMAX(*face_index, 0); + *glyph_index = index; + + return 1; +} + +/** + * \brief Get a glyph + * \param ch character code + **/ +FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, + uint32_t ch, int face_index, int index, + ASS_Hinting hinting, int deco) +{ + int error; + FT_Glyph glyph; + FT_Face face = font->faces[face_index]; + int flags = 0; + int vertical = font->desc.vertical; flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | FT_LOAD_IGNORE_TRANSFORM; @@ -505,10 +547,16 @@ // 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 }; + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); + int desc = 0; + + if (os2) + desc = FT_MulFix(os2->sTypoDescender, face->size->metrics.y_scale); + + FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 0, -desc); FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, - face->glyph->metrics.vertAdvance, - 0); + face->glyph->metrics.vertAdvance, desc); glyph->advance.x = face->glyph->linearVertAdvance; } @@ -561,6 +609,8 @@ for (i = 0; i < font->n_faces; ++i) if (font->faces[i]) FT_Done_Face(font->faces[i]); + if (font->shaper_priv) + ass_shaper_font_data_free(font->shaper_priv); free(font->desc.family); free(font); } @@ -618,9 +668,9 @@ * \param border_x border size, x direction, d6 format * \param border_x border size, y direction, d6 format */ -void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y) +void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y) { - int nc = glyph->outline.n_contours; + int nc = outline->n_contours; int begin, stop; char modified = 0; char *valid_cont = malloc(nc); @@ -630,14 +680,14 @@ int i, j; int inside_direction; - inside_direction = FT_Outline_Get_Orientation(&glyph->outline) == + inside_direction = FT_Outline_Get_Orientation(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); + end = outline->contours[i]; + get_contour_cbox(&boxes[i], outline->points, start, end); } // for each contour, check direction and whether it's "outside" @@ -645,8 +695,8 @@ 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); + end = outline->contours[i]; + int dir = get_contour_direction(outline->points, start, end); valid_cont[i] = 1; if (dir == inside_direction) { for (j = 0; j < nc; j++) { @@ -662,19 +712,19 @@ * 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; + FT_Vector temp = outline->points[start + j]; + char temp2 = outline->tags[start + j]; + outline->points[start + j] = outline->points[end - j]; + outline->points[end - j] = temp; + outline->tags[start + j] = outline->tags[end - j]; + 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); + get_contour_cbox(&box, 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) { @@ -687,13 +737,12 @@ // if we need to modify the outline, rewrite it and skip // the contours that we determined should be removed. if (modified) { - FT_Outline *outline = &glyph->outline; int p = 0, c = 0; 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]; + begin = (i == 0) ? 0 : outline->contours[i - 1] + 1; + stop = outline->contours[i]; for (j = begin; j <= stop; j++) { outline->points[p].x = outline->points[j].x; outline->points[p].y = outline->points[j].y;
--- a/libass/ass_font.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_font.h Sat Dec 03 21:35:56 2011 +0000 @@ -22,13 +22,19 @@ #include <stdint.h> #include <ft2build.h> #include FT_GLYPH_H +#include FT_OUTLINE_H + #include "ass.h" #include "ass_types.h" +#define VERTICAL_LOWER_BOUND 0x02f1 + #define ASS_FONT_MAX_FACES 10 #define DECO_UNDERLINE 1 #define DECO_STRIKETHROUGH 2 +typedef struct ass_shaper_font_data ASS_ShaperFontData; + typedef struct { char *family; unsigned bold; @@ -42,25 +48,33 @@ ASS_Library *library; FT_Library ftlibrary; FT_Face faces[ASS_FONT_MAX_FACES]; + ASS_ShaperFontData *shaper_priv; int n_faces; double scale_x, scale_y; // current transform FT_Vector v; // current shift double size; } ASS_Font; -// FIXME: passing the hashmap via a void pointer is very ugly. -ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, +#include "ass_cache.h" + +ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, FT_Library ftlibrary, void *fc_priv, ASS_FontDesc *desc); void ass_font_set_transform(ASS_Font *font, double scale_x, double scale_y, FT_Vector *v); +void ass_face_set_size(FT_Face face, double size); void ass_font_set_size(ASS_Font *font, double size); void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, int *desc); +int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol, + int *face_index, int *glyph_index); FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, - uint32_t ch, ASS_Hinting hinting, int flags); + uint32_t ch, int face_index, int index, + ASS_Hinting hinting, int deco); 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); +void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y); +void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest); +void outline_free(FT_Library lib, FT_Outline *outline); #endif /* LIBASS_FONT_H */
--- a/libass/ass_library.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_library.c Sat Dec 03 21:35:56 2011 +0000 @@ -78,6 +78,7 @@ free(*p); } free(priv->style_overrides); + priv->style_overrides = NULL; if (!list) return;
--- a/libass/ass_parse.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_parse.c Sat Dec 03 21:35:56 2011 +0000 @@ -47,17 +47,18 @@ return 0; } -static void change_font_size(ASS_Renderer *render_priv, double sz) +double ensure_font_size(ASS_Renderer *priv, double size) { - double size = sz * render_priv->font_scale; - if (size < 1) size = 1; - else if (size > render_priv->height * 2) - size = render_priv->height * 2; + else if (size > priv->height * 2) + size = priv->height * 2; - ass_font_set_size(render_priv->state.font, size); + return size; +} +static void change_font_size(ASS_Renderer *render_priv, double sz) +{ render_priv->state.font_size = sz; } @@ -189,17 +190,18 @@ { unsigned a; double cf; - if (now <= t1) { + + if (now < t1) { a = a1; } else if (now >= t4) { a = a3; - } else if (now < t2) { // and > t1 + } else if (now < t2 && t2 > t1) { cf = ((double) (now - t1)) / (t2 - t1); a = a1 * (1 - cf) + a2 * cf; - } else if (now > t3) { + } else if (now >= t3 && t4 > t3) { cf = ((double) (now - t3)) / (t4 - t3); a = a2 * (1 - cf) + a3 * cf; - } else { // t2 <= now <= t3 + } else { // t2 <= now < t3 a = a2; } @@ -216,13 +218,9 @@ int res = 0; ASS_Drawing *drawing = render_priv->state.clip_drawing; - if (drawing && drawing->glyph) - FT_Done_Glyph((FT_Glyph) drawing->glyph); ass_drawing_free(drawing); - render_priv->state.clip_drawing = ass_drawing_new( - render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->ftlibrary); + render_priv->state.clip_drawing = + ass_drawing_new(render_priv->library, render_priv->ftlibrary); drawing = render_priv->state.clip_drawing; skipopt('('); res = mystrtoi(&p, &scale); @@ -259,6 +257,7 @@ else val = -1.; change_border(render_priv, val, render_priv->state.border_y); + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "ybord")) { double val; if (mystrtod(&p, &val)) @@ -273,6 +272,7 @@ else val = 0.; render_priv->state.shadow_x = val; + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "yshad")) { double val; if (mystrtod(&p, &val)) @@ -280,6 +280,7 @@ else val = 0.; render_priv->state.shadow_y = val; + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "fax")) { double val; if (mystrtod(&p, &val)) @@ -331,6 +332,7 @@ render_priv->state.blur = val; } else render_priv->state.blur = 0.0; + render_priv->state.bm_run_id++; // ASS standard tags } else if (mystrcmp(&p, "fsc")) { char tp = *p++; @@ -391,6 +393,7 @@ } else val = -1.; // reset to default change_border(render_priv, val, val); + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "move")) { double x1, x2, y1, y2; long long t1, t2, delta_t, t; @@ -492,6 +495,7 @@ change_alpha(&render_priv->state.c[3], render_priv->state.style->BackColour, pwr); } + render_priv->state.bm_run_id++; // FIXME: simplify } else if (mystrcmp(&p, "an")) { int val; @@ -682,6 +686,7 @@ val = render_priv->state.style->PrimaryColour; ass_msg(render_priv->library, MSGL_DBG2, "color: %X", val); change_color(&render_priv->state.c[0], val, pwr); + render_priv->state.bm_run_id++; } else if ((*p >= '1') && (*p <= '4') && (++p) && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { char n = *(p - 2); @@ -711,9 +716,11 @@ switch (cmd) { case 'c': change_color(render_priv->state.c + cidx, val, pwr); + render_priv->state.bm_run_id++; break; case 'a': change_alpha(render_priv->state.c + cidx, val >> 24, pwr); + render_priv->state.bm_run_id++; break; default: ass_msg(render_priv->library, MSGL_WARN, "Bad command: %c%c", @@ -733,6 +740,7 @@ render_priv->state.be = val; } else render_priv->state.be = 0; + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "b")) { int b; if (mystrtoi(&p, &b)) { @@ -781,18 +789,21 @@ } else val = 0.; render_priv->state.shadow_x = render_priv->state.shadow_y = val; + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "s")) { int val; if (mystrtoi(&p, &val) && val) render_priv->state.flags |= DECO_STRIKETHROUGH; else render_priv->state.flags &= ~DECO_STRIKETHROUGH; + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "u")) { int val; if (mystrtoi(&p, &val) && val) render_priv->state.flags |= DECO_UNDERLINE; else render_priv->state.flags &= ~DECO_UNDERLINE; + render_priv->state.bm_run_id++; } else if (mystrcmp(&p, "pbo")) { double val = 0; if (mystrtod(&p, &val)) @@ -809,6 +820,11 @@ if (!mystrtoi(&p, &val)) val = render_priv->track->WrapStyle; render_priv->state.wrap_style = val; + } else if (mystrcmp(&p, "fe")) { + int val; + if (!mystrtoi(&p, &val)) + val = render_priv->state.style->Encoding; + render_priv->state.font_encoding = val; } return p; @@ -890,6 +906,77 @@ } /** + * \brief determine karaoke effects + * Karaoke effects cannot be calculated during parse stage (get_next_char()), + * so they are done in a separate step. + * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's + * (the first glyph of the karaoke word)'s effect_type and effect_timing. + * This function: + * 1. sets effect_type for all glyphs in the word (_karaoke_ word) + * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts + * (left part is filled with PrimaryColour, right one - with SecondaryColour). + */ +void process_karaoke_effects(ASS_Renderer *render_priv) +{ + GlyphInfo *cur, *cur2; + GlyphInfo *s1, *e1; // start and end of the current word + GlyphInfo *s2; // start of the next word + int i; + int timing; // current timing + int tm_start, tm_end; // timings at start and end of the current word + int tm_current; + double dt; + int x; + int x_start, x_end; + + tm_current = render_priv->time - render_priv->state.event->Start; + timing = 0; + s1 = s2 = 0; + for (i = 0; i <= render_priv->text_info.length; ++i) { + cur = render_priv->text_info.glyphs + i; + if ((i == render_priv->text_info.length) + || (cur->effect_type != EF_NONE)) { + s1 = s2; + s2 = cur; + if (s1) { + e1 = s2 - 1; + tm_start = timing + s1->effect_skip_timing; + tm_end = tm_start + s1->effect_timing; + timing = tm_end; + x_start = 1000000; + x_end = -1000000; + for (cur2 = s1; cur2 <= e1; ++cur2) { + x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x)); + x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x)); + } + + dt = (tm_current - tm_start); + if ((s1->effect_type == EF_KARAOKE) + || (s1->effect_type == EF_KARAOKE_KO)) { + if (dt > 0) + x = x_end + 1; + else + x = x_start; + } else if (s1->effect_type == EF_KARAOKE_KF) { + dt /= (tm_end - tm_start); + x = x_start + (x_end - x_start) * dt; + } else { + ass_msg(render_priv->library, MSGL_ERR, + "Unknown effect type"); + continue; + } + + for (cur2 = s1; cur2 <= e1; ++cur2) { + cur2->effect_type = s1->effect_type; + cur2->effect_timing = x - d6_to_int(cur2->pos.x); + } + } + } + } +} + + +/** * \brief Get next ucs4 char from string, parsing and executing style overrides * \param str string pointer * \return ucs4 code of the next char
--- a/libass/ass_parse.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_parse.h Sat Dec 03 21:35:56 2011 +0000 @@ -27,9 +27,11 @@ #define _a(c) ((c) & 0xFF) void update_font(ASS_Renderer *render_priv); +double ensure_font_size(ASS_Renderer *priv, double size); void change_border(ASS_Renderer *render_priv, double border_x, double border_y); void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event); +void process_karaoke_effects(ASS_Renderer *render_priv); unsigned get_next_char(ASS_Renderer *render_priv, char **str); extern void change_alpha(uint32_t *var, uint32_t new, double pwr); extern uint32_t mult_alpha(uint32_t a, uint32_t b);
--- a/libass/ass_render.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_render.c Sat Dec 03 21:35:56 2011 +0000 @@ -23,44 +23,13 @@ #include "ass_render.h" #include "ass_parse.h" +#include "ass_shaper.h" #define MAX_GLYPHS_INITIAL 1024 #define MAX_LINES_INITIAL 64 #define SUBPIXEL_MASK 63 #define SUBPIXEL_ACCURACY 7 -static void ass_lazy_track_init(ASS_Renderer *render_priv) -{ - ASS_Track *track = render_priv->track; - - if (track->PlayResX && track->PlayResY) - return; - if (!track->PlayResX && !track->PlayResY) { - ass_msg(render_priv->library, MSGL_WARN, - "Neither PlayResX nor PlayResY defined. Assuming 384x288"); - track->PlayResX = 384; - track->PlayResY = 288; - } else { - if (!track->PlayResY && track->PlayResX == 1280) { - track->PlayResY = 1024; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResY undefined, setting to %d", track->PlayResY); - } else if (!track->PlayResY) { - track->PlayResY = track->PlayResX * 3 / 4; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResY undefined, setting to %d", track->PlayResY); - } else if (!track->PlayResX && track->PlayResY == 1024) { - track->PlayResX = 1280; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResX undefined, setting to %d", track->PlayResX); - } else if (!track->PlayResX) { - track->PlayResX = track->PlayResY * 4 / 3; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResX undefined, setting to %d", track->PlayResX); - } - } -} - ASS_Renderer *ass_renderer_init(ASS_Library *library) { int error; @@ -75,10 +44,8 @@ } FT_Library_Version(ft, &vmajor, &vminor, &vpatch); - ass_msg(library, MSGL_V, "FreeType library version: %d.%d.%d", + ass_msg(library, MSGL_V, "Raster: FreeType %d.%d.%d", vmajor, vminor, vpatch); - ass_msg(library, MSGL_V, "FreeType headers version: %d.%d.%d", - FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH); priv = calloc(1, sizeof(ASS_Renderer)); if (!priv) { @@ -92,10 +59,10 @@ priv->ftlibrary = ft; // images_root and related stuff is zero-filled in calloc - priv->cache.font_cache = ass_font_cache_init(library); - priv->cache.bitmap_cache = ass_bitmap_cache_init(library); - priv->cache.composite_cache = ass_composite_cache_init(library); - priv->cache.glyph_cache = ass_glyph_cache_init(library); + priv->cache.font_cache = ass_font_cache_create(); + priv->cache.bitmap_cache = ass_bitmap_cache_create(); + priv->cache.composite_cache = ass_composite_cache_create(); + priv->cache.outline_cache = ass_outline_cache_create(); priv->cache.glyph_max = GLYPH_CACHE_MAX; priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE; @@ -106,11 +73,19 @@ priv->settings.font_size_coeff = 1.; + priv->shaper = ass_shaper_new(0); + ass_shaper_info(library); +#ifdef CONFIG_HARFBUZZ + priv->settings.shaper = ASS_SHAPING_COMPLEX; +#else + priv->settings.shaper = ASS_SHAPING_SIMPLE; +#endif + ass_init_exit: if (priv) - ass_msg(library, MSGL_V, "Init"); + ass_msg(library, MSGL_V, "Initialized"); else - ass_msg(library, MSGL_ERR, "Init failed"); + ass_msg(library, MSGL_ERR, "Initialization failed"); return priv; } @@ -131,10 +106,10 @@ void ass_renderer_done(ASS_Renderer *render_priv) { - ass_font_cache_done(render_priv->cache.font_cache); - ass_bitmap_cache_done(render_priv->cache.bitmap_cache); - ass_composite_cache_done(render_priv->cache.composite_cache); - ass_glyph_cache_done(render_priv->cache.glyph_cache); + ass_cache_done(render_priv->cache.font_cache); + ass_cache_done(render_priv->cache.bitmap_cache); + ass_cache_done(render_priv->cache.composite_cache); + ass_cache_done(render_priv->cache.outline_cache); ass_free_images(render_priv->images_root); ass_free_images(render_priv->prev_images_root); @@ -149,6 +124,7 @@ fontconfig_done(render_priv->fontconfig_priv); if (render_priv->synth_priv) ass_synth_done(render_priv->synth_priv); + ass_shaper_free(render_priv->shaper); free(render_priv->eimg); free(render_priv->text_info.glyphs); free(render_priv->text_info.lines); @@ -328,18 +304,18 @@ // split up into left and right for karaoke, if needed if (lbrk > r[j].x0) { if (lbrk > r[j].x1) lbrk = r[j].x1; - img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + r[j].x0, + img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->stride + 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); + bm->stride, dst_x + r[j].x0, dst_y + r[j].y0, color); if (!img) break; *tail = img; tail = &img->next; } if (lbrk < r[j].x1) { if (lbrk < r[j].x0) lbrk = r[j].x0; - img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + lbrk, + img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->stride + lbrk, r[j].x1 - lbrk, r[j].y1 - r[j].y0, - bm->w, dst_x + lbrk, dst_y + r[j].y0, color2); + bm->stride, dst_x + lbrk, dst_y + r[j].y0, color2); if (!img) break; *tail = img; tail = &img->next; @@ -419,8 +395,8 @@ if (brk > b_x0) { // draw left part if (brk > b_x1) brk = b_x1; - img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0, - brk - b_x0, b_y1 - b_y0, bm->w, + img = my_draw_bitmap(bm->buffer + bm->stride * b_y0 + b_x0, + brk - b_x0, b_y1 - b_y0, bm->stride, dst_x + b_x0, dst_y + b_y0, color); if (!img) return tail; *tail = img; @@ -429,8 +405,8 @@ if (brk < b_x1) { // draw right part if (brk < b_x0) brk = b_x0; - img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk, - b_x1 - brk, b_y1 - b_y0, bm->w, + img = my_draw_bitmap(bm->buffer + bm->stride * b_y0 + brk, + b_x1 - brk, b_y1 - b_y0, bm->stride, dst_x + brk, dst_y + b_y0, color2); if (!img) return tail; *tail = img; @@ -516,7 +492,7 @@ hk.by = by; hk.as = as; hk.bs = bs; - hv = cache_find_composite(render_priv->cache.composite_cache, &hk); + hv = ass_cache_get(render_priv->cache.composite_cache, &hk); if (hv) { (*last_tail)->bitmap = hv->a; (*tail)->bitmap = hv->b; @@ -539,7 +515,7 @@ // Insert bitmaps into the cache chv.a = (*last_tail)->bitmap; chv.b = (*tail)->bitmap; - cache_add_composite(render_priv->cache.composite_cache, &hk, &chv); + ass_cache_put(render_priv->cache.composite_cache, &hk, &chv); } static void free_list_add(ASS_Renderer *render_priv, void *object) @@ -564,32 +540,31 @@ static void blend_vector_clip(ASS_Renderer *render_priv, ASS_Image *head) { - FT_Glyph glyph; - FT_BitmapGlyph clip_bm; + FT_Outline *outline; + Bitmap *clip_bm = NULL; ASS_Image *cur; ASS_Drawing *drawing = render_priv->state.clip_drawing; - GlyphHashKey key; - GlyphHashValue *val; + BitmapHashKey key; + BitmapHashValue *val; int error; if (!drawing) return; // 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); + key.type = BITMAP_CLIP; + key.u.clip.text = drawing->text; + val = ass_cache_get(render_priv->cache.bitmap_cache, &key); if (val) { - clip_bm = (FT_BitmapGlyph) val->glyph; + clip_bm = val->bm; } else { - GlyphHashValue v; + BitmapHashValue v; // Not found in cache, parse and rasterize it - glyph = (FT_Glyph) *ass_drawing_parse(drawing, 1); - if (!glyph) { + outline = ass_drawing_parse(drawing, 1); + if (!outline) { ass_msg(render_priv->library, MSGL_WARN, "Clip vector parsing failed. Skipping."); goto blend_vector_error; @@ -602,37 +577,27 @@ .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; + FT_Outline_Translate(outline, trans.x, trans.y); } 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) { + clip_bm = outline_to_bitmap(render_priv->library, + render_priv->ftlibrary, outline, 0); + if (clip_bm == NULL) { 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); + key.u.clip.text = strdup(drawing->text); + v.bm = clip_bm; + ass_cache_put(render_priv->cache.bitmap_cache, &key, &v); } +blend_vector_error: if (!clip_bm) goto blend_vector_exit; @@ -645,17 +610,17 @@ unsigned char *abuffer, *bbuffer, *nbuffer; abuffer = cur->bitmap; - bbuffer = clip_bm->bitmap.buffer; + bbuffer = clip_bm->buffer; ax = cur->dst_x; ay = cur->dst_y; aw = cur->w; ah = cur->h; as = cur->stride; bx = clip_bm->left; - by = -clip_bm->top; - bw = clip_bm->bitmap.width; - bh = clip_bm->bitmap.rows; - bs = clip_bm->bitmap.pitch; + by = clip_bm->top; + bw = clip_bm->w; + bh = clip_bm->h; + bs = clip_bm->stride; // Calculate overlap coordinates left = (ax > bx) ? ax : bx; @@ -739,22 +704,31 @@ || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip) continue; - pen_x = - dst_x + (info->pos.x >> 6) + - (int) (info->shadow_x * render_priv->border_scale); - pen_y = - dst_y + (info->pos.y >> 6) + - (int) (info->shadow_y * render_priv->border_scale); - bm = info->bm_s; + while (info) { + if (!info->bm_s) { + info = info->next; + continue; + } - here_tail = tail; - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, - 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) - render_overlap(render_priv, last_tail, here_tail); + pen_x = + dst_x + (info->pos.x >> 6) + + (int) (info->shadow_x * render_priv->border_scale); + pen_y = + dst_y + (info->pos.y >> 6) + + (int) (info->shadow_y * render_priv->border_scale); + bm = info->bm_s; - last_tail = here_tail; + here_tail = tail; + tail = + render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, + 1000000, tail); + + if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) + render_overlap(render_priv, last_tail, here_tail); + last_tail = here_tail; + + info = info->next; + } } last_tail = 0; @@ -764,22 +738,30 @@ || info->skip) continue; - pen_x = dst_x + (info->pos.x >> 6); - pen_y = dst_y + (info->pos.y >> 6); - bm = info->bm_o; + while (info) { + if (!info->bm_o) { + info = info->next; + continue; + } + + pen_x = dst_x + (info->pos.x >> 6); + pen_y = dst_y + (info->pos.y >> 6); + bm = info->bm_o; - if ((info->effect_type == EF_KARAOKE_KO) - && (info->effect_timing <= (info->bbox.xMax >> 6))) { - // do nothing - } else { - here_tail = tail; - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], - 0, 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) - render_overlap(render_priv, last_tail, here_tail); + if ((info->effect_type == EF_KARAOKE_KO) + && (info->effect_timing <= (info->bbox.xMax >> 6))) { + // do nothing + } else { + here_tail = tail; + tail = + render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], + 0, 1000000, tail); + if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) + render_overlap(render_priv, last_tail, here_tail); - last_tail = here_tail; + last_tail = here_tail; + } + info = info->next; } } @@ -789,28 +771,36 @@ || info->skip) continue; - pen_x = dst_x + (info->pos.x >> 6); - pen_y = dst_y + (info->pos.y >> 6); - bm = info->bm; + while (info) { + if (!info->bm) { + info = info->next; + continue; + } + + pen_x = dst_x + (info->pos.x >> 6); + pen_y = dst_y + (info->pos.y >> 6); + bm = info->bm; - if ((info->effect_type == EF_KARAOKE) - || (info->effect_type == EF_KARAOKE_KO)) { - if (info->effect_timing > (info->bbox.xMax >> 6)) + if ((info->effect_type == EF_KARAOKE) + || (info->effect_type == EF_KARAOKE_KO)) { + if (info->effect_timing > (info->bbox.xMax >> 6)) + tail = + render_glyph(render_priv, bm, pen_x, pen_y, + info->c[0], 0, 1000000, tail); + else + tail = + render_glyph(render_priv, bm, pen_x, pen_y, + info->c[1], 0, 1000000, tail); + } else if (info->effect_type == EF_KARAOKE_KF) { tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[0], 0, 1000000, tail); - else + render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], + info->c[1], info->effect_timing, tail); + } else tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[1], 0, 1000000, tail); - } else if (info->effect_type == EF_KARAOKE_KF) { - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - info->c[1], info->effect_timing, tail); - } else - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - 0, 1000000, tail); + render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], + 0, 1000000, tail); + info = info->next; + } } *tail = 0; @@ -819,23 +809,27 @@ return head; } -static void compute_string_bbox(TextInfo *info, DBBox *bbox) +static void compute_string_bbox(TextInfo *text, DBBox *bbox) { int i; - if (info->length > 0) { + if (text->length > 0) { bbox->xMin = 32000; bbox->xMax = -32000; - bbox->yMin = -1 * info->lines[0].asc + d6_to_double(info->glyphs[0].pos.y); - bbox->yMax = info->height - info->lines[0].asc + - d6_to_double(info->glyphs[0].pos.y); + bbox->yMin = -1 * text->lines[0].asc + d6_to_double(text->glyphs[0].pos.y); + bbox->yMax = text->height - text->lines[0].asc + + d6_to_double(text->glyphs[0].pos.y); - for (i = 0; i < info->length; ++i) { - if (info->glyphs[i].skip) continue; - double s = d6_to_double(info->glyphs[i].pos.x); - double e = s + d6_to_double(info->glyphs[i].advance.x); - bbox->xMin = FFMIN(bbox->xMin, s); - bbox->xMax = FFMAX(bbox->xMax, e); + for (i = 0; i < text->length; ++i) { + GlyphInfo *info = text->glyphs + i; + if (info->skip) continue; + while (info) { + double s = d6_to_double(info->pos.x); + double e = s + d6_to_double(info->advance.x); + bbox->xMin = FFMIN(bbox->xMin, s); + bbox->xMax = FFMAX(bbox->xMax, e); + info = info->next; + } } } else bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.; @@ -877,6 +871,7 @@ 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; + render_priv->state.font_encoding = render_priv->state.style->Encoding; } /** @@ -909,10 +904,10 @@ render_priv->state.effect_type = EF_NONE; render_priv->state.effect_timing = 0; render_priv->state.effect_skip_timing = 0; + render_priv->state.bm_run_id = 0; ass_drawing_free(render_priv->state.drawing); - render_priv->state.drawing = ass_drawing_new(render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->ftlibrary); + render_priv->state.drawing = ass_drawing_new(render_priv->library, + render_priv->ftlibrary); apply_transition_effects(render_priv, event); } @@ -930,30 +925,18 @@ * Replace the outline of a glyph by a contour which makes up a simple * opaque rectangle. */ -static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch, - FT_Glyph glyph, int sx, int sy) +static void draw_opaque_box(ASS_Renderer *render_priv, int asc, int desc, + FT_Outline *ol, FT_Vector advance, int sx, int sy) { - int asc = 0, desc = 0; int i; - int adv = d16_to_d6(glyph->advance.x); + int adv = advance.x; double scale_y = render_priv->state.scale_y; double scale_x = render_priv->state.scale_x; - FT_OutlineGlyph og = (FT_OutlineGlyph) glyph; - FT_Outline *ol; // to avoid gaps sx = FFMAX(64, sx); sy = FFMAX(64, sy); - if (ch == -1) { - asc = render_priv->state.drawing->asc; - desc = render_priv->state.drawing->desc; - } else { - ass_font_get_asc_desc(render_priv->state.font, ch, &asc, &desc); - asc *= scale_y; - desc *= scale_y; - } - // Emulate the WTFish behavior of VSFilter, i.e. double-scale // the sizes of the opaque box. adv += double_to_d6(render_priv->state.hspacing * render_priv->font_scale @@ -971,10 +954,8 @@ { .x = -sx, .y = -desc - sy }, }; - FT_Outline_Done(render_priv->ftlibrary, &og->outline); - FT_Outline_New(render_priv->ftlibrary, 4, 1, &og->outline); + FT_Outline_New(render_priv->ftlibrary, 4, 1, ol); - ol = &og->outline; ol->n_points = ol->n_contours = 0; for (i = 0; i < 4; i++) { ol->points[ol->n_points] = points[i]; @@ -987,40 +968,53 @@ * Stroke an outline glyph in x/y direction. Applies various fixups to get * around limitations of the FreeType stroker. */ -static void stroke_outline_glyph(ASS_Renderer *render_priv, - FT_OutlineGlyph *glyph, int sx, int sy) +static void stroke_outline(ASS_Renderer *render_priv, FT_Outline *outline, + int sx, int sy) { if (sx <= 0 && sy <= 0) return; - fix_freetype_stroker(*glyph, sx, sy); + fix_freetype_stroker(outline, sx, sy); // Borders are equal; use the regular stroker if (sx == sy && render_priv->state.stroker) { int error; - error = FT_Glyph_StrokeBorder((FT_Glyph *) glyph, - render_priv->state.stroker, 0, 1); - if (error) + unsigned n_points, n_contours; + + FT_StrokerBorder border = FT_Outline_GetOutsideBorder(outline); + error = FT_Stroker_ParseOutline(render_priv->state.stroker, outline, 0); + if (error) { ass_msg(render_priv->library, MSGL_WARN, - "FT_Glyph_Stroke error: %d", error); + "FT_Stroker_ParseOutline failed, error: %d", error); + } + error = FT_Stroker_GetBorderCounts(render_priv->state.stroker, border, + &n_points, &n_contours); + if (error) { + ass_msg(render_priv->library, MSGL_WARN, + "FT_Stroker_GetBorderCounts failed, error: %d", error); + } + FT_Outline_Done(render_priv->ftlibrary, outline); + FT_Outline_New(render_priv->ftlibrary, n_points, n_contours, outline); + outline->n_points = outline->n_contours = 0; + FT_Stroker_ExportBorder(render_priv->state.stroker, border, outline); // "Stroke" with the outline emboldener in two passes. // The outlines look uglier, but the emboldening never adds any points } else { int i; - FT_Outline *ol = &(*glyph)->outline; FT_Outline nol; - FT_Outline_New(render_priv->ftlibrary, ol->n_points, - ol->n_contours, &nol); - FT_Outline_Copy(ol, &nol); - FT_Outline_Embolden(ol, sx * 2); - FT_Outline_Translate(ol, -sx, -sx); + FT_Outline_New(render_priv->ftlibrary, outline->n_points, + outline->n_contours, &nol); + FT_Outline_Copy(outline, &nol); + + FT_Outline_Embolden(outline, sx * 2); + FT_Outline_Translate(outline, -sx, -sx); FT_Outline_Embolden(&nol, sy * 2); FT_Outline_Translate(&nol, -sy, -sy); - for (i = 0; i < ol->n_points; i++) - ol->points[i].y = nol.points[i].y; + for (i = 0; i < outline->n_points; i++) + outline->points[i].y = nol.points[i].y; FT_Outline_Done(render_priv->ftlibrary, &nol); } @@ -1030,37 +1024,41 @@ * \brief Prepare glyph hash */ static void -fill_glyph_hash(ASS_Renderer *priv, GlyphHashKey *key, - ASS_Drawing *drawing, uint32_t ch) +fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key, + GlyphInfo *info) { - 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; + if (info->drawing) { + DrawingHashKey *key = &outline_key->u.drawing; + outline_key->type = OUTLINE_DRAWING; + key->scale_x = double_to_d16(info->scale_x); + key->scale_y = double_to_d16(info->scale_y); + key->outline.x = double_to_d16(info->border_x); + key->outline.y = double_to_d16(info->border_y); key->border_style = priv->state.style->BorderStyle; - key->drawing_hash = drawing->hash; - // not very clean, but works - key->size = drawing->scale; - key->ch = -1; + key->hash = info->drawing->hash; + key->text = info->drawing->text; + key->pbo = info->drawing->pbo; + key->scale = info->drawing->scale; } 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; + GlyphHashKey *key = &outline_key->u.glyph; + outline_key->type = OUTLINE_GLYPH; + key->font = info->font; + key->size = info->font_size; + key->face_index = info->face_index; + key->glyph_index = info->glyph_index; + key->bold = info->bold; + key->italic = info->italic; + key->scale_x = double_to_d16(info->scale_x); + key->scale_y = double_to_d16(info->scale_y); + key->outline.x = double_to_d16(info->border_x); + key->outline.y = double_to_d16(info->border_y); + key->flags = info->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 * Tries to get both glyphs from cache. * If they can't be found, gets a glyph from font face, generates outline with FT_Stroker, @@ -1068,80 +1066,101 @@ * The glyphs are returned in info->glyph and info->outline_glyph */ static void -get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info, - ASS_Drawing *drawing) +get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) { - GlyphHashValue *val; - GlyphHashKey key; + OutlineHashValue *val; + OutlineHashKey key; - memset(&key, 0, sizeof(key)); - memset(info, 0, sizeof(GlyphInfo)); + memset(&info->hash_key, 0, sizeof(key)); - fill_glyph_hash(render_priv, &key, drawing, symbol); - val = cache_find_glyph(render_priv->cache.glyph_cache, &key); - if (val) { - 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; - if (drawing->hash) { - drawing->asc = val->asc; - drawing->desc = val->desc; - } - } else { - GlyphHashValue v; - if (drawing->hash) { + fill_glyph_hash(priv, &key, info); + val = ass_cache_get(priv->cache.outline_cache, &key); + + if (!val) { + OutlineHashValue v; + memset(&v, 0, sizeof(v)); + + if (info->drawing) { + ASS_Drawing *drawing = info->drawing; + ass_drawing_hash(drawing); if(!ass_drawing_parse(drawing, 0)) return; - info->glyph = (FT_Glyph) drawing->glyph; + outline_copy(priv->ftlibrary, &drawing->outline, + &v.outline); + v.advance.x = drawing->advance.x; + v.advance.y = drawing->advance.y; + v.asc = drawing->asc; + v.desc = drawing->desc; + key.u.drawing.text = strdup(drawing->text); } else { - info->glyph = - ass_font_get_glyph(render_priv->fontconfig_priv, - render_priv->state.font, symbol, - render_priv->settings.hinting, - render_priv->state.flags); + ass_face_set_size(info->font->faces[info->face_index], + info->font_size); + ass_font_set_transform(info->font, info->scale_x, + info->scale_y, NULL); + FT_Glyph glyph = + ass_font_get_glyph(priv->fontconfig_priv, info->font, + info->symbol, info->face_index, info->glyph_index, + priv->settings.hinting, info->flags); + if (glyph != NULL) { + outline_copy(priv->ftlibrary, + &((FT_OutlineGlyph)glyph)->outline, &v.outline); + if (priv->settings.shaper == ASS_SHAPING_SIMPLE) { + v.advance.x = d16_to_d6(glyph->advance.x); + v.advance.y = d16_to_d6(glyph->advance.y); + } + FT_Done_Glyph(glyph); + ass_font_get_asc_desc(info->font, info->symbol, + &v.asc, &v.desc); + v.asc *= info->scale_y; + v.desc *= info->scale_y; + } } - if (!info->glyph) + + if (!v.outline) 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); + FT_Outline_Get_CBox(v.outline, &v.bbox_scaled); + + if (priv->state.style->BorderStyle == 3 && + (info->border_x > 0 || info->border_y > 0)) { + FT_Vector advance; + + v.border = calloc(1, sizeof(FT_Outline)); - if (render_priv->state.style->BorderStyle == 3 && - (render_priv->state.border_x > 0|| - render_priv->state.border_y > 0)) { - FT_Glyph_Copy(info->glyph, &info->outline_glyph); - draw_opaque_box(render_priv, symbol, info->outline_glyph, - double_to_d6(render_priv->state.border_x * - 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) - && key.scale_x && key.scale_y) { + if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing) + advance = v.advance; + else + advance = info->advance; - FT_Glyph_Copy(info->glyph, &info->outline_glyph); - stroke_outline_glyph(render_priv, - (FT_OutlineGlyph *) &info->outline_glyph, - double_to_d6(render_priv->state.border_x * - render_priv->border_scale), - double_to_d6(render_priv->state.border_y * - render_priv->border_scale)); + draw_opaque_box(priv, v.asc, v.desc, v.border, advance, + double_to_d6(info->border_x * priv->border_scale), + double_to_d6(info->border_y * priv->border_scale)); + + } else if ((info->border_x > 0 || info->border_y > 0) + && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) { + + outline_copy(priv->ftlibrary, v.outline, &v.border); + stroke_outline(priv, v.border, + double_to_d6(info->border_x * priv->border_scale), + double_to_d6(info->border_y * priv->border_scale)); } - memset(&v, 0, sizeof(v)); - v.glyph = info->glyph; - v.outline_glyph = info->outline_glyph; - v.advance = info->advance; - v.bbox_scaled = info->bbox; - if (drawing->hash) { - v.asc = drawing->asc; - v.desc = drawing->desc; - } - cache_add_glyph(render_priv->cache.glyph_cache, &key, &v); + v.lib = priv->ftlibrary; + val = ass_cache_put(priv->cache.outline_cache, &key, &v); } + + info->hash_key.u.outline.outline = val; + info->outline = val->outline; + info->border = val->border; + info->bbox = val->bbox_scaled; + if (info->drawing || priv->settings.shaper == ASS_SHAPING_SIMPLE) { + info->cluster_advance.x = info->advance.x = val->advance.x; + info->cluster_advance.y = info->advance.y = val->advance.y; + } + info->asc = val->asc; + info->desc = val->desc; + + ass_drawing_free(info->drawing); } /** @@ -1150,7 +1169,7 @@ * onto the screen plane. */ static void -transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, +transform_3d_points(FT_Vector shift, FT_Outline *outline, double frx, double fry, double frz, double fax, double fay, double scale, int yshift) { @@ -1160,7 +1179,6 @@ 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; @@ -1203,19 +1221,19 @@ * 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, +transform_3d(FT_Vector shift, FT_Outline *outline, FT_Outline *border, 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, + if (outline) + transform_3d_points(shift, outline, frx, fry, frz, fax, fay, scale, yshift); - if (glyph2 && *glyph2) - transform_3d_points(shift, *glyph2, frx, fry, frz, + if (border) + transform_3d_points(shift, border, frx, fry, frz, fax, fay, scale, yshift); } } @@ -1232,81 +1250,80 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) { BitmapHashValue *val; - BitmapHashKey *key = &info->hash_key; - - val = cache_find_bitmap(render_priv->cache.bitmap_cache, key); + OutlineBitmapHashKey *key = &info->hash_key.u.outline; - if (val) { - info->bm = val->bm; - info->bm_o = val->bm_o; - info->bm_s = val->bm_s; - } else { + if (!info->outline || info->symbol == '\n' || info->symbol == 0 || info->skip) + return; + + val = ass_cache_get(render_priv->cache.bitmap_cache, &info->hash_key); + + if (!val) { FT_Vector shift; BitmapHashValue hash_val; int error; double fax_scaled, fay_scaled; - 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_Outline *outline, *border; + double scale_x = render_priv->font_scale_x; + + hash_val.bm = hash_val.bm_o = hash_val.bm_s = 0; + + outline_copy(render_priv->ftlibrary, info->outline, &outline); + outline_copy(render_priv->ftlibrary, info->border, &border); - 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 = 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, &glyph, &outline, - info->frx, info->fry, info->frz, fax_scaled, - fay_scaled, render_priv->font_scale, info->asc); + // calculating rotation shift vector (from rotation origin to the glyph basepoint) + 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; - // PAR correction scaling - FT_Matrix m = { double_to_d16(scale_x), 0, - 0, double_to_d16(1.0) }; + // apply rotation + transform_3d(shift, outline, border, + 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 (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, - glyph, outline, - &info->bm, &info->bm_o, - &info->bm_s, info->be, - info->blur * render_priv->border_scale, - key->shadow_offset, key->border_style); - if (error) - info->symbol = 0; + // subpixel shift + if (outline) { + if (scale_x != 1.0) + FT_Outline_Transform(outline, &m); + FT_Outline_Translate(outline, key->advance.x, -key->advance.y); + } + if (border) { + if (scale_x != 1.0) + FT_Outline_Transform(border, &m); + FT_Outline_Translate(border, key->advance.x, -key->advance.y); + } - // add bitmaps to cache - 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, key, &hash_val); + // render glyph + error = outline_to_bitmap3(render_priv->library, + render_priv->synth_priv, + render_priv->ftlibrary, + outline, border, + &hash_val.bm, &hash_val.bm_o, + &hash_val.bm_s, info->be, + info->blur * render_priv->border_scale, + key->shadow_offset, + render_priv->state.style->BorderStyle); + if (error) + info->symbol = 0; - FT_Done_Glyph(glyph); - FT_Done_Glyph(outline); - } + val = ass_cache_put(render_priv->cache.bitmap_cache, &info->hash_key, + &hash_val); + + outline_free(render_priv->ftlibrary, outline); + outline_free(render_priv->ftlibrary, border); } + info->bm = val->bm; + info->bm_o = val->bm_o; + info->bm_s = val->bm_s; + // VSFilter compatibility: invisible fill and no border? // In this case no shadow is supposed to be rendered. - if (!info->outline_glyph && (info->c[0] & 0xFF) == 0xFF) + if (!info->border && (info->c[0] & 0xFF) == 0xFF) info->bm_s = 0; } @@ -1434,6 +1451,7 @@ double pen_shift_x; double pen_shift_y; int cur_line; + int run_offset; TextInfo *text_info = &render_priv->text_info; last_space = -1; @@ -1474,12 +1492,13 @@ sizeof(LineInfo) * text_info->max_lines); } - if (lead < text_info->length) + if (lead < text_info->length) { text_info->glyphs[lead].linebreak = break_type; - last_space = -1; - s1 = text_info->glyphs + lead; - s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); - text_info->n_lines++; + last_space = -1; + s1 = text_info->glyphs + lead; + s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); + text_info->n_lines++; + } } } #define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y)) @@ -1543,6 +1562,7 @@ pen_shift_x = 0.; pen_shift_y = 0.; cur_line = 1; + run_offset = 0; i = 0; cur = text_info->glyphs + i; @@ -1558,86 +1578,31 @@ double height = text_info->lines[cur_line - 1].desc + text_info->lines[cur_line].asc; + text_info->lines[cur_line - 1].len = i - + text_info->lines[cur_line - 1].offset; + text_info->lines[cur_line].offset = i; cur_line++; + run_offset++; pen_shift_x = d6_to_double(-cur->pos.x); pen_shift_y += height + render_priv->settings.line_spacing; ass_msg(render_priv->library, MSGL_DBG2, "shifting from %d to %d by (%f, %f)", i, text_info->length - 1, pen_shift_x, pen_shift_y); } + cur->bm_run_id += run_offset; cur->pos.x += double_to_d6(pen_shift_x); cur->pos.y += double_to_d6(pen_shift_y); } -} - -/** - * \brief determine karaoke effects - * Karaoke effects cannot be calculated during parse stage (get_next_char()), - * so they are done in a separate step. - * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's - * (the first glyph of the karaoke word)'s effect_type and effect_timing. - * This function: - * 1. sets effect_type for all glyphs in the word (_karaoke_ word) - * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts - * (left part is filled with PrimaryColour, right one - with SecondaryColour). - */ -static void process_karaoke_effects(ASS_Renderer *render_priv) -{ - GlyphInfo *cur, *cur2; - GlyphInfo *s1, *e1; // start and end of the current word - GlyphInfo *s2; // start of the next word - int i; - int timing; // current timing - int tm_start, tm_end; // timings at start and end of the current word - int tm_current; - double dt; - int x; - int x_start, x_end; + text_info->lines[cur_line - 1].len = + text_info->length - text_info->lines[cur_line - 1].offset; - tm_current = render_priv->time - render_priv->state.event->Start; - timing = 0; - s1 = s2 = 0; - for (i = 0; i <= render_priv->text_info.length; ++i) { - cur = render_priv->text_info.glyphs + i; - if ((i == render_priv->text_info.length) - || (cur->effect_type != EF_NONE)) { - s1 = s2; - s2 = cur; - if (s1) { - e1 = s2 - 1; - tm_start = timing + s1->effect_skip_timing; - tm_end = tm_start + s1->effect_timing; - timing = tm_end; - x_start = 1000000; - x_end = -1000000; - for (cur2 = s1; cur2 <= e1; ++cur2) { - x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x)); - x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x)); - } - - dt = (tm_current - tm_start); - if ((s1->effect_type == EF_KARAOKE) - || (s1->effect_type == EF_KARAOKE_KO)) { - if (dt > 0) - x = x_end + 1; - else - x = x_start; - } else if (s1->effect_type == EF_KARAOKE_KF) { - dt /= (tm_end - tm_start); - x = x_start + (x_end - x_start) * dt; - } else { - ass_msg(render_priv->library, MSGL_ERR, - "Unknown effect type"); - continue; - } - - for (cur2 = s1; cur2 <= e1; ++cur2) { - cur2->effect_type = s1->effect_type; - cur2->effect_timing = x - d6_to_int(cur2->pos.x); - } - } - } +#if 0 + // print line info + for (i = 0; i < text_info->n_lines; i++) { + printf("line %d offset %d length %d\n", i, text_info->lines[i].offset, + text_info->lines[i].len); } +#endif } /** @@ -1680,38 +1645,22 @@ * Prepare bitmap hash key of a glyph */ static void -fill_bitmap_hash(ASS_Renderer *priv, BitmapHashKey *hash_key, - ASS_Drawing *drawing, FT_Vector pen, uint32_t code) +fill_bitmap_hash(ASS_Renderer *priv, GlyphInfo *info, + OutlineBitmapHashKey *hash_key) { - 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->frx = rot_key(info->frx); + hash_key->fry = rot_key(info->fry); + hash_key->frz = rot_key(info->frz); + hash_key->fax = double_to_d16(info->fax); + hash_key->fay = double_to_d16(info->fay); + hash_key->be = info->be; + hash_key->blur = info->blur; hash_key->shadow_offset.x = double_to_d6( - priv->state.shadow_x * priv->border_scale - - (int) (priv->state.shadow_x * priv->border_scale)); + info->shadow_x * priv->border_scale - + (int) (info->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; + info->shadow_y * priv->border_scale - + (int) (info->shadow_y * priv->border_scale)); } /** @@ -1734,7 +1683,6 @@ int MarginL, MarginR, MarginV; int last_break; int alignment, halign, valign; - int kern = render_priv->track->Kerning; double device_x = 0; double device_y = 0; TextInfo *text_info = &render_priv->text_info; @@ -1754,11 +1702,9 @@ drawing = render_priv->state.drawing; text_info->length = 0; - pen.x = 0; - pen.y = 0; - previous = 0; num_glyphs = 0; p = event->Text; + // Event parsing. while (1) { // get next char, executing style override @@ -1769,15 +1715,26 @@ ass_drawing_add_char(drawing, (char) code); } while (code && render_priv->state.drawing_mode); // skip everything in drawing mode + if (text_info->length >= text_info->max_glyphs) { + // Raise maximum number of glyphs + text_info->max_glyphs *= 2; + text_info->glyphs = glyphs = + realloc(text_info->glyphs, + sizeof(GlyphInfo) * text_info->max_glyphs); + } + + // Clear current GlyphInfo + memset(&glyphs[text_info->length], 0, sizeof(GlyphInfo)); + // Parse drawing if (drawing->i) { drawing->scale_x = render_priv->state.scale_x * render_priv->font_scale; drawing->scale_y = render_priv->state.scale_y * render_priv->font_scale; - ass_drawing_hash(drawing); p--; - code = -1; + code = 0xfffc; // object replacement character + glyphs[text_info->length].drawing = drawing; } // face could have been changed in get_next_char @@ -1789,61 +1746,9 @@ if (code == 0) break; - if (text_info->length >= text_info->max_glyphs) { - // Raise maximum number of glyphs - text_info->max_glyphs *= 2; - text_info->glyphs = glyphs = - realloc(text_info->glyphs, - sizeof(GlyphInfo) * text_info->max_glyphs); - } - - // Add kerning to pen - if (kern && previous && code && !drawing->hash) { - FT_Vector delta; - delta = - ass_font_get_kerning(render_priv->state.font, previous, - code); - 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->state.scale_y, NULL); - - get_outline_glyph(render_priv, code, - glyphs + text_info->length, drawing); - - // Add additional space after italic to non-italic style changes - if (text_info->length && - glyphs[text_info->length - 1].hash_key.italic && - !render_priv->state.italic) { - int back = text_info->length - 1; - GlyphInfo *og = &glyphs[back]; - while (back && og->bbox.xMax - og->bbox.xMin == 0 - && og->hash_key.italic) - og = &glyphs[--back]; - if (og->bbox.xMax > og->advance.x) { - // The FreeType oblique slants by 6/16 - pen.x += og->bbox.yMax * 0.375; - } - } - - glyphs[text_info->length].pos.x = pen.x; - glyphs[text_info->length].pos.y = pen.y; - - 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 += glyphs[text_info->length].advance.y; - pen.y += (render_priv->state.fay * render_priv->state.scale_y) * - glyphs[text_info->length].advance.x; - - previous = code; - + // Fill glyph information glyphs[text_info->length].symbol = code; - glyphs[text_info->length].linebreak = 0; + glyphs[text_info->length].font = render_priv->state.font; for (i = 0; i < 4; ++i) { uint32_t clr = render_priv->state.c[i]; change_alpha(&clr, @@ -1855,53 +1760,108 @@ render_priv->state.effect_timing; glyphs[text_info->length].effect_skip_timing = render_priv->state.effect_skip_timing; + glyphs[text_info->length].font_size = ensure_font_size(render_priv, + render_priv->state.font_size * render_priv->font_scale); 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].scale_x= render_priv->state.scale_x; + glyphs[text_info->length].scale_y = render_priv->state.scale_y; + glyphs[text_info->length].border_x= render_priv->state.border_x; + glyphs[text_info->length].border_y = render_priv->state.border_y; + glyphs[text_info->length].bold = render_priv->state.bold; + glyphs[text_info->length].italic = render_priv->state.italic; + glyphs[text_info->length].flags = render_priv->state.flags; 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) { - 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, - &glyphs[text_info->length].asc, - &glyphs[text_info->length].desc); + glyphs[text_info->length].bm_run_id = render_priv->state.bm_run_id; - glyphs[text_info->length].asc *= render_priv->state.scale_y; - glyphs[text_info->length].desc *= render_priv->state.scale_y; + if (glyphs[text_info->length].drawing) { + drawing = render_priv->state.drawing = + ass_drawing_new(render_priv->library, render_priv->ftlibrary); } - // fill bitmap hash - fill_bitmap_hash(render_priv, &glyphs[text_info->length].hash_key, - drawing, pen, code); - text_info->length++; render_priv->state.effect_type = EF_NONE; render_priv->state.effect_timing = 0; render_priv->state.effect_skip_timing = 0; - if (drawing->hash) { - ass_drawing_free(drawing); - drawing = render_priv->state.drawing = - ass_drawing_new(render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->ftlibrary); - } } - if (text_info->length == 0) { // no valid symbols in the event; this can be smth like {comment} free_render_context(render_priv); return 1; } + // Find shape runs and shape text + ass_shaper_set_base_direction(render_priv->shaper, + resolve_base_direction(render_priv->state.font_encoding)); + ass_shaper_find_runs(render_priv->shaper, render_priv, glyphs, + text_info->length); + ass_shaper_shape(render_priv->shaper, text_info); + + // Retrieve glyphs + for (i = 0; i < text_info->length; i++) { + GlyphInfo *info = glyphs + i; + while (info) { + get_outline_glyph(render_priv, info); + info = info->next; + } + info = glyphs + i; + + // Add additional space after italic to non-italic style changes + if (i && glyphs[i - 1].italic && !info->italic) { + int back = i - 1; + GlyphInfo *og = &glyphs[back]; + while (back && og->bbox.xMax - og->bbox.xMin == 0 + && og->italic) + og = &glyphs[--back]; + if (og->bbox.xMax > og->cluster_advance.x) + og->cluster_advance.x = og->bbox.xMax; + } + + // add horizontal letter spacing + info->cluster_advance.x += double_to_d6(render_priv->state.hspacing * + render_priv->font_scale * info->scale_x); + + // add displacement for vertical shearing + info->cluster_advance.y += (info->fay * info->scale_y) * info->cluster_advance.x; + + } + + // Preliminary layout (for line wrapping) + previous = 0; + pen.x = 0; + pen.y = 0; + for (i = 0; i < text_info->length; i++) { + GlyphInfo *info = glyphs + i; + FT_Vector cluster_pen = pen; + while (info) { + info->pos.x = cluster_pen.x; + info->pos.y = cluster_pen.y; + + cluster_pen.x += info->advance.x; + cluster_pen.y += info->advance.y; + + // fill bitmap hash + info->hash_key.type = BITMAP_OUTLINE; + fill_bitmap_hash(render_priv, info, &info->hash_key.u.outline); + + info = info->next; + } + info = glyphs + i; + pen.x += info->cluster_advance.x; + pen.y += info->cluster_advance.y; + previous = info->symbol; + } + + // depends on glyph x coordinates being monotonous, so it should be done before line wrap process_karaoke_effects(render_priv); @@ -1917,40 +1877,64 @@ MarginV = (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV; - if (render_priv->state.evt_type != EVENT_HSCROLL) { - double max_text_width; + // calculate max length of a line + double max_text_width = + x2scr(render_priv, render_priv->track->PlayResX - MarginR) - + x2scr(render_priv, MarginL); - // calculate max length of a line - max_text_width = - x2scr(render_priv, - render_priv->track->PlayResX - MarginR) - - x2scr(render_priv, MarginL); - + // wrap lines + if (render_priv->state.evt_type != EVENT_HSCROLL) { // rearrange text in several lines wrap_lines_smart(render_priv, max_text_width); + } else { + // no breaking or wrapping, everything in a single line + text_info->lines[0].offset = 0; + text_info->lines[0].len = text_info->length; + text_info->n_lines = 1; + measure_text(render_priv); + } - // align text - 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) - || glyphs[i].linebreak) { - double width, shift = 0; - GlyphInfo *first_glyph = - glyphs + last_break + 1; - GlyphInfo *last_glyph = glyphs + i - 1; + // Reorder text into visual order + FriBidiStrIndex *cmap = ass_shaper_reorder(render_priv->shaper, text_info); - while (first_glyph < last_glyph && first_glyph->skip) - first_glyph++; + // Reposition according to the map + pen.x = 0; + pen.y = 0; + int lineno = 1; + for (i = 0; i < text_info->length; i++) { + GlyphInfo *info = glyphs + cmap[i]; + if (glyphs[i].linebreak) { + pen.x = 0; + pen.y += double_to_d6(text_info->lines[lineno-1].desc); + pen.y += double_to_d6(text_info->lines[lineno].asc); + pen.y += double_to_d6(render_priv->settings.line_spacing); + lineno++; + } + if (info->skip) continue; + FT_Vector cluster_pen = pen; + while (info) { + info->pos.x = info->offset.x + cluster_pen.x; + info->pos.y = info->offset.y + cluster_pen.y; + cluster_pen.x += info->advance.x; + cluster_pen.y += info->advance.y; + info = info->next; + } + info = glyphs + cmap[i]; + pen.x += info->cluster_advance.x; + pen.y += info->cluster_advance.y; + } - while ((last_glyph > first_glyph) - && ((last_glyph->symbol == '\n') - || (last_glyph->symbol == 0) - || (last_glyph->skip))) - last_glyph--; - - width = d6_to_double( - last_glyph->pos.x + last_glyph->advance.x - - first_glyph->pos.x); + // align lines + if (render_priv->state.evt_type != EVENT_HSCROLL) { + last_break = -1; + double width = 0; + for (i = 0; i <= text_info->length; ++i) { // (text_info->length + 1) is the end of the last line + if ((i == text_info->length) || glyphs[i].linebreak) { + // remove letter spacing (which is included in cluster_advance) + if (i > 0) + width -= render_priv->state.hspacing * render_priv->font_scale * + glyphs[i-1].scale_x; + double shift = 0; if (halign == HALIGN_LEFT) { // left aligned, no action shift = 0; } else if (halign == HALIGN_RIGHT) { // right aligned @@ -1959,13 +1943,20 @@ shift = (max_text_width - width) / 2.0; } for (j = last_break + 1; j < i; ++j) { - glyphs[j].pos.x += double_to_d6(shift); + GlyphInfo *info = glyphs + j; + while (info) { + info->pos.x += double_to_d6(shift); + info = info->next; + } } last_break = i - 1; + width = 0; + } + if (i < text_info->length && !glyphs[i].skip && + glyphs[i].symbol != '\n' && glyphs[i].symbol != 0) { + width += d6_to_double(glyphs[i].cluster_advance.x); } } - } else { // render_priv->state.evt_type == EVENT_HSCROLL - measure_text(render_priv); } // determing text bounding box @@ -2091,32 +2082,38 @@ for (i = 0; i < text_info->length; ++i) { GlyphInfo *info = glyphs + i; + while (info) { + OutlineBitmapHashKey *key = &info->hash_key.u.outline; - if (info->hash_key.frx || info->hash_key.fry - || info->hash_key.frz || info->hash_key.fax - || info->hash_key.fay) { - info->hash_key.shift_x = info->pos.x + double_to_d6(device_x - center.x); - info->hash_key.shift_y = - -(info->pos.y + double_to_d6(device_y - center.y)); - } else { - info->hash_key.shift_x = 0; - info->hash_key.shift_y = 0; + if (key->frx || key->fry || key->frz || key->fax || key->fay) { + key->shift_x = info->pos.x + double_to_d6(device_x - center.x); + key->shift_y = -(info->pos.y + double_to_d6(device_y - center.y)); + } else { + key->shift_x = 0; + key->shift_y = 0; + } + info = info->next; } } } // convert glyphs to bitmaps - device_x *= render_priv->font_scale_x; + int left = render_priv->settings.left_margin; + device_x = (device_x - left) * render_priv->font_scale_x + left; for (i = 0; i < text_info->length; ++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, glyphs + i); + GlyphInfo *info = glyphs + i; + while (info) { + OutlineBitmapHashKey *key = &info->hash_key.u.outline; + info->pos.x *= render_priv->font_scale_x; + key->advance.x = + double_to_d6(device_x - (int) device_x + + d6_to_double(info->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; + key->advance.y = + double_to_d6(device_y - (int) device_y + + d6_to_double(info->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; + get_bitmap_glyph(render_priv, info); + info = info->next; + } } memset(event_images, 0, sizeof(*event_images)); @@ -2131,6 +2128,7 @@ event_images->event = event; event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y); + ass_shaper_cleanup(render_priv->shaper, text_info); free_render_context(render_priv); return 0; @@ -2154,24 +2152,16 @@ */ static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache) { - 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); + if (ass_cache_empty(cache->bitmap_cache, cache->bitmap_max_size)) { + ass_cache_empty(cache->composite_cache, 0); ass_free_images(priv->prev_images_root); priv->prev_images_root = 0; } - - 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); + if (ass_cache_empty(cache->outline_cache, cache->glyph_max)) { + ass_cache_empty(cache->bitmap_cache, 0); + ass_cache_empty(cache->composite_cache, 0); + ass_free_images(priv->prev_images_root); + priv->prev_images_root = 0; } } @@ -2202,7 +2192,7 @@ render_priv->track = track; render_priv->time = now; - ass_lazy_track_init(render_priv); + ass_lazy_track_init(render_priv->library, render_priv->track); render_priv->font_scale = settings_priv->font_size_coeff * render_priv->orig_height / render_priv->track->PlayResY; @@ -2213,6 +2203,11 @@ else render_priv->border_scale = 1.; + ass_shaper_set_kerning(render_priv->shaper, track->Kerning); + if (track->Language) + ass_shaper_set_language(render_priv->shaper, track->Language); + ass_shaper_set_level(render_priv->shaper, render_priv->settings.shaper); + // PAR correction render_priv->font_scale_x = render_priv->settings.aspect / render_priv->settings.storage_aspect;
--- a/libass/ass_render.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_render.h Sat Dec 03 21:35:56 2011 +0000 @@ -27,6 +27,9 @@ #include FT_GLYPH_H #include FT_SYNTHESIS_H +// XXX: fix the inclusion mess so we can avoid doing this here +typedef struct ass_shaper ASS_Shaper; + #include "ass.h" #include "ass_font.h" #include "ass_bitmap.h" @@ -73,6 +76,7 @@ double aspect; // frame aspect ratio, d_width / d_height. double storage_aspect; // pixel ratio of the source image ASS_Hinting hinting; + ASS_ShapingLevel shaper; char *default_font; char *default_family; @@ -96,19 +100,26 @@ // describes a glyph // GlyphInfo and TextInfo are used for text centering and word-wrapping operations -typedef struct { +typedef struct glyph_info { unsigned symbol; unsigned skip; // skip glyph when layouting text - FT_Glyph glyph; - FT_Glyph outline_glyph; + ASS_Font *font; + int face_index; + int glyph_index; + double font_size; + ASS_Drawing *drawing; + FT_Outline *outline; + FT_Outline *border; Bitmap *bm; // glyph bitmap Bitmap *bm_o; // outline bitmap Bitmap *bm_s; // shadow bitmap FT_BBox bbox; FT_Vector pos; + FT_Vector offset; char linebreak; // the first (leading) glyph of some line ? uint32_t c[4]; // colors FT_Vector advance; // 26.6 + FT_Vector cluster_advance; Effect effect_type; int effect_timing; // time duration of current karaoke word // after process_karaoke_effects: distance in pixels from the glyph origin. @@ -121,12 +132,24 @@ double shadow_y; double frx, fry, frz; // rotation double fax, fay; // text shearing + double scale_x, scale_y; + double border_x, border_y; + unsigned italic; + unsigned bold; + int flags; + + int bm_run_id; + int shape_run_id; BitmapHashKey hash_key; + + // next glyph in this cluster + struct glyph_info *next; } GlyphInfo; typedef struct { double asc, desc; + int offset, len; } LineInfo; typedef struct { @@ -147,7 +170,6 @@ int parsed_tags; ASS_Font *font; - char *font_path; double font_size; int flags; // decoration flags (underline/strike-through) @@ -186,6 +208,9 @@ int effect_timing; int effect_skip_timing; + // bitmap run id (used for final bitmap rendering) + int bm_run_id; + enum { SCROLL_LR, // left-to-right SCROLL_RL, @@ -200,13 +225,14 @@ unsigned italic; int treat_family_as_pattern; int wrap_style; + int font_encoding; } RenderContext; typedef struct { - Hashmap *font_cache; - Hashmap *glyph_cache; - Hashmap *bitmap_cache; - Hashmap *composite_cache; + Cache *font_cache; + Cache *outline_cache; + Cache *bitmap_cache; + Cache *composite_cache; size_t glyph_max; size_t bitmap_max_size; } CacheStore; @@ -218,6 +244,7 @@ ASS_Settings settings; int render_id; ASS_SynthPriv *synth_priv; + ASS_Shaper *shaper; ASS_Image *images_root; // rendering result is stored here ASS_Image *prev_images_root; @@ -265,4 +292,7 @@ void reset_render_context(ASS_Renderer *render_priv); void ass_free_images(ASS_Image *img); +// XXX: this is actually in ass.c, includes should be fixed later on +void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track); + #endif /* LIBASS_RENDER_H */
--- a/libass/ass_render_api.c Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_render_api.c Sat Dec 03 21:35:56 2011 +0000 @@ -25,12 +25,9 @@ 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_cache_empty(priv->cache.outline_cache, 0); + ass_cache_empty(priv->cache.bitmap_cache, 0); + ass_cache_empty(priv->cache.composite_cache, 0); ass_free_images(priv->prev_images_root); priv->prev_images_root = 0; @@ -61,6 +58,17 @@ } } +void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level) +{ +#ifdef CONFIG_HARFBUZZ + // select the complex shaper for illegal values + if (level == ASS_SHAPING_SIMPLE || level == ASS_SHAPING_COMPLEX) + priv->settings.shaper = level; + else + priv->settings.shaper = ASS_SHAPING_COMPLEX; +#endif +} + 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 ||
--- a/libass/ass_types.h Sat Dec 03 21:33:28 2011 +0000 +++ b/libass/ass_types.h Sat Dec 03 21:35:56 2011 +0000 @@ -112,6 +112,7 @@ int WrapStyle; int ScaledBorderAndShadow; int Kerning; + char *Language; int default_style; // index of default style char *name; // file name in case of external subs, 0 for streams