Ticket #2008: libass-0.10.0-r34354.patch
| File libass-0.10.0-r34354.patch, 169.4 KB (added by , 15 years ago) |
|---|
-
libass/ass.c
28 28 #include <sys/stat.h> 29 29 #include <unistd.h> 30 30 #include <inttypes.h> 31 #include <ctype.h> 31 32 32 33 #ifdef CONFIG_ICONV 33 34 #include <iconv.h> … … 69 70 } 70 71 free(track->style_format); 71 72 free(track->event_format); 73 free(track->Language); 72 74 if (track->styles) { 73 75 for (i = 0; i < track->n_styles; ++i) 74 76 ass_free_style(track, i); … … 595 597 track->ScaledBorderAndShadow = parse_bool(str + 22); 596 598 } else if (!strncmp(str, "Kerning:", 8)) { 597 599 track->Kerning = parse_bool(str + 8); 600 } else if (!strncmp(str, "Language:", 9)) { 601 char *p = str + 9; 602 while (*p && isspace(*p)) p++; 603 track->Language = malloc(3); 604 strncpy(track->Language, p, 2); 605 track->Language[2] = 0; 598 606 } 599 607 return 0; 600 608 } … … 1269 1277 track->parser_priv = calloc(1, sizeof(ASS_ParserPriv)); 1270 1278 return track; 1271 1279 } 1280 1281 /** 1282 * \brief Prepare track for rendering 1283 */ 1284 void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track) 1285 { 1286 if (track->PlayResX && track->PlayResY) 1287 return; 1288 if (!track->PlayResX && !track->PlayResY) { 1289 ass_msg(lib, MSGL_WARN, 1290 "Neither PlayResX nor PlayResY defined. Assuming 384x288"); 1291 track->PlayResX = 384; 1292 track->PlayResY = 288; 1293 } else { 1294 if (!track->PlayResY && track->PlayResX == 1280) { 1295 track->PlayResY = 1024; 1296 ass_msg(lib, MSGL_WARN, 1297 "PlayResY undefined, setting to %d", track->PlayResY); 1298 } else if (!track->PlayResY) { 1299 track->PlayResY = track->PlayResX * 3 / 4; 1300 ass_msg(lib, MSGL_WARN, 1301 "PlayResY undefined, setting to %d", track->PlayResY); 1302 } else if (!track->PlayResX && track->PlayResY == 1024) { 1303 track->PlayResX = 1280; 1304 ass_msg(lib, MSGL_WARN, 1305 "PlayResX undefined, setting to %d", track->PlayResX); 1306 } else if (!track->PlayResX) { 1307 track->PlayResX = track->PlayResY * 4 / 3; 1308 ass_msg(lib, MSGL_WARN, 1309 "PlayResX undefined, setting to %d", track->PlayResX); 1310 } 1311 } 1312 } -
libass/ass.h
23 23 #include <stdarg.h> 24 24 #include "ass_types.h" 25 25 26 #define LIBASS_VERSION 0x0 091300026 #define LIBASS_VERSION 0x01000000 27 27 28 28 /* 29 29 * A linked list of images produced by an ass renderer. … … 61 61 } ASS_Hinting; 62 62 63 63 /** 64 * \brief Text shaping levels. 65 * 66 * SIMPLE is a fast, font-agnostic shaper that can do only substitutions. 67 * COMPLEX is a slower shaper using OpenType for substitutions and positioning. 68 * 69 * libass uses the best shaper available by default. 70 */ 71 typedef enum { 72 ASS_SHAPING_SIMPLE = 0, 73 ASS_SHAPING_COMPLEX 74 } ASS_ShapingLevel; 75 76 /** 64 77 * \brief Initialize the library. 65 78 * \return library handle or NULL if failed 66 79 */ … … 147 160 void ass_set_frame_size(ASS_Renderer *priv, int w, int h); 148 161 149 162 /** 163 * \brief Set shaping level. This is merely a hint, the renderer will use 164 * whatever is available if the request cannot be fulfilled. 165 * \param level shaping level 166 */ 167 void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level); 168 169 /** 150 170 * \brief Set frame margins. These values may be negative if pan-and-scan 151 171 * is used. 152 172 * \param priv renderer handle -
libass/ass_bitmap.c
1 1 /* 2 2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> 3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> 3 4 * 4 5 * This file is part of libass. 5 6 * … … 22 23 #include <assert.h> 23 24 #include <ft2build.h> 24 25 #include FT_GLYPH_H 26 #include FT_OUTLINE_H 25 27 26 28 #include "ass_utils.h" 27 29 #include "ass_bitmap.h" … … 133 135 static Bitmap *alloc_bitmap(int w, int h) 134 136 { 135 137 Bitmap *bm; 138 unsigned s = w; // XXX: alignment 136 139 bm = malloc(sizeof(Bitmap)); 137 bm->buffer = calloc( w, h);140 bm->buffer = calloc(s, h); 138 141 bm->w = w; 139 142 bm->h = h; 143 bm->stride = s; 140 144 bm->left = bm->top = 0; 141 145 return bm; 142 146 } … … 153 157 Bitmap *dst = alloc_bitmap(src->w, src->h); 154 158 dst->left = src->left; 155 159 dst->top = src->top; 156 memcpy(dst->buffer, src->buffer, src-> w* src->h);160 memcpy(dst->buffer, src->buffer, src->stride * src->h); 157 161 return dst; 158 162 } 159 163 160 int check_glyph_area(ASS_Library *library, FT_Glyph glyph) 164 Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib, 165 FT_Outline *outline, int bord) 161 166 { 162 FT_BBox bbox;163 long long dx, dy;164 FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox);165 dx = bbox.xMax - bbox.xMin;166 dy = bbox.yMax - bbox.yMin;167 if (dx * dy > 8000000) {168 ass_msg(library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx",169 (int) dx, (int) dy);170 return 1;171 } else172 return 0;173 }174 175 static Bitmap *glyph_to_bitmap_internal(ASS_Library *library,176 FT_Glyph glyph, int bord)177 {178 FT_BitmapGlyph bg;179 FT_Bitmap *bit;180 167 Bitmap *bm; 181 168 int w, h; 182 unsigned char *src;183 unsigned char *dst;184 int i;185 169 int error; 170 FT_BBox bbox; 171 FT_Bitmap bitmap; 186 172 187 if (check_glyph_area(library, glyph)) 188 return 0; 189 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0); 190 if (error) { 191 ass_msg(library, MSGL_WARN, "FT_Glyph_To_Bitmap error %d", 192 error); 193 return 0; 194 } 173 FT_Outline_Get_CBox(outline, &bbox); 174 // move glyph to origin (0, 0) 175 bbox.xMin &= ~63; 176 bbox.yMin &= ~63; 177 FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin); 178 // bitmap size 179 bbox.xMax = (bbox.xMax + 63) & ~63; 180 bbox.yMax = (bbox.yMax + 63) & ~63; 181 w = (bbox.xMax - bbox.xMin) >> 6; 182 h = (bbox.yMax - bbox.yMin) >> 6; 183 // pen offset 184 bbox.xMin >>= 6; 185 bbox.yMax >>= 6; 195 186 196 bg = (FT_BitmapGlyph) glyph; 197 bit = &(bg->bitmap); 198 if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) { 199 ass_msg(library, MSGL_WARN, "Unsupported pixel mode: %d", 200 (int) (bit->pixel_mode)); 201 FT_Done_Glyph(glyph); 202 return 0; 187 if (w * h > 8000000) { 188 ass_msg(library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx", 189 w, h); 190 return NULL; 203 191 } 204 192 205 w = bit->width; 206 h = bit->rows; 193 // allocate and set up bitmap 207 194 bm = alloc_bitmap(w + 2 * bord, h + 2 * bord); 208 bm->left = bg->left - bord; 209 bm->top = -bg->top - bord; 195 bm->left = bbox.xMin - bord; 196 bm->top = -bbox.yMax - bord; 197 bitmap.width = w; 198 bitmap.rows = h; 199 bitmap.pitch = bm->stride; 200 bitmap.buffer = bm->buffer + bord + bm->stride * bord; 201 bitmap.num_grays = 256; 202 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; 210 203 211 src = bit->buffer; 212 dst = bm->buffer + bord + bm->w * bord; 213 for (i = 0; i < h; ++i) { 214 memcpy(dst, src, w); 215 src += bit->pitch; 216 dst += bm->w; 204 // render into target bitmap 205 if ((error = FT_Outline_Get_Bitmap(ftlib, outline, &bitmap))) { 206 ass_msg(library, MSGL_WARN, "Failed to rasterize glyph: %d\n", error); 207 ass_free_bitmap(bm); 208 return NULL; 217 209 } 218 210 219 FT_Done_Glyph(glyph);220 211 return bm; 221 212 } 222 213 … … 232 223 const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; 233 224 const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top; 234 225 const int r = 235 bm_o->left + bm_o-> w<236 bm_g->left + bm_g-> w ? bm_o->left + bm_o->w : bm_g->left + bm_g->w;226 bm_o->left + bm_o->stride < 227 bm_g->left + bm_g->stride ? bm_o->left + bm_o->stride : bm_g->left + bm_g->stride; 237 228 const int b = 238 229 bm_o->top + bm_o->h < 239 230 bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h; 240 231 241 232 unsigned char *g = 242 bm_g->buffer + (t - bm_g->top) * bm_g-> w+ (l - bm_g->left);233 bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left); 243 234 unsigned char *o = 244 bm_o->buffer + (t - bm_o->top) * bm_o-> w+ (l - bm_o->left);235 bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left); 245 236 246 237 for (y = 0; y < b - t; ++y) { 247 238 for (x = 0; x < r - l; ++x) { … … 250 241 c_o = o[x]; 251 242 o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0; 252 243 } 253 g += bm_g-> w;254 o += bm_o-> w;244 g += bm_g->stride; 245 o += bm_o->stride; 255 246 } 256 247 } 257 248 … … 259 250 * \brief Shift a bitmap by the fraction of a pixel in x and y direction 260 251 * expressed in 26.6 fixed point 261 252 */ 262 static void shift_bitmap(unsigned char *buf, int w, int h, int shift_x, 263 int shift_y) 253 static void shift_bitmap(Bitmap *bm, int shift_x, int shift_y) 264 254 { 265 255 int x, y, b; 256 int w = bm->w; 257 int h = bm->h; 258 int s = bm->stride; 259 unsigned char *buf = bm->buffer; 266 260 267 261 // Shift in x direction 268 262 if (shift_x > 0) { 269 263 for (y = 0; y < h; y++) { 270 264 for (x = w - 1; x > 0; x--) { 271 b = (buf[x + y * w- 1] * shift_x) >> 6;272 buf[x + y * w- 1] -= b;273 buf[x + y * w] += b;265 b = (buf[x + y * s - 1] * shift_x) >> 6; 266 buf[x + y * s - 1] -= b; 267 buf[x + y * s] += b; 274 268 } 275 269 } 276 270 } else if (shift_x < 0) { 277 271 shift_x = -shift_x; 278 272 for (y = 0; y < h; y++) { 279 273 for (x = 0; x < w - 1; x++) { 280 b = (buf[x + y * w+ 1] * shift_x) >> 6;281 buf[x + y * w+ 1] -= b;282 buf[x + y * w] += b;274 b = (buf[x + y * s + 1] * shift_x) >> 6; 275 buf[x + y * s + 1] -= b; 276 buf[x + y * s] += b; 283 277 } 284 278 } 285 279 } … … 288 282 if (shift_y > 0) { 289 283 for (x = 0; x < w; x++) { 290 284 for (y = h - 1; y > 0; y--) { 291 b = (buf[x + (y - 1) * w] * shift_y) >> 6;292 buf[x + (y - 1) * w] -= b;293 buf[x + y * w] += b;285 b = (buf[x + (y - 1) * s] * shift_y) >> 6; 286 buf[x + (y - 1) * s] -= b; 287 buf[x + y * s] += b; 294 288 } 295 289 } 296 290 } else if (shift_y < 0) { 297 291 shift_y = -shift_y; 298 292 for (x = 0; x < w; x++) { 299 293 for (y = 0; y < h - 1; y++) { 300 b = (buf[x + (y + 1) * w] * shift_y) >> 6;301 buf[x + (y + 1) * w] -= b;302 buf[x + y * w] += b;294 b = (buf[x + (y + 1) * s] * shift_y) >> 6; 295 buf[x + (y + 1) * s] -= b; 296 buf[x + y * s] += b; 303 297 } 304 298 } 305 299 } … … 430 424 * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel 431 425 * This blur is the same as the one employed by vsfilter. 432 426 */ 433 static void be_blur( unsigned char *buf, int w, int h)427 static void be_blur(Bitmap *bm) 434 428 { 429 int w = bm->w; 430 int h = bm->h; 431 int s = bm->stride; 432 unsigned char *buf = bm->buffer; 435 433 unsigned int x, y; 436 434 unsigned int old_sum, new_sum; 437 435 438 436 for (y = 0; y < h; y++) { 439 old_sum = 2 * buf[y * w];437 old_sum = 2 * buf[y * s]; 440 438 for (x = 0; x < w - 1; x++) { 441 new_sum = buf[y * w + x] + buf[y * w+ x + 1];442 buf[y * w+ x] = (old_sum + new_sum) >> 2;439 new_sum = buf[y * s + x] + buf[y * s + x + 1]; 440 buf[y * s + x] = (old_sum + new_sum) >> 2; 443 441 old_sum = new_sum; 444 442 } 445 443 } … … 447 445 for (x = 0; x < w; x++) { 448 446 old_sum = 2 * buf[x]; 449 447 for (y = 0; y < h - 1; y++) { 450 new_sum = buf[y * w + x] + buf[(y + 1) * w+ x];451 buf[y * w+ x] = (old_sum + new_sum) >> 2;448 new_sum = buf[y * s + x] + buf[(y + 1) * s + x]; 449 buf[y * s + x] = (old_sum + new_sum) >> 2; 452 450 old_sum = new_sum; 453 451 } 454 452 } 455 453 } 456 454 457 int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *priv_blur,458 FT_Glyph glyph, FT_Glyph outline_glyph,459 Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s,460 int be, double blur_radius, FT_Vector shadow_offset,461 int border_style)455 int outline_to_bitmap3(ASS_Library *library, ASS_SynthPriv *priv_blur, 456 FT_Library ftlib, FT_Outline *outline, FT_Outline *border, 457 Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, 458 int be, double blur_radius, FT_Vector shadow_offset, 459 int border_style) 462 460 { 463 461 blur_radius *= 2; 464 462 int bbord = be > 0 ? sqrt(2 * be) : 0; … … 471 469 472 470 *bm_g = *bm_o = *bm_s = 0; 473 471 474 if ( glyph)475 *bm_g = glyph_to_bitmap_internal(library, glyph, bord);472 if (outline) 473 *bm_g = outline_to_bitmap(library, ftlib, outline, bord); 476 474 if (!*bm_g) 477 475 return 1; 478 476 479 if ( outline_glyph) {480 *bm_o = glyph_to_bitmap_internal(library, outline_glyph, bord);477 if (border) { 478 *bm_o = outline_to_bitmap(library, ftlib, border, bord); 481 479 if (!*bm_o) { 482 480 return 1; 483 481 } … … 486 484 // Apply box blur (multiple passes, if requested) 487 485 while (be--) { 488 486 if (*bm_o) 489 be_blur( (*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h);487 be_blur(*bm_o); 490 488 else 491 be_blur( (*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h);489 be_blur(*bm_g); 492 490 } 493 491 494 492 // Apply gaussian blur … … 500 498 generate_tables(priv_blur, blur_radius); 501 499 if (*bm_o) 502 500 ass_gauss_blur((*bm_o)->buffer, priv_blur->tmp, 503 (*bm_o)->w, (*bm_o)->h, (*bm_o)-> w,501 (*bm_o)->w, (*bm_o)->h, (*bm_o)->stride, 504 502 (int *) priv_blur->gt2, priv_blur->g_r, 505 503 priv_blur->g_w); 506 504 else 507 505 ass_gauss_blur((*bm_g)->buffer, priv_blur->tmp, 508 (*bm_g)->w, (*bm_g)->h, (*bm_g)-> w,506 (*bm_g)->w, (*bm_g)->h, (*bm_g)->stride, 509 507 (int *) priv_blur->gt2, priv_blur->g_r, 510 508 priv_blur->g_w); 511 509 } … … 521 519 522 520 assert(bm_s); 523 521 524 shift_bitmap((*bm_s)->buffer, (*bm_s)->w,(*bm_s)->h, 525 shadow_offset.x, shadow_offset.y); 522 shift_bitmap(*bm_s, shadow_offset.x, shadow_offset.y); 526 523 527 524 return 0; 528 525 } -
libass/ass_bitmap.h
32 32 typedef struct { 33 33 int left, top; 34 34 int w, h; // width, height 35 int stride; 35 36 unsigned char *buffer; // w x h buffer 36 37 } Bitmap; 37 38 39 Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib, 40 FT_Outline *outline, int bord); 38 41 /** 39 42 * \brief perform glyph rendering 40 43 * \param glyph original glyph … … 44 47 * \param bm_g out: pointer to the bitmap of glyph shadow is returned here 45 48 * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps 46 49 */ 47 int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *priv_blur,48 FT_Glyph glyph, FT_Glyph outline_glyph,49 Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s,50 int be, double blur_radius, FT_Vector shadow_offset,51 int border_style);50 int outline_to_bitmap3(ASS_Library *library, ASS_SynthPriv *priv_blur, 51 FT_Library ftlib, FT_Outline *outline, FT_Outline *border, 52 Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, 53 int be, double blur_radius, FT_Vector shadow_offset, 54 int border_style); 52 55 53 56 void ass_free_bitmap(Bitmap *bm); 54 int check_glyph_area(ASS_Library *library, FT_Glyph glyph);55 57 56 58 #endif /* LIBASS_BITMAP_H */ -
libass/ass_cache.c
1 1 /* 2 2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> 3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> 3 4 * 4 5 * This file is part of libass. 5 6 * … … 20 21 21 22 #include <inttypes.h> 22 23 #include <ft2build.h> 23 #include FT_FREETYPE_H 24 #include FT_GLYPH_H 25 24 #include FT_OUTLINE_H 26 25 #include <assert.h> 27 26 28 27 #include "ass_utils.h" 29 #include "ass.h"30 #include "ass_fontconfig.h"31 28 #include "ass_font.h" 32 #include "ass_bitmap.h"33 29 #include "ass_cache.h" 34 30 35 static unsigned hashmap_hash(void *buf, size_t len) 36 { 37 return fnv_32a_buf(buf, len, FNV1_32A_INIT); 38 } 31 // type-specific functions 32 // create hash/compare functions for bitmap, outline and composite cache 33 #define CREATE_HASH_FUNCTIONS 34 #include "ass_cache_template.h" 35 #define CREATE_COMPARISON_FUNCTIONS 36 #include "ass_cache_template.h" 39 37 40 static int hashmap_key_compare(void *a, void *b, size_t size)41 {42 return memcmp(a, b, size) == 0;43 }44 45 static void hashmap_item_dtor(void *key, size_t key_size, void *value,46 size_t value_size)47 {48 free(key);49 free(value);50 }51 52 Hashmap *hashmap_init(ASS_Library *library, size_t key_size,53 size_t value_size, int nbuckets,54 HashmapItemDtor item_dtor,55 HashmapKeyCompare key_compare,56 HashmapHash hash)57 {58 Hashmap *map = calloc(1, sizeof(Hashmap));59 map->library = library;60 map->nbuckets = nbuckets;61 map->key_size = key_size;62 map->value_size = value_size;63 map->root = calloc(nbuckets, sizeof(hashmap_item_p));64 map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor;65 map->key_compare = key_compare ? key_compare : hashmap_key_compare;66 map->hash = hash ? hash : hashmap_hash;67 return map;68 }69 70 void hashmap_done(Hashmap *map)71 {72 int i;73 // print stats74 if (map->count > 0 || map->hit_count + map->miss_count > 0)75 ass_msg(map->library, MSGL_V,76 "cache statistics: \n total accesses: %d\n hits: %d\n "77 "misses: %d\n object count: %d",78 map->hit_count + map->miss_count, map->hit_count,79 map->miss_count, map->count);80 81 for (i = 0; i < map->nbuckets; ++i) {82 HashmapItem *item = map->root[i];83 while (item) {84 HashmapItem *next = item->next;85 map->item_dtor(item->key, map->key_size, item->value,86 map->value_size);87 free(item);88 item = next;89 }90 }91 free(map->root);92 free(map);93 }94 95 // does nothing if key already exists96 void *hashmap_insert(Hashmap *map, void *key, void *value)97 {98 unsigned hash = map->hash(key, map->key_size);99 HashmapItem **next = map->root + (hash % map->nbuckets);100 while (*next) {101 if (map->key_compare(key, (*next)->key, map->key_size))102 return (*next)->value;103 next = &((*next)->next);104 assert(next);105 }106 (*next) = malloc(sizeof(HashmapItem));107 (*next)->key = malloc(map->key_size);108 (*next)->value = malloc(map->value_size);109 memcpy((*next)->key, key, map->key_size);110 memcpy((*next)->value, value, map->value_size);111 (*next)->next = 0;112 113 map->count++;114 return (*next)->value;115 }116 117 void *hashmap_find(Hashmap *map, void *key)118 {119 unsigned hash = map->hash(key, map->key_size);120 HashmapItem *item = map->root[hash % map->nbuckets];121 while (item) {122 if (map->key_compare(key, item->key, map->key_size)) {123 map->hit_count++;124 return item->value;125 }126 item = item->next;127 }128 map->miss_count++;129 return 0;130 }131 132 //---------------------------------133 38 // font cache 134 135 static unsigned font_desc_hash(void *buf, size_t len) 39 static unsigned font_hash(void *buf, size_t len) 136 40 { 137 41 ASS_FontDesc *desc = buf; 138 42 unsigned hval; 139 43 hval = fnv_32a_str(desc->family, FNV1_32A_INIT); 140 44 hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); 141 45 hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval); 46 hval = fnv_32a_buf(&desc->treat_family_as_pattern, 47 sizeof(desc->treat_family_as_pattern), hval); 48 hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval); 142 49 return hval; 143 50 } 144 51 145 static intfont_compare(void *key1, void *key2, size_t key_size)52 static unsigned font_compare(void *key1, void *key2, size_t key_size) 146 53 { 147 54 ASS_FontDesc *a = key1; 148 55 ASS_FontDesc *b = key2; … … 159 66 return 1; 160 67 } 161 68 162 static void font_hash_dtor(void *key, size_t key_size, void *value, 163 size_t value_size) 69 static void font_destruct(void *key, void *value) 164 70 { 165 71 ass_font_free(value); 166 72 free(key); 167 73 } 168 74 169 ASS_Font *ass_font_cache_find(Hashmap *font_cache,170 ASS_FontDesc *desc)171 {172 return hashmap_find(font_cache, desc);173 }174 175 /**176 * \brief Add a face struct to cache.177 * \param font font struct178 */179 void *ass_font_cache_add(Hashmap *font_cache, ASS_Font *font)180 {181 return hashmap_insert(font_cache, &(font->desc), font);182 }183 184 Hashmap *ass_font_cache_init(ASS_Library *library)185 {186 Hashmap *font_cache;187 font_cache = hashmap_init(library, sizeof(ASS_FontDesc),188 sizeof(ASS_Font),189 1000,190 font_hash_dtor, font_compare, font_desc_hash);191 return font_cache;192 }193 194 void ass_font_cache_done(Hashmap *font_cache)195 {196 hashmap_done(font_cache);197 }198 199 200 // Create hash/compare functions for bitmap and glyph201 #define CREATE_HASH_FUNCTIONS202 #include "ass_cache_template.h"203 #define CREATE_COMPARISON_FUNCTIONS204 #include "ass_cache_template.h"205 206 //---------------------------------207 75 // bitmap cache 208 209 static void bitmap_hash_dtor(void *key, size_t key_size, void *value, 210 size_t value_size) 76 static void bitmap_destruct(void *key, void *value) 211 77 { 212 78 BitmapHashValue *v = value; 79 BitmapHashKey *k = key; 213 80 if (v->bm) 214 81 ass_free_bitmap(v->bm); 215 82 if (v->bm_o) 216 83 ass_free_bitmap(v->bm_o); 217 84 if (v->bm_s) 218 85 ass_free_bitmap(v->bm_s); 86 if (k->type == BITMAP_CLIP) 87 free(k->u.clip.text); 219 88 free(key); 220 89 free(value); 221 90 } 222 91 223 void *cache_add_bitmap(Hashmap *bitmap_cache, BitmapHashKey *key, 224 BitmapHashValue *val) 92 static size_t bitmap_size(void *value, size_t value_size) 225 93 { 226 // Note: this is only an approximation94 BitmapHashValue *val = value; 227 95 if (val->bm_o) 228 bitmap_cache->cache_size +=val->bm_o->w * val->bm_o->h * 3;96 return val->bm_o->w * val->bm_o->h * 3; 229 97 else if (val->bm) 230 bitmap_cache->cache_size += val->bm->w * val->bm->h * 3; 98 return val->bm->w * val->bm->h * 3; 99 return 0; 100 } 231 101 232 return hashmap_insert(bitmap_cache, key, val); 102 static unsigned bitmap_hash(void *key, size_t key_size) 103 { 104 BitmapHashKey *k = key; 105 switch (k->type) { 106 case BITMAP_OUTLINE: return outline_bitmap_hash(&k->u, key_size); 107 case BITMAP_CLIP: return clip_bitmap_hash(&k->u, key_size); 108 default: return 0; 109 } 233 110 } 234 111 235 /** 236 * \brief Get a bitmap from bitmap cache. 237 * \param key hash key 238 * \return requested hash val or 0 if not found 239 */ 240 BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache, 241 BitmapHashKey *key) 112 static unsigned bitmap_compare (void *a, void *b, size_t key_size) 242 113 { 243 return hashmap_find(bitmap_cache, key); 114 BitmapHashKey *ak = a; 115 BitmapHashKey *bk = b; 116 if (ak->type != bk->type) return 0; 117 switch (ak->type) { 118 case BITMAP_OUTLINE: return outline_bitmap_compare(&ak->u, &bk->u, key_size); 119 case BITMAP_CLIP: return clip_bitmap_compare(&ak->u, &bk->u, key_size); 120 default: return 0; 121 } 244 122 } 245 123 246 Hashmap *ass_bitmap_cache_init(ASS_Library *library) 124 // composite cache 125 static void composite_destruct(void *key, void *value) 247 126 { 248 Hashmap *bitmap_cache; 249 bitmap_cache = hashmap_init(library, 250 sizeof(BitmapHashKey), 251 sizeof(BitmapHashValue), 252 0xFFFF + 13, 253 bitmap_hash_dtor, bitmap_compare, 254 bitmap_hash); 255 return bitmap_cache; 127 CompositeHashValue *v = value; 128 free(v->a); 129 free(v->b); 130 free(key); 131 free(value); 256 132 } 257 133 258 void ass_bitmap_cache_done(Hashmap *bitmap_cache) 134 // outline cache 135 136 static unsigned outline_hash(void *key, size_t key_size) 259 137 { 260 hashmap_done(bitmap_cache); 138 OutlineHashKey *k = key; 139 switch (k->type) { 140 case OUTLINE_GLYPH: return glyph_hash(&k->u, key_size); 141 case OUTLINE_DRAWING: return drawing_hash(&k->u, key_size); 142 default: return 0; 143 } 261 144 } 262 145 263 Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache)146 static unsigned outline_compare(void *a, void *b, size_t key_size) 264 147 { 265 ASS_Library *lib = bitmap_cache->library; 266 267 ass_bitmap_cache_done(bitmap_cache); 268 return ass_bitmap_cache_init(lib); 148 OutlineHashKey *ak = a; 149 OutlineHashKey *bk = b; 150 if (ak->type != bk->type) return 0; 151 switch (ak->type) { 152 case OUTLINE_GLYPH: return glyph_compare(&ak->u, &bk->u, key_size); 153 case OUTLINE_DRAWING: return drawing_compare(&ak->u, &bk->u, key_size); 154 default: return 0; 155 } 269 156 } 270 157 271 //--------------------------------- 272 // glyph cache 273 274 static void glyph_hash_dtor(void *key, size_t key_size, void *value, 275 size_t value_size) 158 static void outline_destruct(void *key, void *value) 276 159 { 277 GlyphHashValue *v = value; 278 if (v->glyph) 279 FT_Done_Glyph(v->glyph); 280 if (v->outline_glyph) 281 FT_Done_Glyph(v->outline_glyph); 160 OutlineHashValue *v = value; 161 OutlineHashKey *k = key; 162 if (v->outline) 163 outline_free(v->lib, v->outline); 164 if (v->border) 165 outline_free(v->lib, v->border); 166 if (k->type == OUTLINE_DRAWING) 167 free(k->u.drawing.text); 282 168 free(key); 283 169 free(value); 284 170 } 285 171 286 void *cache_add_glyph(Hashmap *glyph_cache, GlyphHashKey *key, 287 GlyphHashValue *val) 172 173 174 // Cache data 175 typedef struct cache_item { 176 void *key; 177 void *value; 178 struct cache_item *next; 179 } CacheItem; 180 181 struct cache { 182 unsigned buckets; 183 CacheItem **map; 184 185 HashFunction hash_func; 186 ItemSize size_func; 187 HashCompare compare_func; 188 CacheItemDestructor destruct_func; 189 size_t key_size; 190 size_t value_size; 191 192 size_t cache_size; 193 unsigned hits; 194 unsigned misses; 195 unsigned items; 196 }; 197 198 // Hash for a simple (single value or array) type 199 static unsigned hash_simple(void *key, size_t key_size) 288 200 { 289 if (val->glyph && val->glyph->format == FT_GLYPH_FORMAT_BITMAP) { 290 FT_Bitmap *bitmap = &((FT_BitmapGlyph) val->glyph)->bitmap; 291 glyph_cache->cache_size += bitmap->rows * bitmap->pitch; 292 } 293 294 return hashmap_insert(glyph_cache, key, val); 201 return fnv_32a_buf(key, key_size, FNV1_32A_INIT); 295 202 } 296 203 297 /** 298 * \brief Get a glyph from glyph cache. 299 * \param key hash key 300 * \return requested hash val or 0 if not found 301 */ 302 GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, 303 GlyphHashKey *key) 204 // Comparison of a simple type 205 static unsigned compare_simple(void *a, void *b, size_t key_size) 304 206 { 305 return hashmap_find(glyph_cache, key);207 return memcmp(a, b, key_size) == 0; 306 208 } 307 209 308 Hashmap *ass_glyph_cache_init(ASS_Library *library) 210 // Default destructor 211 static void destruct_simple(void *key, void *value) 309 212 { 310 Hashmap *glyph_cache; 311 glyph_cache = hashmap_init(library, sizeof(GlyphHashKey), 312 sizeof(GlyphHashValue), 313 0xFFFF + 13, 314 glyph_hash_dtor, glyph_compare, glyph_hash); 315 return glyph_cache; 213 free(key); 214 free(value); 316 215 } 317 216 318 void ass_glyph_cache_done(Hashmap *glyph_cache) 217 218 // Create a cache with type-specific hash/compare/destruct/size functions 219 Cache *ass_cache_create(HashFunction hash_func, HashCompare compare_func, 220 CacheItemDestructor destruct_func, ItemSize size_func, 221 size_t key_size, size_t value_size) 319 222 { 320 hashmap_done(glyph_cache); 223 Cache *cache = calloc(1, sizeof(*cache)); 224 cache->buckets = 0xFFFF; 225 cache->hash_func = hash_simple; 226 cache->compare_func = compare_simple; 227 cache->destruct_func = destruct_simple; 228 cache->size_func = size_func; 229 if (hash_func) 230 cache->hash_func = hash_func; 231 if (compare_func) 232 cache->compare_func = compare_func; 233 if (destruct_func) 234 cache->destruct_func = destruct_func; 235 cache->key_size = key_size; 236 cache->value_size = value_size; 237 cache->map = calloc(cache->buckets, sizeof(CacheItem *)); 238 239 return cache; 321 240 } 322 241 323 Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache)242 void *ass_cache_put(Cache *cache, void *key, void *value) 324 243 { 325 ASS_Library *lib = glyph_cache->library; 244 unsigned bucket = cache->hash_func(key, cache->key_size) % cache->buckets; 245 CacheItem **item = &cache->map[bucket]; 246 while (*item) 247 item = &(*item)->next; 248 (*item) = calloc(1, sizeof(CacheItem)); 249 (*item)->key = malloc(cache->key_size); 250 (*item)->value = malloc(cache->value_size); 251 memcpy((*item)->key, key, cache->key_size); 252 memcpy((*item)->value, value, cache->value_size); 326 253 327 ass_glyph_cache_done(glyph_cache); 328 return ass_glyph_cache_init(lib); 254 cache->items++; 255 if (cache->size_func) 256 cache->cache_size += cache->size_func(value, cache->value_size); 257 else 258 cache->cache_size++; 259 260 return (*item)->value; 329 261 } 330 262 263 void *ass_cache_get(Cache *cache, void *key) 264 { 265 unsigned bucket = cache->hash_func(key, cache->key_size) % cache->buckets; 266 CacheItem *item = cache->map[bucket]; 267 while (item) { 268 if (cache->compare_func(key, item->key, cache->key_size)) { 269 cache->hits++; 270 return item->value; 271 } 272 item = item->next; 273 } 274 cache->misses++; 275 return NULL; 276 } 331 277 332 //--------------------------------- 333 // composite cache 278 int ass_cache_empty(Cache *cache, size_t max_size) 279 { 280 int i; 334 281 335 static void composite_hash_dtor(void *key, size_t key_size, void *value, 336 size_t value_size) 282 if (cache->cache_size < max_size) 283 return 0; 284 285 for (i = 0; i < cache->buckets; i++) { 286 CacheItem *item = cache->map[i]; 287 while (item) { 288 CacheItem *next = item->next; 289 cache->destruct_func(item->key, item->value); 290 free(item); 291 item = next; 292 } 293 cache->map[i] = NULL; 294 } 295 296 cache->items = cache->hits = cache->misses = cache->cache_size = 0; 297 298 return 1; 299 } 300 301 void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits, 302 unsigned *misses, unsigned *count) 337 303 { 338 CompositeHashValue *v = value; 339 free(v->a); 340 free(v->b); 341 free(key); 342 free(value); 304 if (size) 305 *size = cache->cache_size; 306 if (hits) 307 *hits = cache->hits; 308 if (misses) 309 *misses = cache->misses; 310 if (count) 311 *count = cache->items; 343 312 } 344 313 345 void *cache_add_composite(Hashmap *composite_cache, 346 CompositeHashKey *key, 347 CompositeHashValue *val) 314 void ass_cache_done(Cache *cache) 348 315 { 349 return hashmap_insert(composite_cache, key, val); 316 ass_cache_empty(cache, 0); 317 free(cache->map); 318 free(cache); 350 319 } 351 320 352 /** 353 * \brief Get a composite bitmap from composite cache. 354 * \param key hash key 355 * \return requested hash val or 0 if not found 356 */ 357 CompositeHashValue *cache_find_composite(Hashmap *composite_cache, 358 CompositeHashKey *key) 321 // Type-specific creation function 322 Cache *ass_font_cache_create(void) 359 323 { 360 return hashmap_find(composite_cache, key); 324 return ass_cache_create(font_hash, font_compare, font_destruct, 325 (ItemSize)NULL, sizeof(ASS_FontDesc), sizeof(ASS_Font)); 361 326 } 362 327 363 Hashmap *ass_composite_cache_init(ASS_Library *library)328 Cache *ass_outline_cache_create(void) 364 329 { 365 Hashmap *composite_cache; 366 composite_cache = hashmap_init(library, sizeof(CompositeHashKey), 367 sizeof(CompositeHashValue), 368 0xFFFF + 13, 369 composite_hash_dtor, composite_compare, 370 composite_hash); 371 return composite_cache; 330 return ass_cache_create(outline_hash, outline_compare, outline_destruct, 331 NULL, sizeof(OutlineHashKey), sizeof(OutlineHashValue)); 372 332 } 373 333 374 void ass_composite_cache_done(Hashmap *composite_cache)334 Cache *ass_glyph_metrics_cache_create(void) 375 335 { 376 hashmap_done(composite_cache); 336 return ass_cache_create(glyph_metrics_hash, glyph_metrics_compare, NULL, 337 (ItemSize) NULL, sizeof(GlyphMetricsHashKey), 338 sizeof(GlyphMetricsHashValue)); 377 339 } 378 340 379 Hashmap *ass_composite_cache_reset(Hashmap *composite_cache)341 Cache *ass_bitmap_cache_create(void) 380 342 { 381 ASS_Library *lib = composite_cache->library; 343 return ass_cache_create(bitmap_hash, bitmap_compare, bitmap_destruct, 344 bitmap_size, sizeof(BitmapHashKey), sizeof(BitmapHashValue)); 345 } 382 346 383 ass_composite_cache_done(composite_cache); 384 return ass_composite_cache_init(lib); 347 Cache *ass_composite_cache_create(void) 348 { 349 return ass_cache_create(composite_hash, composite_compare, 350 composite_destruct, (ItemSize)NULL, sizeof(CompositeHashKey), 351 sizeof(CompositeHashValue)); 385 352 } -
libass/ass_cache.h
1 1 /* 2 2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> 3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> 3 4 * 4 5 * This file is part of libass. 5 6 * … … 23 24 #include "ass_font.h" 24 25 #include "ass_bitmap.h" 25 26 26 typedef void (*HashmapItemDtor) (void *key, size_t key_size, 27 void *value, size_t value_size); 28 typedef int (*HashmapKeyCompare) (void *key1, void *key2, 29 size_t key_size); 30 typedef unsigned (*HashmapHash) (void *key, size_t key_size); 27 typedef struct cache Cache; 31 28 32 typedef struct hashmap_item { 33 void *key; 34 void *value; 35 struct hashmap_item *next; 36 } HashmapItem; 37 typedef HashmapItem *hashmap_item_p; 29 // cache values 38 30 39 31 typedef struct { 40 int nbuckets;41 size_t key_size, value_size;42 hashmap_item_p *root;43 HashmapItemDtor item_dtor; // a destructor for hashmap key/value pairs44 HashmapKeyCompare key_compare;45 HashmapHash hash;46 size_t cache_size;47 // stats48 int hit_count;49 int miss_count;50 int count;51 ASS_Library *library;52 } Hashmap;53 54 Hashmap *hashmap_init(ASS_Library *library, size_t key_size,55 size_t value_size, int nbuckets,56 HashmapItemDtor item_dtor,57 HashmapKeyCompare key_compare,58 HashmapHash hash);59 void hashmap_done(Hashmap *map);60 void *hashmap_insert(Hashmap *map, void *key, void *value);61 void *hashmap_find(Hashmap *map, void *key);62 63 Hashmap *ass_font_cache_init(ASS_Library *library);64 ASS_Font *ass_font_cache_find(Hashmap *, ASS_FontDesc *desc);65 void *ass_font_cache_add(Hashmap *, ASS_Font *font);66 void ass_font_cache_done(Hashmap *);67 68 // Create definitions for bitmap_hash_key and glyph_hash_key69 #define CREATE_STRUCT_DEFINITIONS70 #include "ass_cache_template.h"71 72 typedef struct {73 32 Bitmap *bm; // the actual bitmaps 74 33 Bitmap *bm_o; 75 34 Bitmap *bm_s; 76 35 } BitmapHashValue; 77 36 78 Hashmap *ass_bitmap_cache_init(ASS_Library *library);79 void *cache_add_bitmap(Hashmap *, BitmapHashKey *key,80 BitmapHashValue *val);81 BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache,82 BitmapHashKey *key);83 Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache);84 void ass_bitmap_cache_done(Hashmap *bitmap_cache);85 86 87 37 typedef struct { 88 38 unsigned char *a; 89 39 unsigned char *b; 90 40 } CompositeHashValue; 91 41 92 Hashmap *ass_composite_cache_init(ASS_Library *library);93 void *cache_add_composite(Hashmap *, CompositeHashKey *key,94 CompositeHashValue *val);95 CompositeHashValue *cache_find_composite(Hashmap *composite_cache,96 CompositeHashKey *key);97 Hashmap *ass_composite_cache_reset(Hashmap *composite_cache);98 void ass_composite_cache_done(Hashmap *composite_cache);99 100 101 42 typedef struct { 102 FT_Glyph glyph; 103 FT_Glyph outline_glyph; 43 FT_Library lib; 44 FT_Outline *outline; 45 FT_Outline *border; 104 46 FT_BBox bbox_scaled; // bbox after scaling, but before rotation 105 FT_Vector advance; // 26.6, advance distance to the next bitmapin line106 int asc, desc; // ascender/descender of a drawing107 } GlyphHashValue;47 FT_Vector advance; // 26.6, advance distance to the next outline in line 48 int asc, desc; // ascender/descender 49 } OutlineHashValue; 108 50 109 Hashmap *ass_glyph_cache_init(ASS_Library *library); 110 void *cache_add_glyph(Hashmap *, GlyphHashKey *key, 111 GlyphHashValue *val); 112 GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, 113 GlyphHashKey *key); 114 Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache); 115 void ass_glyph_cache_done(Hashmap *glyph_cache); 51 typedef struct { 52 FT_Glyph_Metrics metrics; 53 } GlyphMetricsHashValue; 116 54 55 // Create definitions for bitmap, outline and composite hash keys 56 #define CREATE_STRUCT_DEFINITIONS 57 #include "ass_cache_template.h" 58 59 // Type-specific function pointers 60 typedef unsigned(*HashFunction)(void *key, size_t key_size); 61 typedef size_t(*ItemSize)(void *value, size_t value_size); 62 typedef unsigned(*HashCompare)(void *a, void *b, size_t key_size); 63 typedef void(*CacheItemDestructor)(void *key, void *value); 64 65 // cache hash keys 66 67 typedef struct outline_hash_key { 68 enum { 69 OUTLINE_GLYPH, 70 OUTLINE_DRAWING, 71 } type; 72 union { 73 GlyphHashKey glyph; 74 DrawingHashKey drawing; 75 } u; 76 } OutlineHashKey; 77 78 typedef struct bitmap_hash_key { 79 enum { 80 BITMAP_OUTLINE, 81 BITMAP_CLIP, 82 } type; 83 union { 84 OutlineBitmapHashKey outline; 85 ClipMaskHashKey clip; 86 } u; 87 } BitmapHashKey; 88 89 Cache *ass_cache_create(HashFunction hash_func, HashCompare compare_func, 90 CacheItemDestructor destruct_func, ItemSize size_func, 91 size_t key_size, size_t value_size); 92 void *ass_cache_put(Cache *cache, void *key, void *value); 93 void *ass_cache_get(Cache *cache, void *key); 94 int ass_cache_empty(Cache *cache, size_t max_size); 95 void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits, 96 unsigned *misses, unsigned *count); 97 void ass_cache_done(Cache *cache); 98 Cache *ass_font_cache_create(void); 99 Cache *ass_outline_cache_create(void); 100 Cache *ass_glyph_metrics_cache_create(void); 101 Cache *ass_bitmap_cache_create(void); 102 Cache *ass_composite_cache_create(void); 103 117 104 #endif /* LIBASS_CACHE_H */ -
libass/ass_cache_template.h
4 4 typedef struct structname { 5 5 #define GENERIC(type, member) \ 6 6 type member; 7 #define STRING(member) \ 8 char *member; 7 9 #define FTVECTOR(member) \ 8 10 FT_Vector member; 9 11 #define BITMAPHASHKEY(member) \ … … 14 16 #elif defined(CREATE_COMPARISON_FUNCTIONS) 15 17 #undef CREATE_COMPARISON_FUNCTIONS 16 18 #define START(funcname, structname) \ 17 static intfuncname##_compare(void *key1, void *key2, size_t key_size) \19 static unsigned funcname##_compare(void *key1, void *key2, size_t key_size) \ 18 20 { \ 19 21 struct structname *a = key1; \ 20 22 struct structname *b = key2; \ 21 23 return // conditions follow 22 24 #define GENERIC(type, member) \ 23 25 a->member == b->member && 26 #define STRING(member) \ 27 strcmp(a->member, b->member) == 0 && 24 28 #define FTVECTOR(member) \ 25 29 a->member.x == b->member.x && a->member.y == b->member.y && 26 30 #define BITMAPHASHKEY(member) \ … … 38 42 unsigned hval = FNV1_32A_INIT; 39 43 #define GENERIC(type, member) \ 40 44 hval = fnv_32a_buf(&p->member, sizeof(p->member), hval); 45 #define STRING(member) \ 46 hval = fnv_32a_str(p->member, hval); 41 47 #define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y); 42 48 #define BITMAPHASHKEY(member) { \ 43 49 unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \ … … 53 59 54 60 55 61 56 // describes a bitmap; bitmaps with equivalents structs are considered identical 57 START(bitmap, bitmap_hash_key) 58 GENERIC(char, bitmap) // bool : true = bitmap, false = outline 59 GENERIC(ASS_Font *, font) 60 GENERIC(double, size) // font size 61 GENERIC(uint32_t, ch) // character code 62 FTVECTOR(outline) // border width, 16.16 fixed point value 63 GENERIC(int, bold) 64 GENERIC(int, italic) 62 // describes an outline bitmap 63 START(outline_bitmap, outline_bitmap_hash_key) 64 GENERIC(OutlineHashValue *, outline) 65 65 GENERIC(char, be) // blur edges 66 66 GENERIC(double, blur) // gaussian blur 67 GENERIC(unsigned, scale_x) // 16.1668 GENERIC(unsigned, scale_y) // 16.1669 67 GENERIC(int, frx) // signed 16.16 70 68 GENERIC(int, fry) // signed 16.16 71 69 GENERIC(int, frz) // signed 16.16 … … 78 76 GENERIC(int, shift_y) 79 77 FTVECTOR(advance) // subpixel shift vector 80 78 FTVECTOR(shadow_offset) // shadow subpixel shift 81 GENERIC(unsigned, drawing_hash) // hashcode of a drawing 82 GENERIC(unsigned, flags) // glyph decoration 83 GENERIC(unsigned, border_style) 84 END(BitmapHashKey) 79 END(OutlineBitmapHashKey) 85 80 81 // describe a clip mask bitmap 82 START(clip_bitmap, clip_bitmap_hash_key) 83 STRING(text) 84 END(ClipMaskHashKey) 85 86 86 // describes an outline glyph 87 87 START(glyph, glyph_hash_key) 88 88 GENERIC(ASS_Font *, font) 89 89 GENERIC(double, size) // font size 90 GENERIC(uint32_t, ch) // character code 90 GENERIC(int, face_index) 91 GENERIC(int, glyph_index) 91 92 GENERIC(int, bold) 92 93 GENERIC(int, italic) 93 94 GENERIC(unsigned, scale_x) // 16.16 94 95 GENERIC(unsigned, scale_y) // 16.16 95 96 FTVECTOR(outline) // border width, 16.16 96 GENERIC(unsigned, drawing_hash) // hashcode of a drawing97 97 GENERIC(unsigned, flags) // glyph decoration flags 98 98 GENERIC(unsigned, border_style) 99 99 END(GlyphHashKey) 100 100 101 START(glyph_metrics, glyph_metrics_hash_key) 102 GENERIC(ASS_Font *, font) 103 GENERIC(double, size) 104 GENERIC(int, face_index) 105 GENERIC(int, glyph_index) 106 GENERIC(unsigned, scale_x) 107 GENERIC(unsigned, scale_y) 108 END(GlyphMetricsHashKey) 109 110 // describes an outline drawing 111 START(drawing, drawing_hash_key) 112 GENERIC(unsigned, scale_x) 113 GENERIC(unsigned, scale_y) 114 GENERIC(int, pbo) 115 FTVECTOR(outline) 116 GENERIC(unsigned, border_style) 117 GENERIC(int, scale) 118 GENERIC(unsigned, hash) 119 STRING(text) 120 END(DrawingHashKey) 121 101 122 // Cache for composited bitmaps 102 123 START(composite, composite_hash_key) 103 124 GENERIC(int, aw) … … 117 138 118 139 #undef START 119 140 #undef GENERIC 141 #undef STRING 120 142 #undef FTVECTOR 121 143 #undef BITMAPHASHKEY 122 144 #undef END -
libass/ass_drawing.c
17 17 */ 18 18 19 19 #include <ft2build.h> 20 #include FT_GLYPH_H21 20 #include FT_OUTLINE_H 22 21 #include FT_BBOX_H 23 22 #include <math.h> 24 23 25 24 #include "ass_utils.h" 26 #include "ass_font.h"27 25 #include "ass_drawing.h" 28 26 29 27 #define CURVE_ACCURACY 64.0 … … 31 29 #define GLYPH_INITIAL_CONTOURS 5 32 30 33 31 /* 34 * \brief Get and prepare a FreeType glyph35 */36 static void drawing_make_glyph(ASS_Drawing *drawing, void *fontconfig_priv,37 ASS_Font *font)38 {39 FT_OutlineGlyph glyph;40 41 // This is hacky...42 glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font,43 (uint32_t) ' ', 0, 0);44 if (glyph) {45 FT_Outline_Done(drawing->ftlibrary, &glyph->outline);46 FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS,47 GLYPH_INITIAL_CONTOURS, &glyph->outline);48 49 glyph->outline.n_contours = 0;50 glyph->outline.n_points = 0;51 glyph->root.advance.x = glyph->root.advance.y = 0;52 }53 drawing->glyph = glyph;54 }55 56 /*57 32 * \brief Add a single point to a contour. 58 33 */ 59 34 static inline void drawing_add_point(ASS_Drawing *drawing, 60 35 FT_Vector *point) 61 36 { 62 FT_Outline *ol = &drawing-> glyph->outline;37 FT_Outline *ol = &drawing->outline; 63 38 64 39 if (ol->n_points >= drawing->max_points) { 65 40 drawing->max_points *= 2; … … 75 50 } 76 51 77 52 /* 78 * \brief Close a contour and check glyphsize overflow.53 * \brief Close a contour and check outline size overflow. 79 54 */ 80 55 static inline void drawing_close_shape(ASS_Drawing *drawing) 81 56 { 82 FT_Outline *ol = &drawing-> glyph->outline;57 FT_Outline *ol = &drawing->outline; 83 58 84 59 if (ol->n_contours >= drawing->max_contours) { 85 60 drawing->max_contours *= 2; … … 107 82 108 83 /* 109 84 * \brief Finish a drawing. This only sets the horizontal advance according 110 * to the glyph's bbox at the moment.85 * to the outline's bbox at the moment. 111 86 */ 112 87 static void drawing_finish(ASS_Drawing *drawing, int raw_mode) 113 88 { 114 89 int i, offset; 115 90 FT_BBox bbox = drawing->cbox; 116 FT_Outline *ol = &drawing-> glyph->outline;91 FT_Outline *ol = &drawing->outline; 117 92 118 93 // Close the last contour 119 94 drawing_close_shape(drawing); … … 126 101 if (raw_mode) 127 102 return; 128 103 129 drawing-> glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin);104 drawing->advance.x = bbox.xMax - bbox.xMin; 130 105 131 106 drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y); 132 107 drawing->asc = bbox.yMax - bbox.yMin + drawing->desc; … … 355 330 /* 356 331 * \brief Create and initialize a new drawing and return it 357 332 */ 358 ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font, 359 FT_Library lib) 333 ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib) 360 334 { 361 335 ASS_Drawing *drawing; 362 336 … … 365 339 drawing->size = DRAWING_INITIAL_SIZE; 366 340 drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX; 367 341 drawing->cbox.xMax = drawing->cbox.yMax = INT_MIN; 368 drawing->fontconfig_priv = fontconfig_priv; 369 drawing->font = font; 370 drawing->ftlibrary = lib; 371 if (font) 372 drawing->library = font->library; 373 342 drawing->ftlibrary = ftlib; 343 drawing->library = lib; 374 344 drawing->scale_x = 1.; 375 345 drawing->scale_y = 1.; 376 346 drawing->max_contours = GLYPH_INITIAL_CONTOURS; 377 347 drawing->max_points = GLYPH_INITIAL_POINTS; 378 348 349 FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS, 350 GLYPH_INITIAL_CONTOURS, &drawing->outline); 351 drawing->outline.n_contours = 0; 352 drawing->outline.n_points = 0; 353 379 354 return drawing; 380 355 } 381 356 … … 386 361 { 387 362 if (drawing) { 388 363 free(drawing->text); 364 FT_Outline_Done(drawing->ftlibrary, &drawing->outline); 389 365 } 390 366 free(drawing); 391 367 } … … 416 392 /* 417 393 * \brief Convert token list to outline. Calls the line and curve evaluators. 418 394 */ 419 FT_Outline Glyph*ass_drawing_parse(ASS_Drawing *drawing, int raw_mode)395 FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) 420 396 { 421 397 int started = 0; 422 398 ASS_DrawingToken *token; 423 399 FT_Vector pen = {0, 0}; 424 400 425 if (drawing->font)426 drawing_make_glyph(drawing, drawing->fontconfig_priv, drawing->font);427 if (!drawing->glyph)428 return NULL;429 430 401 drawing->tokens = drawing_tokenize(drawing->text); 431 402 drawing_prepare(drawing); 432 403 … … 486 457 487 458 drawing_finish(drawing, raw_mode); 488 459 drawing_free_tokens(drawing->tokens); 489 return &drawing-> glyph;460 return &drawing->outline; 490 461 } -
libass/ass_drawing.h
20 20 #define LIBASS_DRAWING_H 21 21 22 22 #include <ft2build.h> 23 #include FT_ GLYPH_H23 #include FT_OUTLINE_H 24 24 25 25 #include "ass.h" 26 26 … … 53 53 double scale_y; // FontScaleY 54 54 int asc; // ascender 55 55 int desc; // descender 56 FT_OutlineGlyph glyph; // the "fake" glyph created for later rendering 56 FT_Outline outline; // target outline 57 FT_Vector advance; // advance (from cbox) 57 58 int hash; // hash value (for caching) 58 59 59 60 // private 60 61 FT_Library ftlibrary; // needed for font ops 61 ASS_Font *font; // dito62 void *fontconfig_priv; // dito63 62 ASS_Library *library; 64 63 int size; // current buffer size 65 64 ASS_DrawingToken *tokens; // tokenized drawing … … 70 69 FT_BBox cbox; // bounding box, or let's say... VSFilter's idea of it 71 70 } ASS_Drawing; 72 71 73 ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font, 74 FT_Library lib); 72 ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib); 75 73 void ass_drawing_free(ASS_Drawing* drawing); 76 74 void ass_drawing_add_char(ASS_Drawing* drawing, char symbol); 77 75 void ass_drawing_hash(ASS_Drawing* drawing); 78 FT_Outline Glyph*ass_drawing_parse(ASS_Drawing *drawing, int raw_mode);76 FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode); 79 77 80 78 #endif /* LIBASS_DRAWING_H */ -
libass/ass_font.c
30 30 #include "ass.h" 31 31 #include "ass_library.h" 32 32 #include "ass_font.h" 33 #include "ass_bitmap.h"34 #include "ass_cache.h"35 33 #include "ass_fontconfig.h" 36 34 #include "ass_utils.h" 35 #include "ass_shaper.h" 37 36 38 #define VERTICAL_LOWER_BOUND 0x02f139 40 37 /** 41 38 * Select a good charmap, prefer Microsoft Unicode charmaps. 42 39 * Otherwise, let FreeType decide. … … 91 88 return -1; 92 89 } 93 90 94 static void face_set_size(FT_Face face, double size);95 96 91 static void buggy_font_workaround(FT_Face face) 97 92 { 98 93 // Some fonts have zero Ascender/Descender fields in 'hhea' table. … … 161 156 buggy_font_workaround(face); 162 157 163 158 font->faces[font->n_faces++] = face; 164 face_set_size(face, font->size);159 ass_face_set_size(face, font->size); 165 160 free(path); 166 161 return font->n_faces - 1; 167 162 } … … 169 164 /** 170 165 * \brief Create a new ASS_Font according to "desc" argument 171 166 */ 172 ASS_Font *ass_font_new( void*font_cache, ASS_Library *library,167 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, 173 168 FT_Library ftlibrary, void *fc_priv, 174 169 ASS_FontDesc *desc) 175 170 { … … 177 172 ASS_Font *fontp; 178 173 ASS_Font font; 179 174 180 fontp = ass_ font_cache_find((Hashmap *)font_cache, desc);175 fontp = ass_cache_get(font_cache, desc); 181 176 if (fontp) 182 177 return fontp; 183 178 184 179 font.library = library; 185 180 font.ftlibrary = ftlibrary; 181 font.shaper_priv = NULL; 186 182 font.n_faces = 0; 187 183 font.desc.family = strdup(desc->family); 188 184 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; … … 199 195 free(font.desc.family); 200 196 return 0; 201 197 } else 202 return ass_ font_cache_add((Hashmap *) font_cache, &font);198 return ass_cache_put(font_cache, &font.desc, &font); 203 199 } 204 200 205 201 /** … … 216 212 } 217 213 } 218 214 219 static voidface_set_size(FT_Face face, double size)215 void ass_face_set_size(FT_Face face, double size) 220 216 { 221 217 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); 222 218 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); … … 251 247 if (font->size != size) { 252 248 font->size = size; 253 249 for (i = 0; i < font->n_faces; ++i) 254 face_set_size(font->faces[i], size);250 ass_face_set_size(font->faces[i], size); 255 251 } 256 252 } 257 253 … … 276 272 *asc = FT_MulFix(face->ascender, y_scale); 277 273 *desc = FT_MulFix(-face->descender, y_scale); 278 274 } 279 if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) {280 *asc = FT_MulFix(face->max_advance_width, y_scale);281 }282 275 return; 283 276 } 284 277 } … … 388 381 return 0; 389 382 } 390 383 384 void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest) 385 { 386 if (source == NULL) { 387 *dest = NULL; 388 return; 389 } 390 *dest = calloc(1, sizeof(**dest)); 391 392 FT_Outline_New(lib, source->n_points, source->n_contours, *dest); 393 FT_Outline_Copy(source, *dest); 394 } 395 396 void outline_free(FT_Library lib, FT_Outline *outline) 397 { 398 if (outline) 399 FT_Outline_Done(lib, outline); 400 free(outline); 401 } 402 391 403 /** 392 404 * Slightly embold a glyph without touching its metrics 393 405 */ … … 405 417 } 406 418 407 419 /** 408 * \brief Get a glyph 409 * \param ch character code 410 **/ 411 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, 412 uint32_t ch, ASS_Hinting hinting, int deco) 420 * \brief Get glyph and face index 421 * Finds a face that has the requested codepoint and returns both face 422 * and glyph index. 423 */ 424 int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol, 425 int *face_index, int *glyph_index) 413 426 { 414 int error;415 427 int index = 0; 416 428 int i; 417 FT_Glyph glyph;418 429 FT_Face face = 0; 419 int flags = 0;420 int vertical = font->desc.vertical;421 430 422 if (ch < 0x20) 431 *glyph_index = 0; 432 433 if (symbol < 0x20) { 434 *face_index = 0; 423 435 return 0; 436 } 424 437 // Handle NBSP like a regular space when rendering the glyph 425 if (ch == 0xa0) 426 ch = ' '; 427 if (font->n_faces == 0) 438 if (symbol == 0xa0) 439 symbol = ' '; 440 if (font->n_faces == 0) { 441 *face_index = 0; 428 442 return 0; 443 } 429 444 430 for (i = 0; i < font->n_faces; ++i) { 445 // try with the requested face 446 if (*face_index < font->n_faces) { 447 face = font->faces[*face_index]; 448 index = FT_Get_Char_Index(face, symbol); 449 } 450 451 // not found in requested face, try all others 452 for (i = 0; i < font->n_faces && index == 0; ++i) { 431 453 face = font->faces[i]; 432 index = FT_Get_Char_Index(face, ch);454 index = FT_Get_Char_Index(face, symbol); 433 455 if (index) 434 break;456 *face_index = i; 435 457 } 436 458 437 459 #ifdef CONFIG_FONTCONFIG … … 439 461 int face_idx; 440 462 ass_msg(font->library, MSGL_INFO, 441 463 "Glyph 0x%X not found, selecting one more " 442 "font for (%s, %d, %d)", ch, font->desc.family,464 "font for (%s, %d, %d)", symbol, font->desc.family, 443 465 font->desc.bold, font->desc.italic); 444 face_idx = add_face(fontconfig_priv, font, ch);466 face_idx = *face_index = add_face(fcpriv, font, symbol); 445 467 if (face_idx >= 0) { 446 468 face = font->faces[face_idx]; 447 index = FT_Get_Char_Index(face, ch);469 index = FT_Get_Char_Index(face, symbol); 448 470 if (index == 0 && face->num_charmaps > 0) { 449 471 int i; 450 472 ass_msg(font->library, MSGL_WARN, 451 "Glyph 0x%X not found, broken font? Trying all charmaps", ch);473 "Glyph 0x%X not found, broken font? Trying all charmaps", symbol); 452 474 for (i = 0; i < face->num_charmaps; i++) { 453 475 FT_Set_Charmap(face, face->charmaps[i]); 454 if ((index = FT_Get_Char_Index(face, ch)) != 0) break;476 if ((index = FT_Get_Char_Index(face, symbol)) != 0) break; 455 477 } 456 478 } 457 479 if (index == 0) { 458 480 ass_msg(font->library, MSGL_ERR, 459 481 "Glyph 0x%X not found in font for (%s, %d, %d)", 460 ch, font->desc.family, font->desc.bold,482 symbol, font->desc.family, font->desc.bold, 461 483 font->desc.italic); 462 484 } 463 485 } 464 486 } 465 487 #endif 488 // FIXME: make sure we have a valid face_index. this is a HACK. 489 *face_index = FFMAX(*face_index, 0); 490 *glyph_index = index; 466 491 492 return 1; 493 } 494 495 /** 496 * \brief Get a glyph 497 * \param ch character code 498 **/ 499 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, 500 uint32_t ch, int face_index, int index, 501 ASS_Hinting hinting, int deco) 502 { 503 int error; 504 FT_Glyph glyph; 505 FT_Face face = font->faces[face_index]; 506 int flags = 0; 507 int vertical = font->desc.vertical; 508 467 509 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH 468 510 | FT_LOAD_IGNORE_TRANSFORM; 469 511 switch (hinting) { … … 505 547 // Rotate glyph, if needed 506 548 if (vertical && ch >= VERTICAL_LOWER_BOUND) { 507 549 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; 550 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); 551 int desc = 0; 552 553 if (os2) 554 desc = FT_MulFix(os2->sTypoDescender, face->size->metrics.y_scale); 555 556 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 0, -desc); 508 557 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); 509 558 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 510 face->glyph->metrics.vertAdvance, 511 0); 559 face->glyph->metrics.vertAdvance, desc); 512 560 glyph->advance.x = face->glyph->linearVertAdvance; 513 561 } 514 562 … … 561 609 for (i = 0; i < font->n_faces; ++i) 562 610 if (font->faces[i]) 563 611 FT_Done_Face(font->faces[i]); 612 if (font->shaper_priv) 613 ass_shaper_font_data_free(font->shaper_priv); 564 614 free(font->desc.family); 565 615 free(font); 566 616 } … … 618 668 * \param border_x border size, x direction, d6 format 619 669 * \param border_x border size, y direction, d6 format 620 670 */ 621 void fix_freetype_stroker(FT_Outline Glyph glyph, int border_x, int border_y)671 void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y) 622 672 { 623 int nc = glyph->outline.n_contours;673 int nc = outline->n_contours; 624 674 int begin, stop; 625 675 char modified = 0; 626 676 char *valid_cont = malloc(nc); … … 630 680 int i, j; 631 681 int inside_direction; 632 682 633 inside_direction = FT_Outline_Get_Orientation( &glyph->outline) ==683 inside_direction = FT_Outline_Get_Orientation(outline) == 634 684 FT_ORIENTATION_TRUETYPE; 635 685 636 686 // create a list of cboxes of the contours 637 687 for (i = 0; i < nc; i++) { 638 688 start = end + 1; 639 end = glyph->outline.contours[i];640 get_contour_cbox(&boxes[i], glyph->outline.points, start, end);689 end = outline->contours[i]; 690 get_contour_cbox(&boxes[i], outline->points, start, end); 641 691 } 642 692 643 693 // for each contour, check direction and whether it's "outside" … … 645 695 end = -1; 646 696 for (i = 0; i < nc; i++) { 647 697 start = end + 1; 648 end = glyph->outline.contours[i];649 int dir = get_contour_direction( glyph->outline.points, start, end);698 end = outline->contours[i]; 699 int dir = get_contour_direction(outline->points, start, end); 650 700 valid_cont[i] = 1; 651 701 if (dir == inside_direction) { 652 702 for (j = 0; j < nc; j++) { … … 662 712 * inside of - assume the font is buggy and it should be 663 713 * an "outside" contour, and reverse it */ 664 714 for (j = 0; j < (end + 1 - start) / 2; j++) { 665 FT_Vector temp = glyph->outline.points[start + j];666 char temp2 = glyph->outline.tags[start + j];667 glyph->outline.points[start + j] = glyph->outline.points[end - j];668 glyph->outline.points[end - j] = temp;669 glyph->outline.tags[start + j] = glyph->outline.tags[end - j];670 glyph->outline.tags[end - j] = temp2;715 FT_Vector temp = outline->points[start + j]; 716 char temp2 = outline->tags[start + j]; 717 outline->points[start + j] = outline->points[end - j]; 718 outline->points[end - j] = temp; 719 outline->tags[start + j] = outline->tags[end - j]; 720 outline->tags[end - j] = temp2; 671 721 } 672 722 dir ^= 1; 673 723 } 674 724 check_inside: 675 725 if (dir == inside_direction) { 676 726 FT_BBox box; 677 get_contour_cbox(&box, glyph->outline.points, start, end);727 get_contour_cbox(&box, outline->points, start, end); 678 728 int width = box.xMax - box.xMin; 679 729 int height = box.yMax - box.yMin; 680 730 if (width < border_x * 2 || height < border_y * 2) { … … 687 737 // if we need to modify the outline, rewrite it and skip 688 738 // the contours that we determined should be removed. 689 739 if (modified) { 690 FT_Outline *outline = &glyph->outline;691 740 int p = 0, c = 0; 692 741 for (i = 0; i < nc; i++) { 693 742 if (!valid_cont[i]) 694 743 continue; 695 begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1;696 stop = glyph->outline.contours[i];744 begin = (i == 0) ? 0 : outline->contours[i - 1] + 1; 745 stop = outline->contours[i]; 697 746 for (j = begin; j <= stop; j++) { 698 747 outline->points[p].x = outline->points[j].x; 699 748 outline->points[p].y = outline->points[j].y; -
libass/ass_font.h
22 22 #include <stdint.h> 23 23 #include <ft2build.h> 24 24 #include FT_GLYPH_H 25 #include FT_OUTLINE_H 26 25 27 #include "ass.h" 26 28 #include "ass_types.h" 27 29 30 #define VERTICAL_LOWER_BOUND 0x02f1 31 28 32 #define ASS_FONT_MAX_FACES 10 29 33 #define DECO_UNDERLINE 1 30 34 #define DECO_STRIKETHROUGH 2 31 35 36 typedef struct ass_shaper_font_data ASS_ShaperFontData; 37 32 38 typedef struct { 33 39 char *family; 34 40 unsigned bold; … … 42 48 ASS_Library *library; 43 49 FT_Library ftlibrary; 44 50 FT_Face faces[ASS_FONT_MAX_FACES]; 51 ASS_ShaperFontData *shaper_priv; 45 52 int n_faces; 46 53 double scale_x, scale_y; // current transform 47 54 FT_Vector v; // current shift 48 55 double size; 49 56 } ASS_Font; 50 57 51 // FIXME: passing the hashmap via a void pointer is very ugly. 52 ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, 58 #include "ass_cache.h" 59 60 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, 53 61 FT_Library ftlibrary, void *fc_priv, 54 62 ASS_FontDesc *desc); 55 63 void ass_font_set_transform(ASS_Font *font, double scale_x, 56 64 double scale_y, FT_Vector *v); 65 void ass_face_set_size(FT_Face face, double size); 57 66 void ass_font_set_size(ASS_Font *font, double size); 58 67 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, 59 68 int *desc); 69 int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol, 70 int *face_index, int *glyph_index); 60 71 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, 61 uint32_t ch, ASS_Hinting hinting, int flags); 72 uint32_t ch, int face_index, int index, 73 ASS_Hinting hinting, int deco); 62 74 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2); 63 75 void ass_font_free(ASS_Font *font); 64 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y); 76 void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y); 77 void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest); 78 void outline_free(FT_Library lib, FT_Outline *outline); 65 79 66 80 #endif /* LIBASS_FONT_H */ -
libass/ass_library.c
78 78 free(*p); 79 79 } 80 80 free(priv->style_overrides); 81 priv->style_overrides = NULL; 81 82 82 83 if (!list) 83 84 return; -
libass/ass_parse.c
47 47 return 0; 48 48 } 49 49 50 static void change_font_size(ASS_Renderer *render_priv, double sz)50 double ensure_font_size(ASS_Renderer *priv, double size) 51 51 { 52 double size = sz * render_priv->font_scale;53 54 52 if (size < 1) 55 53 size = 1; 56 else if (size > render_priv->height * 2)57 size = render_priv->height * 2;54 else if (size > priv->height * 2) 55 size = priv->height * 2; 58 56 59 ass_font_set_size(render_priv->state.font, size); 57 return size; 58 } 60 59 60 static void change_font_size(ASS_Renderer *render_priv, double sz) 61 { 61 62 render_priv->state.font_size = sz; 62 63 } 63 64 … … 189 190 { 190 191 unsigned a; 191 192 double cf; 192 if (now <= t1) { 193 194 if (now < t1) { 193 195 a = a1; 194 196 } else if (now >= t4) { 195 197 a = a3; 196 } else if (now < t2 ) { // and > t1198 } else if (now < t2 && t2 > t1) { 197 199 cf = ((double) (now - t1)) / (t2 - t1); 198 200 a = a1 * (1 - cf) + a2 * cf; 199 } else if (now > t3) {201 } else if (now >= t3 && t4 > t3) { 200 202 cf = ((double) (now - t3)) / (t4 - t3); 201 203 a = a2 * (1 - cf) + a3 * cf; 202 } else { // t2 <= now < =t3204 } else { // t2 <= now < t3 203 205 a = a2; 204 206 } 205 207 … … 216 218 int res = 0; 217 219 ASS_Drawing *drawing = render_priv->state.clip_drawing; 218 220 219 if (drawing && drawing->glyph)220 FT_Done_Glyph((FT_Glyph) drawing->glyph);221 221 ass_drawing_free(drawing); 222 render_priv->state.clip_drawing = ass_drawing_new( 223 render_priv->fontconfig_priv, 224 render_priv->state.font, 225 render_priv->ftlibrary); 222 render_priv->state.clip_drawing = 223 ass_drawing_new(render_priv->library, render_priv->ftlibrary); 226 224 drawing = render_priv->state.clip_drawing; 227 225 skipopt('('); 228 226 res = mystrtoi(&p, &scale); … … 259 257 else 260 258 val = -1.; 261 259 change_border(render_priv, val, render_priv->state.border_y); 260 render_priv->state.bm_run_id++; 262 261 } else if (mystrcmp(&p, "ybord")) { 263 262 double val; 264 263 if (mystrtod(&p, &val)) … … 273 272 else 274 273 val = 0.; 275 274 render_priv->state.shadow_x = val; 275 render_priv->state.bm_run_id++; 276 276 } else if (mystrcmp(&p, "yshad")) { 277 277 double val; 278 278 if (mystrtod(&p, &val)) … … 280 280 else 281 281 val = 0.; 282 282 render_priv->state.shadow_y = val; 283 render_priv->state.bm_run_id++; 283 284 } else if (mystrcmp(&p, "fax")) { 284 285 double val; 285 286 if (mystrtod(&p, &val)) … … 331 332 render_priv->state.blur = val; 332 333 } else 333 334 render_priv->state.blur = 0.0; 335 render_priv->state.bm_run_id++; 334 336 // ASS standard tags 335 337 } else if (mystrcmp(&p, "fsc")) { 336 338 char tp = *p++; … … 391 393 } else 392 394 val = -1.; // reset to default 393 395 change_border(render_priv, val, val); 396 render_priv->state.bm_run_id++; 394 397 } else if (mystrcmp(&p, "move")) { 395 398 double x1, x2, y1, y2; 396 399 long long t1, t2, delta_t, t; … … 492 495 change_alpha(&render_priv->state.c[3], 493 496 render_priv->state.style->BackColour, pwr); 494 497 } 498 render_priv->state.bm_run_id++; 495 499 // FIXME: simplify 496 500 } else if (mystrcmp(&p, "an")) { 497 501 int val; … … 682 686 val = render_priv->state.style->PrimaryColour; 683 687 ass_msg(render_priv->library, MSGL_DBG2, "color: %X", val); 684 688 change_color(&render_priv->state.c[0], val, pwr); 689 render_priv->state.bm_run_id++; 685 690 } else if ((*p >= '1') && (*p <= '4') && (++p) 686 691 && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { 687 692 char n = *(p - 2); … … 711 716 switch (cmd) { 712 717 case 'c': 713 718 change_color(render_priv->state.c + cidx, val, pwr); 719 render_priv->state.bm_run_id++; 714 720 break; 715 721 case 'a': 716 722 change_alpha(render_priv->state.c + cidx, val >> 24, pwr); 723 render_priv->state.bm_run_id++; 717 724 break; 718 725 default: 719 726 ass_msg(render_priv->library, MSGL_WARN, "Bad command: %c%c", … … 733 740 render_priv->state.be = val; 734 741 } else 735 742 render_priv->state.be = 0; 743 render_priv->state.bm_run_id++; 736 744 } else if (mystrcmp(&p, "b")) { 737 745 int b; 738 746 if (mystrtoi(&p, &b)) { … … 781 789 } else 782 790 val = 0.; 783 791 render_priv->state.shadow_x = render_priv->state.shadow_y = val; 792 render_priv->state.bm_run_id++; 784 793 } else if (mystrcmp(&p, "s")) { 785 794 int val; 786 795 if (mystrtoi(&p, &val) && val) 787 796 render_priv->state.flags |= DECO_STRIKETHROUGH; 788 797 else 789 798 render_priv->state.flags &= ~DECO_STRIKETHROUGH; 799 render_priv->state.bm_run_id++; 790 800 } else if (mystrcmp(&p, "u")) { 791 801 int val; 792 802 if (mystrtoi(&p, &val) && val) 793 803 render_priv->state.flags |= DECO_UNDERLINE; 794 804 else 795 805 render_priv->state.flags &= ~DECO_UNDERLINE; 806 render_priv->state.bm_run_id++; 796 807 } else if (mystrcmp(&p, "pbo")) { 797 808 double val = 0; 798 809 if (mystrtod(&p, &val)) … … 809 820 if (!mystrtoi(&p, &val)) 810 821 val = render_priv->track->WrapStyle; 811 822 render_priv->state.wrap_style = val; 823 } else if (mystrcmp(&p, "fe")) { 824 int val; 825 if (!mystrtoi(&p, &val)) 826 val = render_priv->state.style->Encoding; 827 render_priv->state.font_encoding = val; 812 828 } 813 829 814 830 return p; … … 890 906 } 891 907 892 908 /** 909 * \brief determine karaoke effects 910 * Karaoke effects cannot be calculated during parse stage (get_next_char()), 911 * so they are done in a separate step. 912 * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's 913 * (the first glyph of the karaoke word)'s effect_type and effect_timing. 914 * This function: 915 * 1. sets effect_type for all glyphs in the word (_karaoke_ word) 916 * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts 917 * (left part is filled with PrimaryColour, right one - with SecondaryColour). 918 */ 919 void process_karaoke_effects(ASS_Renderer *render_priv) 920 { 921 GlyphInfo *cur, *cur2; 922 GlyphInfo *s1, *e1; // start and end of the current word 923 GlyphInfo *s2; // start of the next word 924 int i; 925 int timing; // current timing 926 int tm_start, tm_end; // timings at start and end of the current word 927 int tm_current; 928 double dt; 929 int x; 930 int x_start, x_end; 931 932 tm_current = render_priv->time - render_priv->state.event->Start; 933 timing = 0; 934 s1 = s2 = 0; 935 for (i = 0; i <= render_priv->text_info.length; ++i) { 936 cur = render_priv->text_info.glyphs + i; 937 if ((i == render_priv->text_info.length) 938 || (cur->effect_type != EF_NONE)) { 939 s1 = s2; 940 s2 = cur; 941 if (s1) { 942 e1 = s2 - 1; 943 tm_start = timing + s1->effect_skip_timing; 944 tm_end = tm_start + s1->effect_timing; 945 timing = tm_end; 946 x_start = 1000000; 947 x_end = -1000000; 948 for (cur2 = s1; cur2 <= e1; ++cur2) { 949 x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x)); 950 x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x)); 951 } 952 953 dt = (tm_current - tm_start); 954 if ((s1->effect_type == EF_KARAOKE) 955 || (s1->effect_type == EF_KARAOKE_KO)) { 956 if (dt > 0) 957 x = x_end + 1; 958 else 959 x = x_start; 960 } else if (s1->effect_type == EF_KARAOKE_KF) { 961 dt /= (tm_end - tm_start); 962 x = x_start + (x_end - x_start) * dt; 963 } else { 964 ass_msg(render_priv->library, MSGL_ERR, 965 "Unknown effect type"); 966 continue; 967 } 968 969 for (cur2 = s1; cur2 <= e1; ++cur2) { 970 cur2->effect_type = s1->effect_type; 971 cur2->effect_timing = x - d6_to_int(cur2->pos.x); 972 } 973 } 974 } 975 } 976 } 977 978 979 /** 893 980 * \brief Get next ucs4 char from string, parsing and executing style overrides 894 981 * \param str string pointer 895 982 * \return ucs4 code of the next char -
libass/ass_parse.h
27 27 #define _a(c) ((c) & 0xFF) 28 28 29 29 void update_font(ASS_Renderer *render_priv); 30 double ensure_font_size(ASS_Renderer *priv, double size); 30 31 void change_border(ASS_Renderer *render_priv, double border_x, 31 32 double border_y); 32 33 void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event); 34 void process_karaoke_effects(ASS_Renderer *render_priv); 33 35 unsigned get_next_char(ASS_Renderer *render_priv, char **str); 34 36 extern void change_alpha(uint32_t *var, uint32_t new, double pwr); 35 37 extern uint32_t mult_alpha(uint32_t a, uint32_t b); -
libass/ass_render.c
23 23 24 24 #include "ass_render.h" 25 25 #include "ass_parse.h" 26 #include "ass_shaper.h" 26 27 27 28 #define MAX_GLYPHS_INITIAL 1024 28 29 #define MAX_LINES_INITIAL 64 29 30 #define SUBPIXEL_MASK 63 30 31 #define SUBPIXEL_ACCURACY 7 31 32 32 static void ass_lazy_track_init(ASS_Renderer *render_priv)33 {34 ASS_Track *track = render_priv->track;35 36 if (track->PlayResX && track->PlayResY)37 return;38 if (!track->PlayResX && !track->PlayResY) {39 ass_msg(render_priv->library, MSGL_WARN,40 "Neither PlayResX nor PlayResY defined. Assuming 384x288");41 track->PlayResX = 384;42 track->PlayResY = 288;43 } else {44 if (!track->PlayResY && track->PlayResX == 1280) {45 track->PlayResY = 1024;46 ass_msg(render_priv->library, MSGL_WARN,47 "PlayResY undefined, setting to %d", track->PlayResY);48 } else if (!track->PlayResY) {49 track->PlayResY = track->PlayResX * 3 / 4;50 ass_msg(render_priv->library, MSGL_WARN,51 "PlayResY undefined, setting to %d", track->PlayResY);52 } else if (!track->PlayResX && track->PlayResY == 1024) {53 track->PlayResX = 1280;54 ass_msg(render_priv->library, MSGL_WARN,55 "PlayResX undefined, setting to %d", track->PlayResX);56 } else if (!track->PlayResX) {57 track->PlayResX = track->PlayResY * 4 / 3;58 ass_msg(render_priv->library, MSGL_WARN,59 "PlayResX undefined, setting to %d", track->PlayResX);60 }61 }62 }63 64 33 ASS_Renderer *ass_renderer_init(ASS_Library *library) 65 34 { 66 35 int error; … … 75 44 } 76 45 77 46 FT_Library_Version(ft, &vmajor, &vminor, &vpatch); 78 ass_msg(library, MSGL_V, " FreeType library version:%d.%d.%d",47 ass_msg(library, MSGL_V, "Raster: FreeType %d.%d.%d", 79 48 vmajor, vminor, vpatch); 80 ass_msg(library, MSGL_V, "FreeType headers version: %d.%d.%d",81 FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);82 49 83 50 priv = calloc(1, sizeof(ASS_Renderer)); 84 51 if (!priv) { … … 92 59 priv->ftlibrary = ft; 93 60 // images_root and related stuff is zero-filled in calloc 94 61 95 priv->cache.font_cache = ass_font_cache_ init(library);96 priv->cache.bitmap_cache = ass_bitmap_cache_ init(library);97 priv->cache.composite_cache = ass_composite_cache_ init(library);98 priv->cache. glyph_cache = ass_glyph_cache_init(library);62 priv->cache.font_cache = ass_font_cache_create(); 63 priv->cache.bitmap_cache = ass_bitmap_cache_create(); 64 priv->cache.composite_cache = ass_composite_cache_create(); 65 priv->cache.outline_cache = ass_outline_cache_create(); 99 66 priv->cache.glyph_max = GLYPH_CACHE_MAX; 100 67 priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE; 101 68 … … 106 73 107 74 priv->settings.font_size_coeff = 1.; 108 75 76 priv->shaper = ass_shaper_new(0); 77 ass_shaper_info(library); 78 #ifdef CONFIG_HARFBUZZ 79 priv->settings.shaper = ASS_SHAPING_COMPLEX; 80 #else 81 priv->settings.shaper = ASS_SHAPING_SIMPLE; 82 #endif 83 109 84 ass_init_exit: 110 85 if (priv) 111 ass_msg(library, MSGL_V, "Init ");86 ass_msg(library, MSGL_V, "Initialized"); 112 87 else 113 ass_msg(library, MSGL_ERR, "Init failed");88 ass_msg(library, MSGL_ERR, "Initialization failed"); 114 89 115 90 return priv; 116 91 } … … 131 106 132 107 void ass_renderer_done(ASS_Renderer *render_priv) 133 108 { 134 ass_ font_cache_done(render_priv->cache.font_cache);135 ass_ bitmap_cache_done(render_priv->cache.bitmap_cache);136 ass_c omposite_cache_done(render_priv->cache.composite_cache);137 ass_ glyph_cache_done(render_priv->cache.glyph_cache);109 ass_cache_done(render_priv->cache.font_cache); 110 ass_cache_done(render_priv->cache.bitmap_cache); 111 ass_cache_done(render_priv->cache.composite_cache); 112 ass_cache_done(render_priv->cache.outline_cache); 138 113 139 114 ass_free_images(render_priv->images_root); 140 115 ass_free_images(render_priv->prev_images_root); … … 149 124 fontconfig_done(render_priv->fontconfig_priv); 150 125 if (render_priv->synth_priv) 151 126 ass_synth_done(render_priv->synth_priv); 127 ass_shaper_free(render_priv->shaper); 152 128 free(render_priv->eimg); 153 129 free(render_priv->text_info.glyphs); 154 130 free(render_priv->text_info.lines); … … 328 304 // split up into left and right for karaoke, if needed 329 305 if (lbrk > r[j].x0) { 330 306 if (lbrk > r[j].x1) lbrk = r[j].x1; 331 img = my_draw_bitmap(bm->buffer + r[j].y0 * bm-> w+ r[j].x0,307 img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->stride + r[j].x0, 332 308 lbrk - r[j].x0, r[j].y1 - r[j].y0, 333 bm-> w, dst_x + r[j].x0, dst_y + r[j].y0, color);309 bm->stride, dst_x + r[j].x0, dst_y + r[j].y0, color); 334 310 if (!img) break; 335 311 *tail = img; 336 312 tail = &img->next; 337 313 } 338 314 if (lbrk < r[j].x1) { 339 315 if (lbrk < r[j].x0) lbrk = r[j].x0; 340 img = my_draw_bitmap(bm->buffer + r[j].y0 * bm-> w+ lbrk,316 img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->stride + lbrk, 341 317 r[j].x1 - lbrk, r[j].y1 - r[j].y0, 342 bm-> w, dst_x + lbrk, dst_y + r[j].y0, color2);318 bm->stride, dst_x + lbrk, dst_y + r[j].y0, color2); 343 319 if (!img) break; 344 320 *tail = img; 345 321 tail = &img->next; … … 419 395 if (brk > b_x0) { // draw left part 420 396 if (brk > b_x1) 421 397 brk = b_x1; 422 img = my_draw_bitmap(bm->buffer + bm-> w* b_y0 + b_x0,423 brk - b_x0, b_y1 - b_y0, bm-> w,398 img = my_draw_bitmap(bm->buffer + bm->stride * b_y0 + b_x0, 399 brk - b_x0, b_y1 - b_y0, bm->stride, 424 400 dst_x + b_x0, dst_y + b_y0, color); 425 401 if (!img) return tail; 426 402 *tail = img; … … 429 405 if (brk < b_x1) { // draw right part 430 406 if (brk < b_x0) 431 407 brk = b_x0; 432 img = my_draw_bitmap(bm->buffer + bm-> w* b_y0 + brk,433 b_x1 - brk, b_y1 - b_y0, bm-> w,408 img = my_draw_bitmap(bm->buffer + bm->stride * b_y0 + brk, 409 b_x1 - brk, b_y1 - b_y0, bm->stride, 434 410 dst_x + brk, dst_y + b_y0, color2); 435 411 if (!img) return tail; 436 412 *tail = img; … … 516 492 hk.by = by; 517 493 hk.as = as; 518 494 hk.bs = bs; 519 hv = cache_find_composite(render_priv->cache.composite_cache, &hk);495 hv = ass_cache_get(render_priv->cache.composite_cache, &hk); 520 496 if (hv) { 521 497 (*last_tail)->bitmap = hv->a; 522 498 (*tail)->bitmap = hv->b; … … 539 515 // Insert bitmaps into the cache 540 516 chv.a = (*last_tail)->bitmap; 541 517 chv.b = (*tail)->bitmap; 542 cache_add_composite(render_priv->cache.composite_cache, &hk, &chv);518 ass_cache_put(render_priv->cache.composite_cache, &hk, &chv); 543 519 } 544 520 545 521 static void free_list_add(ASS_Renderer *render_priv, void *object) … … 564 540 static void blend_vector_clip(ASS_Renderer *render_priv, 565 541 ASS_Image *head) 566 542 { 567 FT_ Glyph glyph;568 FT_BitmapGlyph clip_bm;543 FT_Outline *outline; 544 Bitmap *clip_bm = NULL; 569 545 ASS_Image *cur; 570 546 ASS_Drawing *drawing = render_priv->state.clip_drawing; 571 GlyphHashKey key;572 GlyphHashValue *val;547 BitmapHashKey key; 548 BitmapHashValue *val; 573 549 int error; 574 550 575 551 if (!drawing) 576 552 return; 577 553 578 554 // Try to get mask from cache 579 ass_drawing_hash(drawing);580 555 memset(&key, 0, sizeof(key)); 581 key. ch = -2;582 key. drawing_hash = drawing->hash;583 val = cache_find_glyph(render_priv->cache.glyph_cache, &key);556 key.type = BITMAP_CLIP; 557 key.u.clip.text = drawing->text; 558 val = ass_cache_get(render_priv->cache.bitmap_cache, &key); 584 559 585 560 if (val) { 586 clip_bm = (FT_BitmapGlyph) val->glyph;561 clip_bm = val->bm; 587 562 } else { 588 GlyphHashValue v;563 BitmapHashValue v; 589 564 590 565 // Not found in cache, parse and rasterize it 591 glyph = (FT_Glyph) *ass_drawing_parse(drawing, 1);592 if (! glyph) {566 outline = ass_drawing_parse(drawing, 1); 567 if (!outline) { 593 568 ass_msg(render_priv->library, MSGL_WARN, 594 569 "Clip vector parsing failed. Skipping."); 595 570 goto blend_vector_error; … … 602 577 .x = int_to_d6(render_priv->settings.left_margin), 603 578 .y = -int_to_d6(render_priv->settings.top_margin), 604 579 }; 605 FT_Outline_Translate(&drawing->glyph->outline, 606 trans.x, trans.y); 580 FT_Outline_Translate(outline, trans.x, trans.y); 607 581 } 608 582 609 // Check glyph bounding box size610 if (check_glyph_area(render_priv->library, glyph)) {611 FT_Done_Glyph(glyph);612 glyph = 0;613 goto blend_vector_error;614 }615 616 583 ass_msg(render_priv->library, MSGL_DBG2, 617 584 "Parsed vector clip: scales (%f, %f) string [%s]\n", 618 585 drawing->scale_x, drawing->scale_y, drawing->text); 619 586 620 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); 621 if (error) { 587 clip_bm = outline_to_bitmap(render_priv->library, 588 render_priv->ftlibrary, outline, 0); 589 if (clip_bm == NULL) { 622 590 ass_msg(render_priv->library, MSGL_WARN, 623 591 "Clip vector rasterization failed: %d. Skipping.", error); 624 FT_Done_Glyph(glyph);625 glyph = 0;626 592 } 627 593 628 blend_vector_error:629 clip_bm = (FT_BitmapGlyph) glyph;630 631 594 // Add to cache 632 595 memset(&v, 0, sizeof(v)); 633 v.glyph = glyph; 634 cache_add_glyph(render_priv->cache.glyph_cache, &key, &v); 596 key.u.clip.text = strdup(drawing->text); 597 v.bm = clip_bm; 598 ass_cache_put(render_priv->cache.bitmap_cache, &key, &v); 635 599 } 600 blend_vector_error: 636 601 637 602 if (!clip_bm) goto blend_vector_exit; 638 603 … … 645 610 unsigned char *abuffer, *bbuffer, *nbuffer; 646 611 647 612 abuffer = cur->bitmap; 648 bbuffer = clip_bm->b itmap.buffer;613 bbuffer = clip_bm->buffer; 649 614 ax = cur->dst_x; 650 615 ay = cur->dst_y; 651 616 aw = cur->w; 652 617 ah = cur->h; 653 618 as = cur->stride; 654 619 bx = clip_bm->left; 655 by = -clip_bm->top;656 bw = clip_bm-> bitmap.width;657 bh = clip_bm-> bitmap.rows;658 bs = clip_bm-> bitmap.pitch;620 by = clip_bm->top; 621 bw = clip_bm->w; 622 bh = clip_bm->h; 623 bs = clip_bm->stride; 659 624 660 625 // Calculate overlap coordinates 661 626 left = (ax > bx) ? ax : bx; … … 739 704 || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip) 740 705 continue; 741 706 742 pen_x = 743 dst_x + (info->pos.x >> 6) + 744 (int) (info->shadow_x * render_priv->border_scale); 745 pen_y = 746 dst_y + (info->pos.y >> 6) + 747 (int) (info->shadow_y * render_priv->border_scale); 748 bm = info->bm_s; 707 while (info) { 708 if (!info->bm_s) { 709 info = info->next; 710 continue; 711 } 749 712 750 here_tail = tail; 751 tail = 752 render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, 753 1000000, tail); 754 if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) 755 render_overlap(render_priv, last_tail, here_tail); 713 pen_x = 714 dst_x + (info->pos.x >> 6) + 715 (int) (info->shadow_x * render_priv->border_scale); 716 pen_y = 717 dst_y + (info->pos.y >> 6) + 718 (int) (info->shadow_y * render_priv->border_scale); 719 bm = info->bm_s; 756 720 757 last_tail = here_tail; 721 here_tail = tail; 722 tail = 723 render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, 724 1000000, tail); 725 726 if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) 727 render_overlap(render_priv, last_tail, here_tail); 728 last_tail = here_tail; 729 730 info = info->next; 731 } 758 732 } 759 733 760 734 last_tail = 0; … … 764 738 || info->skip) 765 739 continue; 766 740 767 pen_x = dst_x + (info->pos.x >> 6); 768 pen_y = dst_y + (info->pos.y >> 6); 769 bm = info->bm_o; 741 while (info) { 742 if (!info->bm_o) { 743 info = info->next; 744 continue; 745 } 770 746 771 if ((info->effect_type == EF_KARAOKE_KO) 772 && (info->effect_timing <= (info->bbox.xMax >> 6))) { 773 // do nothing 774 } else { 775 here_tail = tail; 776 tail = 777 render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], 778 0, 1000000, tail); 779 if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) 780 render_overlap(render_priv, last_tail, here_tail); 747 pen_x = dst_x + (info->pos.x >> 6); 748 pen_y = dst_y + (info->pos.y >> 6); 749 bm = info->bm_o; 781 750 782 last_tail = here_tail; 751 if ((info->effect_type == EF_KARAOKE_KO) 752 && (info->effect_timing <= (info->bbox.xMax >> 6))) { 753 // do nothing 754 } else { 755 here_tail = tail; 756 tail = 757 render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], 758 0, 1000000, tail); 759 if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) 760 render_overlap(render_priv, last_tail, here_tail); 761 762 last_tail = here_tail; 763 } 764 info = info->next; 783 765 } 784 766 } 785 767 … … 789 771 || info->skip) 790 772 continue; 791 773 792 pen_x = dst_x + (info->pos.x >> 6); 793 pen_y = dst_y + (info->pos.y >> 6); 794 bm = info->bm; 774 while (info) { 775 if (!info->bm) { 776 info = info->next; 777 continue; 778 } 795 779 796 if ((info->effect_type == EF_KARAOKE) 797 || (info->effect_type == EF_KARAOKE_KO)) { 798 if (info->effect_timing > (info->bbox.xMax >> 6)) 780 pen_x = dst_x + (info->pos.x >> 6); 781 pen_y = dst_y + (info->pos.y >> 6); 782 bm = info->bm; 783 784 if ((info->effect_type == EF_KARAOKE) 785 || (info->effect_type == EF_KARAOKE_KO)) { 786 if (info->effect_timing > (info->bbox.xMax >> 6)) 787 tail = 788 render_glyph(render_priv, bm, pen_x, pen_y, 789 info->c[0], 0, 1000000, tail); 790 else 791 tail = 792 render_glyph(render_priv, bm, pen_x, pen_y, 793 info->c[1], 0, 1000000, tail); 794 } else if (info->effect_type == EF_KARAOKE_KF) { 799 795 tail = 800 render_glyph(render_priv, bm, pen_x, pen_y, 801 info->c[0], 0, 1000000, tail);802 else796 render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], 797 info->c[1], info->effect_timing, tail); 798 } else 803 799 tail = 804 render_glyph(render_priv, bm, pen_x, pen_y, 805 info->c[1], 0, 1000000, tail); 806 } else if (info->effect_type == EF_KARAOKE_KF) { 807 tail = 808 render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], 809 info->c[1], info->effect_timing, tail); 810 } else 811 tail = 812 render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], 813 0, 1000000, tail); 800 render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], 801 0, 1000000, tail); 802 info = info->next; 803 } 814 804 } 815 805 816 806 *tail = 0; … … 819 809 return head; 820 810 } 821 811 822 static void compute_string_bbox(TextInfo * info, DBBox *bbox)812 static void compute_string_bbox(TextInfo *text, DBBox *bbox) 823 813 { 824 814 int i; 825 815 826 if ( info->length > 0) {816 if (text->length > 0) { 827 817 bbox->xMin = 32000; 828 818 bbox->xMax = -32000; 829 bbox->yMin = -1 * info->lines[0].asc + d6_to_double(info->glyphs[0].pos.y);830 bbox->yMax = info->height - info->lines[0].asc +831 d6_to_double( info->glyphs[0].pos.y);819 bbox->yMin = -1 * text->lines[0].asc + d6_to_double(text->glyphs[0].pos.y); 820 bbox->yMax = text->height - text->lines[0].asc + 821 d6_to_double(text->glyphs[0].pos.y); 832 822 833 for (i = 0; i < info->length; ++i) { 834 if (info->glyphs[i].skip) continue; 835 double s = d6_to_double(info->glyphs[i].pos.x); 836 double e = s + d6_to_double(info->glyphs[i].advance.x); 837 bbox->xMin = FFMIN(bbox->xMin, s); 838 bbox->xMax = FFMAX(bbox->xMax, e); 823 for (i = 0; i < text->length; ++i) { 824 GlyphInfo *info = text->glyphs + i; 825 if (info->skip) continue; 826 while (info) { 827 double s = d6_to_double(info->pos.x); 828 double e = s + d6_to_double(info->advance.x); 829 bbox->xMin = FFMIN(bbox->xMin, s); 830 bbox->xMax = FFMAX(bbox->xMax, e); 831 info = info->next; 832 } 839 833 } 840 834 } else 841 835 bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.; … … 877 871 render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.; 878 872 render_priv->state.fax = render_priv->state.fay = 0.; 879 873 render_priv->state.wrap_style = render_priv->track->WrapStyle; 874 render_priv->state.font_encoding = render_priv->state.style->Encoding; 880 875 } 881 876 882 877 /** … … 909 904 render_priv->state.effect_type = EF_NONE; 910 905 render_priv->state.effect_timing = 0; 911 906 render_priv->state.effect_skip_timing = 0; 907 render_priv->state.bm_run_id = 0; 912 908 ass_drawing_free(render_priv->state.drawing); 913 render_priv->state.drawing = ass_drawing_new(render_priv->fontconfig_priv, 914 render_priv->state.font, 915 render_priv->ftlibrary); 909 render_priv->state.drawing = ass_drawing_new(render_priv->library, 910 render_priv->ftlibrary); 916 911 917 912 apply_transition_effects(render_priv, event); 918 913 } … … 930 925 * Replace the outline of a glyph by a contour which makes up a simple 931 926 * opaque rectangle. 932 927 */ 933 static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch,934 FT_ Glyph glyph, int sx, int sy)928 static void draw_opaque_box(ASS_Renderer *render_priv, int asc, int desc, 929 FT_Outline *ol, FT_Vector advance, int sx, int sy) 935 930 { 936 int asc = 0, desc = 0;937 931 int i; 938 int adv = d16_to_d6(glyph->advance.x);932 int adv = advance.x; 939 933 double scale_y = render_priv->state.scale_y; 940 934 double scale_x = render_priv->state.scale_x; 941 FT_OutlineGlyph og = (FT_OutlineGlyph) glyph;942 FT_Outline *ol;943 935 944 936 // to avoid gaps 945 937 sx = FFMAX(64, sx); 946 938 sy = FFMAX(64, sy); 947 939 948 if (ch == -1) {949 asc = render_priv->state.drawing->asc;950 desc = render_priv->state.drawing->desc;951 } else {952 ass_font_get_asc_desc(render_priv->state.font, ch, &asc, &desc);953 asc *= scale_y;954 desc *= scale_y;955 }956 957 940 // Emulate the WTFish behavior of VSFilter, i.e. double-scale 958 941 // the sizes of the opaque box. 959 942 adv += double_to_d6(render_priv->state.hspacing * render_priv->font_scale … … 971 954 { .x = -sx, .y = -desc - sy }, 972 955 }; 973 956 974 FT_Outline_Done(render_priv->ftlibrary, &og->outline); 975 FT_Outline_New(render_priv->ftlibrary, 4, 1, &og->outline); 957 FT_Outline_New(render_priv->ftlibrary, 4, 1, ol); 976 958 977 ol = &og->outline;978 959 ol->n_points = ol->n_contours = 0; 979 960 for (i = 0; i < 4; i++) { 980 961 ol->points[ol->n_points] = points[i]; … … 987 968 * Stroke an outline glyph in x/y direction. Applies various fixups to get 988 969 * around limitations of the FreeType stroker. 989 970 */ 990 static void stroke_outline _glyph(ASS_Renderer *render_priv,991 FT_OutlineGlyph *glyph,int sx, int sy)971 static void stroke_outline(ASS_Renderer *render_priv, FT_Outline *outline, 972 int sx, int sy) 992 973 { 993 974 if (sx <= 0 && sy <= 0) 994 975 return; 995 976 996 fix_freetype_stroker( *glyph, sx, sy);977 fix_freetype_stroker(outline, sx, sy); 997 978 998 979 // Borders are equal; use the regular stroker 999 980 if (sx == sy && render_priv->state.stroker) { 1000 981 int error; 1001 error = FT_Glyph_StrokeBorder((FT_Glyph *) glyph, 1002 render_priv->state.stroker, 0, 1); 1003 if (error) 982 unsigned n_points, n_contours; 983 984 FT_StrokerBorder border = FT_Outline_GetOutsideBorder(outline); 985 error = FT_Stroker_ParseOutline(render_priv->state.stroker, outline, 0); 986 if (error) { 1004 987 ass_msg(render_priv->library, MSGL_WARN, 1005 "FT_Glyph_Stroke error: %d", error); 988 "FT_Stroker_ParseOutline failed, error: %d", error); 989 } 990 error = FT_Stroker_GetBorderCounts(render_priv->state.stroker, border, 991 &n_points, &n_contours); 992 if (error) { 993 ass_msg(render_priv->library, MSGL_WARN, 994 "FT_Stroker_GetBorderCounts failed, error: %d", error); 995 } 996 FT_Outline_Done(render_priv->ftlibrary, outline); 997 FT_Outline_New(render_priv->ftlibrary, n_points, n_contours, outline); 998 outline->n_points = outline->n_contours = 0; 999 FT_Stroker_ExportBorder(render_priv->state.stroker, border, outline); 1006 1000 1007 1001 // "Stroke" with the outline emboldener in two passes. 1008 1002 // The outlines look uglier, but the emboldening never adds any points 1009 1003 } else { 1010 1004 int i; 1011 FT_Outline *ol = &(*glyph)->outline;1012 1005 FT_Outline nol; 1013 FT_Outline_New(render_priv->ftlibrary, ol->n_points,1014 ol->n_contours, &nol);1015 FT_Outline_Copy(ol, &nol);1016 1006 1017 FT_Outline_Embolden(ol, sx * 2); 1018 FT_Outline_Translate(ol, -sx, -sx); 1007 FT_Outline_New(render_priv->ftlibrary, outline->n_points, 1008 outline->n_contours, &nol); 1009 FT_Outline_Copy(outline, &nol); 1010 1011 FT_Outline_Embolden(outline, sx * 2); 1012 FT_Outline_Translate(outline, -sx, -sx); 1019 1013 FT_Outline_Embolden(&nol, sy * 2); 1020 1014 FT_Outline_Translate(&nol, -sy, -sy); 1021 1015 1022 for (i = 0; i < o l->n_points; i++)1023 o l->points[i].y = nol.points[i].y;1016 for (i = 0; i < outline->n_points; i++) 1017 outline->points[i].y = nol.points[i].y; 1024 1018 1025 1019 FT_Outline_Done(render_priv->ftlibrary, &nol); 1026 1020 } … … 1030 1024 * \brief Prepare glyph hash 1031 1025 */ 1032 1026 static void 1033 fill_glyph_hash(ASS_Renderer *priv, GlyphHashKey *key,1034 ASS_Drawing *drawing, uint32_t ch)1027 fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key, 1028 GlyphInfo *info) 1035 1029 { 1036 if (drawing->hash) { 1037 key->scale_x = double_to_d16(priv->state.scale_x); 1038 key->scale_y = double_to_d16(priv->state.scale_y); 1039 key->outline.x = priv->state.border_x * 0xFFFF; 1040 key->outline.y = priv->state.border_y * 0xFFFF; 1030 if (info->drawing) { 1031 DrawingHashKey *key = &outline_key->u.drawing; 1032 outline_key->type = OUTLINE_DRAWING; 1033 key->scale_x = double_to_d16(info->scale_x); 1034 key->scale_y = double_to_d16(info->scale_y); 1035 key->outline.x = double_to_d16(info->border_x); 1036 key->outline.y = double_to_d16(info->border_y); 1041 1037 key->border_style = priv->state.style->BorderStyle; 1042 key-> drawing_hash =drawing->hash;1043 // not very clean, but works1044 key-> size = drawing->scale;1045 key-> ch = -1;1038 key->hash = info->drawing->hash; 1039 key->text = info->drawing->text; 1040 key->pbo = info->drawing->pbo; 1041 key->scale = info->drawing->scale; 1046 1042 } else { 1047 key->font = priv->state.font; 1048 key->size = priv->state.font_size; 1049 key->ch = ch; 1050 key->bold = priv->state.bold; 1051 key->italic = priv->state.italic; 1052 key->scale_x = double_to_d16(priv->state.scale_x); 1053 key->scale_y = double_to_d16(priv->state.scale_y); 1054 key->outline.x = priv->state.border_x * 0xFFFF; 1055 key->outline.y = priv->state.border_y * 0xFFFF; 1056 key->flags = priv->state.flags; 1043 GlyphHashKey *key = &outline_key->u.glyph; 1044 outline_key->type = OUTLINE_GLYPH; 1045 key->font = info->font; 1046 key->size = info->font_size; 1047 key->face_index = info->face_index; 1048 key->glyph_index = info->glyph_index; 1049 key->bold = info->bold; 1050 key->italic = info->italic; 1051 key->scale_x = double_to_d16(info->scale_x); 1052 key->scale_y = double_to_d16(info->scale_y); 1053 key->outline.x = double_to_d16(info->border_x); 1054 key->outline.y = double_to_d16(info->border_y); 1055 key->flags = info->flags; 1057 1056 key->border_style = priv->state.style->BorderStyle; 1058 1057 } 1059 1058 } 1060 1059 1061 1060 /** 1062 1061 * \brief Get normal and outline (border) glyphs 1063 * \param symbol ucs4 char1064 1062 * \param info out: struct filled with extracted data 1065 1063 * Tries to get both glyphs from cache. 1066 1064 * If they can't be found, gets a glyph from font face, generates outline with FT_Stroker, … … 1068 1066 * The glyphs are returned in info->glyph and info->outline_glyph 1069 1067 */ 1070 1068 static void 1071 get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info, 1072 ASS_Drawing *drawing) 1069 get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) 1073 1070 { 1074 GlyphHashValue *val;1075 GlyphHashKey key;1071 OutlineHashValue *val; 1072 OutlineHashKey key; 1076 1073 1077 memset(&key, 0, sizeof(key)); 1078 memset(info, 0, sizeof(GlyphInfo)); 1074 memset(&info->hash_key, 0, sizeof(key)); 1079 1075 1080 fill_glyph_hash(render_priv, &key, drawing, symbol); 1081 val = cache_find_glyph(render_priv->cache.glyph_cache, &key); 1082 if (val) { 1083 info->glyph = val->glyph; 1084 info->outline_glyph = val->outline_glyph; 1085 info->bbox = val->bbox_scaled; 1086 info->advance.x = val->advance.x; 1087 info->advance.y = val->advance.y; 1088 if (drawing->hash) { 1089 drawing->asc = val->asc; 1090 drawing->desc = val->desc; 1091 } 1092 } else { 1093 GlyphHashValue v; 1094 if (drawing->hash) { 1076 fill_glyph_hash(priv, &key, info); 1077 val = ass_cache_get(priv->cache.outline_cache, &key); 1078 1079 if (!val) { 1080 OutlineHashValue v; 1081 memset(&v, 0, sizeof(v)); 1082 1083 if (info->drawing) { 1084 ASS_Drawing *drawing = info->drawing; 1085 ass_drawing_hash(drawing); 1095 1086 if(!ass_drawing_parse(drawing, 0)) 1096 1087 return; 1097 info->glyph = (FT_Glyph) drawing->glyph; 1088 outline_copy(priv->ftlibrary, &drawing->outline, 1089 &v.outline); 1090 v.advance.x = drawing->advance.x; 1091 v.advance.y = drawing->advance.y; 1092 v.asc = drawing->asc; 1093 v.desc = drawing->desc; 1094 key.u.drawing.text = strdup(drawing->text); 1098 1095 } else { 1099 info->glyph = 1100 ass_font_get_glyph(render_priv->fontconfig_priv, 1101 render_priv->state.font, symbol, 1102 render_priv->settings.hinting, 1103 render_priv->state.flags); 1096 ass_face_set_size(info->font->faces[info->face_index], 1097 info->font_size); 1098 ass_font_set_transform(info->font, info->scale_x, 1099 info->scale_y, NULL); 1100 FT_Glyph glyph = 1101 ass_font_get_glyph(priv->fontconfig_priv, info->font, 1102 info->symbol, info->face_index, info->glyph_index, 1103 priv->settings.hinting, info->flags); 1104 if (glyph != NULL) { 1105 outline_copy(priv->ftlibrary, 1106 &((FT_OutlineGlyph)glyph)->outline, &v.outline); 1107 if (priv->settings.shaper == ASS_SHAPING_SIMPLE) { 1108 v.advance.x = d16_to_d6(glyph->advance.x); 1109 v.advance.y = d16_to_d6(glyph->advance.y); 1110 } 1111 FT_Done_Glyph(glyph); 1112 ass_font_get_asc_desc(info->font, info->symbol, 1113 &v.asc, &v.desc); 1114 v.asc *= info->scale_y; 1115 v.desc *= info->scale_y; 1116 } 1104 1117 } 1105 if (!info->glyph) 1118 1119 if (!v.outline) 1106 1120 return; 1107 1121 1108 info->advance.x = d16_to_d6(info->glyph->advance.x); 1109 info->advance.y = d16_to_d6(info->glyph->advance.y); 1110 FT_Glyph_Get_CBox(info->glyph, FT_GLYPH_BBOX_SUBPIXELS, &info->bbox); 1122 FT_Outline_Get_CBox(v.outline, &v.bbox_scaled); 1111 1123 1112 if (render_priv->state.style->BorderStyle == 3 && 1113 (render_priv->state.border_x > 0|| 1114 render_priv->state.border_y > 0)) { 1115 FT_Glyph_Copy(info->glyph, &info->outline_glyph); 1116 draw_opaque_box(render_priv, symbol, info->outline_glyph, 1117 double_to_d6(render_priv->state.border_x * 1118 render_priv->border_scale), 1119 double_to_d6(render_priv->state.border_y * 1120 render_priv->border_scale)); 1121 } else if ((render_priv->state.border_x > 0 1122 || render_priv->state.border_y > 0) 1123 && key.scale_x && key.scale_y) { 1124 if (priv->state.style->BorderStyle == 3 && 1125 (info->border_x > 0 || info->border_y > 0)) { 1126 FT_Vector advance; 1124 1127 1125 FT_Glyph_Copy(info->glyph, &info->outline_glyph); 1126 stroke_outline_glyph(render_priv, 1127 (FT_OutlineGlyph *) &info->outline_glyph, 1128 double_to_d6(render_priv->state.border_x * 1129 render_priv->border_scale), 1130 double_to_d6(render_priv->state.border_y * 1131 render_priv->border_scale)); 1128 v.border = calloc(1, sizeof(FT_Outline)); 1129 1130 if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing) 1131 advance = v.advance; 1132 else 1133 advance = info->advance; 1134 1135 draw_opaque_box(priv, v.asc, v.desc, v.border, advance, 1136 double_to_d6(info->border_x * priv->border_scale), 1137 double_to_d6(info->border_y * priv->border_scale)); 1138 1139 } else if ((info->border_x > 0 || info->border_y > 0) 1140 && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) { 1141 1142 outline_copy(priv->ftlibrary, v.outline, &v.border); 1143 stroke_outline(priv, v.border, 1144 double_to_d6(info->border_x * priv->border_scale), 1145 double_to_d6(info->border_y * priv->border_scale)); 1132 1146 } 1133 1147 1134 memset(&v, 0, sizeof(v)); 1135 v.glyph = info->glyph; 1136 v.outline_glyph = info->outline_glyph; 1137 v.advance = info->advance; 1138 v.bbox_scaled = info->bbox; 1139 if (drawing->hash) { 1140 v.asc = drawing->asc; 1141 v.desc = drawing->desc; 1142 } 1143 cache_add_glyph(render_priv->cache.glyph_cache, &key, &v); 1148 v.lib = priv->ftlibrary; 1149 val = ass_cache_put(priv->cache.outline_cache, &key, &v); 1144 1150 } 1151 1152 info->hash_key.u.outline.outline = val; 1153 info->outline = val->outline; 1154 info->border = val->border; 1155 info->bbox = val->bbox_scaled; 1156 if (info->drawing || priv->settings.shaper == ASS_SHAPING_SIMPLE) { 1157 info->cluster_advance.x = info->advance.x = val->advance.x; 1158 info->cluster_advance.y = info->advance.y = val->advance.y; 1159 } 1160 info->asc = val->asc; 1161 info->desc = val->desc; 1162 1163 ass_drawing_free(info->drawing); 1145 1164 } 1146 1165 1147 1166 /** … … 1150 1169 * onto the screen plane. 1151 1170 */ 1152 1171 static void 1153 transform_3d_points(FT_Vector shift, FT_ Glyph glyph, double frx, double fry,1172 transform_3d_points(FT_Vector shift, FT_Outline *outline, double frx, double fry, 1154 1173 double frz, double fax, double fay, double scale, 1155 1174 int yshift) 1156 1175 { … … 1160 1179 double cx = cos(frx); 1161 1180 double cy = cos(fry); 1162 1181 double cz = cos(frz); 1163 FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline;1164 1182 FT_Vector *p = outline->points; 1165 1183 double x, y, z, xx, yy, zz; 1166 1184 int i, dist; … … 1203 1221 * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it. 1204 1222 */ 1205 1223 static void 1206 transform_3d(FT_Vector shift, FT_ Glyph *glyph, FT_Glyph *glyph2,1224 transform_3d(FT_Vector shift, FT_Outline *outline, FT_Outline *border, 1207 1225 double frx, double fry, double frz, double fax, double fay, 1208 1226 double scale, int yshift) 1209 1227 { 1210 1228 frx = -frx; 1211 1229 frz = -frz; 1212 1230 if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) { 1213 if ( glyph && *glyph)1214 transform_3d_points(shift, *glyph, frx, fry, frz,1231 if (outline) 1232 transform_3d_points(shift, outline, frx, fry, frz, 1215 1233 fax, fay, scale, yshift); 1216 1234 1217 if ( glyph2 && *glyph2)1218 transform_3d_points(shift, *glyph2, frx, fry, frz,1235 if (border) 1236 transform_3d_points(shift, border, frx, fry, frz, 1219 1237 fax, fay, scale, yshift); 1220 1238 } 1221 1239 } … … 1232 1250 get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) 1233 1251 { 1234 1252 BitmapHashValue *val; 1235 BitmapHashKey *key = &info->hash_key;1253 OutlineBitmapHashKey *key = &info->hash_key.u.outline; 1236 1254 1237 val = cache_find_bitmap(render_priv->cache.bitmap_cache, key); 1255 if (!info->outline || info->symbol == '\n' || info->symbol == 0 || info->skip) 1256 return; 1238 1257 1239 if (val) { 1240 info->bm = val->bm; 1241 info->bm_o = val->bm_o; 1242 info->bm_s = val->bm_s; 1243 } else { 1258 val = ass_cache_get(render_priv->cache.bitmap_cache, &info->hash_key); 1259 1260 if (!val) { 1244 1261 FT_Vector shift; 1245 1262 BitmapHashValue hash_val; 1246 1263 int error; 1247 1264 double fax_scaled, fay_scaled; 1248 info->bm = info->bm_o = info->bm_s = 0; 1249 if (info->glyph && info->symbol != '\n' && info->symbol != 0 1250 && !info->skip) { 1251 FT_Glyph glyph; 1252 FT_Glyph outline; 1253 double scale_x = render_priv->font_scale_x; 1265 FT_Outline *outline, *border; 1266 double scale_x = render_priv->font_scale_x; 1254 1267 1255 FT_Glyph_Copy(info->glyph, &glyph); 1256 FT_Glyph_Copy(info->outline_glyph, &outline); 1257 // calculating rotation shift vector (from rotation origin to the glyph basepoint) 1258 shift.x = key->shift_x; 1259 shift.y = key->shift_y; 1260 fax_scaled = info->fax * 1261 render_priv->state.scale_x; 1262 fay_scaled = info->fay * render_priv->state.scale_y; 1263 // apply rotation 1264 transform_3d(shift, &glyph, &outline, 1265 info->frx, info->fry, info->frz, fax_scaled, 1266 fay_scaled, render_priv->font_scale, info->asc); 1268 hash_val.bm = hash_val.bm_o = hash_val.bm_s = 0; 1267 1269 1268 // PAR correction scaling 1269 FT_Matrix m = { double_to_d16(scale_x), 0, 1270 0, double_to_d16(1.0) }; 1270 outline_copy(render_priv->ftlibrary, info->outline, &outline); 1271 outline_copy(render_priv->ftlibrary, info->border, &border); 1271 1272 1272 // subpixel shift 1273 if (glyph) { 1274 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; 1275 if (scale_x != 1.0) 1276 FT_Outline_Transform(outl, &m); 1277 FT_Outline_Translate(outl, key->advance.x, -key->advance.y); 1278 } 1279 if (outline) { 1280 FT_Outline *outl = &((FT_OutlineGlyph) outline)->outline; 1281 if (scale_x != 1.0) 1282 FT_Outline_Transform(outl, &m); 1283 FT_Outline_Translate(outl, key->advance.x, -key->advance.y); 1284 } 1285 // render glyph 1286 error = glyph_to_bitmap(render_priv->library, 1287 render_priv->synth_priv, 1288 glyph, outline, 1289 &info->bm, &info->bm_o, 1290 &info->bm_s, info->be, 1291 info->blur * render_priv->border_scale, 1292 key->shadow_offset, key->border_style); 1293 if (error) 1294 info->symbol = 0; 1273 // calculating rotation shift vector (from rotation origin to the glyph basepoint) 1274 shift.x = key->shift_x; 1275 shift.y = key->shift_y; 1276 fax_scaled = info->fax * render_priv->state.scale_x; 1277 fay_scaled = info->fay * render_priv->state.scale_y; 1295 1278 1296 // add bitmaps to cache 1297 hash_val.bm_o = info->bm_o; 1298 hash_val.bm = info->bm; 1299 hash_val.bm_s = info->bm_s; 1300 cache_add_bitmap(render_priv->cache.bitmap_cache, key, &hash_val); 1279 // apply rotation 1280 transform_3d(shift, outline, border, 1281 info->frx, info->fry, info->frz, fax_scaled, 1282 fay_scaled, render_priv->font_scale, info->asc); 1301 1283 1302 FT_Done_Glyph(glyph); 1303 FT_Done_Glyph(outline); 1284 // PAR correction scaling 1285 FT_Matrix m = { double_to_d16(scale_x), 0, 1286 0, double_to_d16(1.0) }; 1287 1288 // subpixel shift 1289 if (outline) { 1290 if (scale_x != 1.0) 1291 FT_Outline_Transform(outline, &m); 1292 FT_Outline_Translate(outline, key->advance.x, -key->advance.y); 1304 1293 } 1294 if (border) { 1295 if (scale_x != 1.0) 1296 FT_Outline_Transform(border, &m); 1297 FT_Outline_Translate(border, key->advance.x, -key->advance.y); 1298 } 1299 1300 // render glyph 1301 error = outline_to_bitmap3(render_priv->library, 1302 render_priv->synth_priv, 1303 render_priv->ftlibrary, 1304 outline, border, 1305 &hash_val.bm, &hash_val.bm_o, 1306 &hash_val.bm_s, info->be, 1307 info->blur * render_priv->border_scale, 1308 key->shadow_offset, 1309 render_priv->state.style->BorderStyle); 1310 if (error) 1311 info->symbol = 0; 1312 1313 val = ass_cache_put(render_priv->cache.bitmap_cache, &info->hash_key, 1314 &hash_val); 1315 1316 outline_free(render_priv->ftlibrary, outline); 1317 outline_free(render_priv->ftlibrary, border); 1305 1318 } 1306 1319 1320 info->bm = val->bm; 1321 info->bm_o = val->bm_o; 1322 info->bm_s = val->bm_s; 1323 1307 1324 // VSFilter compatibility: invisible fill and no border? 1308 1325 // In this case no shadow is supposed to be rendered. 1309 if (!info-> outline_glyph&& (info->c[0] & 0xFF) == 0xFF)1326 if (!info->border && (info->c[0] & 0xFF) == 0xFF) 1310 1327 info->bm_s = 0; 1311 1328 } 1312 1329 … … 1434 1451 double pen_shift_x; 1435 1452 double pen_shift_y; 1436 1453 int cur_line; 1454 int run_offset; 1437 1455 TextInfo *text_info = &render_priv->text_info; 1438 1456 1439 1457 last_space = -1; … … 1474 1492 sizeof(LineInfo) * 1475 1493 text_info->max_lines); 1476 1494 } 1477 if (lead < text_info->length) 1495 if (lead < text_info->length) { 1478 1496 text_info->glyphs[lead].linebreak = break_type; 1479 last_space = -1; 1480 s1 = text_info->glyphs + lead; 1481 s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); 1482 text_info->n_lines++; 1497 last_space = -1; 1498 s1 = text_info->glyphs + lead; 1499 s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); 1500 text_info->n_lines++; 1501 } 1483 1502 } 1484 1503 } 1485 1504 #define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y)) … … 1543 1562 pen_shift_x = 0.; 1544 1563 pen_shift_y = 0.; 1545 1564 cur_line = 1; 1565 run_offset = 0; 1546 1566 1547 1567 i = 0; 1548 1568 cur = text_info->glyphs + i; … … 1558 1578 double height = 1559 1579 text_info->lines[cur_line - 1].desc + 1560 1580 text_info->lines[cur_line].asc; 1581 text_info->lines[cur_line - 1].len = i - 1582 text_info->lines[cur_line - 1].offset; 1583 text_info->lines[cur_line].offset = i; 1561 1584 cur_line++; 1585 run_offset++; 1562 1586 pen_shift_x = d6_to_double(-cur->pos.x); 1563 1587 pen_shift_y += height + render_priv->settings.line_spacing; 1564 1588 ass_msg(render_priv->library, MSGL_DBG2, 1565 1589 "shifting from %d to %d by (%f, %f)", i, 1566 1590 text_info->length - 1, pen_shift_x, pen_shift_y); 1567 1591 } 1592 cur->bm_run_id += run_offset; 1568 1593 cur->pos.x += double_to_d6(pen_shift_x); 1569 1594 cur->pos.y += double_to_d6(pen_shift_y); 1570 1595 } 1571 } 1596 text_info->lines[cur_line - 1].len = 1597 text_info->length - text_info->lines[cur_line - 1].offset; 1572 1598 1573 /** 1574 * \brief determine karaoke effects 1575 * Karaoke effects cannot be calculated during parse stage (get_next_char()), 1576 * so they are done in a separate step. 1577 * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's 1578 * (the first glyph of the karaoke word)'s effect_type and effect_timing. 1579 * This function: 1580 * 1. sets effect_type for all glyphs in the word (_karaoke_ word) 1581 * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts 1582 * (left part is filled with PrimaryColour, right one - with SecondaryColour). 1583 */ 1584 static void process_karaoke_effects(ASS_Renderer *render_priv) 1585 { 1586 GlyphInfo *cur, *cur2; 1587 GlyphInfo *s1, *e1; // start and end of the current word 1588 GlyphInfo *s2; // start of the next word 1589 int i; 1590 int timing; // current timing 1591 int tm_start, tm_end; // timings at start and end of the current word 1592 int tm_current; 1593 double dt; 1594 int x; 1595 int x_start, x_end; 1596 1597 tm_current = render_priv->time - render_priv->state.event->Start; 1598 timing = 0; 1599 s1 = s2 = 0; 1600 for (i = 0; i <= render_priv->text_info.length; ++i) { 1601 cur = render_priv->text_info.glyphs + i; 1602 if ((i == render_priv->text_info.length) 1603 || (cur->effect_type != EF_NONE)) { 1604 s1 = s2; 1605 s2 = cur; 1606 if (s1) { 1607 e1 = s2 - 1; 1608 tm_start = timing + s1->effect_skip_timing; 1609 tm_end = tm_start + s1->effect_timing; 1610 timing = tm_end; 1611 x_start = 1000000; 1612 x_end = -1000000; 1613 for (cur2 = s1; cur2 <= e1; ++cur2) { 1614 x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x)); 1615 x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x)); 1616 } 1617 1618 dt = (tm_current - tm_start); 1619 if ((s1->effect_type == EF_KARAOKE) 1620 || (s1->effect_type == EF_KARAOKE_KO)) { 1621 if (dt > 0) 1622 x = x_end + 1; 1623 else 1624 x = x_start; 1625 } else if (s1->effect_type == EF_KARAOKE_KF) { 1626 dt /= (tm_end - tm_start); 1627 x = x_start + (x_end - x_start) * dt; 1628 } else { 1629 ass_msg(render_priv->library, MSGL_ERR, 1630 "Unknown effect type"); 1631 continue; 1632 } 1633 1634 for (cur2 = s1; cur2 <= e1; ++cur2) { 1635 cur2->effect_type = s1->effect_type; 1636 cur2->effect_timing = x - d6_to_int(cur2->pos.x); 1637 } 1638 } 1639 } 1599 #if 0 1600 // print line info 1601 for (i = 0; i < text_info->n_lines; i++) { 1602 printf("line %d offset %d length %d\n", i, text_info->lines[i].offset, 1603 text_info->lines[i].len); 1640 1604 } 1605 #endif 1641 1606 } 1642 1607 1643 1608 /** … … 1680 1645 * Prepare bitmap hash key of a glyph 1681 1646 */ 1682 1647 static void 1683 fill_bitmap_hash(ASS_Renderer *priv, BitmapHashKey *hash_key,1684 ASS_Drawing *drawing, FT_Vector pen, uint32_t code)1648 fill_bitmap_hash(ASS_Renderer *priv, GlyphInfo *info, 1649 OutlineBitmapHashKey *hash_key) 1685 1650 { 1686 if (!drawing->hash) { 1687 hash_key->font = priv->state.font; 1688 hash_key->size = priv->state.font_size; 1689 hash_key->bold = priv->state.bold; 1690 hash_key->italic = priv->state.italic; 1691 } else { 1692 hash_key->drawing_hash = drawing->hash; 1693 hash_key->size = drawing->scale; 1694 } 1695 hash_key->ch = code; 1696 hash_key->outline.x = double_to_d16(priv->state.border_x); 1697 hash_key->outline.y = double_to_d16(priv->state.border_y); 1698 hash_key->scale_x = double_to_d16(priv->state.scale_x); 1699 hash_key->scale_y = double_to_d16(priv->state.scale_y); 1700 hash_key->frx = rot_key(priv->state.frx); 1701 hash_key->fry = rot_key(priv->state.fry); 1702 hash_key->frz = rot_key(priv->state.frz); 1703 hash_key->fax = double_to_d16(priv->state.fax); 1704 hash_key->fay = double_to_d16(priv->state.fay); 1705 hash_key->be = priv->state.be; 1706 hash_key->blur = priv->state.blur; 1707 hash_key->border_style = priv->state.style->BorderStyle; 1651 hash_key->frx = rot_key(info->frx); 1652 hash_key->fry = rot_key(info->fry); 1653 hash_key->frz = rot_key(info->frz); 1654 hash_key->fax = double_to_d16(info->fax); 1655 hash_key->fay = double_to_d16(info->fay); 1656 hash_key->be = info->be; 1657 hash_key->blur = info->blur; 1708 1658 hash_key->shadow_offset.x = double_to_d6( 1709 priv->state.shadow_x * priv->border_scale -1710 (int) ( priv->state.shadow_x * priv->border_scale));1659 info->shadow_x * priv->border_scale - 1660 (int) (info->shadow_x * priv->border_scale)); 1711 1661 hash_key->shadow_offset.y = double_to_d6( 1712 priv->state.shadow_y * priv->border_scale - 1713 (int) (priv->state.shadow_y * priv->border_scale)); 1714 hash_key->flags = priv->state.flags; 1662 info->shadow_y * priv->border_scale - 1663 (int) (info->shadow_y * priv->border_scale)); 1715 1664 } 1716 1665 1717 1666 /** … … 1734 1683 int MarginL, MarginR, MarginV; 1735 1684 int last_break; 1736 1685 int alignment, halign, valign; 1737 int kern = render_priv->track->Kerning;1738 1686 double device_x = 0; 1739 1687 double device_y = 0; 1740 1688 TextInfo *text_info = &render_priv->text_info; … … 1754 1702 1755 1703 drawing = render_priv->state.drawing; 1756 1704 text_info->length = 0; 1757 pen.x = 0;1758 pen.y = 0;1759 previous = 0;1760 1705 num_glyphs = 0; 1761 1706 p = event->Text; 1707 1762 1708 // Event parsing. 1763 1709 while (1) { 1764 1710 // get next char, executing style override … … 1769 1715 ass_drawing_add_char(drawing, (char) code); 1770 1716 } while (code && render_priv->state.drawing_mode); // skip everything in drawing mode 1771 1717 1718 if (text_info->length >= text_info->max_glyphs) { 1719 // Raise maximum number of glyphs 1720 text_info->max_glyphs *= 2; 1721 text_info->glyphs = glyphs = 1722 realloc(text_info->glyphs, 1723 sizeof(GlyphInfo) * text_info->max_glyphs); 1724 } 1725 1726 // Clear current GlyphInfo 1727 memset(&glyphs[text_info->length], 0, sizeof(GlyphInfo)); 1728 1772 1729 // Parse drawing 1773 1730 if (drawing->i) { 1774 1731 drawing->scale_x = render_priv->state.scale_x * 1775 1732 render_priv->font_scale; 1776 1733 drawing->scale_y = render_priv->state.scale_y * 1777 1734 render_priv->font_scale; 1778 ass_drawing_hash(drawing);1779 1735 p--; 1780 code = -1; 1736 code = 0xfffc; // object replacement character 1737 glyphs[text_info->length].drawing = drawing; 1781 1738 } 1782 1739 1783 1740 // face could have been changed in get_next_char … … 1789 1746 if (code == 0) 1790 1747 break; 1791 1748 1792 if (text_info->length >= text_info->max_glyphs) { 1793 // Raise maximum number of glyphs 1794 text_info->max_glyphs *= 2; 1795 text_info->glyphs = glyphs = 1796 realloc(text_info->glyphs, 1797 sizeof(GlyphInfo) * text_info->max_glyphs); 1798 } 1799 1800 // Add kerning to pen 1801 if (kern && previous && code && !drawing->hash) { 1802 FT_Vector delta; 1803 delta = 1804 ass_font_get_kerning(render_priv->state.font, previous, 1805 code); 1806 pen.x += delta.x * render_priv->state.scale_x; 1807 pen.y += delta.y * render_priv->state.scale_y; 1808 } 1809 1810 ass_font_set_transform(render_priv->state.font, 1811 render_priv->state.scale_x, 1812 render_priv->state.scale_y, NULL); 1813 1814 get_outline_glyph(render_priv, code, 1815 glyphs + text_info->length, drawing); 1816 1817 // Add additional space after italic to non-italic style changes 1818 if (text_info->length && 1819 glyphs[text_info->length - 1].hash_key.italic && 1820 !render_priv->state.italic) { 1821 int back = text_info->length - 1; 1822 GlyphInfo *og = &glyphs[back]; 1823 while (back && og->bbox.xMax - og->bbox.xMin == 0 1824 && og->hash_key.italic) 1825 og = &glyphs[--back]; 1826 if (og->bbox.xMax > og->advance.x) { 1827 // The FreeType oblique slants by 6/16 1828 pen.x += og->bbox.yMax * 0.375; 1829 } 1830 } 1831 1832 glyphs[text_info->length].pos.x = pen.x; 1833 glyphs[text_info->length].pos.y = pen.y; 1834 1835 pen.x += glyphs[text_info->length].advance.x; 1836 pen.x += double_to_d6(render_priv->state.hspacing * 1837 render_priv->font_scale 1838 * render_priv->state.scale_x); 1839 pen.y += glyphs[text_info->length].advance.y; 1840 pen.y += (render_priv->state.fay * render_priv->state.scale_y) * 1841 glyphs[text_info->length].advance.x; 1842 1843 previous = code; 1844 1749 // Fill glyph information 1845 1750 glyphs[text_info->length].symbol = code; 1846 glyphs[text_info->length]. linebreak = 0;1751 glyphs[text_info->length].font = render_priv->state.font; 1847 1752 for (i = 0; i < 4; ++i) { 1848 1753 uint32_t clr = render_priv->state.c[i]; 1849 1754 change_alpha(&clr, … … 1855 1760 render_priv->state.effect_timing; 1856 1761 glyphs[text_info->length].effect_skip_timing = 1857 1762 render_priv->state.effect_skip_timing; 1763 glyphs[text_info->length].font_size = ensure_font_size(render_priv, 1764 render_priv->state.font_size * render_priv->font_scale); 1858 1765 glyphs[text_info->length].be = render_priv->state.be; 1859 1766 glyphs[text_info->length].blur = render_priv->state.blur; 1860 1767 glyphs[text_info->length].shadow_x = render_priv->state.shadow_x; 1861 1768 glyphs[text_info->length].shadow_y = render_priv->state.shadow_y; 1769 glyphs[text_info->length].scale_x= render_priv->state.scale_x; 1770 glyphs[text_info->length].scale_y = render_priv->state.scale_y; 1771 glyphs[text_info->length].border_x= render_priv->state.border_x; 1772 glyphs[text_info->length].border_y = render_priv->state.border_y; 1773 glyphs[text_info->length].bold = render_priv->state.bold; 1774 glyphs[text_info->length].italic = render_priv->state.italic; 1775 glyphs[text_info->length].flags = render_priv->state.flags; 1862 1776 glyphs[text_info->length].frx = render_priv->state.frx; 1863 1777 glyphs[text_info->length].fry = render_priv->state.fry; 1864 1778 glyphs[text_info->length].frz = render_priv->state.frz; 1865 1779 glyphs[text_info->length].fax = render_priv->state.fax; 1866 1780 glyphs[text_info->length].fay = render_priv->state.fay; 1867 if (drawing->hash) { 1868 glyphs[text_info->length].asc = drawing->asc; 1869 glyphs[text_info->length].desc = drawing->desc; 1870 } else { 1871 ass_font_get_asc_desc(render_priv->state.font, code, 1872 &glyphs[text_info->length].asc, 1873 &glyphs[text_info->length].desc); 1781 glyphs[text_info->length].bm_run_id = render_priv->state.bm_run_id; 1874 1782 1875 glyphs[text_info->length].asc *= render_priv->state.scale_y; 1876 glyphs[text_info->length].desc *= render_priv->state.scale_y; 1783 if (glyphs[text_info->length].drawing) { 1784 drawing = render_priv->state.drawing = 1785 ass_drawing_new(render_priv->library, render_priv->ftlibrary); 1877 1786 } 1878 1787 1879 // fill bitmap hash1880 fill_bitmap_hash(render_priv, &glyphs[text_info->length].hash_key,1881 drawing, pen, code);1882 1883 1788 text_info->length++; 1884 1789 1885 1790 render_priv->state.effect_type = EF_NONE; 1886 1791 render_priv->state.effect_timing = 0; 1887 1792 render_priv->state.effect_skip_timing = 0; 1888 1793 1889 if (drawing->hash) {1890 ass_drawing_free(drawing);1891 drawing = render_priv->state.drawing =1892 ass_drawing_new(render_priv->fontconfig_priv,1893 render_priv->state.font,1894 render_priv->ftlibrary);1895 }1896 1794 } 1897 1795 1898 1899 1796 if (text_info->length == 0) { 1900 1797 // no valid symbols in the event; this can be smth like {comment} 1901 1798 free_render_context(render_priv); 1902 1799 return 1; 1903 1800 } 1904 1801 1802 // Find shape runs and shape text 1803 ass_shaper_set_base_direction(render_priv->shaper, 1804 resolve_base_direction(render_priv->state.font_encoding)); 1805 ass_shaper_find_runs(render_priv->shaper, render_priv, glyphs, 1806 text_info->length); 1807 ass_shaper_shape(render_priv->shaper, text_info); 1808 1809 // Retrieve glyphs 1810 for (i = 0; i < text_info->length; i++) { 1811 GlyphInfo *info = glyphs + i; 1812 while (info) { 1813 get_outline_glyph(render_priv, info); 1814 info = info->next; 1815 } 1816 info = glyphs + i; 1817 1818 // Add additional space after italic to non-italic style changes 1819 if (i && glyphs[i - 1].italic && !info->italic) { 1820 int back = i - 1; 1821 GlyphInfo *og = &glyphs[back]; 1822 while (back && og->bbox.xMax - og->bbox.xMin == 0 1823 && og->italic) 1824 og = &glyphs[--back]; 1825 if (og->bbox.xMax > og->cluster_advance.x) 1826 og->cluster_advance.x = og->bbox.xMax; 1827 } 1828 1829 // add horizontal letter spacing 1830 info->cluster_advance.x += double_to_d6(render_priv->state.hspacing * 1831 render_priv->font_scale * info->scale_x); 1832 1833 // add displacement for vertical shearing 1834 info->cluster_advance.y += (info->fay * info->scale_y) * info->cluster_advance.x; 1835 1836 } 1837 1838 // Preliminary layout (for line wrapping) 1839 previous = 0; 1840 pen.x = 0; 1841 pen.y = 0; 1842 for (i = 0; i < text_info->length; i++) { 1843 GlyphInfo *info = glyphs + i; 1844 FT_Vector cluster_pen = pen; 1845 while (info) { 1846 info->pos.x = cluster_pen.x; 1847 info->pos.y = cluster_pen.y; 1848 1849 cluster_pen.x += info->advance.x; 1850 cluster_pen.y += info->advance.y; 1851 1852 // fill bitmap hash 1853 info->hash_key.type = BITMAP_OUTLINE; 1854 fill_bitmap_hash(render_priv, info, &info->hash_key.u.outline); 1855 1856 info = info->next; 1857 } 1858 info = glyphs + i; 1859 pen.x += info->cluster_advance.x; 1860 pen.y += info->cluster_advance.y; 1861 previous = info->symbol; 1862 } 1863 1864 1905 1865 // depends on glyph x coordinates being monotonous, so it should be done before line wrap 1906 1866 process_karaoke_effects(render_priv); 1907 1867 … … 1917 1877 MarginV = 1918 1878 (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV; 1919 1879 1880 // calculate max length of a line 1881 double max_text_width = 1882 x2scr(render_priv, render_priv->track->PlayResX - MarginR) - 1883 x2scr(render_priv, MarginL); 1884 1885 // wrap lines 1920 1886 if (render_priv->state.evt_type != EVENT_HSCROLL) { 1921 double max_text_width;1922 1923 // calculate max length of a line1924 max_text_width =1925 x2scr(render_priv,1926 render_priv->track->PlayResX - MarginR) -1927 x2scr(render_priv, MarginL);1928 1929 1887 // rearrange text in several lines 1930 1888 wrap_lines_smart(render_priv, max_text_width); 1889 } else { 1890 // no breaking or wrapping, everything in a single line 1891 text_info->lines[0].offset = 0; 1892 text_info->lines[0].len = text_info->length; 1893 text_info->n_lines = 1; 1894 measure_text(render_priv); 1895 } 1931 1896 1932 // align text 1933 last_break = -1; 1934 for (i = 1; i < text_info->length + 1; ++i) { // (text_info->length + 1) is the end of the last line 1935 if ((i == text_info->length) 1936 || glyphs[i].linebreak) { 1937 double width, shift = 0; 1938 GlyphInfo *first_glyph = 1939 glyphs + last_break + 1; 1940 GlyphInfo *last_glyph = glyphs + i - 1; 1897 // Reorder text into visual order 1898 FriBidiStrIndex *cmap = ass_shaper_reorder(render_priv->shaper, text_info); 1941 1899 1942 while (first_glyph < last_glyph && first_glyph->skip) 1943 first_glyph++; 1900 // Reposition according to the map 1901 pen.x = 0; 1902 pen.y = 0; 1903 int lineno = 1; 1904 for (i = 0; i < text_info->length; i++) { 1905 GlyphInfo *info = glyphs + cmap[i]; 1906 if (glyphs[i].linebreak) { 1907 pen.x = 0; 1908 pen.y += double_to_d6(text_info->lines[lineno-1].desc); 1909 pen.y += double_to_d6(text_info->lines[lineno].asc); 1910 pen.y += double_to_d6(render_priv->settings.line_spacing); 1911 lineno++; 1912 } 1913 if (info->skip) continue; 1914 FT_Vector cluster_pen = pen; 1915 while (info) { 1916 info->pos.x = info->offset.x + cluster_pen.x; 1917 info->pos.y = info->offset.y + cluster_pen.y; 1918 cluster_pen.x += info->advance.x; 1919 cluster_pen.y += info->advance.y; 1920 info = info->next; 1921 } 1922 info = glyphs + cmap[i]; 1923 pen.x += info->cluster_advance.x; 1924 pen.y += info->cluster_advance.y; 1925 } 1944 1926 1945 while ((last_glyph > first_glyph) 1946 && ((last_glyph->symbol == '\n') 1947 || (last_glyph->symbol == 0) 1948 || (last_glyph->skip))) 1949 last_glyph--; 1950 1951 width = d6_to_double( 1952 last_glyph->pos.x + last_glyph->advance.x - 1953 first_glyph->pos.x); 1927 // align lines 1928 if (render_priv->state.evt_type != EVENT_HSCROLL) { 1929 last_break = -1; 1930 double width = 0; 1931 for (i = 0; i <= text_info->length; ++i) { // (text_info->length + 1) is the end of the last line 1932 if ((i == text_info->length) || glyphs[i].linebreak) { 1933 // remove letter spacing (which is included in cluster_advance) 1934 if (i > 0) 1935 width -= render_priv->state.hspacing * render_priv->font_scale * 1936 glyphs[i-1].scale_x; 1937 double shift = 0; 1954 1938 if (halign == HALIGN_LEFT) { // left aligned, no action 1955 1939 shift = 0; 1956 1940 } else if (halign == HALIGN_RIGHT) { // right aligned … … 1959 1943 shift = (max_text_width - width) / 2.0; 1960 1944 } 1961 1945 for (j = last_break + 1; j < i; ++j) { 1962 glyphs[j].pos.x += double_to_d6(shift); 1946 GlyphInfo *info = glyphs + j; 1947 while (info) { 1948 info->pos.x += double_to_d6(shift); 1949 info = info->next; 1950 } 1963 1951 } 1964 1952 last_break = i - 1; 1953 width = 0; 1965 1954 } 1955 if (i < text_info->length && !glyphs[i].skip && 1956 glyphs[i].symbol != '\n' && glyphs[i].symbol != 0) { 1957 width += d6_to_double(glyphs[i].cluster_advance.x); 1958 } 1966 1959 } 1967 } else { // render_priv->state.evt_type == EVENT_HSCROLL1968 measure_text(render_priv);1969 1960 } 1970 1961 1971 1962 // determing text bounding box … … 2091 2082 2092 2083 for (i = 0; i < text_info->length; ++i) { 2093 2084 GlyphInfo *info = glyphs + i; 2085 while (info) { 2086 OutlineBitmapHashKey *key = &info->hash_key.u.outline; 2094 2087 2095 if (info->hash_key.frx || info->hash_key.fry 2096 || info->hash_key.frz || info->hash_key.fax 2097 || info->hash_key.fay) { 2098 info->hash_key.shift_x = info->pos.x + double_to_d6(device_x - center.x); 2099 info->hash_key.shift_y = 2100 -(info->pos.y + double_to_d6(device_y - center.y)); 2101 } else { 2102 info->hash_key.shift_x = 0; 2103 info->hash_key.shift_y = 0; 2088 if (key->frx || key->fry || key->frz || key->fax || key->fay) { 2089 key->shift_x = info->pos.x + double_to_d6(device_x - center.x); 2090 key->shift_y = -(info->pos.y + double_to_d6(device_y - center.y)); 2091 } else { 2092 key->shift_x = 0; 2093 key->shift_y = 0; 2094 } 2095 info = info->next; 2104 2096 } 2105 2097 } 2106 2098 } 2107 2099 2108 2100 // convert glyphs to bitmaps 2109 device_x *= render_priv->font_scale_x; 2101 int left = render_priv->settings.left_margin; 2102 device_x = (device_x - left) * render_priv->font_scale_x + left; 2110 2103 for (i = 0; i < text_info->length; ++i) { 2111 GlyphInfo *g = glyphs + i; 2112 g->pos.x *= render_priv->font_scale_x; 2113 g->hash_key.advance.x = 2114 double_to_d6(device_x - (int) device_x + 2115 d6_to_double(g->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; 2116 g->hash_key.advance.y = 2117 double_to_d6(device_y - (int) device_y + 2118 d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; 2119 get_bitmap_glyph(render_priv, glyphs + i); 2104 GlyphInfo *info = glyphs + i; 2105 while (info) { 2106 OutlineBitmapHashKey *key = &info->hash_key.u.outline; 2107 info->pos.x *= render_priv->font_scale_x; 2108 key->advance.x = 2109 double_to_d6(device_x - (int) device_x + 2110 d6_to_double(info->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; 2111 key->advance.y = 2112 double_to_d6(device_y - (int) device_y + 2113 d6_to_double(info->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; 2114 get_bitmap_glyph(render_priv, info); 2115 info = info->next; 2116 } 2120 2117 } 2121 2118 2122 2119 memset(event_images, 0, sizeof(*event_images)); … … 2131 2128 event_images->event = event; 2132 2129 event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y); 2133 2130 2131 ass_shaper_cleanup(render_priv->shaper, text_info); 2134 2132 free_render_context(render_priv); 2135 2133 2136 2134 return 0; … … 2154 2152 */ 2155 2153 static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache) 2156 2154 { 2157 if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) { 2158 ass_msg(priv->library, MSGL_V, 2159 "Hitting hard bitmap cache limit (was: %ld bytes), " 2160 "resetting.", (long) cache->bitmap_cache->cache_size); 2161 cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache); 2162 cache->composite_cache = ass_composite_cache_reset( 2163 cache->composite_cache); 2155 if (ass_cache_empty(cache->bitmap_cache, cache->bitmap_max_size)) { 2156 ass_cache_empty(cache->composite_cache, 0); 2164 2157 ass_free_images(priv->prev_images_root); 2165 2158 priv->prev_images_root = 0; 2166 2159 } 2167 2168 if (cache->glyph_cache->count > cache->glyph_max 2169 || cache->glyph_cache->cache_size > cache->bitmap_max_size) { 2170 ass_msg(priv->library, MSGL_V, 2171 "Hitting hard glyph cache limit (was: %d glyphs, %ld bytes), " 2172 "resetting.", 2173 cache->glyph_cache->count, (long) cache->glyph_cache->cache_size); 2174 cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache); 2160 if (ass_cache_empty(cache->outline_cache, cache->glyph_max)) { 2161 ass_cache_empty(cache->bitmap_cache, 0); 2162 ass_cache_empty(cache->composite_cache, 0); 2163 ass_free_images(priv->prev_images_root); 2164 priv->prev_images_root = 0; 2175 2165 } 2176 2166 } 2177 2167 … … 2202 2192 render_priv->track = track; 2203 2193 render_priv->time = now; 2204 2194 2205 ass_lazy_track_init(render_priv );2195 ass_lazy_track_init(render_priv->library, render_priv->track); 2206 2196 2207 2197 render_priv->font_scale = settings_priv->font_size_coeff * 2208 2198 render_priv->orig_height / render_priv->track->PlayResY; … … 2213 2203 else 2214 2204 render_priv->border_scale = 1.; 2215 2205 2206 ass_shaper_set_kerning(render_priv->shaper, track->Kerning); 2207 if (track->Language) 2208 ass_shaper_set_language(render_priv->shaper, track->Language); 2209 ass_shaper_set_level(render_priv->shaper, render_priv->settings.shaper); 2210 2216 2211 // PAR correction 2217 2212 render_priv->font_scale_x = render_priv->settings.aspect / 2218 2213 render_priv->settings.storage_aspect; -
libass/ass_render.h
27 27 #include FT_GLYPH_H 28 28 #include FT_SYNTHESIS_H 29 29 30 // XXX: fix the inclusion mess so we can avoid doing this here 31 typedef struct ass_shaper ASS_Shaper; 32 30 33 #include "ass.h" 31 34 #include "ass_font.h" 32 35 #include "ass_bitmap.h" … … 73 76 double aspect; // frame aspect ratio, d_width / d_height. 74 77 double storage_aspect; // pixel ratio of the source image 75 78 ASS_Hinting hinting; 79 ASS_ShapingLevel shaper; 76 80 77 81 char *default_font; 78 82 char *default_family; … … 96 100 97 101 // describes a glyph 98 102 // GlyphInfo and TextInfo are used for text centering and word-wrapping operations 99 typedef struct {103 typedef struct glyph_info { 100 104 unsigned symbol; 101 105 unsigned skip; // skip glyph when layouting text 102 FT_Glyph glyph; 103 FT_Glyph outline_glyph; 106 ASS_Font *font; 107 int face_index; 108 int glyph_index; 109 double font_size; 110 ASS_Drawing *drawing; 111 FT_Outline *outline; 112 FT_Outline *border; 104 113 Bitmap *bm; // glyph bitmap 105 114 Bitmap *bm_o; // outline bitmap 106 115 Bitmap *bm_s; // shadow bitmap 107 116 FT_BBox bbox; 108 117 FT_Vector pos; 118 FT_Vector offset; 109 119 char linebreak; // the first (leading) glyph of some line ? 110 120 uint32_t c[4]; // colors 111 121 FT_Vector advance; // 26.6 122 FT_Vector cluster_advance; 112 123 Effect effect_type; 113 124 int effect_timing; // time duration of current karaoke word 114 125 // after process_karaoke_effects: distance in pixels from the glyph origin. … … 121 132 double shadow_y; 122 133 double frx, fry, frz; // rotation 123 134 double fax, fay; // text shearing 135 double scale_x, scale_y; 136 double border_x, border_y; 137 unsigned italic; 138 unsigned bold; 139 int flags; 124 140 141 int bm_run_id; 142 int shape_run_id; 143 125 144 BitmapHashKey hash_key; 145 146 // next glyph in this cluster 147 struct glyph_info *next; 126 148 } GlyphInfo; 127 149 128 150 typedef struct { 129 151 double asc, desc; 152 int offset, len; 130 153 } LineInfo; 131 154 132 155 typedef struct { … … 147 170 int parsed_tags; 148 171 149 172 ASS_Font *font; 150 char *font_path;151 173 double font_size; 152 174 int flags; // decoration flags (underline/strike-through) 153 175 … … 186 208 int effect_timing; 187 209 int effect_skip_timing; 188 210 211 // bitmap run id (used for final bitmap rendering) 212 int bm_run_id; 213 189 214 enum { 190 215 SCROLL_LR, // left-to-right 191 216 SCROLL_RL, … … 200 225 unsigned italic; 201 226 int treat_family_as_pattern; 202 227 int wrap_style; 228 int font_encoding; 203 229 } RenderContext; 204 230 205 231 typedef struct { 206 Hashmap*font_cache;207 Hashmap *glyph_cache;208 Hashmap*bitmap_cache;209 Hashmap*composite_cache;232 Cache *font_cache; 233 Cache *outline_cache; 234 Cache *bitmap_cache; 235 Cache *composite_cache; 210 236 size_t glyph_max; 211 237 size_t bitmap_max_size; 212 238 } CacheStore; … … 218 244 ASS_Settings settings; 219 245 int render_id; 220 246 ASS_SynthPriv *synth_priv; 247 ASS_Shaper *shaper; 221 248 222 249 ASS_Image *images_root; // rendering result is stored here 223 250 ASS_Image *prev_images_root; … … 265 292 void reset_render_context(ASS_Renderer *render_priv); 266 293 void ass_free_images(ASS_Image *img); 267 294 295 // XXX: this is actually in ass.c, includes should be fixed later on 296 void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track); 297 268 298 #endif /* LIBASS_RENDER_H */ -
libass/ass_render_api.c
25 25 ASS_Settings *settings = &priv->settings; 26 26 27 27 priv->render_id++; 28 priv->cache.glyph_cache = 29 ass_glyph_cache_reset(priv->cache.glyph_cache); 30 priv->cache.bitmap_cache = 31 ass_bitmap_cache_reset(priv->cache.bitmap_cache); 32 priv->cache.composite_cache = 33 ass_composite_cache_reset(priv->cache.composite_cache); 28 ass_cache_empty(priv->cache.outline_cache, 0); 29 ass_cache_empty(priv->cache.bitmap_cache, 0); 30 ass_cache_empty(priv->cache.composite_cache, 0); 34 31 ass_free_images(priv->prev_images_root); 35 32 priv->prev_images_root = 0; 36 33 … … 61 58 } 62 59 } 63 60 61 void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level) 62 { 63 #ifdef CONFIG_HARFBUZZ 64 // select the complex shaper for illegal values 65 if (level == ASS_SHAPING_SIMPLE || level == ASS_SHAPING_COMPLEX) 66 priv->settings.shaper = level; 67 else 68 priv->settings.shaper = ASS_SHAPING_COMPLEX; 69 #endif 70 } 71 64 72 void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r) 65 73 { 66 74 if (priv->settings.left_margin != l || priv->settings.right_margin != r || -
libass/ass_shaper.c
1 /* 2 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> 3 * 4 * This file is part of libass. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "config.h" 20 21 #include <fribidi/fribidi.h> 22 23 #include "ass_shaper.h" 24 #include "ass_render.h" 25 #include "ass_font.h" 26 #include "ass_parse.h" 27 #include "ass_cache.h" 28 29 #define MAX_RUNS 50 30 31 #ifdef CONFIG_HARFBUZZ 32 #include <hb-ft.h> 33 enum { 34 VERT = 0, 35 VKNA, 36 KERN 37 }; 38 #define NUM_FEATURES 3 39 #endif 40 41 struct ass_shaper { 42 ASS_ShapingLevel shaping_level; 43 44 // FriBidi log2vis 45 int n_glyphs; 46 FriBidiChar *event_text; 47 FriBidiCharType *ctypes; 48 FriBidiLevel *emblevels; 49 FriBidiStrIndex *cmap; 50 FriBidiParType base_direction; 51 52 #ifdef CONFIG_HARFBUZZ 53 // OpenType features 54 int n_features; 55 hb_feature_t *features; 56 hb_language_t language; 57 58 // Glyph metrics cache, to speed up shaping 59 Cache *metrics_cache; 60 #endif 61 }; 62 63 #ifdef CONFIG_HARFBUZZ 64 struct ass_shaper_metrics_data { 65 Cache *metrics_cache; 66 GlyphMetricsHashKey hash_key; 67 int vertical; 68 }; 69 70 struct ass_shaper_font_data { 71 hb_font_t *fonts[ASS_FONT_MAX_FACES]; 72 hb_font_funcs_t *font_funcs[ASS_FONT_MAX_FACES]; 73 struct ass_shaper_metrics_data *metrics_data[ASS_FONT_MAX_FACES]; 74 }; 75 #endif 76 77 /** 78 * \brief Print version information 79 */ 80 void ass_shaper_info(ASS_Library *lib) 81 { 82 ass_msg(lib, MSGL_V, "Shaper: FriBidi " 83 FRIBIDI_VERSION " (SIMPLE)" 84 #ifdef CONFIG_HARFBUZZ 85 " HarfBuzz-ng %s (COMPLEX)", hb_version_string() 86 #endif 87 ); 88 } 89 90 /** 91 * \brief grow arrays, if needed 92 * \param new_size requested size 93 */ 94 static void check_allocations(ASS_Shaper *shaper, size_t new_size) 95 { 96 if (new_size > shaper->n_glyphs) { 97 shaper->event_text = realloc(shaper->event_text, sizeof(FriBidiChar) * new_size); 98 shaper->ctypes = realloc(shaper->ctypes, sizeof(FriBidiCharType) * new_size); 99 shaper->emblevels = realloc(shaper->emblevels, sizeof(FriBidiLevel) * new_size); 100 shaper->cmap = realloc(shaper->cmap, sizeof(FriBidiStrIndex) * new_size); 101 } 102 } 103 104 /** 105 * \brief Free shaper and related data 106 */ 107 void ass_shaper_free(ASS_Shaper *shaper) 108 { 109 #ifdef CONFIG_HARFBUZZ 110 ass_cache_done(shaper->metrics_cache); 111 free(shaper->features); 112 #endif 113 free(shaper->event_text); 114 free(shaper->ctypes); 115 free(shaper->emblevels); 116 free(shaper->cmap); 117 free(shaper); 118 } 119 120 void ass_shaper_font_data_free(ASS_ShaperFontData *priv) 121 { 122 #ifdef CONFIG_HARFBUZZ 123 int i; 124 for (i = 0; i < ASS_FONT_MAX_FACES; i++) 125 if (priv->fonts[i]) { 126 free(priv->metrics_data[i]); 127 hb_font_destroy(priv->fonts[i]); 128 hb_font_funcs_destroy(priv->font_funcs[i]); 129 } 130 free(priv); 131 #endif 132 } 133 134 #ifdef CONFIG_HARFBUZZ 135 /** 136 * \brief set up the HarfBuzz OpenType feature list with some 137 * standard features. 138 */ 139 static void init_features(ASS_Shaper *shaper) 140 { 141 shaper->features = calloc(sizeof(hb_feature_t), NUM_FEATURES); 142 143 shaper->n_features = NUM_FEATURES; 144 shaper->features[VERT].tag = HB_TAG('v', 'e', 'r', 't'); 145 shaper->features[VERT].end = INT_MAX; 146 shaper->features[VKNA].tag = HB_TAG('v', 'k', 'n', 'a'); 147 shaper->features[VKNA].end = INT_MAX; 148 shaper->features[KERN].tag = HB_TAG('k', 'e', 'r', 'n'); 149 shaper->features[KERN].end = INT_MAX; 150 } 151 152 /** 153 * \brief Set features depending on properties of the run 154 */ 155 static void set_run_features(ASS_Shaper *shaper, GlyphInfo *info) 156 { 157 // enable vertical substitutions for @font runs 158 if (info->font->desc.vertical) 159 shaper->features[VERT].value = shaper->features[VKNA].value = 1; 160 else 161 shaper->features[VERT].value = shaper->features[VKNA].value = 0; 162 } 163 164 /** 165 * \brief Update HarfBuzz's idea of font metrics 166 * \param hb_font HarfBuzz font 167 * \param face associated FreeType font face 168 */ 169 static void update_hb_size(hb_font_t *hb_font, FT_Face face) 170 { 171 hb_font_set_scale (hb_font, 172 ((uint64_t) face->size->metrics.x_scale * (uint64_t) face->units_per_EM) >> 16, 173 ((uint64_t) face->size->metrics.y_scale * (uint64_t) face->units_per_EM) >> 16); 174 hb_font_set_ppem (hb_font, face->size->metrics.x_ppem, 175 face->size->metrics.y_ppem); 176 } 177 178 179 /* 180 * Cached glyph metrics getters follow 181 * 182 * These functions replace HarfBuzz' standard FreeType font functions 183 * and provide cached access to essential glyph metrics. This usually 184 * speeds up shaping a lot. It also allows us to use custom load flags. 185 * 186 */ 187 188 GlyphMetricsHashValue * 189 get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face, 190 hb_codepoint_t glyph) 191 { 192 GlyphMetricsHashValue *val; 193 194 metrics->hash_key.glyph_index = glyph; 195 val = ass_cache_get(metrics->metrics_cache, &metrics->hash_key); 196 197 if (!val) { 198 int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH 199 | FT_LOAD_IGNORE_TRANSFORM; 200 GlyphMetricsHashValue new_val; 201 202 if (FT_Load_Glyph(face, glyph, load_flags)) 203 return NULL; 204 205 memcpy(&new_val.metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics)); 206 val = ass_cache_put(metrics->metrics_cache, &metrics->hash_key, &new_val); 207 } 208 209 return val; 210 } 211 212 static hb_bool_t 213 get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, 214 hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data) 215 { 216 FT_Face face = font_data; 217 218 if (variation) 219 *glyph = FT_Face_GetCharVariantIndex(face, unicode, variation); 220 else 221 *glyph = FT_Get_Char_Index(face, unicode); 222 223 return *glyph != 0; 224 } 225 226 static hb_position_t 227 cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, 228 void *user_data) 229 { 230 FT_Face face = font_data; 231 struct ass_shaper_metrics_data *metrics_priv = user_data; 232 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); 233 234 if (!metrics) 235 return 0; 236 237 if (metrics_priv->vertical && glyph > VERTICAL_LOWER_BOUND) 238 return metrics->metrics.vertAdvance; 239 240 return metrics->metrics.horiAdvance; 241 } 242 243 static hb_position_t 244 cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, 245 void *user_data) 246 { 247 FT_Face face = font_data; 248 struct ass_shaper_metrics_data *metrics_priv = user_data; 249 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); 250 251 if (!metrics) 252 return 0; 253 254 return metrics->metrics.vertAdvance; 255 256 } 257 258 static hb_bool_t 259 cached_h_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, 260 hb_position_t *x, hb_position_t *y, void *user_data) 261 { 262 return 1; 263 } 264 265 static hb_bool_t 266 cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, 267 hb_position_t *x, hb_position_t *y, void *user_data) 268 { 269 FT_Face face = font_data; 270 struct ass_shaper_metrics_data *metrics_priv = user_data; 271 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); 272 273 if (!metrics) 274 return 0; 275 276 *x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX; 277 *y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY); 278 279 return 1; 280 } 281 282 static hb_position_t 283 get_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first, 284 hb_codepoint_t second, void *user_data) 285 { 286 FT_Face face = font_data; 287 FT_Vector kern; 288 289 if (FT_Get_Kerning (face, first, second, FT_KERNING_DEFAULT, &kern)) 290 return 0; 291 292 return kern.x; 293 } 294 295 static hb_position_t 296 get_v_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first, 297 hb_codepoint_t second, void *user_data) 298 { 299 return 0; 300 } 301 302 static hb_bool_t 303 cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph, 304 hb_glyph_extents_t *extents, void *user_data) 305 { 306 FT_Face face = font_data; 307 struct ass_shaper_metrics_data *metrics_priv = user_data; 308 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); 309 310 if (!metrics) 311 return 0; 312 313 extents->x_bearing = metrics->metrics.horiBearingX; 314 extents->y_bearing = metrics->metrics.horiBearingY; 315 extents->width = metrics->metrics.width; 316 extents->height = metrics->metrics.height; 317 318 return 1; 319 } 320 321 static hb_bool_t 322 get_contour_point(hb_font_t *font, void *font_data, hb_codepoint_t glyph, 323 unsigned int point_index, hb_position_t *x, 324 hb_position_t *y, void *user_data) 325 { 326 FT_Face face = font_data; 327 int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH 328 | FT_LOAD_IGNORE_TRANSFORM; 329 330 if (FT_Load_Glyph(face, glyph, load_flags)) 331 return 0; 332 333 if (point_index >= (unsigned)face->glyph->outline.n_points) 334 return 0; 335 336 *x = face->glyph->outline.points[point_index].x; 337 *y = face->glyph->outline.points[point_index].y; 338 339 return 1; 340 } 341 342 /** 343 * \brief Retrieve HarfBuzz font from cache. 344 * Create it from FreeType font, if needed. 345 * \param info glyph cluster 346 * \return HarfBuzz font 347 */ 348 static hb_font_t *get_hb_font(ASS_Shaper *shaper, GlyphInfo *info) 349 { 350 ASS_Font *font = info->font; 351 hb_font_t **hb_fonts; 352 353 if (!font->shaper_priv) 354 font->shaper_priv = calloc(sizeof(ASS_ShaperFontData), 1); 355 356 357 hb_fonts = font->shaper_priv->fonts; 358 if (!hb_fonts[info->face_index]) { 359 hb_fonts[info->face_index] = 360 hb_ft_font_create(font->faces[info->face_index], NULL); 361 362 // set up cached metrics access 363 font->shaper_priv->metrics_data[info->face_index] = 364 calloc(sizeof(struct ass_shaper_metrics_data), 1); 365 struct ass_shaper_metrics_data *metrics = 366 font->shaper_priv->metrics_data[info->face_index]; 367 metrics->metrics_cache = shaper->metrics_cache; 368 metrics->vertical = info->font->desc.vertical; 369 370 hb_font_funcs_t *funcs = hb_font_funcs_create(); 371 font->shaper_priv->font_funcs[info->face_index] = funcs; 372 hb_font_funcs_set_glyph_func(funcs, get_glyph, 373 metrics, NULL); 374 hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance, 375 metrics, NULL); 376 hb_font_funcs_set_glyph_v_advance_func(funcs, cached_v_advance, 377 metrics, NULL); 378 hb_font_funcs_set_glyph_h_origin_func(funcs, cached_h_origin, 379 metrics, NULL); 380 hb_font_funcs_set_glyph_v_origin_func(funcs, cached_v_origin, 381 metrics, NULL); 382 hb_font_funcs_set_glyph_h_kerning_func(funcs, get_h_kerning, 383 metrics, NULL); 384 hb_font_funcs_set_glyph_v_kerning_func(funcs, get_v_kerning, 385 metrics, NULL); 386 hb_font_funcs_set_glyph_extents_func(funcs, cached_extents, 387 metrics, NULL); 388 hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point, 389 metrics, NULL); 390 hb_font_set_funcs(hb_fonts[info->face_index], funcs, 391 font->faces[info->face_index], NULL); 392 } 393 394 ass_face_set_size(font->faces[info->face_index], info->font_size); 395 update_hb_size(hb_fonts[info->face_index], font->faces[info->face_index]); 396 397 // update hash key for cached metrics 398 struct ass_shaper_metrics_data *metrics = 399 font->shaper_priv->metrics_data[info->face_index]; 400 metrics->hash_key.font = info->font; 401 metrics->hash_key.face_index = info->face_index; 402 metrics->hash_key.size = info->font_size; 403 metrics->hash_key.scale_x = double_to_d6(info->scale_x); 404 metrics->hash_key.scale_y = double_to_d6(info->scale_y); 405 406 return hb_fonts[info->face_index]; 407 } 408 409 /** 410 * \brief Shape event text with HarfBuzz. Full OpenType shaping. 411 * \param glyphs glyph clusters 412 * \param len number of clusters 413 */ 414 static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len) 415 { 416 int i, j; 417 int run = 0; 418 struct { 419 int offset; 420 int end; 421 hb_buffer_t *buf; 422 hb_font_t *font; 423 } runs[MAX_RUNS]; 424 425 426 for (i = 0; i < len && run < MAX_RUNS; i++, run++) { 427 // get length and level of the current run 428 int k = i; 429 int level = glyphs[i].shape_run_id; 430 int direction = shaper->emblevels[k] % 2; 431 while (i < (len - 1) && level == glyphs[i+1].shape_run_id) 432 i++; 433 runs[run].offset = k; 434 runs[run].end = i; 435 runs[run].buf = hb_buffer_create(); 436 runs[run].font = get_hb_font(shaper, glyphs + k); 437 set_run_features(shaper, glyphs + k); 438 hb_buffer_pre_allocate(runs[run].buf, i - k + 1); 439 hb_buffer_set_direction(runs[run].buf, direction ? HB_DIRECTION_RTL : 440 HB_DIRECTION_LTR); 441 hb_buffer_set_language(runs[run].buf, shaper->language); 442 hb_buffer_add_utf32(runs[run].buf, shaper->event_text + k, i - k + 1, 443 0, i - k + 1); 444 hb_shape(runs[run].font, runs[run].buf, shaper->features, 445 shaper->n_features); 446 } 447 448 // Initialize: skip all glyphs, this is undone later as needed 449 for (i = 0; i < len; i++) 450 glyphs[i].skip = 1; 451 452 // Update glyph indexes, positions and advances from the shaped runs 453 for (i = 0; i < run; i++) { 454 int num_glyphs = hb_buffer_get_length(runs[i].buf); 455 hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(runs[i].buf, NULL); 456 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(runs[i].buf, NULL); 457 458 for (j = 0; j < num_glyphs; j++) { 459 int idx = glyph_info[j].cluster + runs[i].offset; 460 GlyphInfo *info = glyphs + idx; 461 GlyphInfo *root = info; 462 463 // if we have more than one glyph per cluster, allocate a new one 464 // and attach to the root glyph 465 if (info->skip == 0) { 466 while (info->next) 467 info = info->next; 468 info->next = malloc(sizeof(GlyphInfo)); 469 memcpy(info->next, info, sizeof(GlyphInfo)); 470 info = info->next; 471 info->next = NULL; 472 } 473 474 // set position and advance 475 info->skip = 0; 476 info->glyph_index = glyph_info[j].codepoint; 477 info->offset.x = pos[j].x_offset * info->scale_x; 478 info->offset.y = -pos[j].y_offset * info->scale_y; 479 info->advance.x = pos[j].x_advance * info->scale_x; 480 info->advance.y = -pos[j].y_advance * info->scale_y; 481 482 // accumulate advance in the root glyph 483 root->cluster_advance.x += info->advance.x; 484 root->cluster_advance.y += info->advance.y; 485 } 486 } 487 488 // Free runs and associated data 489 for (i = 0; i < run; i++) { 490 hb_buffer_destroy(runs[i].buf); 491 } 492 493 } 494 #endif 495 496 /** 497 * \brief Shape event text with FriBidi. Does mirroring and simple 498 * Arabic shaping. 499 * \param len number of clusters 500 */ 501 static void shape_fribidi(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len) 502 { 503 int i; 504 FriBidiJoiningType *joins = calloc(sizeof(*joins), len); 505 506 // shape on codepoint level 507 fribidi_get_joining_types(shaper->event_text, len, joins); 508 fribidi_join_arabic(shaper->ctypes, len, shaper->emblevels, joins); 509 fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, 510 shaper->emblevels, len, joins, shaper->event_text); 511 512 // update indexes 513 for (i = 0; i < len; i++) { 514 GlyphInfo *info = glyphs + i; 515 FT_Face face = info->font->faces[info->face_index]; 516 info->symbol = shaper->event_text[i]; 517 info->glyph_index = FT_Get_Char_Index(face, shaper->event_text[i]); 518 } 519 520 free(joins); 521 } 522 523 /** 524 * \brief Toggle kerning for HarfBuzz shaping. 525 * NOTE: currently only works with OpenType fonts, the TrueType fallback *always* 526 * kerns. It's a bug in HarfBuzz. 527 */ 528 void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern) 529 { 530 #ifdef CONFIG_HARFBUZZ 531 shaper->features[KERN].value = !!kern; 532 #endif 533 } 534 535 /** 536 * \brief Find shape runs according to the event's selected fonts 537 */ 538 void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv, 539 GlyphInfo *glyphs, size_t len) 540 { 541 int i; 542 int shape_run = 0; 543 544 for (i = 0; i < len; i++) { 545 GlyphInfo *last = glyphs + i - 1; 546 GlyphInfo *info = glyphs + i; 547 // skip drawings 548 if (info->symbol == 0xfffc) 549 continue; 550 // set size and get glyph index 551 ass_font_get_index(render_priv->fontconfig_priv, info->font, 552 info->symbol, &info->face_index, &info->glyph_index); 553 // shape runs share the same font face and size 554 if (i > 0 && (last->font != info->font || 555 last->font_size != info->font_size || 556 last->face_index != info->face_index)) 557 shape_run++; 558 info->shape_run_id = shape_run; 559 } 560 561 } 562 563 /** 564 * \brief Set base direction (paragraph direction) of the text. 565 * \param dir base direction 566 */ 567 void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir) 568 { 569 shaper->base_direction = dir; 570 } 571 572 /** 573 * \brief Set language hint. Some languages have specific character variants, 574 * like Serbian Cyrillic. 575 * \param lang ISO 639-1 two-letter language code 576 */ 577 void ass_shaper_set_language(ASS_Shaper *shaper, const char *code) 578 { 579 #ifdef CONFIG_HARFBUZZ 580 shaper->language = hb_language_from_string(code, -1); 581 #endif 582 } 583 584 /** 585 * Set shaping level. Essentially switches between FriBidi and HarfBuzz. 586 */ 587 void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level) 588 { 589 shaper->shaping_level = level; 590 } 591 592 /** 593 * \brief Shape an event's text. Calculates directional runs and shapes them. 594 * \param text_info event's text 595 */ 596 void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info) 597 { 598 int i, last_break; 599 FriBidiParType dir; 600 GlyphInfo *glyphs = text_info->glyphs; 601 602 check_allocations(shaper, text_info->length); 603 604 // Get bidi character types and embedding levels 605 last_break = 0; 606 for (i = 0; i < text_info->length; i++) { 607 shaper->event_text[i] = glyphs[i].symbol; 608 // embedding levels should be calculated paragraph by paragraph 609 if (glyphs[i].symbol == '\n' || i == text_info->length - 1) { 610 dir = shaper->base_direction; 611 fribidi_get_bidi_types(shaper->event_text + last_break, 612 i - last_break + 1, shaper->ctypes + last_break); 613 fribidi_get_par_embedding_levels(shaper->ctypes + last_break, 614 i - last_break + 1, &dir, shaper->emblevels + last_break); 615 last_break = i + 1; 616 } 617 } 618 619 // add embedding levels to shape runs for final runs 620 for (i = 0; i < text_info->length; i++) { 621 glyphs[i].shape_run_id += shaper->emblevels[i]; 622 } 623 624 #ifdef CONFIG_HARFBUZZ 625 switch (shaper->shaping_level) { 626 case ASS_SHAPING_SIMPLE: 627 shape_fribidi(shaper, glyphs, text_info->length); 628 break; 629 case ASS_SHAPING_COMPLEX: 630 shape_harfbuzz(shaper, glyphs, text_info->length); 631 break; 632 } 633 #else 634 shape_fribidi(shaper, glyphs, text_info->length); 635 #endif 636 637 638 // clean up 639 for (i = 0; i < text_info->length; i++) { 640 // Skip direction override control characters 641 // NOTE: Behdad said HarfBuzz is supposed to remove these, but this hasn't 642 // been implemented yet 643 if (glyphs[i].symbol <= 0x202F && glyphs[i].symbol >= 0x202a) { 644 glyphs[i].symbol = 0; 645 glyphs[i].skip++; 646 } 647 } 648 } 649 650 /** 651 * \brief Create a new shaper instance and preallocate data structures 652 * \param prealloc preallocation size 653 */ 654 ASS_Shaper *ass_shaper_new(size_t prealloc) 655 { 656 ASS_Shaper *shaper = calloc(sizeof(*shaper), 1); 657 658 shaper->base_direction = FRIBIDI_PAR_ON; 659 check_allocations(shaper, prealloc); 660 661 #ifdef CONFIG_HARFBUZZ 662 init_features(shaper); 663 shaper->metrics_cache = ass_glyph_metrics_cache_create(); 664 #endif 665 666 return shaper; 667 } 668 669 670 /** 671 * \brief clean up additional data temporarily needed for shaping and 672 * (e.g. additional glyphs allocated) 673 */ 674 void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info) 675 { 676 int i; 677 678 for (i = 0; i < text_info->length; i++) { 679 GlyphInfo *info = text_info->glyphs + i; 680 info = info->next; 681 while (info) { 682 GlyphInfo *next = info->next; 683 free(info); 684 info = next; 685 } 686 } 687 } 688 689 /** 690 * \brief Calculate reorder map to render glyphs in visual order 691 */ 692 FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info) 693 { 694 int i; 695 696 // Initialize reorder map 697 for (i = 0; i < text_info->length; i++) 698 shaper->cmap[i] = i; 699 700 // Create reorder map line-by-line 701 for (i = 0; i < text_info->n_lines; i++) { 702 LineInfo *line = text_info->lines + i; 703 int level; 704 FriBidiParType dir = FRIBIDI_PAR_ON; 705 706 level = fribidi_reorder_line(0, 707 shaper->ctypes + line->offset, line->len, 0, dir, 708 shaper->emblevels + line->offset, NULL, 709 shaper->cmap + line->offset); 710 } 711 712 return shaper->cmap; 713 } 714 715 /** 716 * \brief Resolve a Windows font encoding number to a suitable 717 * base direction. 177 and 178 are Hebrew and Arabic respectively, and 718 * they map to RTL. 1 is autodetection and is mapped to just that. 719 * Everything else is mapped to LTR. 720 * \param enc Windows font encoding 721 */ 722 FriBidiParType resolve_base_direction(int enc) 723 { 724 switch (enc) { 725 case 1: 726 return FRIBIDI_PAR_ON; 727 case 177: 728 case 178: 729 return FRIBIDI_PAR_RTL; 730 default: 731 return FRIBIDI_PAR_LTR; 732 } 733 } -
libass/ass_shaper.h
1 /* 2 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> 3 * 4 * This file is part of libass. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #ifndef LIBASS_SHAPER_H 20 #define LIBASS_SHAPER_H 21 22 #include "config.h" 23 24 #include <fribidi/fribidi.h> 25 #include "ass_render.h" 26 27 void ass_shaper_info(ASS_Library *lib); 28 ASS_Shaper *ass_shaper_new(size_t prealloc); 29 void ass_shaper_free(ASS_Shaper *shaper); 30 void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern); 31 void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv, 32 GlyphInfo *glyphs, size_t len); 33 void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir); 34 void ass_shaper_set_language(ASS_Shaper *shaper, const char *code); 35 void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level); 36 void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info); 37 void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info); 38 FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info); 39 FriBidiParType resolve_base_direction(int font_encoding); 40 41 void ass_shaper_font_data_free(ASS_ShaperFontData *priv); 42 43 #endif -
libass/ass_types.h
112 112 int WrapStyle; 113 113 int ScaledBorderAndShadow; 114 114 int Kerning; 115 char *Language; 115 116 116 117 int default_style; // index of default style 117 118 char *name; // file name in case of external subs, 0 for streams -
Makefile
115 115 libass/ass_parse.c \ 116 116 libass/ass_render.c \ 117 117 libass/ass_render_api.c \ 118 libass/ass_shaper.c \ 118 119 libass/ass_strtod.c \ 119 120 libass/ass_utils.c \ 120 121
