summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libass/Makefile.am3
-rw-r--r--libass/ass_cache.c27
-rw-r--r--libass/ass_cache.h1
-rw-r--r--libass/ass_cache_template.c2
-rw-r--r--libass/ass_drawing.c477
-rw-r--r--libass/ass_drawing.h76
-rw-r--r--libass/ass_render.c150
-rw-r--r--libass/ass_utils.h26
8 files changed, 694 insertions, 68 deletions
diff --git a/libass/Makefile.am b/libass/Makefile.am
index ccdeebc..81b8b96 100644
--- a/libass/Makefile.am
+++ b/libass/Makefile.am
@@ -6,7 +6,8 @@ lib_LTLIBRARIES = libass.la
libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontconfig.c ass_render.c \
ass_utils.c ass_bitmap.c ass_library.c ass_bitmap.h \
ass_cache.h ass_fontconfig.h ass_font.h ass.h \
- ass_library.h ass_types.h ass_utils.h help_mp.h
+ ass_library.h ass_types.h ass_utils.h ass_drawing.c \
+ ass_drawing.h help_mp.h
libass_la_LDFLAGS = -version-info $(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE)
libass_la_LDFLAGS += -export-symbols $(srcdir)/libass.sym
diff --git a/libass/ass_cache.c b/libass/ass_cache.c
index 04b3acb..fc70b0d 100644
--- a/libass/ass_cache.c
+++ b/libass/ass_cache.c
@@ -34,33 +34,6 @@
#include "ass_bitmap.h"
#include "ass_cache.h"
-
-#define FNV1_32A_INIT (unsigned)0x811c9dc5
-
-static inline unsigned fnv_32a_buf(void *buf, size_t len, unsigned hval)
-{
- unsigned char *bp = buf;
- unsigned char *be = bp + len;
- while (bp < be) {
- hval ^= (unsigned) *bp++;
- hval +=
- (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) +
- (hval << 24);
- }
- return hval;
-}
-static inline unsigned fnv_32a_str(char *str, unsigned hval)
-{
- unsigned char *s = (unsigned char *) str;
- while (*s) {
- hval ^= (unsigned) *s++;
- hval +=
- (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) +
- (hval << 24);
- }
- return hval;
-}
-
static unsigned hashmap_hash(void *buf, size_t len)
{
return fnv_32a_buf(buf, len, FNV1_32A_INIT);
diff --git a/libass/ass_cache.h b/libass/ass_cache.h
index c1d25b2..d8de97a 100644
--- a/libass/ass_cache.h
+++ b/libass/ass_cache.h
@@ -102,6 +102,7 @@ typedef struct glyph_hash_val_s {
FT_Glyph outline_glyph;
FT_BBox bbox_scaled; // bbox after scaling, but before rotation
FT_Vector advance; // 26.6, advance distance to the next bitmap in line
+ int asc, desc; // ascender/descender of a drawing
} glyph_hash_val_t;
hashmap_t *ass_glyph_cache_init(void);
diff --git a/libass/ass_cache_template.c b/libass/ass_cache_template.c
index dd9adbd..bccb6db 100644
--- a/libass/ass_cache_template.c
+++ b/libass/ass_cache_template.c
@@ -78,6 +78,7 @@ START(bitmap, bipmap_hash_key_s)
GENERIC(int, shift_y)
FTVECTOR(advance) // subpixel shift vector
FTVECTOR(shadow_offset) // shadow subpixel shift
+ GENERIC(unsigned, drawing_hash) // hashcode of a drawing
END(bitmap_hash_key_t)
// describes an outline glyph
@@ -91,6 +92,7 @@ START(glyph, glyph_hash_key_s)
GENERIC(unsigned, scale_y) // 16.16
FTVECTOR(advance) // subpixel shift vector
FTVECTOR(outline) // border width, 16.16
+ GENERIC(unsigned, drawing_hash) // hashcode of a drawing
END(glyph_hash_key_t)
// Cache for composited bitmaps
diff --git a/libass/ass_drawing.c b/libass/ass_drawing.c
new file mode 100644
index 0000000..30d742f
--- /dev/null
+++ b/libass/ass_drawing.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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
+#define GLYPH_INITIAL_POINTS 100
+#define GLYPH_INITIAL_CONTOURS 5
+
+/*
+ * \brief Get and prepare a FreeType glyph
+ */
+static void drawing_make_glyph(ass_drawing_t *drawing, void *fontconfig_priv,
+ ass_font_t *font, ass_hinting_t hint)
+{
+ FT_OutlineGlyph glyph;
+
+ // This is hacky...
+ glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font,
+ (uint32_t) ' ', hint);
+
+ 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_t *drawing,
+ FT_Vector *point)
+{
+ FT_Outline *ol = &drawing->glyph->outline;
+
+ 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);
+ }
+
+ ol->points[ol->n_points].x = point->x;
+ ol->points[ol->n_points].y = point->y;
+ ol->tags[ol->n_points] = 1;
+ ol->n_points++;
+}
+
+/*
+ * \brief Close a contour and check glyph size overflow.
+ */
+static inline void drawing_close_shape(ass_drawing_t *drawing)
+{
+ FT_Outline *ol = &drawing->glyph->outline;
+
+ if (ol->n_contours >= drawing->max_contours) {
+ drawing->max_contours *= 2;
+ ol->contours = realloc(ol->contours, sizeof(short) *
+ drawing->max_contours);
+ }
+
+ ol->contours[ol->n_contours] = ol->n_points - 1;
+ ol->n_contours++;
+}
+
+/*
+ * \brief Prepare drawing for parsing. This just sets a few parameters.
+ */
+static void drawing_prepare(ass_drawing_t *drawing)
+{
+ // Scaling parameters
+ drawing->point_scale_x = drawing->scale_x *
+ 64.0 / (1 << (drawing->scale - 1));
+ drawing->point_scale_y = drawing->scale_y *
+ 64.0 / (1 << (drawing->scale - 1));
+}
+
+/*
+ * \brief Finish a drawing. This only sets the horizontal advance according
+ * to the glyph's bbox at the moment.
+ */
+static void drawing_finish(ass_drawing_t *drawing)
+{
+ int i, offset;
+ FT_BBox bbox;
+ FT_Outline *ol = &drawing->glyph->outline;
+
+ // Close the last contour
+ drawing_close_shape(drawing);
+
+#if 0
+ // Dump points
+ for (i = 0; i < ol->n_points; i++) {
+ printf("point (%d, %d)\n", (int) ol->points[i].x,
+ (int) ol->points[i].y);
+ }
+
+ // Dump contours
+ for (i = 0; i < ol->n_contours; i++)
+ printf("contour %d\n", ol->contours[i]);
+#endif
+
+ FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox);
+ drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin);
+
+ drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y);
+ drawing->asc = bbox.yMax - bbox.yMin + drawing->desc;
+
+ // Place it onto the baseline
+ offset = (bbox.yMax - bbox.yMin) + double_to_d6(-drawing->pbo *
+ drawing->scale_y);
+ for (i = 0; i < ol->n_points; i++)
+ ol->points[i].y += offset;
+}
+
+/*
+ * \brief Check whether a number of items on the list is available
+ */
+static int token_check_values(ass_drawing_token_t *token, int i, int type)
+{
+ int j;
+ for (j = 0; j < i; j++) {
+ if (!token || token->type != type) return 0;
+ token = token->next;
+ }
+
+ return 1;
+}
+
+/*
+ * \brief Tokenize a drawing string into a list of ass_drawing_token_t
+ * This also expands points for closing b-splines
+ */
+static ass_drawing_token_t *drawing_tokenize(char *str)
+{
+ char *p = str;
+ int i, val, type = -1, is_set = 0;
+ FT_Vector point = {0, 0};
+
+ ass_drawing_token_t *root = NULL, *tail = NULL, *spline_start = NULL;
+
+ while (*p) {
+ if (*p == 'c' && spline_start) {
+ // Close b-splines: add the first three points of the b-spline
+ // back to the end
+ if (token_check_values(spline_start->next, 2, TOKEN_B_SPLINE)) {
+ for (i = 0; i < 3; i++) {
+ tail->next = calloc(1, sizeof(ass_drawing_token_t));
+ tail->next->prev = tail;
+ tail = tail->next;
+ tail->type = TOKEN_B_SPLINE;
+ tail->point = spline_start->point;
+ spline_start = spline_start->next;
+ }
+ spline_start = NULL;
+ }
+ } else if (!is_set && mystrtoi(&p, &val)) {
+ point.x = val;
+ is_set = 1;
+ p--;
+ } else if (is_set == 1 && mystrtoi(&p, &val)) {
+ point.y = val;
+ is_set = 2;
+ p--;
+ } else if (*p == 'm')
+ type = TOKEN_MOVE;
+ else if (*p == 'n')
+ type = TOKEN_MOVE_NC;
+ else if (*p == 'l')
+ type = TOKEN_LINE;
+ else if (*p == 'b')
+ type = TOKEN_CUBIC_BEZIER;
+ else if (*p == 'q')
+ type = TOKEN_CONIC_BEZIER;
+ else if (*p == 's')
+ type = TOKEN_B_SPLINE;
+ // We're simply ignoring TOKEN_EXTEND_B_SPLINE here.
+ // This is not harmful at all, since it can be ommitted with
+ // similar result (the spline is extended anyway).
+
+ if (type != -1 && is_set == 2) {
+ if (root) {
+ tail->next = calloc(1, sizeof(ass_drawing_token_t));
+ tail->next->prev = tail;
+ tail = tail->next;
+ } else
+ root = tail = calloc(1, sizeof(ass_drawing_token_t));
+ tail->type = type;
+ tail->point = point;
+ is_set = 0;
+ if (type == TOKEN_B_SPLINE && !spline_start)
+ spline_start = tail->prev;
+ }
+ p++;
+ }
+
+#if 0
+ // Check tokens
+ ass_drawing_token_t *t = root;
+ while(t) {
+ printf("token %d point (%d, %d)\n", t->type, t->point.x, t->point.y);
+ t = t->next;
+ }
+#endif
+
+ return root;
+}
+
+/*
+ * \brief Free a list of tokens
+ */
+static void drawing_free_tokens(ass_drawing_token_t *token)
+{
+ while (token) {
+ ass_drawing_token_t *at = token;
+ token = token->next;
+ free(at);
+ }
+}
+
+/*
+ * \brief Translate and scale a point coordinate according to baseline
+ * offset and scale.
+ */
+static inline void translate_point(ass_drawing_t *drawing, FT_Vector *point)
+{
+ point->x = drawing->point_scale_x * point->x;
+ point->y = drawing->point_scale_y * -point->y;
+}
+
+/*
+ * \brief Evaluate a curve into lines
+ * 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_t *drawing,
+ ass_drawing_token_t *token, char spline,
+ int started)
+{
+ double cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0;
+ double t, h, max_accel, max_accel1, max_accel2;
+ FT_Vector cur = {0, 0};
+
+ cur = token->point;
+ translate_point(drawing, &cur);
+ int x0 = cur.x;
+ int y0 = cur.y;
+ token = token->next;
+ cur = token->point;
+ translate_point(drawing, &cur);
+ int x1 = cur.x;
+ int y1 = cur.y;
+ token = token->next;
+ cur = token->point;
+ translate_point(drawing, &cur);
+ int x2 = cur.x;
+ int y2 = cur.y;
+ token = token->next;
+ cur = token->point;
+ translate_point(drawing, &cur);
+ int x3 = cur.x;
+ int y3 = cur.y;
+
+ if (spline) {
+ // 1 [-1 +3 -3 +1]
+ // - * [+3 -6 +3 0]
+ // 6 [-3 0 +3 0]
+ // [+1 +4 +1 0]
+
+ double div6 = 1.0/6.0;
+
+ cx3 = div6*(- x0+3*x1-3*x2+x3);
+ cx2 = div6*( 3*x0-6*x1+3*x2);
+ cx1 = div6*(-3*x0 +3*x2);
+ cx0 = div6*( x0+4*x1+1*x2);
+
+ cy3 = div6*(- y0+3*y1-3*y2+y3);
+ cy2 = div6*( 3*y0-6*y1+3*y2);
+ cy1 = div6*(-3*y0 +3*y2);
+ cy0 = div6*( y0+4*y1+1*y2);
+ } else {
+ // [-1 +3 -3 +1]
+ // [+3 -6 +3 0]
+ // [-3 +3 0 0]
+ // [+1 0 0 0]
+
+ cx3 = - x0+3*x1-3*x2+x3;
+ cx2 = 3*x0-6*x1+3*x2;
+ cx1 = -3*x0+3*x1;
+ cx0 = x0;
+
+ cy3 = - y0+3*y1-3*y2+y3;
+ cy2 = 3*y0-6*y1+3*y2;
+ cy1 = -3*y0+3*y1;
+ cy0 = y0;
+ }
+
+ max_accel1 = fabs(2 * cy2) + fabs(6 * cy3);
+ max_accel2 = fabs(2 * cx2) + fabs(6 * cx3);
+
+ max_accel = FFMAX(max_accel1, max_accel2);
+ h = 1.0;
+
+ if (max_accel > CURVE_ACCURACY)
+ h = sqrt(CURVE_ACCURACY / max_accel);
+
+ if (!started) {
+ cur.x = cx0;
+ cur.y = cy0;
+ drawing_add_point(drawing, &cur);
+ }
+
+ for (t = 0; t < 1.0; t += h) {
+ cur.x = cx0 + t * (cx1 + t * (cx2 + t * cx3));
+ cur.y = cy0 + t * (cy1 + t * (cy2 + t * cy3));
+ drawing_add_point(drawing, &cur);
+ }
+
+ cur.x = cx0 + cx1 + cx2 + cx3;
+ cur.y = cy0 + cy1 + cy2 + cy3;
+ drawing_add_point(drawing, &cur);
+}
+
+/*
+ * \brief Create and initialize a new drawing and return it
+ */
+ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
+ ass_hinting_t hint, FT_Library lib)
+{
+ ass_drawing_t* drawing;
+
+ drawing = calloc(1, sizeof(*drawing));
+ drawing->text = malloc(DRAWING_INITIAL_SIZE);
+ drawing->size = DRAWING_INITIAL_SIZE;
+
+ drawing->ftlibrary = lib;
+ drawing_make_glyph(drawing, fontconfig_priv, font, hint);
+
+ drawing->scale_x = 1.;
+ drawing->scale_y = 1.;
+ drawing->max_contours = GLYPH_INITIAL_CONTOURS;
+ drawing->max_points = GLYPH_INITIAL_POINTS;
+
+ return drawing;
+}
+
+/*
+ * \brief Free a drawing
+ */
+void ass_drawing_free(ass_drawing_t* drawing)
+{
+ free(drawing->text);
+ free(drawing);
+}
+
+/*
+ * \brief Add one ASCII character to the drawing text buffer
+ */
+void ass_drawing_add_char(ass_drawing_t* drawing, char symbol)
+{
+ drawing->text[drawing->i++] = symbol;
+ drawing->text[drawing->i] = 0;
+
+ if (drawing->i + 1 >= drawing->size) {
+ drawing->size *= 2;
+ drawing->text = realloc(drawing->text, drawing->size);
+ }
+}
+
+/*
+ * \brief Create a hashcode for the drawing
+ * XXX: To avoid collisions a better hash algorithm might be useful.
+ */
+void ass_drawing_hash(ass_drawing_t* drawing)
+{
+ drawing->hash = fnv_32a_str(drawing->text, FNV1_32A_INIT);
+}
+
+/*
+ * \brief Convert token list to outline. Calls the line and curve evaluators.
+ */
+FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing)
+{
+ int started = 0;
+ ass_drawing_token_t *token;
+ FT_Vector pen = {0, 0};
+
+ drawing->tokens = drawing_tokenize(drawing->text);
+ drawing_prepare(drawing);
+
+ token = drawing->tokens;
+ while (token) {
+ // Draw something according to current command
+ switch (token->type) {
+ case TOKEN_MOVE_NC:
+ pen = token->point;
+ translate_point(drawing, &pen);
+ token = token->next;
+ break;
+ case TOKEN_MOVE:
+ pen = token->point;
+ translate_point(drawing, &pen);
+ if (started) {
+ drawing_close_shape(drawing);
+ started = 0;
+ }
+ token = token->next;
+ break;
+ case TOKEN_LINE: {
+ FT_Vector to;
+ to = token->point;
+ translate_point(drawing, &to);
+ if (!started) drawing_add_point(drawing, &pen);
+ drawing_add_point(drawing, &to);
+ started = 1;
+ token = token->next;
+ break;
+ }
+ case TOKEN_CUBIC_BEZIER:
+ if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) &&
+ token->prev) {
+ drawing_evaluate_curve(drawing, token->prev, 0, started);
+ token = token->next;
+ token = token->next;
+ token = token->next;
+ started = 1;
+ } else
+ token = token->next;
+ break;
+ case TOKEN_B_SPLINE:
+ if (token_check_values(token, 3, TOKEN_B_SPLINE) &&
+ token->prev) {
+ drawing_evaluate_curve(drawing, token->prev, 1, started);
+ token = token->next;
+ started = 1;
+ } else
+ token = token->next;
+ break;
+ default:
+ token = token->next;
+ break;
+ }
+ }
+
+ drawing_finish(drawing);
+ drawing_free_tokens(drawing->tokens);
+ return &drawing->glyph;
+}
+
+
diff --git a/libass/ass_drawing.h b/libass/ass_drawing.h
new file mode 100644
index 0000000..323c05d
--- /dev/null
+++ b/libass/ass_drawing.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LIBASS_DRAWING_H
+#define LIBASS_DRAWING_H
+
+#include <ft2build.h>
+#include FT_GLYPH_H
+
+#include "ass.h"
+
+#define DRAWING_INITIAL_SIZE 256
+
+enum ass_token_type {
+ TOKEN_MOVE,
+ TOKEN_MOVE_NC,
+ TOKEN_LINE,
+ TOKEN_CUBIC_BEZIER,
+ TOKEN_CONIC_BEZIER,
+ TOKEN_B_SPLINE,
+ TOKEN_EXTEND_SPLINE,
+ TOKEN_CLOSE
+};
+
+typedef struct ass_drawing_token_s {
+ enum ass_token_type type;
+ FT_Vector point;
+ struct ass_drawing_token_s *next;
+ struct ass_drawing_token_s *prev;
+} ass_drawing_token_t;
+
+typedef struct ass_drawing_s {
+ char *text; // drawing string
+ int i; // text index
+ int scale; // scale (1-64) for subpixel accuracy
+ double pbo; // drawing will be shifted in y direction by this amount
+ double scale_x; // FontScaleX
+ double scale_y; // FontScaleY
+ int asc; // ascender
+ int desc; // descender
+ FT_OutlineGlyph glyph; // the "fake" glyph created for later rendering
+ int hash; // hash value (for caching)
+
+ // private
+ FT_Library ftlibrary; // FT library instance, needed for font ops
+ int size; // current buffer size
+ ass_drawing_token_t *tokens; // tokenized drawing
+ int max_points; // current maximum size
+ int max_contours;
+ double point_scale_x;
+ double point_scale_y;
+} ass_drawing_t;
+
+ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
+ ass_hinting_t hint, FT_Library lib);
+void ass_drawing_free(ass_drawing_t* drawing);
+void ass_drawing_add_char(ass_drawing_t* drawing, char symbol);
+void ass_drawing_hash(ass_drawing_t* drawing);
+FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing);
+
+#endif /* LIBASS_DRAWING_H */
diff --git a/libass/ass_render.c b/libass/ass_render.c
index cc8242f..c112399 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -36,6 +36,7 @@
#include "ass_utils.h"
#include "ass_fontconfig.h"
#include "ass_library.h"
+#include "ass_drawing.h"
#define MAX_GLYPHS_INITIAL 1024
#define MAX_LINES_INITIAL 64
@@ -171,6 +172,7 @@ typedef struct render_context_s {
double shadow_x;
double shadow_y;
int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags
+ ass_drawing_t *drawing; // current drawing
effect_t effect_type;
int effect_timing;
@@ -1560,12 +1562,15 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
val = 0.;
render_priv->state.shadow_x = render_priv->state.shadow_y = val;
} else if (mystrcmp(&p, "pbo")) {
- int val = 0;
- mystrtoi(&p, &val); // ignored
+ double val = 0;
+ if (mystrtod(&p, &val))
+ render_priv->state.drawing->pbo = val;
} else if (mystrcmp(&p, "p")) {
int val;
if (!mystrtoi(&p, &val))
val = 0;
+ if (val)
+ render_priv->state.drawing->scale = val;
render_priv->state.drawing_mode = !!val;
}
@@ -1765,12 +1770,18 @@ init_render_context(ass_renderer_t *render_priv, ass_event_t *event)
render_priv->state.effect_type = EF_NONE;
render_priv->state.effect_timing = 0;
render_priv->state.effect_skip_timing = 0;
+ render_priv->state.drawing =
+ ass_drawing_new(render_priv->fontconfig_priv,
+ render_priv->state.font,
+ render_priv->settings.hinting,
+ render_priv->ftlibrary);
apply_transition_effects(render_priv, event);
}
-static void free_render_context(void)
+static void free_render_context(ass_renderer_t *render_priv)
{
+ ass_drawing_free(render_priv->state.drawing);
}
// Calculate the cbox of a series of points
@@ -1867,23 +1878,33 @@ static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x,
*/
static void
get_outline_glyph(ass_renderer_t *render_priv, int symbol,
- glyph_info_t *info, FT_Vector *advance)
+ glyph_info_t *info, FT_Vector *advance,
+ ass_drawing_t *drawing)
{
int error;
glyph_hash_val_t *val;
glyph_hash_key_t key;
memset(&key, 0, sizeof(key));
- key.font = render_priv->state.font;
- key.size = render_priv->state.font_size;
- key.ch = symbol;
- key.scale_x = double_to_d16(render_priv->state.scale_x);
- key.scale_y = double_to_d16(render_priv->state.scale_y);
- key.advance = *advance;
- key.bold = render_priv->state.bold;
- key.italic = render_priv->state.italic;
- key.outline.x = render_priv->state.border_x * 0xFFFF;
- key.outline.y = render_priv->state.border_y * 0xFFFF;
+ if (drawing->hash) {
+ key.scale_x = double_to_d16(render_priv->state.scale_x);
+ key.scale_y = double_to_d16(render_priv->state.scale_y);
+ key.advance = *advance;
+ key.outline.x = render_priv->state.border_x * 0xFFFF;
+ key.outline.y = render_priv->state.border_y * 0xFFFF;
+ key.drawing_hash = drawing->hash;
+ } else {
+ key.font = render_priv->state.font;
+ key.size = render_priv->state.font_size;
+ key.ch = symbol;
+ key.bold = render_priv->state.bold;
+ key.italic = render_priv->state.italic;
+ key.scale_x = double_to_d16(render_priv->state.scale_x);
+ key.scale_y = double_to_d16(render_priv->state.scale_y);
+ key.advance = *advance;
+ key.outline.x = render_priv->state.border_x * 0xFFFF;
+ key.outline.y = render_priv->state.border_y * 0xFFFF;
+ }
memset(info, 0, sizeof(glyph_info_t));
val = cache_find_glyph(render_priv->cache.glyph_cache, &key);
@@ -1894,12 +1915,21 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
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 {
glyph_hash_val_t v;
- info->glyph =
- ass_font_get_glyph(render_priv->fontconfig_priv,
- render_priv->state.font, symbol,
- render_priv->settings.hinting);
+ if (drawing->hash) {
+ ass_drawing_parse(drawing);
+ FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph);
+ } else {
+ info->glyph =
+ ass_font_get_glyph(render_priv->fontconfig_priv,
+ render_priv->state.font, symbol,
+ render_priv->settings.hinting);
+ }
if (!info->glyph)
return;
info->advance.x = d16_to_d6(info->glyph->advance.x);
@@ -1953,6 +1983,10 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
FT_Glyph_Copy(info->outline_glyph, &v.outline_glyph);
v.advance = info->advance;
v.bbox_scaled = info->bbox;
+ if (drawing->hash) {
+ v.asc = drawing->asc;
+ v.desc = drawing->desc;
+ }
cache_add_glyph(render_priv->cache.glyph_cache, &key, &v);
}
}
@@ -2433,6 +2467,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
double device_x = 0;
double device_y = 0;
text_info_t *text_info = &render_priv->text_info;
+ ass_drawing_t *drawing;
if (event->Style >= render_priv->track->n_styles) {
ass_msg(MSGL_WARN, MSGTR_LIBASS_NoStyleFound);
@@ -2445,6 +2480,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
init_render_context(render_priv, event);
+ drawing = render_priv->state.drawing;
text_info->length = 0;
pen.x = 0;
pen.y = 0;
@@ -2457,11 +2493,25 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
// this affects render_context
do {
code = get_next_char(render_priv, &p);
+ if (render_priv->state.drawing_mode && code)
+ ass_drawing_add_char(drawing, (char) code);
} while (code && render_priv->state.drawing_mode); // skip everything in drawing mode
+ // Parse drawing
+ if (drawing->i) {
+ drawing->scale_x = render_priv->state.scale_x *
+ render_priv->font_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;
+ }
+
// face could have been changed in get_next_char
if (!render_priv->state.font) {
- free_render_context();
+ free_render_context(render_priv);
return 1;
}
@@ -2485,7 +2535,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
}
// Add kerning to pen
- if (previous && code) {
+ if (previous && code && !drawing->hash) {
FT_Vector delta;
delta =
ass_font_get_kerning(render_priv->state.font, previous,
@@ -2500,7 +2550,8 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
render_priv->state.scale_y, &shift);
get_outline_glyph(render_priv, code,
- text_info->glyphs + text_info->length, &shift);
+ text_info->glyphs + text_info->length, &shift,
+ drawing);
text_info->glyphs[text_info->length].pos.x = pen.x;
text_info->glyphs[text_info->length].pos.y = pen.y;
@@ -2539,19 +2590,34 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
text_info->glyphs[text_info->length].frz = render_priv->state.frz;
text_info->glyphs[text_info->length].fax = render_priv->state.fax;
text_info->glyphs[text_info->length].fay = render_priv->state.fay;
- ass_font_get_asc_desc(render_priv->state.font, code,
- &text_info->glyphs[text_info->length].asc,
- &text_info->glyphs[text_info->length].desc);
- text_info->glyphs[text_info->length].asc *=
- render_priv->state.scale_y;
- text_info->glyphs[text_info->length].desc *=
- render_priv->state.scale_y;
+ if (drawing->hash) {
+ text_info->glyphs[text_info->length].asc = drawing->asc;
+ text_info->glyphs[text_info->length].desc = drawing->desc;
+ } else {
+ ass_font_get_asc_desc(render_priv->state.font, code,
+ &text_info->glyphs[text_info->length].asc,
+ &text_info->glyphs[text_info->length].desc);
+
+ text_info->glyphs[text_info->length].asc *=
+ render_priv->state.scale_y;
+ text_info->glyphs[text_info->length].desc *=
+ render_priv->state.scale_y;
+ }
// fill bitmap_hash_key
- text_info->glyphs[text_info->length].hash_key.font =
- render_priv->state.font;
- text_info->glyphs[text_info->length].hash_key.size =
- render_priv->state.font_size;
+ if (!drawing->hash) {
+ text_info->glyphs[text_info->length].hash_key.font =
+ render_priv->state.font;
+ text_info->glyphs[text_info->length].hash_key.size =
+ render_priv->state.font_size;
+ text_info->glyphs[text_info->length].hash_key.bold =
+ render_priv->state.bold;
+ text_info->glyphs[text_info->length].hash_key.italic =
+ render_priv->state.italic;
+ } else
+ text_info->glyphs[text_info->length].hash_key.drawing_hash =
+ drawing->hash;
+ text_info->glyphs[text_info->length].hash_key.ch = code;
text_info->glyphs[text_info->length].hash_key.outline.x =
render_priv->state.border_x * 0xFFFF;
text_info->glyphs[text_info->length].hash_key.outline.y =
@@ -2570,11 +2636,6 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
render_priv->state.fax * 0xFFFF;
text_info->glyphs[text_info->length].hash_key.fay =
render_priv->state.fay * 0xFFFF;
- text_info->glyphs[text_info->length].hash_key.bold =
- render_priv->state.bold;
- text_info->glyphs[text_info->length].hash_key.italic =
- render_priv->state.italic;
- text_info->glyphs[text_info->length].hash_key.ch = code;
text_info->glyphs[text_info->length].hash_key.advance.x = pen.x;
text_info->glyphs[text_info->length].hash_key.advance.y = pen.y;
text_info->glyphs[text_info->length].hash_key.be =
@@ -2597,11 +2658,20 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
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->settings.hinting,
+ render_priv->ftlibrary);
+ }
}
if (text_info->length == 0) {
// no valid symbols in the event; this can be smth like {comment}
- free_render_context();
+ free_render_context(render_priv);
return 1;
}
// depends on glyph x coordinates being monotonous, so it should be done before line wrap
@@ -2813,7 +2883,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
d6_to_double(g->pos.y & SUBPIXEL_MASK));
get_bitmap_glyph(render_priv, text_info->glyphs + i);
}
-
+
memset(event_images, 0, sizeof(*event_images));
event_images->top = device_y - text_info->lines[0].asc;
event_images->height = text_info->height;
@@ -2822,7 +2892,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
event_images->event = event;
event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y);
- free_render_context();
+ free_render_context(render_priv);
return 0;
}
diff --git a/libass/ass_utils.h b/libass/ass_utils.h
index 553a69d..cb1fae7 100644
--- a/libass/ass_utils.h
+++ b/libass/ass_utils.h
@@ -99,4 +99,30 @@ static inline int double_to_d16(double x)
return (int) (x * 0x10000);
}
+#define FNV1_32A_INIT (unsigned)0x811c9dc5
+
+static inline unsigned fnv_32a_buf(void *buf, size_t len, unsigned hval)
+{
+ unsigned char *bp = buf;
+ unsigned char *be = bp + len;
+ while (bp < be) {
+ hval ^= (unsigned) *bp++;
+ hval +=
+ (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) +
+ (hval << 24);
+ }
+ return hval;
+}
+static inline unsigned fnv_32a_str(char *str, unsigned hval)
+{
+ unsigned char *s = (unsigned char *) str;
+ while (*s) {
+ hval ^= (unsigned) *s++;
+ hval +=
+ (hval << 1) + (hval &