Ticket #2008: libass-0.10.0-r34354.patch

File libass-0.10.0-r34354.patch, 169.4 KB (added by subjunk@…, 15 years ago)

Here's a patch for the update, created against r34354

  • libass/ass.c

     
    2828#include <sys/stat.h>
    2929#include <unistd.h>
    3030#include <inttypes.h>
     31#include <ctype.h>
    3132
    3233#ifdef CONFIG_ICONV
    3334#include <iconv.h>
     
    6970    }
    7071    free(track->style_format);
    7172    free(track->event_format);
     73    free(track->Language);
    7274    if (track->styles) {
    7375        for (i = 0; i < track->n_styles; ++i)
    7476            ass_free_style(track, i);
     
    595597        track->ScaledBorderAndShadow = parse_bool(str + 22);
    596598    } else if (!strncmp(str, "Kerning:", 8)) {
    597599        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;
    598606    }
    599607    return 0;
    600608}
     
    12691277    track->parser_priv = calloc(1, sizeof(ASS_ParserPriv));
    12701278    return track;
    12711279}
     1280
     1281/**
     1282 * \brief Prepare track for rendering
     1283 */
     1284void 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

     
    2323#include <stdarg.h>
    2424#include "ass_types.h"
    2525
    26 #define LIBASS_VERSION 0x00913000
     26#define LIBASS_VERSION 0x01000000
    2727
    2828/*
    2929 * A linked list of images produced by an ass renderer.
     
    6161} ASS_Hinting;
    6262
    6363/**
     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 */
     71typedef enum {
     72    ASS_SHAPING_SIMPLE = 0,
     73    ASS_SHAPING_COMPLEX
     74} ASS_ShapingLevel;
     75
     76/**
    6477 * \brief Initialize the library.
    6578 * \return library handle or NULL if failed
    6679 */
     
    147160void ass_set_frame_size(ASS_Renderer *priv, int w, int h);
    148161
    149162/**
     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 */
     167void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level);
     168
     169/**
    150170 * \brief Set frame margins.  These values may be negative if pan-and-scan
    151171 * is used.
    152172 * \param priv renderer handle
  • libass/ass_bitmap.c

     
    11/*
    22 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
     3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
    34 *
    45 * This file is part of libass.
    56 *
     
    2223#include <assert.h>
    2324#include <ft2build.h>
    2425#include FT_GLYPH_H
     26#include FT_OUTLINE_H
    2527
    2628#include "ass_utils.h"
    2729#include "ass_bitmap.h"
     
    133135static Bitmap *alloc_bitmap(int w, int h)
    134136{
    135137    Bitmap *bm;
     138    unsigned s = w; // XXX: alignment
    136139    bm = malloc(sizeof(Bitmap));
    137     bm->buffer = calloc(w, h);
     140    bm->buffer = calloc(s, h);
    138141    bm->w = w;
    139142    bm->h = h;
     143    bm->stride = s;
    140144    bm->left = bm->top = 0;
    141145    return bm;
    142146}
     
    153157    Bitmap *dst = alloc_bitmap(src->w, src->h);
    154158    dst->left = src->left;
    155159    dst->top = src->top;
    156     memcpy(dst->buffer, src->buffer, src->w * src->h);
     160    memcpy(dst->buffer, src->buffer, src->stride * src->h);
    157161    return dst;
    158162}
    159163
    160 int check_glyph_area(ASS_Library *library, FT_Glyph glyph)
     164Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib,
     165                          FT_Outline *outline, int bord)
    161166{
    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     } else
    172         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;
    180167    Bitmap *bm;
    181168    int w, h;
    182     unsigned char *src;
    183     unsigned char *dst;
    184     int i;
    185169    int error;
     170    FT_BBox bbox;
     171    FT_Bitmap bitmap;
    186172
    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;
    195186
    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;
    203191    }
    204192
    205     w = bit->width;
    206     h = bit->rows;
     193    // allocate and set up bitmap
    207194    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;
    210203
    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;
    217209    }
    218210
    219     FT_Done_Glyph(glyph);
    220211    return bm;
    221212}
    222213
     
    232223    const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left;
    233224    const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top;
    234225    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;
    237228    const int b =
    238229        bm_o->top + bm_o->h <
    239230        bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h;
    240231
    241232    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);
    243234    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);
    245236
    246237    for (y = 0; y < b - t; ++y) {
    247238        for (x = 0; x < r - l; ++x) {
     
    250241            c_o = o[x];
    251242            o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0;
    252243        }
    253         g += bm_g->w;
    254         o += bm_o->w;
     244        g += bm_g->stride;
     245        o += bm_o->stride;
    255246    }
    256247}
    257248
     
    259250 * \brief Shift a bitmap by the fraction of a pixel in x and y direction
    260251 * expressed in 26.6 fixed point
    261252 */
    262 static void shift_bitmap(unsigned char *buf, int w, int h, int shift_x,
    263                          int shift_y)
     253static void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
    264254{
    265255    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;
    266260
    267261    // Shift in x direction
    268262    if (shift_x > 0) {
    269263        for (y = 0; y < h; y++) {
    270264            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;
    274268            }
    275269        }
    276270    } else if (shift_x < 0) {
    277271        shift_x = -shift_x;
    278272        for (y = 0; y < h; y++) {
    279273            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;
    283277            }
    284278        }
    285279    }
     
    288282    if (shift_y > 0) {
    289283        for (x = 0; x < w; x++) {
    290284            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;
    294288            }
    295289        }
    296290    } else if (shift_y < 0) {
    297291        shift_y = -shift_y;
    298292        for (x = 0; x < w; x++) {
    299293            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;
    303297            }
    304298        }
    305299    }
     
    430424 * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
    431425 * This blur is the same as the one employed by vsfilter.
    432426 */
    433 static void be_blur(unsigned char *buf, int w, int h)
     427static void be_blur(Bitmap *bm)
    434428{
     429    int w = bm->w;
     430    int h = bm->h;
     431    int s = bm->stride;
     432    unsigned char *buf = bm->buffer;
    435433    unsigned int x, y;
    436434    unsigned int old_sum, new_sum;
    437435
    438436    for (y = 0; y < h; y++) {
    439         old_sum = 2 * buf[y * w];
     437        old_sum = 2 * buf[y * s];
    440438        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;
    443441            old_sum = new_sum;
    444442        }
    445443    }
     
    447445    for (x = 0; x < w; x++) {
    448446        old_sum = 2 * buf[x];
    449447        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;
    452450            old_sum = new_sum;
    453451        }
    454452    }
    455453}
    456454
    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)
     455int 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)
    462460{
    463461    blur_radius *= 2;
    464462    int bbord = be > 0 ? sqrt(2 * be) : 0;
     
    471469
    472470    *bm_g = *bm_o = *bm_s = 0;
    473471
    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);
    476474    if (!*bm_g)
    477475        return 1;
    478476
    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);
    481479        if (!*bm_o) {
    482480            return 1;
    483481        }
     
    486484    // Apply box blur (multiple passes, if requested)
    487485    while (be--) {
    488486        if (*bm_o)
    489             be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h);
     487            be_blur(*bm_o);
    490488        else
    491             be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h);
     489            be_blur(*bm_g);
    492490    }
    493491
    494492    // Apply gaussian blur
     
    500498        generate_tables(priv_blur, blur_radius);
    501499        if (*bm_o)
    502500            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,
    504502                           (int *) priv_blur->gt2, priv_blur->g_r,
    505503                           priv_blur->g_w);
    506504        else
    507505            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,
    509507                           (int *) priv_blur->gt2, priv_blur->g_r,
    510508                           priv_blur->g_w);
    511509    }
     
    521519
    522520    assert(bm_s);
    523521
    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);
    526523
    527524    return 0;
    528525}
  • libass/ass_bitmap.h

     
    3232typedef struct {
    3333    int left, top;
    3434    int w, h;                   // width, height
     35    int stride;
    3536    unsigned char *buffer;      // w x h buffer
    3637} Bitmap;
    3738
     39Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib,
     40                          FT_Outline *outline, int bord);
    3841/**
    3942 * \brief perform glyph rendering
    4043 * \param glyph original glyph
     
    4447 * \param bm_g out: pointer to the bitmap of glyph shadow is returned here
    4548 * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps
    4649 */
    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);
     50int 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);
    5255
    5356void ass_free_bitmap(Bitmap *bm);
    54 int check_glyph_area(ASS_Library *library, FT_Glyph glyph);
    5557
    5658#endif                          /* LIBASS_BITMAP_H */
  • libass/ass_cache.c

     
    11/*
    22 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
     3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
    34 *
    45 * This file is part of libass.
    56 *
     
    2021
    2122#include <inttypes.h>
    2223#include <ft2build.h>
    23 #include FT_FREETYPE_H
    24 #include FT_GLYPH_H
    25 
     24#include FT_OUTLINE_H
    2625#include <assert.h>
    2726
    2827#include "ass_utils.h"
    29 #include "ass.h"
    30 #include "ass_fontconfig.h"
    3128#include "ass_font.h"
    32 #include "ass_bitmap.h"
    3329#include "ass_cache.h"
    3430
    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"
    3937
    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 stats
    74     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 exists
    96 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 //---------------------------------
    13338// font cache
    134 
    135 static unsigned font_desc_hash(void *buf, size_t len)
     39static unsigned font_hash(void *buf, size_t len)
    13640{
    13741    ASS_FontDesc *desc = buf;
    13842    unsigned hval;
    13943    hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
    14044    hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
    14145    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);
    14249    return hval;
    14350}
    14451
    145 static int font_compare(void *key1, void *key2, size_t key_size)
     52static unsigned font_compare(void *key1, void *key2, size_t key_size)
    14653{
    14754    ASS_FontDesc *a = key1;
    14855    ASS_FontDesc *b = key2;
     
    15966    return 1;
    16067}
    16168
    162 static void font_hash_dtor(void *key, size_t key_size, void *value,
    163                            size_t value_size)
     69static void font_destruct(void *key, void *value)
    16470{
    16571    ass_font_free(value);
    16672    free(key);
    16773}
    16874
    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 struct
    178 */
    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 glyph
    201 #define CREATE_HASH_FUNCTIONS
    202 #include "ass_cache_template.h"
    203 #define CREATE_COMPARISON_FUNCTIONS
    204 #include "ass_cache_template.h"
    205 
    206 //---------------------------------
    20775// bitmap cache
    208 
    209 static void bitmap_hash_dtor(void *key, size_t key_size, void *value,
    210                              size_t value_size)
     76static void bitmap_destruct(void *key, void *value)
    21177{
    21278    BitmapHashValue *v = value;
     79    BitmapHashKey *k = key;
    21380    if (v->bm)
    21481        ass_free_bitmap(v->bm);
    21582    if (v->bm_o)
    21683        ass_free_bitmap(v->bm_o);
    21784    if (v->bm_s)
    21885        ass_free_bitmap(v->bm_s);
     86    if (k->type == BITMAP_CLIP)
     87        free(k->u.clip.text);
    21988    free(key);
    22089    free(value);
    22190}
    22291
    223 void *cache_add_bitmap(Hashmap *bitmap_cache, BitmapHashKey *key,
    224                        BitmapHashValue *val)
     92static size_t bitmap_size(void *value, size_t value_size)
    22593{
    226     // Note: this is only an approximation
     94    BitmapHashValue *val = value;
    22795    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;
    22997    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}
    231101
    232     return hashmap_insert(bitmap_cache, key, val);
     102static 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    }
    233110}
    234111
    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)
     112static unsigned bitmap_compare (void *a, void *b, size_t key_size)
    242113{
    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    }
    244122}
    245123
    246 Hashmap *ass_bitmap_cache_init(ASS_Library *library)
     124// composite cache
     125static void composite_destruct(void *key, void *value)
    247126{
    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);
    256132}
    257133
    258 void ass_bitmap_cache_done(Hashmap *bitmap_cache)
     134// outline cache
     135
     136static unsigned outline_hash(void *key, size_t key_size)
    259137{
    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    }
    261144}
    262145
    263 Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache)
     146static unsigned outline_compare(void *a, void *b, size_t key_size)
    264147{
    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    }
    269156}
    270157
    271 //---------------------------------
    272 // glyph cache
    273 
    274 static void glyph_hash_dtor(void *key, size_t key_size, void *value,
    275                             size_t value_size)
     158static void outline_destruct(void *key, void *value)
    276159{
    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);
    282168    free(key);
    283169    free(value);
    284170}
    285171
    286 void *cache_add_glyph(Hashmap *glyph_cache, GlyphHashKey *key,
    287                       GlyphHashValue *val)
     172
     173
     174// Cache data
     175typedef struct cache_item {
     176    void *key;
     177    void *value;
     178    struct cache_item *next;
     179} CacheItem;
     180
     181struct 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
     199static unsigned hash_simple(void *key, size_t key_size)
    288200{
    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);
    295202}
    296203
    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
     205static unsigned compare_simple(void *a, void *b, size_t key_size)
    304206{
    305     return hashmap_find(glyph_cache, key);
     207    return memcmp(a, b, key_size) == 0;
    306208}
    307209
    308 Hashmap *ass_glyph_cache_init(ASS_Library *library)
     210// Default destructor
     211static void destruct_simple(void *key, void *value)
    309212{
    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);
    316215}
    317216
    318 void ass_glyph_cache_done(Hashmap *glyph_cache)
     217
     218// Create a cache with type-specific hash/compare/destruct/size functions
     219Cache *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)
    319222{
    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;
    321240}
    322241
    323 Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache)
     242void *ass_cache_put(Cache *cache, void *key, void *value)
    324243{
    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);
    326253
    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;
    329261}
    330262
     263void *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}
    331277
    332 //---------------------------------
    333 // composite cache
     278int ass_cache_empty(Cache *cache, size_t max_size)
     279{
     280    int i;
    334281
    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
     301void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits,
     302                     unsigned *misses, unsigned *count)
    337303{
    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;
    343312}
    344313
    345 void *cache_add_composite(Hashmap *composite_cache,
    346                           CompositeHashKey *key,
    347                           CompositeHashValue *val)
     314void ass_cache_done(Cache *cache)
    348315{
    349     return hashmap_insert(composite_cache, key, val);
     316    ass_cache_empty(cache, 0);
     317    free(cache->map);
     318    free(cache);
    350319}
    351320
    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
     322Cache *ass_font_cache_create(void)
    359323{
    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));
    361326}
    362327
    363 Hashmap *ass_composite_cache_init(ASS_Library *library)
     328Cache *ass_outline_cache_create(void)
    364329{
    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));
    372332}
    373333
    374 void ass_composite_cache_done(Hashmap *composite_cache)
     334Cache *ass_glyph_metrics_cache_create(void)
    375335{
    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));
    377339}
    378340
    379 Hashmap *ass_composite_cache_reset(Hashmap *composite_cache)
     341Cache *ass_bitmap_cache_create(void)
    380342{
    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}
    382346
    383     ass_composite_cache_done(composite_cache);
    384     return ass_composite_cache_init(lib);
     347Cache *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));
    385352}
  • libass/ass_cache.h

     
    11/*
    22 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
     3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
    34 *
    45 * This file is part of libass.
    56 *
     
    2324#include "ass_font.h"
    2425#include "ass_bitmap.h"
    2526
    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);
     27typedef struct cache Cache;
    3128
    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
    3830
    3931typedef 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 pairs
    44     HashmapKeyCompare key_compare;
    45     HashmapHash hash;
    46     size_t cache_size;
    47     // stats
    48     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_key
    69 #define CREATE_STRUCT_DEFINITIONS
    70 #include "ass_cache_template.h"
    71 
    72 typedef struct {
    7332    Bitmap *bm;               // the actual bitmaps
    7433    Bitmap *bm_o;
    7534    Bitmap *bm_s;
    7635} BitmapHashValue;
    7736
    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 
    8737typedef struct {
    8838    unsigned char *a;
    8939    unsigned char *b;
    9040} CompositeHashValue;
    9141
    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 
    10142typedef struct {
    102     FT_Glyph glyph;
    103     FT_Glyph outline_glyph;
     43    FT_Library lib;
     44    FT_Outline *outline;
     45    FT_Outline *border;
    10446    FT_BBox bbox_scaled;        // bbox after scaling, but before rotation
    105     FT_Vector advance;          // 26.6, advance distance to the next bitmap in line
    106     int asc, desc;              // ascender/descender of a drawing
    107 } GlyphHashValue;
     47    FT_Vector advance;          // 26.6, advance distance to the next outline in line
     48    int asc, desc;              // ascender/descender
     49} OutlineHashValue;
    10850
    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);
     51typedef struct {
     52    FT_Glyph_Metrics metrics;
     53} GlyphMetricsHashValue;
    11654
     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
     60typedef unsigned(*HashFunction)(void *key, size_t key_size);
     61typedef size_t(*ItemSize)(void *value, size_t value_size);
     62typedef unsigned(*HashCompare)(void *a, void *b, size_t key_size);
     63typedef void(*CacheItemDestructor)(void *key, void *value);
     64
     65// cache hash keys
     66
     67typedef 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
     78typedef 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
     89Cache *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);
     92void *ass_cache_put(Cache *cache, void *key, void *value);
     93void *ass_cache_get(Cache *cache, void *key);
     94int ass_cache_empty(Cache *cache, size_t max_size);
     95void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits,
     96                     unsigned *misses, unsigned *count);
     97void ass_cache_done(Cache *cache);
     98Cache *ass_font_cache_create(void);
     99Cache *ass_outline_cache_create(void);
     100Cache *ass_glyph_metrics_cache_create(void);
     101Cache *ass_bitmap_cache_create(void);
     102Cache *ass_composite_cache_create(void);
     103
    117104#endif                          /* LIBASS_CACHE_H */
  • libass/ass_cache_template.h

     
    44    typedef struct structname {
    55#define GENERIC(type, member) \
    66        type member;
     7#define STRING(member) \
     8        char *member;
    79#define FTVECTOR(member) \
    810        FT_Vector member;
    911#define BITMAPHASHKEY(member) \
     
    1416#elif defined(CREATE_COMPARISON_FUNCTIONS)
    1517#undef CREATE_COMPARISON_FUNCTIONS
    1618#define START(funcname, structname) \
    17     static int funcname##_compare(void *key1, void *key2, size_t key_size) \
     19    static unsigned funcname##_compare(void *key1, void *key2, size_t key_size) \
    1820    { \
    1921        struct structname *a = key1; \
    2022        struct structname *b = key2; \
    2123        return // conditions follow
    2224#define GENERIC(type, member) \
    2325            a->member == b->member &&
     26#define STRING(member) \
     27            strcmp(a->member, b->member) == 0 &&
    2428#define FTVECTOR(member) \
    2529            a->member.x == b->member.x && a->member.y == b->member.y &&
    2630#define BITMAPHASHKEY(member) \
     
    3842        unsigned hval = FNV1_32A_INIT;
    3943#define GENERIC(type, member) \
    4044        hval = fnv_32a_buf(&p->member, sizeof(p->member), hval);
     45#define STRING(member) \
     46        hval = fnv_32a_str(p->member, hval);
    4147#define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y);
    4248#define BITMAPHASHKEY(member) { \
    4349        unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \
     
    5359
    5460
    5561
    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
     63START(outline_bitmap, outline_bitmap_hash_key)
     64    GENERIC(OutlineHashValue *, outline)
    6565    GENERIC(char, be) // blur edges
    6666    GENERIC(double, blur) // gaussian blur
    67     GENERIC(unsigned, scale_x) // 16.16
    68     GENERIC(unsigned, scale_y) // 16.16
    6967    GENERIC(int, frx) // signed 16.16
    7068    GENERIC(int, fry) // signed 16.16
    7169    GENERIC(int, frz) // signed 16.16
     
    7876    GENERIC(int, shift_y)
    7977    FTVECTOR(advance) // subpixel shift vector
    8078    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)
     79END(OutlineBitmapHashKey)
    8580
     81// describe a clip mask bitmap
     82START(clip_bitmap, clip_bitmap_hash_key)
     83    STRING(text)
     84END(ClipMaskHashKey)
     85
    8686// describes an outline glyph
    8787START(glyph, glyph_hash_key)
    8888    GENERIC(ASS_Font *, font)
    8989    GENERIC(double, size) // font size
    90     GENERIC(uint32_t, ch) // character code
     90    GENERIC(int, face_index)
     91    GENERIC(int, glyph_index)
    9192    GENERIC(int, bold)
    9293    GENERIC(int, italic)
    9394    GENERIC(unsigned, scale_x) // 16.16
    9495    GENERIC(unsigned, scale_y) // 16.16
    9596    FTVECTOR(outline) // border width, 16.16
    96     GENERIC(unsigned, drawing_hash) // hashcode of a drawing
    9797    GENERIC(unsigned, flags)    // glyph decoration flags
    9898    GENERIC(unsigned, border_style)
    9999END(GlyphHashKey)
    100100
     101START(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)
     108END(GlyphMetricsHashKey)
     109
     110// describes an outline drawing
     111START(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)
     120END(DrawingHashKey)
     121
    101122// Cache for composited bitmaps
    102123START(composite, composite_hash_key)
    103124    GENERIC(int, aw)
     
    117138
    118139#undef START
    119140#undef GENERIC
     141#undef STRING
    120142#undef FTVECTOR
    121143#undef BITMAPHASHKEY
    122144#undef END
  • libass/ass_drawing.c

     
    1717 */
    1818
    1919#include <ft2build.h>
    20 #include FT_GLYPH_H
    2120#include FT_OUTLINE_H
    2221#include FT_BBOX_H
    2322#include <math.h>
    2423
    2524#include "ass_utils.h"
    26 #include "ass_font.h"
    2725#include "ass_drawing.h"
    2826
    2927#define CURVE_ACCURACY 64.0
     
    3129#define GLYPH_INITIAL_CONTOURS 5
    3230
    3331/*
    34  * \brief Get and prepare a FreeType glyph
    35  */
    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 /*
    5732 * \brief Add a single point to a contour.
    5833 */
    5934static inline void drawing_add_point(ASS_Drawing *drawing,
    6035                                     FT_Vector *point)
    6136{
    62     FT_Outline *ol = &drawing->glyph->outline;
     37    FT_Outline *ol = &drawing->outline;
    6338
    6439    if (ol->n_points >= drawing->max_points) {
    6540        drawing->max_points *= 2;
     
    7550}
    7651
    7752/*
    78  * \brief Close a contour and check glyph size overflow.
     53 * \brief Close a contour and check outline size overflow.
    7954 */
    8055static inline void drawing_close_shape(ASS_Drawing *drawing)
    8156{
    82     FT_Outline *ol = &drawing->glyph->outline;
     57    FT_Outline *ol = &drawing->outline;
    8358
    8459    if (ol->n_contours >= drawing->max_contours) {
    8560        drawing->max_contours *= 2;
     
    10782
    10883/*
    10984 * \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.
    11186 */
    11287static void drawing_finish(ASS_Drawing *drawing, int raw_mode)
    11388{
    11489    int i, offset;
    11590    FT_BBox bbox = drawing->cbox;
    116     FT_Outline *ol = &drawing->glyph->outline;
     91    FT_Outline *ol = &drawing->outline;
    11792
    11893    // Close the last contour
    11994    drawing_close_shape(drawing);
     
    126101    if (raw_mode)
    127102        return;
    128103
    129     drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin);
     104    drawing->advance.x = bbox.xMax - bbox.xMin;
    130105
    131106    drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y);
    132107    drawing->asc = bbox.yMax - bbox.yMin + drawing->desc;
     
    355330/*
    356331 * \brief Create and initialize a new drawing and return it
    357332 */
    358 ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font,
    359                              FT_Library lib)
     333ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib)
    360334{
    361335    ASS_Drawing *drawing;
    362336
     
    365339    drawing->size = DRAWING_INITIAL_SIZE;
    366340    drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX;
    367341    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;
    374344    drawing->scale_x = 1.;
    375345    drawing->scale_y = 1.;
    376346    drawing->max_contours = GLYPH_INITIAL_CONTOURS;
    377347    drawing->max_points = GLYPH_INITIAL_POINTS;
    378348
     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
    379354    return drawing;
    380355}
    381356
     
    386361{
    387362    if (drawing) {
    388363        free(drawing->text);
     364        FT_Outline_Done(drawing->ftlibrary, &drawing->outline);
    389365    }
    390366    free(drawing);
    391367}
     
    416392/*
    417393 * \brief Convert token list to outline.  Calls the line and curve evaluators.
    418394 */
    419 FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode)
     395FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode)
    420396{
    421397    int started = 0;
    422398    ASS_DrawingToken *token;
    423399    FT_Vector pen = {0, 0};
    424400
    425     if (drawing->font)
    426         drawing_make_glyph(drawing, drawing->fontconfig_priv, drawing->font);
    427     if (!drawing->glyph)
    428         return NULL;
    429 
    430401    drawing->tokens = drawing_tokenize(drawing->text);
    431402    drawing_prepare(drawing);
    432403
     
    486457
    487458    drawing_finish(drawing, raw_mode);
    488459    drawing_free_tokens(drawing->tokens);
    489     return &drawing->glyph;
     460    return &drawing->outline;
    490461}
  • libass/ass_drawing.h

     
    2020#define LIBASS_DRAWING_H
    2121
    2222#include <ft2build.h>
    23 #include FT_GLYPH_H
     23#include FT_OUTLINE_H
    2424
    2525#include "ass.h"
    2626
     
    5353    double scale_y;     // FontScaleY
    5454    int asc;            // ascender
    5555    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)
    5758    int hash;           // hash value (for caching)
    5859
    5960    // private
    6061    FT_Library ftlibrary;   // needed for font ops
    61     ASS_Font *font;         // dito
    62     void *fontconfig_priv;  // dito
    6362    ASS_Library *library;
    6463    int size;           // current buffer size
    6564    ASS_DrawingToken *tokens;    // tokenized drawing
     
    7069    FT_BBox cbox;   // bounding box, or let's say... VSFilter's idea of it
    7170} ASS_Drawing;
    7271
    73 ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font,
    74                              FT_Library lib);
     72ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib);
    7573void ass_drawing_free(ASS_Drawing* drawing);
    7674void ass_drawing_add_char(ASS_Drawing* drawing, char symbol);
    7775void ass_drawing_hash(ASS_Drawing* drawing);
    78 FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode);
     76FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode);
    7977
    8078#endif /* LIBASS_DRAWING_H */
  • libass/ass_font.c

     
    3030#include "ass.h"
    3131#include "ass_library.h"
    3232#include "ass_font.h"
    33 #include "ass_bitmap.h"
    34 #include "ass_cache.h"
    3533#include "ass_fontconfig.h"
    3634#include "ass_utils.h"
     35#include "ass_shaper.h"
    3736
    38 #define VERTICAL_LOWER_BOUND 0x02f1
    39 
    4037/**
    4138 * Select a good charmap, prefer Microsoft Unicode charmaps.
    4239 * Otherwise, let FreeType decide.
     
    9188    return -1;
    9289}
    9390
    94 static void face_set_size(FT_Face face, double size);
    95 
    9691static void buggy_font_workaround(FT_Face face)
    9792{
    9893    // Some fonts have zero Ascender/Descender fields in 'hhea' table.
     
    161156    buggy_font_workaround(face);
    162157
    163158    font->faces[font->n_faces++] = face;
    164     face_set_size(face, font->size);
     159    ass_face_set_size(face, font->size);
    165160    free(path);
    166161    return font->n_faces - 1;
    167162}
     
    169164/**
    170165 * \brief Create a new ASS_Font according to "desc" argument
    171166 */
    172 ASS_Font *ass_font_new(void *font_cache, ASS_Library *library,
     167ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
    173168                       FT_Library ftlibrary, void *fc_priv,
    174169                       ASS_FontDesc *desc)
    175170{
     
    177172    ASS_Font *fontp;
    178173    ASS_Font font;
    179174
    180     fontp = ass_font_cache_find((Hashmap *) font_cache, desc);
     175    fontp = ass_cache_get(font_cache, desc);
    181176    if (fontp)
    182177        return fontp;
    183178
    184179    font.library = library;
    185180    font.ftlibrary = ftlibrary;
     181    font.shaper_priv = NULL;
    186182    font.n_faces = 0;
    187183    font.desc.family = strdup(desc->family);
    188184    font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
     
    199195        free(font.desc.family);
    200196        return 0;
    201197    } else
    202         return ass_font_cache_add((Hashmap *) font_cache, &font);
     198        return ass_cache_put(font_cache, &font.desc, &font);
    203199}
    204200
    205201/**
     
    216212    }
    217213}
    218214
    219 static void face_set_size(FT_Face face, double size)
     215void ass_face_set_size(FT_Face face, double size)
    220216{
    221217    TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
    222218    TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
     
    251247    if (font->size != size) {
    252248        font->size = size;
    253249        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);
    255251    }
    256252}
    257253
     
    276272                *asc = FT_MulFix(face->ascender, y_scale);
    277273                *desc = FT_MulFix(-face->descender, y_scale);
    278274            }
    279             if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) {
    280                 *asc = FT_MulFix(face->max_advance_width, y_scale);
    281             }
    282275            return;
    283276        }
    284277    }
     
    388381    return 0;
    389382}
    390383
     384void 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
     396void outline_free(FT_Library lib, FT_Outline *outline)
     397{
     398    if (outline)
     399        FT_Outline_Done(lib, outline);
     400    free(outline);
     401}
     402
    391403/**
    392404 * Slightly embold a glyph without touching its metrics
    393405 */
     
    405417}
    406418
    407419/**
    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 */
     424int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
     425                       int *face_index, int *glyph_index)
    413426{
    414     int error;
    415427    int index = 0;
    416428    int i;
    417     FT_Glyph glyph;
    418429    FT_Face face = 0;
    419     int flags = 0;
    420     int vertical = font->desc.vertical;
    421430
    422     if (ch < 0x20)
     431    *glyph_index = 0;
     432
     433    if (symbol < 0x20) {
     434        *face_index = 0;
    423435        return 0;
     436    }
    424437    // 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;
    428442        return 0;
     443    }
    429444
    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) {
    431453        face = font->faces[i];
    432         index = FT_Get_Char_Index(face, ch);
     454        index = FT_Get_Char_Index(face, symbol);
    433455        if (index)
    434             break;
     456            *face_index = i;
    435457    }
    436458
    437459#ifdef CONFIG_FONTCONFIG
     
    439461        int face_idx;
    440462        ass_msg(font->library, MSGL_INFO,
    441463                "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,
    443465                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);
    445467        if (face_idx >= 0) {
    446468            face = font->faces[face_idx];
    447             index = FT_Get_Char_Index(face, ch);
     469            index = FT_Get_Char_Index(face, symbol);
    448470            if (index == 0 && face->num_charmaps > 0) {
    449471                int i;
    450472                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);
    452474                for (i = 0; i < face->num_charmaps; i++) {
    453475                    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;
    455477                }
    456478            }
    457479            if (index == 0) {
    458480                ass_msg(font->library, MSGL_ERR,
    459481                        "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,
    461483                        font->desc.italic);
    462484            }
    463485        }
    464486    }
    465487#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;
    466491
     492    return 1;
     493}
     494
     495/**
     496 * \brief Get a glyph
     497 * \param ch character code
     498 **/
     499FT_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
    467509    flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
    468510            | FT_LOAD_IGNORE_TRANSFORM;
    469511    switch (hinting) {
     
    505547    // Rotate glyph, if needed
    506548    if (vertical && ch >= VERTICAL_LOWER_BOUND) {
    507549        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);
    508557        FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m);
    509558        FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline,
    510                              face->glyph->metrics.vertAdvance,
    511                              0);
     559                             face->glyph->metrics.vertAdvance, desc);
    512560        glyph->advance.x = face->glyph->linearVertAdvance;
    513561    }
    514562
     
    561609    for (i = 0; i < font->n_faces; ++i)
    562610        if (font->faces[i])
    563611            FT_Done_Face(font->faces[i]);
     612    if (font->shaper_priv)
     613        ass_shaper_font_data_free(font->shaper_priv);
    564614    free(font->desc.family);
    565615    free(font);
    566616}
     
    618668 * \param border_x border size, x direction, d6 format
    619669 * \param border_x border size, y direction, d6 format
    620670 */
    621 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y)
     671void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y)
    622672{
    623     int nc = glyph->outline.n_contours;
     673    int nc = outline->n_contours;
    624674    int begin, stop;
    625675    char modified = 0;
    626676    char *valid_cont = malloc(nc);
     
    630680    int i, j;
    631681    int inside_direction;
    632682
    633     inside_direction = FT_Outline_Get_Orientation(&glyph->outline) ==
     683    inside_direction = FT_Outline_Get_Orientation(outline) ==
    634684        FT_ORIENTATION_TRUETYPE;
    635685
    636686    // create a list of cboxes of the contours
    637687    for (i = 0; i < nc; i++) {
    638688        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);
    641691    }
    642692
    643693    // for each contour, check direction and whether it's "outside"
     
    645695    end = -1;
    646696    for (i = 0; i < nc; i++) {
    647697        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);
    650700        valid_cont[i] = 1;
    651701        if (dir == inside_direction) {
    652702            for (j = 0; j < nc; j++) {
     
    662712             * inside of - assume the font is buggy and it should be
    663713             * an "outside" contour, and reverse it */
    664714            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;
    671721            }
    672722            dir ^= 1;
    673723        }
    674724        check_inside:
    675725        if (dir == inside_direction) {
    676726            FT_BBox box;
    677             get_contour_cbox(&box, glyph->outline.points, start, end);
     727            get_contour_cbox(&box, outline->points, start, end);
    678728            int width = box.xMax - box.xMin;
    679729            int height = box.yMax - box.yMin;
    680730            if (width < border_x * 2 || height < border_y * 2) {
     
    687737    // if we need to modify the outline, rewrite it and skip
    688738    // the contours that we determined should be removed.
    689739    if (modified) {
    690         FT_Outline *outline = &glyph->outline;
    691740        int p = 0, c = 0;
    692741        for (i = 0; i < nc; i++) {
    693742            if (!valid_cont[i])
    694743                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];
    697746            for (j = begin; j <= stop; j++) {
    698747                outline->points[p].x = outline->points[j].x;
    699748                outline->points[p].y = outline->points[j].y;
  • libass/ass_font.h

     
    2222#include <stdint.h>
    2323#include <ft2build.h>
    2424#include FT_GLYPH_H
     25#include FT_OUTLINE_H
     26
    2527#include "ass.h"
    2628#include "ass_types.h"
    2729
     30#define VERTICAL_LOWER_BOUND 0x02f1
     31
    2832#define ASS_FONT_MAX_FACES 10
    2933#define DECO_UNDERLINE 1
    3034#define DECO_STRIKETHROUGH 2
    3135
     36typedef struct ass_shaper_font_data ASS_ShaperFontData;
     37
    3238typedef struct {
    3339    char *family;
    3440    unsigned bold;
     
    4248    ASS_Library *library;
    4349    FT_Library ftlibrary;
    4450    FT_Face faces[ASS_FONT_MAX_FACES];
     51    ASS_ShaperFontData *shaper_priv;
    4552    int n_faces;
    4653    double scale_x, scale_y;    // current transform
    4754    FT_Vector v;                // current shift
    4855    double size;
    4956} ASS_Font;
    5057
    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
     60ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
    5361                       FT_Library ftlibrary, void *fc_priv,
    5462                       ASS_FontDesc *desc);
    5563void ass_font_set_transform(ASS_Font *font, double scale_x,
    5664                            double scale_y, FT_Vector *v);
     65void ass_face_set_size(FT_Face face, double size);
    5766void ass_font_set_size(ASS_Font *font, double size);
    5867void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc,
    5968                           int *desc);
     69int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
     70                       int *face_index, int *glyph_index);
    6071FT_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);
    6274FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2);
    6375void ass_font_free(ASS_Font *font);
    64 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y);
     76void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y);
     77void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest);
     78void outline_free(FT_Library lib, FT_Outline *outline);
    6579
    6680#endif                          /* LIBASS_FONT_H */
  • libass/ass_library.c

     
    7878            free(*p);
    7979    }
    8080    free(priv->style_overrides);
     81    priv->style_overrides = NULL;
    8182
    8283    if (!list)
    8384        return;
  • libass/ass_parse.c

     
    4747        return 0;
    4848}
    4949
    50 static void change_font_size(ASS_Renderer *render_priv, double sz)
     50double ensure_font_size(ASS_Renderer *priv, double size)
    5151{
    52     double size = sz * render_priv->font_scale;
    53 
    5452    if (size < 1)
    5553        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;
    5856
    59     ass_font_set_size(render_priv->state.font, size);
     57    return size;
     58}
    6059
     60static void change_font_size(ASS_Renderer *render_priv, double sz)
     61{
    6162    render_priv->state.font_size = sz;
    6263}
    6364
     
    189190{
    190191    unsigned a;
    191192    double cf;
    192     if (now <= t1) {
     193
     194    if (now < t1) {
    193195        a = a1;
    194196    } else if (now >= t4) {
    195197        a = a3;
    196     } else if (now < t2) {      // and > t1
     198    } else if (now < t2 && t2 > t1) {
    197199        cf = ((double) (now - t1)) / (t2 - t1);
    198200        a = a1 * (1 - cf) + a2 * cf;
    199     } else if (now > t3) {
     201    } else if (now >= t3 && t4 > t3) {
    200202        cf = ((double) (now - t3)) / (t4 - t3);
    201203        a = a2 * (1 - cf) + a3 * cf;
    202     } else {                    // t2 <= now <= t3
     204    } else {                    // t2 <= now < t3
    203205        a = a2;
    204206    }
    205207
     
    216218    int res = 0;
    217219    ASS_Drawing *drawing = render_priv->state.clip_drawing;
    218220
    219     if (drawing && drawing->glyph)
    220         FT_Done_Glyph((FT_Glyph) drawing->glyph);
    221221    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);
    226224    drawing = render_priv->state.clip_drawing;
    227225    skipopt('(');
    228226    res = mystrtoi(&p, &scale);
     
    259257        else
    260258            val = -1.;
    261259        change_border(render_priv, val, render_priv->state.border_y);
     260        render_priv->state.bm_run_id++;
    262261    } else if (mystrcmp(&p, "ybord")) {
    263262        double val;
    264263        if (mystrtod(&p, &val))
     
    273272        else
    274273            val = 0.;
    275274        render_priv->state.shadow_x = val;
     275        render_priv->state.bm_run_id++;
    276276    } else if (mystrcmp(&p, "yshad")) {
    277277        double val;
    278278        if (mystrtod(&p, &val))
     
    280280        else
    281281            val = 0.;
    282282        render_priv->state.shadow_y = val;
     283        render_priv->state.bm_run_id++;
    283284    } else if (mystrcmp(&p, "fax")) {
    284285        double val;
    285286        if (mystrtod(&p, &val))
     
    331332            render_priv->state.blur = val;
    332333        } else
    333334            render_priv->state.blur = 0.0;
     335        render_priv->state.bm_run_id++;
    334336        // ASS standard tags
    335337    } else if (mystrcmp(&p, "fsc")) {
    336338        char tp = *p++;
     
    391393        } else
    392394            val = -1.;          // reset to default
    393395        change_border(render_priv, val, val);
     396        render_priv->state.bm_run_id++;
    394397    } else if (mystrcmp(&p, "move")) {
    395398        double x1, x2, y1, y2;
    396399        long long t1, t2, delta_t, t;
     
    492495            change_alpha(&render_priv->state.c[3],
    493496                         render_priv->state.style->BackColour, pwr);
    494497        }
     498        render_priv->state.bm_run_id++;
    495499        // FIXME: simplify
    496500    } else if (mystrcmp(&p, "an")) {
    497501        int val;
     
    682686            val = render_priv->state.style->PrimaryColour;
    683687        ass_msg(render_priv->library, MSGL_DBG2, "color: %X", val);
    684688        change_color(&render_priv->state.c[0], val, pwr);
     689        render_priv->state.bm_run_id++;
    685690    } else if ((*p >= '1') && (*p <= '4') && (++p)
    686691               && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) {
    687692        char n = *(p - 2);
     
    711716        switch (cmd) {
    712717        case 'c':
    713718            change_color(render_priv->state.c + cidx, val, pwr);
     719            render_priv->state.bm_run_id++;
    714720            break;
    715721        case 'a':
    716722            change_alpha(render_priv->state.c + cidx, val >> 24, pwr);
     723            render_priv->state.bm_run_id++;
    717724            break;
    718725        default:
    719726            ass_msg(render_priv->library, MSGL_WARN, "Bad command: %c%c",
     
    733740            render_priv->state.be = val;
    734741        } else
    735742            render_priv->state.be = 0;
     743        render_priv->state.bm_run_id++;
    736744    } else if (mystrcmp(&p, "b")) {
    737745        int b;
    738746        if (mystrtoi(&p, &b)) {
     
    781789        } else
    782790            val = 0.;
    783791        render_priv->state.shadow_x = render_priv->state.shadow_y = val;
     792        render_priv->state.bm_run_id++;
    784793    } else if (mystrcmp(&p, "s")) {
    785794        int val;
    786795        if (mystrtoi(&p, &val) && val)
    787796            render_priv->state.flags |= DECO_STRIKETHROUGH;
    788797        else
    789798            render_priv->state.flags &= ~DECO_STRIKETHROUGH;
     799        render_priv->state.bm_run_id++;
    790800    } else if (mystrcmp(&p, "u")) {
    791801        int val;
    792802        if (mystrtoi(&p, &val) && val)
    793803            render_priv->state.flags |= DECO_UNDERLINE;
    794804        else
    795805            render_priv->state.flags &= ~DECO_UNDERLINE;
     806        render_priv->state.bm_run_id++;
    796807    } else if (mystrcmp(&p, "pbo")) {
    797808        double val = 0;
    798809        if (mystrtod(&p, &val))
     
    809820        if (!mystrtoi(&p, &val))
    810821            val = render_priv->track->WrapStyle;
    811822        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;
    812828    }
    813829
    814830    return p;
     
    890906}
    891907
    892908/**
     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 */
     919void 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/**
    893980 * \brief Get next ucs4 char from string, parsing and executing style overrides
    894981 * \param str string pointer
    895982 * \return ucs4 code of the next char
  • libass/ass_parse.h

     
    2727#define _a(c)   ((c) & 0xFF)
    2828
    2929void update_font(ASS_Renderer *render_priv);
     30double ensure_font_size(ASS_Renderer *priv, double size);
    3031void change_border(ASS_Renderer *render_priv, double border_x,
    3132                   double border_y);
    3233void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event);
     34void process_karaoke_effects(ASS_Renderer *render_priv);
    3335unsigned get_next_char(ASS_Renderer *render_priv, char **str);
    3436extern void change_alpha(uint32_t *var, uint32_t new, double pwr);
    3537extern uint32_t mult_alpha(uint32_t a, uint32_t b);
  • libass/ass_render.c

     
    2323
    2424#include "ass_render.h"
    2525#include "ass_parse.h"
     26#include "ass_shaper.h"
    2627
    2728#define MAX_GLYPHS_INITIAL 1024
    2829#define MAX_LINES_INITIAL 64
    2930#define SUBPIXEL_MASK 63
    3031#define SUBPIXEL_ACCURACY 7
    3132
    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 
    6433ASS_Renderer *ass_renderer_init(ASS_Library *library)
    6534{
    6635    int error;
     
    7544    }
    7645
    7746    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",
    7948           vmajor, vminor, vpatch);
    80     ass_msg(library, MSGL_V, "FreeType headers version: %d.%d.%d",
    81            FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
    8249
    8350    priv = calloc(1, sizeof(ASS_Renderer));
    8451    if (!priv) {
     
    9259    priv->ftlibrary = ft;
    9360    // images_root and related stuff is zero-filled in calloc
    9461
    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();
    9966    priv->cache.glyph_max = GLYPH_CACHE_MAX;
    10067    priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE;
    10168
     
    10673
    10774    priv->settings.font_size_coeff = 1.;
    10875
     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
    10984  ass_init_exit:
    11085    if (priv)
    111         ass_msg(library, MSGL_V, "Init");
     86        ass_msg(library, MSGL_V, "Initialized");
    11287    else
    113         ass_msg(library, MSGL_ERR, "Init failed");
     88        ass_msg(library, MSGL_ERR, "Initialization failed");
    11489
    11590    return priv;
    11691}
     
    131106
    132107void ass_renderer_done(ASS_Renderer *render_priv)
    133108{
    134     ass_font_cache_done(render_priv->cache.font_cache);
    135     ass_bitmap_cache_done(render_priv->cache.bitmap_cache);
    136     ass_composite_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);
    138113
    139114    ass_free_images(render_priv->images_root);
    140115    ass_free_images(render_priv->prev_images_root);
     
    149124        fontconfig_done(render_priv->fontconfig_priv);
    150125    if (render_priv->synth_priv)
    151126        ass_synth_done(render_priv->synth_priv);
     127    ass_shaper_free(render_priv->shaper);
    152128    free(render_priv->eimg);
    153129    free(render_priv->text_info.glyphs);
    154130    free(render_priv->text_info.lines);
     
    328304        // split up into left and right for karaoke, if needed
    329305        if (lbrk > r[j].x0) {
    330306            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,
    332308                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);
    334310            if (!img) break;
    335311            *tail = img;
    336312            tail = &img->next;
    337313        }
    338314        if (lbrk < r[j].x1) {
    339315            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,
    341317                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);
    343319            if (!img) break;
    344320            *tail = img;
    345321            tail = &img->next;
     
    419395    if (brk > b_x0) {           // draw left part
    420396        if (brk > b_x1)
    421397            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,
    424400                             dst_x + b_x0, dst_y + b_y0, color);
    425401        if (!img) return tail;
    426402        *tail = img;
     
    429405    if (brk < b_x1) {           // draw right part
    430406        if (brk < b_x0)
    431407            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,
    434410                             dst_x + brk, dst_y + b_y0, color2);
    435411        if (!img) return tail;
    436412        *tail = img;
     
    516492    hk.by = by;
    517493    hk.as = as;
    518494    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);
    520496    if (hv) {
    521497        (*last_tail)->bitmap = hv->a;
    522498        (*tail)->bitmap = hv->b;
     
    539515    // Insert bitmaps into the cache
    540516    chv.a = (*last_tail)->bitmap;
    541517    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);
    543519}
    544520
    545521static void free_list_add(ASS_Renderer *render_priv, void *object)
     
    564540static void blend_vector_clip(ASS_Renderer *render_priv,
    565541                              ASS_Image *head)
    566542{
    567     FT_Glyph glyph;
    568     FT_BitmapGlyph clip_bm;
     543    FT_Outline *outline;
     544    Bitmap *clip_bm = NULL;
    569545    ASS_Image *cur;
    570546    ASS_Drawing *drawing = render_priv->state.clip_drawing;
    571     GlyphHashKey key;
    572     GlyphHashValue *val;
     547    BitmapHashKey key;
     548    BitmapHashValue *val;
    573549    int error;
    574550
    575551    if (!drawing)
    576552        return;
    577553
    578554    // Try to get mask from cache
    579     ass_drawing_hash(drawing);
    580555    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);
    584559
    585560    if (val) {
    586         clip_bm = (FT_BitmapGlyph) val->glyph;
     561        clip_bm = val->bm;
    587562    } else {
    588         GlyphHashValue v;
     563        BitmapHashValue v;
    589564
    590565        // 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) {
    593568            ass_msg(render_priv->library, MSGL_WARN,
    594569                    "Clip vector parsing failed. Skipping.");
    595570            goto blend_vector_error;
     
    602577                .x = int_to_d6(render_priv->settings.left_margin),
    603578                .y = -int_to_d6(render_priv->settings.top_margin),
    604579            };
    605             FT_Outline_Translate(&drawing->glyph->outline,
    606                                  trans.x, trans.y);
     580            FT_Outline_Translate(outline, trans.x, trans.y);
    607581        }
    608582
    609         // Check glyph bounding box size
    610         if (check_glyph_area(render_priv->library, glyph)) {
    611             FT_Done_Glyph(glyph);
    612             glyph = 0;
    613             goto blend_vector_error;
    614         }
    615 
    616583        ass_msg(render_priv->library, MSGL_DBG2,
    617584                "Parsed vector clip: scales (%f, %f) string [%s]\n",
    618585                drawing->scale_x, drawing->scale_y, drawing->text);
    619586
    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) {
    622590            ass_msg(render_priv->library, MSGL_WARN,
    623591                "Clip vector rasterization failed: %d. Skipping.", error);
    624             FT_Done_Glyph(glyph);
    625             glyph = 0;
    626592        }
    627593
    628 blend_vector_error:
    629         clip_bm = (FT_BitmapGlyph) glyph;
    630 
    631594        // Add to cache
    632595        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);
    635599    }
     600blend_vector_error:
    636601
    637602    if (!clip_bm) goto blend_vector_exit;
    638603
     
    645610        unsigned char *abuffer, *bbuffer, *nbuffer;
    646611
    647612        abuffer = cur->bitmap;
    648         bbuffer = clip_bm->bitmap.buffer;
     613        bbuffer = clip_bm->buffer;
    649614        ax = cur->dst_x;
    650615        ay = cur->dst_y;
    651616        aw = cur->w;
    652617        ah = cur->h;
    653618        as = cur->stride;
    654619        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;
    659624
    660625        // Calculate overlap coordinates
    661626        left = (ax > bx) ? ax : bx;
     
    739704            || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip)
    740705            continue;
    741706
    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            }
    749712
    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;
    756720
    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        }
    758732    }
    759733
    760734    last_tail = 0;
     
    764738            || info->skip)
    765739            continue;
    766740
    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            }
    770746
    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;
    781750
    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;
    783765        }
    784766    }
    785767
     
    789771            || info->skip)
    790772            continue;
    791773
    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            }
    795779
    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) {
    799795                tail =
    800                     render_glyph(render_priv, bm, pen_x, pen_y,
    801                                  info->c[0], 0, 1000000, tail);
    802             else
     796                    render_glyph(render_priv, bm, pen_x, pen_y, info->c[0],
     797                            info->c[1], info->effect_timing, tail);
     798            } else
    803799                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        }
    814804    }
    815805
    816806    *tail = 0;
     
    819809    return head;
    820810}
    821811
    822 static void compute_string_bbox(TextInfo *info, DBBox *bbox)
     812static void compute_string_bbox(TextInfo *text, DBBox *bbox)
    823813{
    824814    int i;
    825815
    826     if (info->length > 0) {
     816    if (text->length > 0) {
    827817        bbox->xMin = 32000;
    828818        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);
    832822
    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            }
    839833        }
    840834    } else
    841835        bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.;
     
    877871    render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.;
    878872    render_priv->state.fax = render_priv->state.fay = 0.;
    879873    render_priv->state.wrap_style = render_priv->track->WrapStyle;
     874    render_priv->state.font_encoding = render_priv->state.style->Encoding;
    880875}
    881876
    882877/**
     
    909904    render_priv->state.effect_type = EF_NONE;
    910905    render_priv->state.effect_timing = 0;
    911906    render_priv->state.effect_skip_timing = 0;
     907    render_priv->state.bm_run_id = 0;
    912908    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);
    916911
    917912    apply_transition_effects(render_priv, event);
    918913}
     
    930925 * Replace the outline of a glyph by a contour which makes up a simple
    931926 * opaque rectangle.
    932927 */
    933 static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch,
    934                             FT_Glyph glyph, int sx, int sy)
     928static void draw_opaque_box(ASS_Renderer *render_priv, int asc, int desc,
     929                            FT_Outline *ol, FT_Vector advance, int sx, int sy)
    935930{
    936     int asc = 0, desc = 0;
    937931    int i;
    938     int adv = d16_to_d6(glyph->advance.x);
     932    int adv = advance.x;
    939933    double scale_y = render_priv->state.scale_y;
    940934    double scale_x = render_priv->state.scale_x;
    941     FT_OutlineGlyph og = (FT_OutlineGlyph) glyph;
    942     FT_Outline *ol;
    943935
    944936    // to avoid gaps
    945937    sx = FFMAX(64, sx);
    946938    sy = FFMAX(64, sy);
    947939
    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 
    957940    // Emulate the WTFish behavior of VSFilter, i.e. double-scale
    958941    // the sizes of the opaque box.
    959942    adv += double_to_d6(render_priv->state.hspacing * render_priv->font_scale
     
    971954        { .x = -sx,         .y = -desc - sy },
    972955    };
    973956
    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);
    976958
    977     ol = &og->outline;
    978959    ol->n_points = ol->n_contours = 0;
    979960    for (i = 0; i < 4; i++) {
    980961        ol->points[ol->n_points] = points[i];
     
    987968 * Stroke an outline glyph in x/y direction.  Applies various fixups to get
    988969 * around limitations of the FreeType stroker.
    989970 */
    990 static void stroke_outline_glyph(ASS_Renderer *render_priv,
    991                                  FT_OutlineGlyph *glyph, int sx, int sy)
     971static void stroke_outline(ASS_Renderer *render_priv, FT_Outline *outline,
     972                           int sx, int sy)
    992973{
    993974    if (sx <= 0 && sy <= 0)
    994975        return;
    995976
    996     fix_freetype_stroker(*glyph, sx, sy);
     977    fix_freetype_stroker(outline, sx, sy);
    997978
    998979    // Borders are equal; use the regular stroker
    999980    if (sx == sy && render_priv->state.stroker) {
    1000981        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) {
    1004987            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);
    10061000
    10071001    // "Stroke" with the outline emboldener in two passes.
    10081002    // The outlines look uglier, but the emboldening never adds any points
    10091003    } else {
    10101004        int i;
    1011         FT_Outline *ol = &(*glyph)->outline;
    10121005        FT_Outline nol;
    1013         FT_Outline_New(render_priv->ftlibrary, ol->n_points,
    1014                        ol->n_contours, &nol);
    1015         FT_Outline_Copy(ol, &nol);
    10161006
    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);
    10191013        FT_Outline_Embolden(&nol, sy * 2);
    10201014        FT_Outline_Translate(&nol, -sy, -sy);
    10211015
    1022         for (i = 0; i < ol->n_points; i++)
    1023             ol->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;
    10241018
    10251019        FT_Outline_Done(render_priv->ftlibrary, &nol);
    10261020    }
     
    10301024 * \brief Prepare glyph hash
    10311025 */
    10321026static void
    1033 fill_glyph_hash(ASS_Renderer *priv, GlyphHashKey *key,
    1034                 ASS_Drawing *drawing, uint32_t ch)
     1027fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key,
     1028                GlyphInfo *info)
    10351029{
    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);
    10411037        key->border_style = priv->state.style->BorderStyle;
    1042         key->drawing_hash = drawing->hash;
    1043         // not very clean, but works
    1044         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;
    10461042    } 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;
    10571056        key->border_style = priv->state.style->BorderStyle;
    10581057    }
    10591058}
    10601059
    10611060/**
    10621061 * \brief Get normal and outline (border) glyphs
    1063  * \param symbol ucs4 char
    10641062 * \param info out: struct filled with extracted data
    10651063 * Tries to get both glyphs from cache.
    10661064 * If they can't be found, gets a glyph from font face, generates outline with FT_Stroker,
     
    10681066 * The glyphs are returned in info->glyph and info->outline_glyph
    10691067 */
    10701068static void
    1071 get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
    1072                   ASS_Drawing *drawing)
     1069get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
    10731070{
    1074     GlyphHashValue *val;
    1075     GlyphHashKey key;
     1071    OutlineHashValue *val;
     1072    OutlineHashKey key;
    10761073
    1077     memset(&key, 0, sizeof(key));
    1078     memset(info, 0, sizeof(GlyphInfo));
     1074    memset(&info->hash_key, 0, sizeof(key));
    10791075
    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);
    10951086            if(!ass_drawing_parse(drawing, 0))
    10961087                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);
    10981095        } 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            }
    11041117        }
    1105         if (!info->glyph)
     1118
     1119        if (!v.outline)
    11061120            return;
    11071121
    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);
    11111123
    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;
    11241127
    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));
    11321146        }
    11331147
    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);
    11441150    }
     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);
    11451164}
    11461165
    11471166/**
     
    11501169 * onto the screen plane.
    11511170 */
    11521171static void
    1153 transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry,
     1172transform_3d_points(FT_Vector shift, FT_Outline *outline, double frx, double fry,
    11541173                    double frz, double fax, double fay, double scale,
    11551174                    int yshift)
    11561175{
     
    11601179    double cx = cos(frx);
    11611180    double cy = cos(fry);
    11621181    double cz = cos(frz);
    1163     FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline;
    11641182    FT_Vector *p = outline->points;
    11651183    double x, y, z, xx, yy, zz;
    11661184    int i, dist;
     
    12031221 * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it.
    12041222 */
    12051223static void
    1206 transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2,
     1224transform_3d(FT_Vector shift, FT_Outline *outline, FT_Outline *border,
    12071225             double frx, double fry, double frz, double fax, double fay,
    12081226             double scale, int yshift)
    12091227{
    12101228    frx = -frx;
    12111229    frz = -frz;
    12121230    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,
    12151233                                fax, fay, scale, yshift);
    12161234
    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,
    12191237                                fax, fay, scale, yshift);
    12201238    }
    12211239}
     
    12321250get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
    12331251{
    12341252    BitmapHashValue *val;
    1235     BitmapHashKey *key = &info->hash_key;
     1253    OutlineBitmapHashKey *key = &info->hash_key.u.outline;
    12361254
    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;
    12381257
    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) {
    12441261        FT_Vector shift;
    12451262        BitmapHashValue hash_val;
    12461263        int error;
    12471264        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;
    12541267
    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;
    12671269
    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);
    12711272
    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;
    12951278
    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);
    13011283
    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);
    13041293        }
     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);
    13051318    }
    13061319
     1320    info->bm = val->bm;
     1321    info->bm_o = val->bm_o;
     1322    info->bm_s = val->bm_s;
     1323
    13071324    // VSFilter compatibility: invisible fill and no border?
    13081325    // 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)
    13101327        info->bm_s = 0;
    13111328}
    13121329
     
    14341451    double pen_shift_x;
    14351452    double pen_shift_y;
    14361453    int cur_line;
     1454    int run_offset;
    14371455    TextInfo *text_info = &render_priv->text_info;
    14381456
    14391457    last_space = -1;
     
    14741492                                           sizeof(LineInfo) *
    14751493                                           text_info->max_lines);
    14761494            }
    1477             if (lead < text_info->length)
     1495            if (lead < text_info->length) {
    14781496                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            }
    14831502        }
    14841503    }
    14851504#define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y))
     
    15431562    pen_shift_x = 0.;
    15441563    pen_shift_y = 0.;
    15451564    cur_line = 1;
     1565    run_offset = 0;
    15461566
    15471567    i = 0;
    15481568    cur = text_info->glyphs + i;
     
    15581578            double height =
    15591579                text_info->lines[cur_line - 1].desc +
    15601580                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;
    15611584            cur_line++;
     1585            run_offset++;
    15621586            pen_shift_x = d6_to_double(-cur->pos.x);
    15631587            pen_shift_y += height + render_priv->settings.line_spacing;
    15641588            ass_msg(render_priv->library, MSGL_DBG2,
    15651589                   "shifting from %d to %d by (%f, %f)", i,
    15661590                   text_info->length - 1, pen_shift_x, pen_shift_y);
    15671591        }
     1592        cur->bm_run_id += run_offset;
    15681593        cur->pos.x += double_to_d6(pen_shift_x);
    15691594        cur->pos.y += double_to_d6(pen_shift_y);
    15701595    }
    1571 }
     1596    text_info->lines[cur_line - 1].len =
     1597        text_info->length - text_info->lines[cur_line - 1].offset;
    15721598
    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);
    16401604    }
     1605#endif
    16411606}
    16421607
    16431608/**
     
    16801645 * Prepare bitmap hash key of a glyph
    16811646 */
    16821647static void
    1683 fill_bitmap_hash(ASS_Renderer *priv, BitmapHashKey *hash_key,
    1684                  ASS_Drawing *drawing, FT_Vector pen, uint32_t code)
     1648fill_bitmap_hash(ASS_Renderer *priv, GlyphInfo *info,
     1649                 OutlineBitmapHashKey *hash_key)
    16851650{
    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;
    17081658    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));
    17111661    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));
    17151664}
    17161665
    17171666/**
     
    17341683    int MarginL, MarginR, MarginV;
    17351684    int last_break;
    17361685    int alignment, halign, valign;
    1737     int kern = render_priv->track->Kerning;
    17381686    double device_x = 0;
    17391687    double device_y = 0;
    17401688    TextInfo *text_info = &render_priv->text_info;
     
    17541702
    17551703    drawing = render_priv->state.drawing;
    17561704    text_info->length = 0;
    1757     pen.x = 0;
    1758     pen.y = 0;
    1759     previous = 0;
    17601705    num_glyphs = 0;
    17611706    p = event->Text;
     1707
    17621708    // Event parsing.
    17631709    while (1) {
    17641710        // get next char, executing style override
     
    17691715                ass_drawing_add_char(drawing, (char) code);
    17701716        } while (code && render_priv->state.drawing_mode);      // skip everything in drawing mode
    17711717
     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
    17721729        // Parse drawing
    17731730        if (drawing->i) {
    17741731            drawing->scale_x = render_priv->state.scale_x *
    17751732                                     render_priv->font_scale;
    17761733            drawing->scale_y = render_priv->state.scale_y *
    17771734                                     render_priv->font_scale;
    1778             ass_drawing_hash(drawing);
    17791735            p--;
    1780             code = -1;
     1736            code = 0xfffc; // object replacement character
     1737            glyphs[text_info->length].drawing = drawing;
    17811738        }
    17821739
    17831740        // face could have been changed in get_next_char
     
    17891746        if (code == 0)
    17901747            break;
    17911748
    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
    18451750        glyphs[text_info->length].symbol = code;
    1846         glyphs[text_info->length].linebreak = 0;
     1751        glyphs[text_info->length].font = render_priv->state.font;
    18471752        for (i = 0; i < 4; ++i) {
    18481753            uint32_t clr = render_priv->state.c[i];
    18491754            change_alpha(&clr,
     
    18551760            render_priv->state.effect_timing;
    18561761        glyphs[text_info->length].effect_skip_timing =
    18571762            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);
    18581765        glyphs[text_info->length].be = render_priv->state.be;
    18591766        glyphs[text_info->length].blur = render_priv->state.blur;
    18601767        glyphs[text_info->length].shadow_x = render_priv->state.shadow_x;
    18611768        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;
    18621776        glyphs[text_info->length].frx = render_priv->state.frx;
    18631777        glyphs[text_info->length].fry = render_priv->state.fry;
    18641778        glyphs[text_info->length].frz = render_priv->state.frz;
    18651779        glyphs[text_info->length].fax = render_priv->state.fax;
    18661780        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;
    18741782
    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);
    18771786        }
    18781787
    1879         // fill bitmap hash
    1880         fill_bitmap_hash(render_priv, &glyphs[text_info->length].hash_key,
    1881                          drawing, pen, code);
    1882 
    18831788        text_info->length++;
    18841789
    18851790        render_priv->state.effect_type = EF_NONE;
    18861791        render_priv->state.effect_timing = 0;
    18871792        render_priv->state.effect_skip_timing = 0;
    18881793
    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         }
    18961794    }
    18971795
    1898 
    18991796    if (text_info->length == 0) {
    19001797        // no valid symbols in the event; this can be smth like {comment}
    19011798        free_render_context(render_priv);
    19021799        return 1;
    19031800    }
    19041801
     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
    19051865    // depends on glyph x coordinates being monotonous, so it should be done before line wrap
    19061866    process_karaoke_effects(render_priv);
    19071867
     
    19171877    MarginV =
    19181878        (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV;
    19191879
     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
    19201886    if (render_priv->state.evt_type != EVENT_HSCROLL) {
    1921         double max_text_width;
    1922 
    1923         // calculate max length of a line
    1924         max_text_width =
    1925             x2scr(render_priv,
    1926                   render_priv->track->PlayResX - MarginR) -
    1927             x2scr(render_priv, MarginL);
    1928 
    19291887        // rearrange text in several lines
    19301888        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    }
    19311896
    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);
    19411899
    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    }
    19441926
    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;
    19541938                if (halign == HALIGN_LEFT) {    // left aligned, no action
    19551939                    shift = 0;
    19561940                } else if (halign == HALIGN_RIGHT) {    // right aligned
     
    19591943                    shift = (max_text_width - width) / 2.0;
    19601944                }
    19611945                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                    }
    19631951                }
    19641952                last_break = i - 1;
     1953                width = 0;
    19651954            }
     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            }
    19661959        }
    1967     } else {                    // render_priv->state.evt_type == EVENT_HSCROLL
    1968         measure_text(render_priv);
    19691960    }
    19701961
    19711962    // determing text bounding box
     
    20912082
    20922083        for (i = 0; i < text_info->length; ++i) {
    20932084            GlyphInfo *info = glyphs + i;
     2085            while (info) {
     2086                OutlineBitmapHashKey *key = &info->hash_key.u.outline;
    20942087
    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;
    21042096            }
    21052097        }
    21062098    }
    21072099
    21082100    // 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;
    21102103    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        }
    21202117    }
    21212118
    21222119    memset(event_images, 0, sizeof(*event_images));
     
    21312128    event_images->event = event;
    21322129    event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y);
    21332130
     2131    ass_shaper_cleanup(render_priv->shaper, text_info);
    21342132    free_render_context(render_priv);
    21352133
    21362134    return 0;
     
    21542152 */
    21552153static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache)
    21562154{
    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);
    21642157        ass_free_images(priv->prev_images_root);
    21652158        priv->prev_images_root = 0;
    21662159    }
    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;
    21752165    }
    21762166}
    21772167
     
    22022192    render_priv->track = track;
    22032193    render_priv->time = now;
    22042194
    2205     ass_lazy_track_init(render_priv);
     2195    ass_lazy_track_init(render_priv->library, render_priv->track);
    22062196
    22072197    render_priv->font_scale = settings_priv->font_size_coeff *
    22082198        render_priv->orig_height / render_priv->track->PlayResY;
     
    22132203    else
    22142204        render_priv->border_scale = 1.;
    22152205
     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
    22162211    // PAR correction
    22172212    render_priv->font_scale_x = render_priv->settings.aspect /
    22182213                                render_priv->settings.storage_aspect;
  • libass/ass_render.h

     
    2727#include FT_GLYPH_H
    2828#include FT_SYNTHESIS_H
    2929
     30// XXX: fix the inclusion mess so we can avoid doing this here
     31typedef struct ass_shaper ASS_Shaper;
     32
    3033#include "ass.h"
    3134#include "ass_font.h"
    3235#include "ass_bitmap.h"
     
    7376    double aspect;              // frame aspect ratio, d_width / d_height.
    7477    double storage_aspect;      // pixel ratio of the source image
    7578    ASS_Hinting hinting;
     79    ASS_ShapingLevel shaper;
    7680
    7781    char *default_font;
    7882    char *default_family;
     
    96100
    97101// describes a glyph
    98102// GlyphInfo and TextInfo are used for text centering and word-wrapping operations
    99 typedef struct {
     103typedef struct glyph_info {
    100104    unsigned symbol;
    101105    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;
    104113    Bitmap *bm;                 // glyph bitmap
    105114    Bitmap *bm_o;               // outline bitmap
    106115    Bitmap *bm_s;               // shadow bitmap
    107116    FT_BBox bbox;
    108117    FT_Vector pos;
     118    FT_Vector offset;
    109119    char linebreak;             // the first (leading) glyph of some line ?
    110120    uint32_t c[4];              // colors
    111121    FT_Vector advance;          // 26.6
     122    FT_Vector cluster_advance;
    112123    Effect effect_type;
    113124    int effect_timing;          // time duration of current karaoke word
    114125    // after process_karaoke_effects: distance in pixels from the glyph origin.
     
    121132    double shadow_y;
    122133    double frx, fry, frz;       // rotation
    123134    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;
    124140
     141    int bm_run_id;
     142    int shape_run_id;
     143
    125144    BitmapHashKey hash_key;
     145
     146    // next glyph in this cluster
     147    struct glyph_info *next;
    126148} GlyphInfo;
    127149
    128150typedef struct {
    129151    double asc, desc;
     152    int offset, len;
    130153} LineInfo;
    131154
    132155typedef struct {
     
    147170    int parsed_tags;
    148171
    149172    ASS_Font *font;
    150     char *font_path;
    151173    double font_size;
    152174    int flags;                  // decoration flags (underline/strike-through)
    153175
     
    186208    int effect_timing;
    187209    int effect_skip_timing;
    188210
     211    // bitmap run id (used for final bitmap rendering)
     212    int bm_run_id;
     213
    189214    enum {
    190215        SCROLL_LR,              // left-to-right
    191216        SCROLL_RL,
     
    200225    unsigned italic;
    201226    int treat_family_as_pattern;
    202227    int wrap_style;
     228    int font_encoding;
    203229} RenderContext;
    204230
    205231typedef 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;
    210236    size_t glyph_max;
    211237    size_t bitmap_max_size;
    212238} CacheStore;
     
    218244    ASS_Settings settings;
    219245    int render_id;
    220246    ASS_SynthPriv *synth_priv;
     247    ASS_Shaper *shaper;
    221248
    222249    ASS_Image *images_root;     // rendering result is stored here
    223250    ASS_Image *prev_images_root;
     
    265292void reset_render_context(ASS_Renderer *render_priv);
    266293void ass_free_images(ASS_Image *img);
    267294
     295// XXX: this is actually in ass.c, includes should be fixed later on
     296void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track);
     297
    268298#endif /* LIBASS_RENDER_H */
  • libass/ass_render_api.c

     
    2525    ASS_Settings *settings = &priv->settings;
    2626
    2727    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);
    3431    ass_free_images(priv->prev_images_root);
    3532    priv->prev_images_root = 0;
    3633
     
    6158    }
    6259}
    6360
     61void 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
    6472void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r)
    6573{
    6674    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>
     33enum {
     34    VERT = 0,
     35    VKNA,
     36    KERN
     37};
     38#define NUM_FEATURES 3
     39#endif
     40
     41struct 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
     64struct ass_shaper_metrics_data {
     65    Cache *metrics_cache;
     66    GlyphMetricsHashKey hash_key;
     67    int vertical;
     68};
     69
     70struct 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 */
     80void 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 */
     94static 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 */
     107void 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
     120void 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 */
     139static 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 */
     155static 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 */
     169static 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
     188GlyphMetricsHashValue *
     189get_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
     212static hb_bool_t
     213get_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
     226static hb_position_t
     227cached_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
     243static hb_position_t
     244cached_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
     258static hb_bool_t
     259cached_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
     265static hb_bool_t
     266cached_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
     282static hb_position_t
     283get_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
     295static hb_position_t
     296get_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
     302static hb_bool_t
     303cached_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
     321static hb_bool_t
     322get_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 */
     348static 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 */
     414static 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 */
     501static 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 */
     528void 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 */
     538void 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 */
     567void 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 */
     577void 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 */
     587void 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 */
     596void 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 */
     654ASS_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 */
     674void 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 */
     692FriBidiStrIndex *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 */
     722FriBidiParType 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
     27void ass_shaper_info(ASS_Library *lib);
     28ASS_Shaper *ass_shaper_new(size_t prealloc);
     29void ass_shaper_free(ASS_Shaper *shaper);
     30void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern);
     31void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
     32                          GlyphInfo *glyphs, size_t len);
     33void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir);
     34void ass_shaper_set_language(ASS_Shaper *shaper, const char *code);
     35void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level);
     36void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info);
     37void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info);
     38FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info);
     39FriBidiParType resolve_base_direction(int font_encoding);
     40
     41void ass_shaper_font_data_free(ASS_ShaperFontData *priv);
     42
     43#endif
  • libass/ass_types.h

     
    112112    int WrapStyle;
    113113    int ScaledBorderAndShadow;
    114114    int Kerning;
     115    char *Language;
    115116
    116117    int default_style;      // index of default style
    117118    char *name;             // file name in case of external subs, 0 for streams
  • Makefile

     
    115115                                        libass/ass_parse.c \
    116116                                        libass/ass_render.c \
    117117                                        libass/ass_render_api.c \
     118                                        libass/ass_shaper.c \
    118119                                        libass/ass_strtod.c \
    119120                                        libass/ass_utils.c \
    120121