From f4b0721d815de8df1e6d95be4769ffc57fe4da1d Mon Sep 17 00:00:00 2001 From: "Dr.Smile" Date: Sun, 2 Dec 2018 22:43:52 +0300 Subject: drawing: extract transformation from parsing --- libass/ass_drawing.c | 127 ++++++++++++++++----------------------------------- libass/ass_drawing.h | 21 +-------- libass/ass_render.c | 66 ++++++++++++++------------ 3 files changed, 80 insertions(+), 134 deletions(-) diff --git a/libass/ass_drawing.c b/libass/ass_drawing.c index 11ff399..fb71e15 100644 --- a/libass/ass_drawing.c +++ b/libass/ass_drawing.c @@ -29,67 +29,29 @@ #include "ass_drawing.h" #include "ass_font.h" -#define GLYPH_INITIAL_POINTS 100 -#define GLYPH_INITIAL_SEGMENTS 100 - -/* - * \brief Prepare drawing for parsing. This just sets a few parameters. - */ -static void drawing_prepare(ASS_Drawing *drawing) -{ - // Scaling parameters - drawing->point_scale_x = drawing->scale_x / (1 << (drawing->scale - 1)); - drawing->point_scale_y = drawing->scale_y / (1 << (drawing->scale - 1)); -} - -/* - * \brief Finish a drawing. This only sets the horizontal advance according - * to the outline's bbox at the moment. - */ -static void drawing_finish(ASS_Drawing *drawing, bool raw_mode) -{ - ASS_Rect bbox = drawing->cbox; - ASS_Outline *ol = &drawing->outline; - - if (drawing->library) - ass_msg(drawing->library, MSGL_V, - "Parsed drawing with %d points and %d segments", - ol->n_points, ol->n_segments); - - if (raw_mode) - return; - - drawing->advance = bbox.x_max - bbox.x_min; - - double pbo = drawing->pbo / (1 << (drawing->scale - 1)); - drawing->desc = double_to_d6(pbo * drawing->scale_y); - drawing->asc = bbox.y_max - bbox.y_min - drawing->desc; - - // Place it onto the baseline - for (size_t i = 0; i < ol->n_points; i++) - ol->points[i].y -= drawing->asc; -} +#define DRAWING_INITIAL_POINTS 100 +#define DRAWING_INITIAL_SEGMENTS 100 /* * \brief Check whether a number of items on the list is available */ -static int token_check_values(ASS_DrawingToken *token, int i, int type) +static bool token_check_values(ASS_DrawingToken *token, int i, int type) { for (int j = 0; j < i; j++) { - if (!token || token->type != type) return 0; + if (!token || token->type != type) return false; token = token->next; } - return 1; + return true; } /* * \brief Tokenize a drawing string into a list of ASS_DrawingToken * This also expands points for closing b-splines */ -static ASS_DrawingToken *drawing_tokenize(char *str) +static ASS_DrawingToken *drawing_tokenize(const char *str) { - char *p = str; + char *p = (char *) str; int type = -1, is_set = 0; double val; ASS_Vector point = {0, 0}; @@ -173,28 +135,16 @@ static void drawing_free_tokens(ASS_DrawingToken *token) } } -/* - * \brief Translate and scale a point coordinate according to baseline - * offset and scale. - */ -static inline void translate_point(ASS_Drawing *drawing, ASS_Vector *point) -{ - point->x = lrint(drawing->point_scale_x * point->x); - point->y = lrint(drawing->point_scale_y * point->y); - - rectangle_update(&drawing->cbox, point->x, point->y, point->x, point->y); -} - /* * \brief Add curve to drawing */ -static bool drawing_add_curve(ASS_Drawing *drawing, ASS_DrawingToken *token, - bool spline, int started) +static bool drawing_add_curve(ASS_Outline *outline, ASS_Rect *cbox, + ASS_DrawingToken *token, bool spline, int started) { ASS_Vector p[4]; for (int i = 0; i < 4; ++i) { p[i] = token->point; - translate_point(drawing, &p[i]); + rectangle_update(cbox, p[i].x, p[i].y, p[i].x, p[i].y); token = token->next; } @@ -217,25 +167,24 @@ static bool drawing_add_curve(ASS_Drawing *drawing, ASS_DrawingToken *token, } return (started || - outline_add_point(&drawing->outline, p[0], 0)) && - outline_add_point(&drawing->outline, p[1], 0) && - outline_add_point(&drawing->outline, p[2], 0) && - outline_add_point(&drawing->outline, p[3], OUTLINE_CUBIC_SPLINE); + outline_add_point(outline, p[0], 0)) && + outline_add_point(outline, p[1], 0) && + outline_add_point(outline, p[2], 0) && + outline_add_point(outline, p[3], OUTLINE_CUBIC_SPLINE); } /* * \brief Convert token list to outline. Calls the line and curve evaluators. */ -ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, ASS_Library *lib, bool raw_mode) +bool ass_drawing_parse(ASS_Outline *outline, ASS_Rect *cbox, + const char *text, ASS_Library *lib) { - drawing->library = lib; - rectangle_reset(&drawing->cbox); - if (!outline_alloc(&drawing->outline, GLYPH_INITIAL_POINTS, GLYPH_INITIAL_SEGMENTS)) - return NULL; - drawing->outline.n_points = drawing->outline.n_segments = 0; + if (!outline_alloc(outline, DRAWING_INITIAL_POINTS, DRAWING_INITIAL_SEGMENTS)) + return false; + outline->n_points = outline->n_segments = 0; + rectangle_reset(cbox); - ASS_DrawingToken *tokens = drawing_tokenize(drawing->text); - drawing_prepare(drawing); + ASS_DrawingToken *tokens = drawing_tokenize(text); bool started = false; ASS_Vector pen = {0, 0}; @@ -245,16 +194,16 @@ ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, ASS_Library *lib, bool raw_ switch (token->type) { case TOKEN_MOVE_NC: pen = token->point; - translate_point(drawing, &pen); + rectangle_update(cbox, pen.x, pen.y, pen.x, pen.y); token = token->next; break; case TOKEN_MOVE: pen = token->point; - translate_point(drawing, &pen); + rectangle_update(cbox, pen.x, pen.y, pen.x, pen.y); if (started) { - if (!outline_add_segment(&drawing->outline, OUTLINE_LINE_SEGMENT)) + if (!outline_add_segment(outline, OUTLINE_LINE_SEGMENT)) goto error; - if (!outline_close_contour(&drawing->outline)) + if (!outline_close_contour(outline)) goto error; started = false; } @@ -262,10 +211,10 @@ ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, ASS_Library *lib, bool raw_ break; case TOKEN_LINE: { ASS_Vector to = token->point; - translate_point(drawing, &to); - if (!started && !outline_add_point(&drawing->outline, pen, 0)) + rectangle_update(cbox, to.x, to.y, to.x, to.y); + if (!started && !outline_add_point(outline, pen, 0)) goto error; - if (!outline_add_point(&drawing->outline, to, OUTLINE_LINE_SEGMENT)) + if (!outline_add_point(outline, to, OUTLINE_LINE_SEGMENT)) goto error; started = true; token = token->next; @@ -274,7 +223,7 @@ ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, ASS_Library *lib, bool raw_ case TOKEN_CUBIC_BEZIER: if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) && token->prev) { - if (!drawing_add_curve(drawing, token->prev, false, started)) + if (!drawing_add_curve(outline, cbox, token->prev, false, started)) goto error; token = token->next; token = token->next; @@ -286,7 +235,7 @@ ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, ASS_Library *lib, bool raw_ case TOKEN_B_SPLINE: if (token_check_values(token, 3, TOKEN_B_SPLINE) && token->prev) { - if (!drawing_add_curve(drawing, token->prev, true, started)) + if (!drawing_add_curve(outline, cbox, token->prev, true, started)) goto error; token = token->next; started = true; @@ -301,18 +250,22 @@ ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, ASS_Library *lib, bool raw_ // Close the last contour if (started) { - if (!outline_add_segment(&drawing->outline, OUTLINE_LINE_SEGMENT)) + if (!outline_add_segment(outline, OUTLINE_LINE_SEGMENT)) goto error; - if (!outline_close_contour(&drawing->outline)) + if (!outline_close_contour(outline)) goto error; } - drawing_finish(drawing, raw_mode); + if (lib) + ass_msg(lib, MSGL_V, + "Parsed drawing with %d points and %d segments", + outline->n_points, outline->n_segments); + drawing_free_tokens(tokens); - return &drawing->outline; + return true; error: drawing_free_tokens(tokens); - outline_free(&drawing->outline); - return NULL; + outline_free(outline); + return false; } diff --git a/libass/ass_drawing.h b/libass/ass_drawing.h index 16d85ed..ea65ac6 100644 --- a/libass/ass_drawing.h +++ b/libass/ass_drawing.h @@ -41,24 +41,7 @@ typedef struct ass_drawing_token { struct ass_drawing_token *prev; } ASS_DrawingToken; -typedef struct { - char *text; // drawing string - 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 - ASS_Outline outline; // target outline - int advance; // advance (from cbox) - - // private - ASS_Library *library; - double point_scale_x; - double point_scale_y; - ASS_Rect cbox; // bounding box, or let's say... VSFilter's idea of it -} ASS_Drawing; - -ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, ASS_Library *lib, bool raw_mode); +bool ass_drawing_parse(ASS_Outline *outline, ASS_Rect *cbox, + const char *text, ASS_Library *lib); #endif /* LIBASS_DRAWING_H */ diff --git a/libass/ass_render.c b/libass/ass_render.c index 5b790c9..f8dee8e 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -1025,18 +1025,27 @@ size_t ass_outline_construct(void *key, void *value, void *priv) if (outline_key->type == OUTLINE_DRAWING) { DrawingHashKey *k = &outline_key->u.drawing; - ASS_Drawing drawing; - drawing.text = k->text; - drawing.scale = k->scale; - drawing.pbo = k->pbo; - drawing.scale_x = scale_x * render_priv->font_scale; - drawing.scale_y = scale_y * render_priv->font_scale; - if (!ass_drawing_parse(&drawing, render_priv->library, false)) + ASS_Rect bbox; + if (!ass_drawing_parse(&v->outline, &bbox, k->text, render_priv->library)) return 1; - outline_move(&v->outline, &drawing.outline); - v->advance = drawing.advance; - v->asc = drawing.asc; - v->desc = drawing.desc; + + ASS_DVector scale, offset = {0}; + double w = render_priv->font_scale / (1 << (k->scale - 1)); + scale.x = scale_x * w; + scale.y = scale_y * w; + + v->advance = (bbox.x_max - bbox.x_min) * scale.x; + + double pbo = (double) k->pbo / (1 << (k->scale - 1)); + v->desc = double_to_d6(pbo * scale_y * render_priv->font_scale); + v->asc = (bbox.y_max - bbox.y_min) * scale.y - v->desc; + offset.y = -v->asc; + + ASS_Outline *ol = &v->outline; + for (size_t i = 0; i < ol->n_points; i++) { + ol->points[i].x = lrint(ol->points[i].x * scale.x + offset.x); + ol->points[i].y = lrint(ol->points[i].y * scale.y + offset.y); + } } else { GlyphHashKey *k = &outline_key->u.glyph; ass_face_set_size(k->font->faces[k->face_index], k->size); @@ -1198,30 +1207,31 @@ size_t ass_bitmap_construct(void *key, void *value, void *priv) const size_t hdr = sizeof(BitmapHashKey) + sizeof(BitmapHashValue); if (bitmap_key->type == BITMAP_CLIP) { - ASS_Drawing drawing; - drawing.text = bitmap_key->u.clip.text; - drawing.scale = bitmap_key->u.clip.scale; - drawing.pbo = 0; - drawing.scale_x = render_priv->font_scale_x * render_priv->font_scale; - drawing.scale_y = render_priv->font_scale; - if (!ass_drawing_parse(&drawing, render_priv->library, true)) { + ASS_Rect cbox; + ASS_Outline outline; + if (!ass_drawing_parse(&outline, &cbox, bitmap_key->u.clip.text, render_priv->library)) { ass_msg(render_priv->library, MSGL_WARN, "Clip vector parsing failed. Skipping."); return hdr; } - // We need to translate the clip according to screen borders - if (render_priv->settings.left_margin != 0 || - render_priv->settings.top_margin != 0) { - ASS_Vector trans = { - .x = int_to_d6(render_priv->settings.left_margin), - .y = int_to_d6(render_priv->settings.top_margin), - }; - outline_translate(&drawing.outline, trans.x, trans.y); + ASS_DVector scale; + double w = render_priv->font_scale / (1 << (bitmap_key->u.clip.scale - 1)); + scale.x = render_priv->font_scale_x * w; + scale.y = w; + + ASS_DVector offset; + offset.x = int_to_d6(render_priv->settings.left_margin); + offset.y = int_to_d6(render_priv->settings.top_margin); + + ASS_Outline *ol = &outline; + for (size_t i = 0; i < ol->n_points; i++) { + ol->points[i].x = lrint(ol->points[i].x * scale.x + offset.x); + ol->points[i].y = lrint(ol->points[i].y * scale.y + offset.y); } - v->bm = outline_to_bitmap(render_priv, &drawing.outline, NULL, 1); - outline_free(&drawing.outline); + v->bm = outline_to_bitmap(render_priv, &outline, NULL, 1); + outline_free(&outline); v->valid = !!v->bm; return bitmap_size(v->bm) + hdr; -- cgit v1.2.3