diff options
author | Dr.Smile <vabnick@gmail.com> | 2021-09-24 18:01:40 +0300 |
---|---|---|
committer | Dr.Smile <vabnick@gmail.com> | 2021-10-05 21:21:54 +0300 |
commit | 7bf4bee0fc9a1d6257a105a3c19df6cf08733f8e (patch) | |
tree | 918dd60cde2c923816367e0177e9c558fe072d89 /libass | |
parent | d9b5e327c5e8d26c9dde2ba9bc401c9f66883726 (diff) | |
download | libass-7bf4bee0fc9a1d6257a105a3c19df6cf08733f8e.tar.bz2 libass-7bf4bee0fc9a1d6257a105a3c19df6cf08733f8e.tar.xz |
outline: refactor and add comments and asserts
Diffstat (limited to 'libass')
-rw-r--r-- | libass/ass_drawing.c | 6 | ||||
-rw-r--r-- | libass/ass_outline.c | 267 | ||||
-rw-r--r-- | libass/ass_outline.h | 23 |
3 files changed, 179 insertions, 117 deletions
diff --git a/libass/ass_drawing.c b/libass/ass_drawing.c index c7f99b7..42638b8 100644 --- a/libass/ass_drawing.c +++ b/libass/ass_drawing.c @@ -202,8 +202,7 @@ bool ass_drawing_parse(ASS_Outline *outline, ASS_Rect *cbox, if (started) { if (!outline_add_segment(outline, OUTLINE_LINE_SEGMENT)) goto error; - if (!outline_close_contour(outline)) - goto error; + outline_close_contour(outline); started = false; } token = token->next; @@ -251,8 +250,7 @@ bool ass_drawing_parse(ASS_Outline *outline, ASS_Rect *cbox, if (started) { if (!outline_add_segment(outline, OUTLINE_LINE_SEGMENT)) goto error; - if (!outline_close_contour(outline)) - goto error; + outline_close_contour(outline); } if (lib) diff --git a/libass/ass_outline.c b/libass/ass_outline.c index b967089..2bdc31a 100644 --- a/libass/ass_outline.c +++ b/libass/ass_outline.c @@ -24,45 +24,79 @@ -bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_segments) +/* + * \brief Initialize ASS_Outline to an empty state + * Equivalent to zeroing of outline object and doesn't free any memory. + */ +void outline_clear(ASS_Outline *outline) { - outline->points = malloc(sizeof(ASS_Vector) * n_points); - outline->segments = malloc(n_segments); + outline->points = NULL; + outline->segments = NULL; + + outline->n_points = outline->max_points = 0; + outline->n_segments = outline->max_segments = 0; +} + +/* + * \brief Initialize ASS_Outline and allocate memory + */ +bool outline_alloc(ASS_Outline *outline, size_t max_points, size_t max_segments) +{ + assert(max_points && max_segments); + if (max_points > SIZE_MAX / sizeof(ASS_Vector)) { + outline_clear(outline); + return false; + } + outline->points = malloc(sizeof(ASS_Vector) * max_points); + outline->segments = malloc(max_segments); if (!outline->points || !outline->segments) { outline_free(outline); return false; } - outline->max_points = n_points; - outline->max_segments = n_segments; + outline->max_points = max_points; + outline->max_segments = max_segments; outline->n_points = outline->n_segments = 0; return true; } -void outline_clear(ASS_Outline *outline) +/* + * \brief Free previously initialized ASS_Outline + * Outline state after the call is the same as after outline_clear(). + * Outline pointer can be NULL. + */ +void outline_free(ASS_Outline *outline) { - outline->points = NULL; - outline->segments = NULL; + if (!outline) + return; - outline->n_points = outline->max_points = 0; - outline->n_segments = outline->max_segments = 0; + free(outline->points); + free(outline->segments); + + outline_clear(outline); } + static bool valid_point(const FT_Vector *pt) { return labs(pt->x) <= OUTLINE_MAX && labs(pt->y) <= OUTLINE_MAX; } +/* + * \brief Convert FT_Ouline into ASS_Outline + * Outline should be preallocated to a sufficient size. + */ bool outline_convert(ASS_Outline *outline, const FT_Outline *source) { enum Status { S_ON, S_Q, S_C1, S_C2 }; - for (size_t i = 0, j = 0; i < source->n_contours; i++) { + for (int i = 0, j = 0; i < source->n_contours; i++) { ASS_Vector pt; - bool skip_last = false; + int skip_last = 0; enum Status st; + char seg; int last = source->contours[i]; if (j > last || last >= source->n_points) @@ -88,7 +122,8 @@ bool outline_convert(ASS_Outline *outline, const FT_Outline *source) pt.y = -source->points[last].y; switch (FT_CURVE_TAG(source->tags[last])) { case FT_CURVE_TAG_ON: - skip_last = true; + skip_last = 1; + last--; break; case FT_CURVE_TAG_CONIC: @@ -99,6 +134,7 @@ bool outline_convert(ASS_Outline *outline, const FT_Outline *source) default: return false; } + assert(outline->n_points < outline->max_points); outline->points[outline->n_points++] = pt; st = S_Q; break; @@ -108,6 +144,7 @@ bool outline_convert(ASS_Outline *outline, const FT_Outline *source) } pt.x = source->points[j].x; pt.y = -source->points[j].y; + assert(outline->n_points < outline->max_points); outline->points[outline->n_points++] = pt; for (j++; j <= last; j++) { @@ -117,20 +154,22 @@ bool outline_convert(ASS_Outline *outline, const FT_Outline *source) case FT_CURVE_TAG_ON: switch (st) { case S_ON: - outline->segments[outline->n_segments++] = OUTLINE_LINE_SEGMENT; + seg = OUTLINE_LINE_SEGMENT; break; case S_Q: - outline->segments[outline->n_segments++] = OUTLINE_QUADRATIC_SPLINE; + seg = OUTLINE_QUADRATIC_SPLINE; break; case S_C2: - outline->segments[outline->n_segments++] = OUTLINE_CUBIC_SPLINE; + seg = OUTLINE_CUBIC_SPLINE; break; default: return false; } + assert(outline->n_segments < outline->max_segments); + outline->segments[outline->n_segments++] = seg; st = S_ON; break; @@ -141,9 +180,11 @@ bool outline_convert(ASS_Outline *outline, const FT_Outline *source) break; case S_Q: + assert(outline->n_segments < outline->max_segments); outline->segments[outline->n_segments++] = OUTLINE_QUADRATIC_SPLINE; pt.x = (pt.x + source->points[j].x) >> 1; pt.y = (pt.y - source->points[j].y) >> 1; + assert(outline->n_points < outline->max_points); outline->points[outline->n_points++] = pt; break; @@ -172,48 +213,38 @@ bool outline_convert(ASS_Outline *outline, const FT_Outline *source) } pt.x = source->points[j].x; pt.y = -source->points[j].y; + assert(outline->n_points < outline->max_points); outline->points[outline->n_points++] = pt; } switch (st) { case S_ON: - if (skip_last) { - outline->n_points--; - break; - } - outline->segments[outline->n_segments++] = OUTLINE_LINE_SEGMENT; + seg = OUTLINE_LINE_SEGMENT | OUTLINE_CONTOUR_END; break; case S_Q: - outline->segments[outline->n_segments++] = OUTLINE_QUADRATIC_SPLINE; + seg = OUTLINE_QUADRATIC_SPLINE | OUTLINE_CONTOUR_END; break; case S_C2: - outline->segments[outline->n_segments++] = OUTLINE_CUBIC_SPLINE; + seg = OUTLINE_CUBIC_SPLINE | OUTLINE_CONTOUR_END; break; default: return false; } - outline->segments[outline->n_segments - 1] |= OUTLINE_CONTOUR_END; - } - return true; -} - -bool outline_rotate_90(ASS_Outline *outline, ASS_Vector offs) -{ - assert(abs(offs.x) <= INT32_MAX - OUTLINE_MAX); - assert(abs(offs.y) <= INT32_MAX - OUTLINE_MAX); - for (size_t i = 0; i < outline->n_points; i++) { - ASS_Vector pt = { offs.x + outline->points[i].y, - offs.y - outline->points[i].x }; - if (abs(pt.x) > OUTLINE_MAX || abs(pt.y) > OUTLINE_MAX) - return false; - outline->points[i] = pt; + assert(outline->n_segments < outline->max_segments); + outline->segments[outline->n_segments++] = seg; + j += skip_last; } return true; } +/* + * \brief Add a rectangle to the outline + * Outline should be preallocated to a sufficient size + * and coordinates should be in the allowable range. + */ void outline_add_rect(ASS_Outline *outline, int32_t x0, int32_t y0, int32_t x1, int32_t y1) { @@ -239,6 +270,81 @@ void outline_add_rect(ASS_Outline *outline, outline->n_segments = pos + 4; } + +/* + * \brief Add a single point to the outline + * Outline should be allocated and will be enlarged if needed. + * Also adds outline segment if segment parameter is nonzero. + */ +bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char segment) +{ + assert(outline->max_points); + if (abs(pt.x) > OUTLINE_MAX || abs(pt.y) > OUTLINE_MAX) + return false; + + if (outline->n_points >= outline->max_points) { + size_t new_size = 2 * outline->max_points; + if (!ASS_REALLOC_ARRAY(outline->points, new_size)) + return false; + outline->max_points = new_size; + } + outline->points[outline->n_points] = pt; + outline->n_points++; + + return !segment || outline_add_segment(outline, segment); +} + +/* + * \brief Add a segment to the outline + * Outline should be allocated and will be enlarged if needed. + */ +bool outline_add_segment(ASS_Outline *outline, char segment) +{ + assert(outline->max_segments); + if (outline->n_segments >= outline->max_segments) { + size_t new_size = 2 * outline->max_segments; + if (!ASS_REALLOC_ARRAY(outline->segments, new_size)) + return false; + outline->max_segments = new_size; + } + outline->segments[outline->n_segments] = segment; + outline->n_segments++; + return true; +} + +/* + * \brief Close last contour + */ +void outline_close_contour(ASS_Outline *outline) +{ + assert(outline->n_segments); + assert(!(outline->segments[outline->n_segments - 1] & ~OUTLINE_COUNT_MASK)); + outline->segments[outline->n_segments - 1] |= OUTLINE_CONTOUR_END; +} + + +/* + * \brief Inplace rotate outline by 90 degrees and translate by offs + */ +bool outline_rotate_90(ASS_Outline *outline, ASS_Vector offs) +{ + assert(abs(offs.x) <= INT32_MAX - OUTLINE_MAX); + assert(abs(offs.y) <= INT32_MAX - OUTLINE_MAX); + for (size_t i = 0; i < outline->n_points; i++) { + ASS_Vector pt = { offs.x + outline->points[i].y, + offs.y - outline->points[i].x }; + if (abs(pt.x) > OUTLINE_MAX || abs(pt.y) > OUTLINE_MAX) + return false; + outline->points[i] = pt; + } + return true; +} + +/* + * \brief Scale outline by {2^scale_ord_x, 2^scale_ord_y} + * Result outline should be uninitialized or empty. + * Source outline can be NULL. + */ bool outline_scale_pow2(ASS_Outline *outline, const ASS_Outline *source, int scale_ord_x, int scale_ord_y) { @@ -286,6 +392,11 @@ bool outline_scale_pow2(ASS_Outline *outline, const ASS_Outline *source, return true; } +/* + * \brief Transform outline by 2x3 matrix + * Result outline should be uninitialized or empty. + * Source outline can be NULL. + */ bool outline_transform_2d(ASS_Outline *outline, const ASS_Outline *source, const double m[2][3]) { @@ -316,6 +427,11 @@ bool outline_transform_2d(ASS_Outline *outline, const ASS_Outline *source, return true; } +/* + * \brief Apply perspective transform by 3x3 matrix to the outline + * Result outline should be uninitialized or empty. + * Source outline can be NULL. + */ bool outline_transform_3d(ASS_Outline *outline, const ASS_Outline *source, const double m[3][3]) { @@ -350,6 +466,9 @@ bool outline_transform_3d(ASS_Outline *outline, const ASS_Outline *source, return true; } +/* + * \brief Find minimal X-coordinate of control points after perspective transform + */ void outline_update_min_transformed_x(const ASS_Outline *outline, const double m[3][3], int32_t *min_x) { @@ -364,70 +483,8 @@ void outline_update_min_transformed_x(const ASS_Outline *outline, } } - -void outline_free(ASS_Outline *outline) -{ - if (!outline) - return; - - free(outline->points); - free(outline->segments); - - outline_clear(outline); -} - - -/* - * \brief Add a single point to a contour. - * Also adds outline segment if segment parameter is nonzero. - */ -bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char segment) -{ - if (abs(pt.x) > OUTLINE_MAX || abs(pt.y) > OUTLINE_MAX) - return false; - - if (outline->n_points >= outline->max_points) { - size_t new_size = 2 * outline->max_points; - if (!ASS_REALLOC_ARRAY(outline->points, new_size)) - return false; - outline->max_points = new_size; - } - outline->points[outline->n_points] = pt; - outline->n_points++; - - return !segment || outline_add_segment(outline, segment); -} - /* - * \brief Add a segment to a contour. - */ -bool outline_add_segment(ASS_Outline *outline, char segment) -{ - if (outline->n_segments >= outline->max_segments) { - size_t new_size = 2 * outline->max_segments; - if (!ASS_REALLOC_ARRAY(outline->segments, new_size)) - return false; - outline->max_segments = new_size; - } - outline->segments[outline->n_segments] = segment; - outline->n_segments++; - return true; -} - -/* - * \brief Close a contour. - */ -bool outline_close_contour(ASS_Outline *outline) -{ - assert(outline->n_segments); - assert(!(outline->segments[outline->n_segments - 1] & ~OUTLINE_COUNT_MASK)); - outline->segments[outline->n_segments - 1] |= OUTLINE_CONTOUR_END; - return true; -} - - -/* - * \brief Update bounding box of control points. + * \brief Update bounding box of control points */ void outline_update_cbox(const ASS_Outline *outline, ASS_Rect *cbox) { @@ -1408,10 +1465,10 @@ static bool close_contour(StrokerState *str, int dir) ~str->last_skip & dir & ~str->first_skip); str->contour_start = true; } - if ((dir & 1) && !outline_close_contour(str->result[0])) - return false; - if ((dir & 2) && !outline_close_contour(str->result[1])) - return false; + if (dir & 1) + outline_close_contour(str->result[0]); + if (dir & 2) + outline_close_contour(str->result[1]); str->contour_first[0] = str->result[0]->n_points; str->contour_first[1] = str->result[1]->n_points; return true; diff --git a/libass/ass_outline.h b/libass/ass_outline.h index 89df528..a7fe580 100644 --- a/libass/ass_outline.h +++ b/libass/ass_outline.h @@ -87,31 +87,38 @@ typedef struct { #define OUTLINE_MAX (((int32_t) 1 << 28) - 1) // cubic spline splitting requires 8 * OUTLINE_MAX + 4 <= INT32_MAX -bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_segments); void outline_clear(ASS_Outline *outline); +bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_segments); +void outline_free(ASS_Outline *outline); +// expects preallocated outline and works inplace bool outline_convert(ASS_Outline *outline, const FT_Outline *source); -bool outline_rotate_90(ASS_Outline *outline, ASS_Vector offs); void outline_add_rect(ASS_Outline *outline, int32_t x0, int32_t y0, int32_t x1, int32_t y1); +// enlarges outline automatically +bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char segment); +bool outline_add_segment(ASS_Outline *outline, char segment); +void outline_close_contour(ASS_Outline *outline); + +// works inplace +bool outline_rotate_90(ASS_Outline *outline, ASS_Vector offs); + +// creates a new outline for the result bool outline_scale_pow2(ASS_Outline *outline, const ASS_Outline *source, int scale_ord_x, int scale_ord_y); bool outline_transform_2d(ASS_Outline *outline, const ASS_Outline *source, const double m[2][3]); bool outline_transform_3d(ASS_Outline *outline, const ASS_Outline *source, const double m[3][3]); + +// info queries void outline_update_min_transformed_x(const ASS_Outline *outline, const double m[3][3], int32_t *min_x); -void outline_free(ASS_Outline *outline); - -bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char segment); -bool outline_add_segment(ASS_Outline *outline, char segment); -bool outline_close_contour(ASS_Outline *outline); - void outline_update_cbox(const ASS_Outline *outline, ASS_Rect *cbox); +// creates new outlines for the results (positive and negative offset outlines) bool outline_stroke(ASS_Outline *result, ASS_Outline *result1, const ASS_Outline *path, int xbord, int ybord, int eps); |