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