From 507ae26358a491c7f665d32a3e07c2f176d152e9 Mon Sep 17 00:00:00 2001 From: "Dr.Smile" Date: Sun, 23 Nov 2014 20:01:03 +0300 Subject: Replace FT_Outline with ASS_Outline --- libass/ass_bitmap.c | 45 ++++++++++++++-- libass/ass_bitmap.h | 14 ++++- libass/ass_cache.c | 8 +-- libass/ass_cache.h | 5 +- libass/ass_drawing.c | 94 ++++++++++++++++---------------- libass/ass_drawing.h | 7 ++- libass/ass_font.c | 137 +++++++++++++++++++++++++++++++++++++++------- libass/ass_font.h | 12 +++-- libass/ass_rasterizer.c | 4 +- libass/ass_rasterizer.h | 7 +-- libass/ass_render.c | 140 +++++++++++++++++++++++++++++++----------------- libass/ass_render.h | 4 +- 12 files changed, 335 insertions(+), 142 deletions(-) diff --git a/libass/ass_bitmap.c b/libass/ass_bitmap.c index 0eb4577..01bf013 100644 --- a/libass/ass_bitmap.c +++ b/libass/ass_bitmap.c @@ -279,7 +279,7 @@ Bitmap *copy_bitmap(const Bitmap *src) #if CONFIG_RASTERIZER Bitmap *outline_to_bitmap(ASS_Renderer *render_priv, - FT_Outline *outline, int bord) + ASS_Outline *outline, int bord) { ASS_Rasterizer *rst = &render_priv->rasterizer; if (!rasterizer_set_outline(rst, outline)) { @@ -343,8 +343,8 @@ Bitmap *outline_to_bitmap(ASS_Renderer *render_priv, #else -Bitmap *outline_to_bitmap(ASS_Renderer *render_priv, - FT_Outline *outline, int bord) +static Bitmap *outline_to_bitmap_ft(ASS_Renderer *render_priv, + FT_Outline *outline, int bord) { Bitmap *bm; int w, h; @@ -406,6 +406,42 @@ Bitmap *outline_to_bitmap(ASS_Renderer *render_priv, return bm; } +Bitmap *outline_to_bitmap(ASS_Renderer *render_priv, + ASS_Outline *outline, int bord) +{ + size_t n_points = outline->n_points; + if (n_points > SHRT_MAX) { + ass_msg(render_priv->library, MSGL_WARN, "Too many outline points: %d", + outline->n_points); + n_points = SHRT_MAX; + } + + size_t n_contours = FFMIN(outline->n_contours, SHRT_MAX); + short contours_small[EFFICIENT_CONTOUR_COUNT]; + short *contours = contours_small; + short *contours_large = NULL; + if (n_contours > EFFICIENT_CONTOUR_COUNT) { + contours_large = malloc(n_contours * sizeof(short)); + if (!contours_large) + return NULL; + contours = contours_large; + } + for (size_t i = 0; i < n_contours; ++i) + contours[i] = FFMIN(outline->contours[i], n_points - 1); + + FT_Outline ftol; + ftol.n_points = n_points; + ftol.n_contours = n_contours; + ftol.points = outline->points; + ftol.tags = outline->tags; + ftol.contours = contours; + ftol.flags = 0; + + Bitmap *bm = outline_to_bitmap_ft(render_priv, &ftol, bord); + free(contours_large); + return bm; +} + #endif /** @@ -671,7 +707,8 @@ void be_blur_c(uint8_t *buf, intptr_t w, } } -int outline_to_bitmap3(ASS_Renderer *render_priv, FT_Outline *outline, FT_Outline *border, +int outline_to_bitmap3(ASS_Renderer *render_priv, + ASS_Outline *outline, ASS_Outline *border, Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, int be, double blur_radius, FT_Vector shadow_offset, int border_style, int border_visible) diff --git a/libass/ass_bitmap.h b/libass/ass_bitmap.h index 82a2159..4a070ba 100644 --- a/libass/ass_bitmap.h +++ b/libass/ass_bitmap.h @@ -29,6 +29,16 @@ typedef struct ass_synth_priv ASS_SynthPriv; ASS_SynthPriv *ass_synth_init(double); void ass_synth_done(ASS_SynthPriv *priv); +typedef struct { + size_t n_contours, max_contours; + size_t *contours; + size_t n_points, max_points; + FT_Vector *points; + char *tags; +} ASS_Outline; + +#define EFFICIENT_CONTOUR_COUNT 8 + typedef struct { int left, top; int w, h; // width, height @@ -37,7 +47,7 @@ typedef struct { } Bitmap; Bitmap *outline_to_bitmap(ASS_Renderer *render_priv, - FT_Outline *outline, int bord); + ASS_Outline *outline, int bord); Bitmap *alloc_bitmap(int w, int h); @@ -54,7 +64,7 @@ void ass_synth_blur(ASS_SynthPriv *priv_blur, int opaque_box, int be, * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps * \param border_visible whether border is visible if border_style is 3 */ -int outline_to_bitmap3(ASS_Renderer *render_priv, FT_Outline *outline, FT_Outline *border, +int outline_to_bitmap3(ASS_Renderer *render_priv, ASS_Outline *outline, ASS_Outline *border, Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, int be, double blur_radius, FT_Vector shadow_offset, int border_style, int border_visible); diff --git a/libass/ass_cache.c b/libass/ass_cache.c index a0a00b3..062ddfa 100644 --- a/libass/ass_cache.c +++ b/libass/ass_cache.c @@ -175,10 +175,10 @@ static void outline_destruct(void *key, void *value) { OutlineHashValue *v = value; OutlineHashKey *k = key; - if (v->outline) - outline_free(v->lib, v->outline); - if (v->border) - outline_free(v->lib, v->border); + outline_free(v->outline); + free(v->outline); + outline_free(v->border); + free(v->border); if (k->type == OUTLINE_DRAWING) free(k->u.drawing.text); free(key); diff --git a/libass/ass_cache.h b/libass/ass_cache.h index 677b705..0600142 100644 --- a/libass/ass_cache.h +++ b/libass/ass_cache.h @@ -42,9 +42,8 @@ typedef struct { } CompositeHashValue; typedef struct { - FT_Library lib; - FT_Outline *outline; - FT_Outline *border; + ASS_Outline *outline; + ASS_Outline *border; FT_BBox bbox_scaled; // bbox after scaling, but before rotation FT_Vector advance; // 26.6, advance distance to the next outline in line int asc, desc; // ascender/descender diff --git a/libass/ass_drawing.c b/libass/ass_drawing.c index 70afae5..e2676df 100644 --- a/libass/ass_drawing.c +++ b/libass/ass_drawing.c @@ -20,10 +20,12 @@ #include FT_OUTLINE_H #include FT_BBOX_H #include +#include #include #include "ass_utils.h" #include "ass_drawing.h" +#include "ass_font.h" #define CURVE_ACCURACY 64.0 #define GLYPH_INITIAL_POINTS 100 @@ -32,46 +34,42 @@ /* * \brief Add a single point to a contour. */ -static inline void drawing_add_point(ASS_Drawing *drawing, +static inline bool drawing_add_point(ASS_Drawing *drawing, const FT_Vector *point, char tags) { - FT_Outline *ol = &drawing->outline; - if (ol->n_points == SHRT_MAX) - return; - - if (ol->n_points >= drawing->max_points) { - drawing->max_points *= 2; - ol->points = realloc(ol->points, sizeof(FT_Vector) * - drawing->max_points); - ol->tags = realloc(ol->tags, drawing->max_points); + ASS_Outline *ol = &drawing->outline; + if (ol->n_points >= ol->max_points) { + size_t new_size = 2 * ol->max_points; + if (!ASS_REALLOC_ARRAY(ol->points, new_size)) + return false; + if (!ASS_REALLOC_ARRAY(ol->tags, new_size)) + return false; + ol->max_points = new_size; } ol->points[ol->n_points].x = point->x; ol->points[ol->n_points].y = point->y; ol->tags[ol->n_points] = tags; ol->n_points++; + return true; } /* * \brief Close a contour and check outline size overflow. */ -static inline void drawing_close_shape(ASS_Drawing *drawing) +static inline bool drawing_close_shape(ASS_Drawing *drawing) { - FT_Outline *ol = &drawing->outline; - if (ol->n_contours == SHRT_MAX) { - if (ol->n_points) - ol->contours[ol->n_contours] = ol->n_points - 1; - return; - } - - if (ol->n_contours >= drawing->max_contours) { - drawing->max_contours *= 2; - ol->contours = realloc(ol->contours, sizeof(short) * - drawing->max_contours); + ASS_Outline *ol = &drawing->outline; + if (ol->n_contours >= ol->max_contours) { + size_t new_size = 2 * ol->max_contours; + if (!ASS_REALLOC_ARRAY(ol->contours, new_size)) + return false; + ol->max_contours = new_size; } ol->contours[ol->n_contours] = ol->n_points - 1; ol->n_contours++; + return true; } /* @@ -93,7 +91,7 @@ static void drawing_finish(ASS_Drawing *drawing, int raw_mode) int i; double pbo; FT_BBox bbox = drawing->cbox; - FT_Outline *ol = &drawing->outline; + ASS_Outline *ol = &drawing->outline; if (drawing->library) ass_msg(drawing->library, MSGL_V, @@ -241,7 +239,7 @@ static inline void translate_point(ASS_Drawing *drawing, FT_Vector *point) * This curve evaluator is also used in VSFilter (RTS.cpp); it's a simple * implementation of the De Casteljau algorithm. */ -static void drawing_evaluate_curve(ASS_Drawing *drawing, +static bool drawing_evaluate_curve(ASS_Drawing *drawing, ASS_DrawingToken *token, char spline, int started) { @@ -270,11 +268,10 @@ static void drawing_evaluate_curve(ASS_Drawing *drawing, p[2].y -= y12; } - if (!started) - drawing_add_point(drawing, &p[0], FT_CURVE_TAG_ON); - drawing_add_point(drawing, &p[1], FT_CURVE_TAG_CUBIC); - drawing_add_point(drawing, &p[2], FT_CURVE_TAG_CUBIC); - drawing_add_point(drawing, &p[3], FT_CURVE_TAG_ON); + return (started || drawing_add_point(drawing, &p[0], FT_CURVE_TAG_ON)) && + drawing_add_point(drawing, &p[1], FT_CURVE_TAG_CUBIC) && + drawing_add_point(drawing, &p[2], FT_CURVE_TAG_CUBIC) && + drawing_add_point(drawing, &p[3], FT_CURVE_TAG_ON); } /* @@ -282,9 +279,7 @@ static void drawing_evaluate_curve(ASS_Drawing *drawing, */ ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib) { - ASS_Drawing *drawing; - - drawing = calloc(1, sizeof(*drawing)); + ASS_Drawing *drawing = calloc(1, sizeof(*drawing)); if (!drawing) return NULL; drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX; @@ -293,14 +288,8 @@ ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library 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; + outline_alloc(&drawing->outline, GLYPH_INITIAL_POINTS, GLYPH_INITIAL_CONTOURS); return drawing; } @@ -311,7 +300,7 @@ void ass_drawing_free(ASS_Drawing* drawing) { if (drawing) { free(drawing->text); - FT_Outline_Done(drawing->ftlibrary, &drawing->outline); + outline_free(&drawing->outline); } free(drawing); } @@ -339,7 +328,7 @@ void ass_drawing_hash(ASS_Drawing* drawing) /* * \brief Convert token list to outline. Calls the line and curve evaluators. */ -FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) +ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) { int started = 0; ASS_DrawingToken *token; @@ -361,7 +350,8 @@ FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) pen = token->point; translate_point(drawing, &pen); if (started) { - drawing_close_shape(drawing); + if (!drawing_close_shape(drawing)) + goto error; started = 0; } token = token->next; @@ -370,8 +360,10 @@ FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) FT_Vector to; to = token->point; translate_point(drawing, &to); - if (!started) drawing_add_point(drawing, &pen, FT_CURVE_TAG_ON); - drawing_add_point(drawing, &to, FT_CURVE_TAG_ON); + if (!started && !drawing_add_point(drawing, &pen, FT_CURVE_TAG_ON)) + goto error; + if (!drawing_add_point(drawing, &to, FT_CURVE_TAG_ON)) + goto error; started = 1; token = token->next; break; @@ -379,7 +371,8 @@ FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) case TOKEN_CUBIC_BEZIER: if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) && token->prev) { - drawing_evaluate_curve(drawing, token->prev, 0, started); + if (!drawing_evaluate_curve(drawing, token->prev, 0, started)) + goto error; token = token->next; token = token->next; token = token->next; @@ -390,7 +383,8 @@ FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) case TOKEN_B_SPLINE: if (token_check_values(token, 3, TOKEN_B_SPLINE) && token->prev) { - drawing_evaluate_curve(drawing, token->prev, 1, started); + if (!drawing_evaluate_curve(drawing, token->prev, 1, started)) + goto error; token = token->next; started = 1; } else @@ -403,10 +397,14 @@ FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) } // Close the last contour - if (started) - drawing_close_shape(drawing); + if (started && !drawing_close_shape(drawing)) + goto error; drawing_finish(drawing, raw_mode); drawing_free_tokens(drawing->tokens); return &drawing->outline; + +error: + drawing_free_tokens(drawing->tokens); + return NULL; } diff --git a/libass/ass_drawing.h b/libass/ass_drawing.h index 62010eb..1e73c27 100644 --- a/libass/ass_drawing.h +++ b/libass/ass_drawing.h @@ -23,6 +23,7 @@ #include FT_OUTLINE_H #include "ass.h" +#include "ass_bitmap.h" typedef enum { TOKEN_MOVE, @@ -50,7 +51,7 @@ typedef struct { double scale_y; // FontScaleY int asc; // ascender int desc; // descender - FT_Outline outline; // target outline + ASS_Outline outline; // target outline FT_Vector advance; // advance (from cbox) int hash; // hash value (for caching) @@ -58,8 +59,6 @@ typedef struct { FT_Library ftlibrary; // needed for font ops ASS_Library *library; ASS_DrawingToken *tokens; // tokenized drawing - int max_points; // current maximum size - int max_contours; double point_scale_x; double point_scale_y; FT_BBox cbox; // bounding box, or let's say... VSFilter's idea of it @@ -69,6 +68,6 @@ ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib); void ass_drawing_free(ASS_Drawing* drawing); void ass_drawing_set_text(ASS_Drawing* drawing, char *str, size_t n); void ass_drawing_hash(ASS_Drawing* drawing); -FT_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode); +ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode); #endif /* LIBASS_DRAWING_H */ diff --git a/libass/ass_font.c b/libass/ass_font.c index 69675c6..7b202a6 100644 --- a/libass/ass_font.c +++ b/libass/ass_font.c @@ -392,23 +392,91 @@ static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font, return 0; } -void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest) + +int outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_contours) { - if (source == NULL) { - *dest = NULL; - return; + outline->contours = malloc(sizeof(size_t) * n_contours); + outline->points = malloc(sizeof(FT_Vector) * n_points); + outline->tags = malloc(n_points); + if (!outline->contours || !outline->points || !outline->tags) + return 0; + + outline->max_contours = n_contours; + outline->max_points = n_points; + return 1; +} + +ASS_Outline *outline_convert(const FT_Outline *source) +{ + if (!source) + return NULL; + + ASS_Outline *ol = calloc(1, sizeof(*ol)); + if (!ol) + return NULL; + + if (!outline_alloc(ol, source->n_points, source->n_contours)) { + outline_free(ol); + free(ol); + return NULL; + } + + //if (source->flags & FT_OUTLINE_REVERSE_FILL) { + if (FT_Outline_Get_Orientation((FT_Outline *)source) != FT_ORIENTATION_TRUETYPE) { + int prev = 0; + for (int i = 0; i < source->n_contours; ++i) { + int last = source->contours[i]; + ol->contours[i] = last; + ol->points[prev] = source->points[prev]; + ol->tags[prev] = source->tags[prev]; + for (int j = 0; j < last - prev; ++j) { + ol->points[last - j] = source->points[prev + j + 1]; + ol->tags[last - j] = source->tags[prev + j + 1]; + } + prev = last + 1; + } + } else { + for (int i = 0; i < source->n_contours; ++i) + ol->contours[i] = source->contours[i]; + memcpy(ol->points, source->points, sizeof(FT_Vector) * source->n_points); + memcpy(ol->tags, source->tags, source->n_points); + } + ol->n_contours = source->n_contours; + ol->n_points = source->n_points; + return ol; +} + +ASS_Outline *outline_copy(const ASS_Outline *source) +{ + if (!source) + return NULL; + + ASS_Outline *ol = calloc(1, sizeof(*ol)); + if (!ol) + return NULL; + + if (!outline_alloc(ol, source->n_points, source->n_contours)) { + outline_free(ol); + free(ol); + return NULL; } - *dest = calloc(1, sizeof(**dest)); - FT_Outline_New(lib, source->n_points, source->n_contours, *dest); - FT_Outline_Copy(source, *dest); + memcpy(ol->contours, source->contours, sizeof(size_t) * source->n_contours); + memcpy(ol->points, source->points, sizeof(FT_Vector) * source->n_points); + memcpy(ol->tags, source->tags, source->n_points); + ol->n_contours = source->n_contours; + ol->n_points = source->n_points; + return ol; } -void outline_free(FT_Library lib, FT_Outline *outline) +void outline_free(ASS_Outline *outline) { - if (outline) - FT_Outline_Done(lib, outline); - free(outline); + if (!outline) + return; + + free(outline->contours); + free(outline->points); + free(outline->tags); } /** @@ -663,6 +731,43 @@ static int get_contour_direction(FT_Vector *points, int start, int end) return sum > 0; } +void outline_translate(const ASS_Outline *outline, FT_Pos dx, FT_Pos dy) +{ + for (size_t i = 0; i < outline->n_points; ++i) { + outline->points[i].x += dx; + outline->points[i].y += dy; + } +} + +void outline_transform(const ASS_Outline *outline, const FT_Matrix *matrix) +{ + for (size_t i = 0; i < outline->n_points; ++i) { + FT_Pos x = FT_MulFix(outline->points[i].x, matrix->xx) + + FT_MulFix(outline->points[i].y, matrix->xy); + FT_Pos y = FT_MulFix(outline->points[i].x, matrix->yx) + + FT_MulFix(outline->points[i].y, matrix->yy); + outline->points[i].x = x; + outline->points[i].y = y; + } +} + +void outline_get_cbox(const ASS_Outline *outline, FT_BBox *cbox) +{ + if (!outline->n_points) { + cbox->xMin = cbox->xMax = 0; + cbox->yMin = cbox->yMax = 0; + return; + } + cbox->xMin = cbox->xMax = outline->points[0].x; + cbox->yMin = cbox->yMax = outline->points[0].y; + for (size_t i = 1; i < outline->n_points; ++i) { + cbox->xMin = FFMIN(cbox->xMin, outline->points[i].x); + cbox->xMax = FFMAX(cbox->xMax, outline->points[i].x); + cbox->yMin = FFMIN(cbox->yMin, outline->points[i].y); + cbox->yMax = FFMAX(cbox->yMax, outline->points[i].y); + } +} + /** * \brief Apply fixups to please the FreeType stroker and improve the * rendering result, especially in case the outline has some anomalies. @@ -679,7 +784,7 @@ static int get_contour_direction(FT_Vector *points, int start, int end) * \param border_x border size, x direction, d6 format * \param border_x border size, y direction, d6 format */ -void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y) +void fix_freetype_stroker(ASS_Outline *outline, int border_x, int border_y) { int nc = outline->n_contours; int begin, stop; @@ -689,10 +794,6 @@ void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y) int end = -1; FT_BBox *boxes = malloc(nc * sizeof(FT_BBox)); int i, j; - int inside_direction; - - inside_direction = FT_Outline_Get_Orientation(outline) == - FT_ORIENTATION_TRUETYPE; // create a list of cboxes of the contours for (i = 0; i < nc; i++) { @@ -709,7 +810,7 @@ void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y) end = outline->contours[i]; int dir = get_contour_direction(outline->points, start, end); valid_cont[i] = 1; - if (dir == inside_direction) { + if (dir) { for (j = 0; j < nc; j++) { if (i == j) continue; @@ -733,7 +834,7 @@ void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y) dir ^= 1; } check_inside: - if (dir == inside_direction) { + if (dir) { FT_BBox box; get_contour_cbox(&box, outline->points, start, end); int width = box.xMax - box.xMin; diff --git a/libass/ass_font.h b/libass/ass_font.h index f80b887..f3c3f8e 100644 --- a/libass/ass_font.h +++ b/libass/ass_font.h @@ -74,8 +74,14 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, 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_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); + +void outline_translate(const ASS_Outline *outline, FT_Pos dx, FT_Pos dy); +void outline_transform(const ASS_Outline *outline, const FT_Matrix *matrix); +void outline_get_cbox(const ASS_Outline *outline, FT_BBox *cbox); +void fix_freetype_stroker(ASS_Outline *outline, int border_x, int border_y); +int outline_alloc(ASS_Outline *outline, size_t n_contours, size_t n_points); +ASS_Outline *outline_convert(const FT_Outline *source); +ASS_Outline *outline_copy(const ASS_Outline *source); +void outline_free(ASS_Outline *outline); #endif /* LIBASS_FONT_H */ diff --git a/libass/ass_rasterizer.c b/libass/ass_rasterizer.c index 93a4d5a..84f9a88 100644 --- a/libass/ass_rasterizer.c +++ b/libass/ass_rasterizer.c @@ -230,13 +230,13 @@ static int add_cubic(ASS_Rasterizer *rst, } -int rasterizer_set_outline(ASS_Rasterizer *rst, const FT_Outline *path) +int rasterizer_set_outline(ASS_Rasterizer *rst, const ASS_Outline *path) { enum Status { S_ON, S_Q, S_C1, S_C2 }; - int i, j = 0; + size_t i, j = 0; rst->size[0] = 0; for (i = 0; i < path->n_contours; ++i) { OutlinePoint start, p[4]; diff --git a/libass/ass_rasterizer.h b/libass/ass_rasterizer.h index 9da475e..6630317 100644 --- a/libass/ass_rasterizer.h +++ b/libass/ass_rasterizer.h @@ -19,11 +19,12 @@ #ifndef LIBASS_RASTERIZER_H #define LIBASS_RASTERIZER_H -#include -#include FT_FREETYPE_H #include #include +#include "ass.h" +#include "ass_font.h" + enum { SEGFLAG_DN = 1, @@ -82,7 +83,7 @@ void rasterizer_done(ASS_Rasterizer *rst); /** * \brief Convert FreeType outline to polyline and calculate exact bounds */ -int rasterizer_set_outline(ASS_Rasterizer *rst, const FT_Outline *path); +int rasterizer_set_outline(ASS_Rasterizer *rst, const ASS_Outline *path); /** * \brief Polyline rasterization function * \param x0, y0, width, height in: source window (full pixel units) diff --git a/libass/ass_render.c b/libass/ass_render.c index d3e262f..4371aa3 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -522,7 +522,6 @@ static bool free_list_add(ASS_Renderer *render_priv, void *object) static void blend_vector_clip(ASS_Renderer *render_priv, ASS_Image *head) { - FT_Outline *outline; Bitmap *clip_bm = NULL; ASS_Image *cur; ASS_Drawing *drawing = render_priv->state.clip_drawing; @@ -544,7 +543,7 @@ static void blend_vector_clip(ASS_Renderer *render_priv, BitmapHashValue v; // Not found in cache, parse and rasterize it - outline = ass_drawing_parse(drawing, 1); + ASS_Outline *outline = ass_drawing_parse(drawing, 1); if (!outline) { ass_msg(render_priv->library, MSGL_WARN, "Clip vector parsing failed. Skipping."); @@ -558,7 +557,7 @@ static void blend_vector_clip(ASS_Renderer *render_priv, .x = int_to_d6(render_priv->settings.left_margin), .y = -int_to_d6(render_priv->settings.top_margin), }; - FT_Outline_Translate(outline, trans.x, trans.y); + outline_translate(outline, trans.x, trans.y); } clip_bm = outline_to_bitmap(render_priv, outline, 0); @@ -953,10 +952,9 @@ static void free_render_context(ASS_Renderer *render_priv) * opaque rectangle. */ static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info, - int asc, int desc, FT_Outline *ol, + int asc, int desc, ASS_Outline *ol, FT_Vector advance, int sx, int sy) { - int i; int adv = advance.x; double scale_y = info->orig_scale_y; double scale_x = info->orig_scale_x; @@ -981,10 +979,10 @@ static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info, { .x = -sx, .y = -desc - sy }, }; - FT_Outline_New(render_priv->ftlibrary, 4, 1, ol); - ol->n_points = ol->n_contours = 0; - for (i = 0; i < 4; i++) { + if (!outline_alloc(ol, 4, 1)) + return; + for (int i = 0; i < 4; ++i) { ol->points[ol->n_points] = points[i]; ol->tags[ol->n_points++] = 1; } @@ -995,7 +993,7 @@ static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info, * Stroke an outline glyph in x/y direction. Applies various fixups to get * around limitations of the FreeType stroker. */ -static void stroke_outline(ASS_Renderer *render_priv, FT_Outline *outline, +static void stroke_outline(ASS_Renderer *render_priv, ASS_Outline *outline, int sx, int sy) { if (sx <= 0 && sy <= 0) @@ -1003,32 +1001,76 @@ static void stroke_outline(ASS_Renderer *render_priv, FT_Outline *outline, fix_freetype_stroker(outline, sx, sy); + size_t n_points = outline->n_points; + if (n_points > SHRT_MAX) { + ass_msg(render_priv->library, MSGL_WARN, "Too many outline points: %d", + outline->n_points); + n_points = SHRT_MAX; + } + + size_t n_contours = FFMIN(outline->n_contours, SHRT_MAX); + short contours_small[EFFICIENT_CONTOUR_COUNT]; + short *contours = contours_small; + short *contours_large = NULL; + if (n_contours > EFFICIENT_CONTOUR_COUNT) { + contours_large = malloc(n_contours * sizeof(short)); + if (!contours_large) + return; + contours = contours_large; + } + for (size_t i = 0; i < n_contours; ++i) + contours[i] = FFMIN(outline->contours[i], n_points - 1); + + FT_Outline ftol; + ftol.n_points = n_points; + ftol.n_contours = n_contours; + ftol.points = outline->points; + ftol.tags = outline->tags; + ftol.contours = contours; + ftol.flags = 0; + // Borders are equal; use the regular stroker if (sx == sy && render_priv->state.stroker) { int error; - unsigned n_points, n_contours; - - FT_StrokerBorder border = FT_Outline_GetOutsideBorder(outline); - error = FT_Stroker_ParseOutline(render_priv->state.stroker, outline, 0); + FT_StrokerBorder border = FT_Outline_GetOutsideBorder(&ftol); + error = FT_Stroker_ParseOutline(render_priv->state.stroker, &ftol, 0); if (error) { ass_msg(render_priv->library, MSGL_WARN, "FT_Stroker_ParseOutline failed, error: %d", error); } + unsigned new_points, new_contours; error = FT_Stroker_GetBorderCounts(render_priv->state.stroker, border, - &n_points, &n_contours); + &new_points, &new_contours); if (error) { ass_msg(render_priv->library, MSGL_WARN, "FT_Stroker_GetBorderCounts failed, error: %d", error); } - FT_Outline_Done(render_priv->ftlibrary, outline); - error = FT_Outline_New(render_priv->ftlibrary, n_points, n_contours, outline); + outline_free(outline); outline->n_points = outline->n_contours = 0; - if (error) { + if (new_contours > FFMAX(EFFICIENT_CONTOUR_COUNT, n_contours)) { + if (!ASS_REALLOC_ARRAY(contours_large, new_contours)) { + free(contours_large); + return; + } + } + n_points = new_points; + n_contours = new_contours; + if (!outline_alloc(outline, n_points, n_contours)) { ass_msg(render_priv->library, MSGL_WARN, - "FT_Outline_New failed, error: %d", error); + "Not enough memory for border outline"); + free(contours_large); return; } - FT_Stroker_ExportBorder(render_priv->state.stroker, border, outline); + ftol.n_points = ftol.n_contours = 0; + ftol.points = outline->points; + ftol.tags = outline->tags; + + FT_Stroker_ExportBorder(render_priv->state.stroker, border, &ftol); + + outline->n_points = n_points; + outline->n_contours = n_contours; + for (size_t i = 0; i < n_contours; ++i) + outline->contours[i] = contours[i]; // "Stroke" with the outline emboldener (in two passes if needed). // The outlines look uglier, but the emboldening never adds any points @@ -1036,27 +1078,29 @@ static void stroke_outline(ASS_Renderer *render_priv, FT_Outline *outline, #if (FREETYPE_MAJOR > 2) || \ ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 4)) || \ ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR == 4) && (FREETYPE_PATCH >= 10)) - FT_Outline_EmboldenXY(outline, sx * 2, sy * 2); - FT_Outline_Translate(outline, -sx, -sy); + FT_Outline_EmboldenXY(&ftol, sx * 2, sy * 2); + FT_Outline_Translate(&ftol, -sx, -sy); #else int i; FT_Outline nol; - FT_Outline_New(render_priv->ftlibrary, outline->n_points, - outline->n_contours, &nol); - FT_Outline_Copy(outline, &nol); + FT_Outline_New(render_priv->ftlibrary, ftol.n_points, + ftol.n_contours, &nol); + FT_Outline_Copy(&ftol, &nol); - FT_Outline_Embolden(outline, sx * 2); - FT_Outline_Translate(outline, -sx, -sx); + FT_Outline_Embolden(&ftol, sx * 2); + FT_Outline_Translate(&ftol, -sx, -sx); FT_Outline_Embolden(&nol, sy * 2); FT_Outline_Translate(&nol, -sy, -sy); - for (i = 0; i < outline->n_points; i++) - outline->points[i].y = nol.points[i].y; + for (i = 0; i < ftol.n_points; i++) + ftol.points[i].y = nol.points[i].y; FT_Outline_Done(render_priv->ftlibrary, &nol); #endif } + + free(contours_large); } /** @@ -1168,8 +1212,7 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) ass_drawing_hash(drawing); if(!ass_drawing_parse(drawing, 0)) return; - outline_copy(priv->ftlibrary, &drawing->outline, - &v.outline); + v.outline = outline_copy(&drawing->outline); v.advance.x = drawing->advance.x; v.advance.y = drawing->advance.y; v.asc = drawing->asc; @@ -1185,8 +1228,7 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) 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); + v.outline = outline_convert(&((FT_OutlineGlyph)glyph)->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); @@ -1202,12 +1244,12 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) if (!v.outline) return; - FT_Outline_Get_CBox(v.outline, &v.bbox_scaled); + outline_get_cbox(v.outline, &v.bbox_scaled); if (info->border_style == 3) { FT_Vector advance; - v.border = calloc(1, sizeof(FT_Outline)); + v.border = calloc(1, sizeof(ASS_Outline)); if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing) advance = v.advance; @@ -1222,13 +1264,12 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) { change_border(priv, info->border_x, info->border_y); - outline_copy(priv->ftlibrary, v.outline, &v.border); + v.border = outline_copy(v.outline); stroke_outline(priv, v.border, double_to_d6(info->border_x * priv->border_scale), double_to_d6(info->border_y * priv->border_scale)); } - v.lib = priv->ftlibrary; val = ass_cache_put(priv->cache.outline_cache, &key, &v); } @@ -1250,7 +1291,7 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) * onto the screen plane. */ static void -transform_3d_points(FT_Vector shift, FT_Outline *outline, double frx, double fry, +transform_3d_points(FT_Vector shift, ASS_Outline *outline, double frx, double fry, double frz, double fax, double fay, double scale, int yshift) { @@ -1262,10 +1303,10 @@ transform_3d_points(FT_Vector shift, FT_Outline *outline, double frx, double fry double cz = cos(frz); FT_Vector *p = outline->points; double x, y, z, xx, yy, zz; - int i, dist; + int dist; dist = 20000 * scale; - for (i = 0; i < outline->n_points; i++) { + for (size_t i = 0; i < outline->n_points; ++i) { x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y)); y = (double) p[i].y + shift.y + (-fay * p[i].x); z = 0.; @@ -1302,7 +1343,7 @@ transform_3d_points(FT_Vector shift, FT_Outline *outline, double frx, double fry * 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_Outline *outline, FT_Outline *border, +transform_3d(FT_Vector shift, ASS_Outline *outline, ASS_Outline *border, double frx, double fry, double frz, double fax, double fay, double scale, int yshift) { @@ -1343,13 +1384,12 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) BitmapHashValue hash_val; int error; double fax_scaled, fay_scaled; - FT_Outline *outline, *border; double scale_x = render_priv->font_scale_x; hash_val.bm = hash_val.bm_o = hash_val.bm_s = 0; - outline_copy(render_priv->ftlibrary, info->outline, &outline); - outline_copy(render_priv->ftlibrary, info->border, &border); + ASS_Outline *outline = outline_copy(info->outline); + ASS_Outline *border = outline_copy(info->border); // calculating rotation shift vector (from rotation origin to the glyph basepoint) shift.x = key->shift_x; @@ -1370,13 +1410,13 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) // subpixel shift if (outline) { if (scale_x != 1.0) - FT_Outline_Transform(outline, &m); - FT_Outline_Translate(outline, key->advance.x, -key->advance.y); + outline_transform(outline, &m); + 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); + outline_transform(border, &m); + outline_translate(border, key->advance.x, -key->advance.y); } // render glyph @@ -1394,8 +1434,10 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) 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); + outline_free(outline); + free(outline); + outline_free(border); + free(border); } info->bm = val->bm; diff --git a/libass/ass_render.h b/libass/ass_render.h index 1e5fa54..e99b0fa 100644 --- a/libass/ass_render.h +++ b/libass/ass_render.h @@ -166,8 +166,8 @@ typedef struct glyph_info { #endif double font_size; ASS_Drawing *drawing; - FT_Outline *outline; - FT_Outline *border; + ASS_Outline *outline; + ASS_Outline *border; Bitmap *bm; // glyph bitmap Bitmap *bm_o; // outline bitmap Bitmap *bm_s; // shadow bitmap -- cgit v1.2.3