summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libass/Makefile.am4
-rw-r--r--libass/ass_bitmap.c1
-rw-r--r--libass/ass_bitmap.h10
-rw-r--r--libass/ass_cache.c1
-rw-r--r--libass/ass_drawing.h1
-rw-r--r--libass/ass_font.c251
-rw-r--r--libass/ass_font.h9
-rw-r--r--libass/ass_outline.c275
-rw-r--r--libass/ass_outline.h49
-rw-r--r--libass/ass_rasterizer.c7
-rw-r--r--libass/ass_rasterizer.h1
-rw-r--r--libass/ass_render.c1
12 files changed, 335 insertions, 275 deletions
diff --git a/libass/Makefile.am b/libass/Makefile.am
index a073f26..3579310 100644
--- a/libass/Makefile.am
+++ b/libass/Makefile.am
@@ -25,8 +25,8 @@ SRC_CORETEXT = ass_coretext.c ass_coretext.h
lib_LTLIBRARIES = libass.la
libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontselect.c ass_render.c \
- ass_utils.c ass_bitmap.c ass_blur.c ass_library.c ass_bitmap.h \
- ass_cache.h ass_fontselect.h ass_font.h ass.h \
+ ass_utils.c ass_bitmap.c ass_outline.c ass_blur.c ass_library.c \
+ ass_bitmap.h ass_outline.h ass_cache.h ass_fontselect.h ass_font.h ass.h \
ass_library.h ass_types.h ass_utils.h ass_drawing.c \
ass_drawing.h ass_cache_template.h ass_render.h \
ass_parse.c ass_parse.h ass_render_api.c ass_shaper.c \
diff --git a/libass/ass_bitmap.c b/libass/ass_bitmap.c
index ff2eac4..40d5eb4 100644
--- a/libass/ass_bitmap.c
+++ b/libass/ass_bitmap.c
@@ -31,6 +31,7 @@
#include FT_OUTLINE_H
#include "ass_utils.h"
+#include "ass_outline.h"
#include "ass_bitmap.h"
#include "ass_render.h"
diff --git a/libass/ass_bitmap.h b/libass/ass_bitmap.h
index ada3228..eb58f94 100644
--- a/libass/ass_bitmap.h
+++ b/libass/ass_bitmap.h
@@ -90,15 +90,7 @@ extern const BitmapEngine ass_bitmap_engine_sse2;
extern const BitmapEngine ass_bitmap_engine_avx2;
-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 ass_outline ASS_Outline;
typedef struct {
int left, top;
diff --git a/libass/ass_cache.c b/libass/ass_cache.c
index 148faa1..577f7f3 100644
--- a/libass/ass_cache.c
+++ b/libass/ass_cache.c
@@ -27,6 +27,7 @@
#include "ass_utils.h"
#include "ass_font.h"
+#include "ass_outline.h"
#include "ass_cache.h"
// type-specific functions
diff --git a/libass/ass_drawing.h b/libass/ass_drawing.h
index 4d63201..28a7040 100644
--- a/libass/ass_drawing.h
+++ b/libass/ass_drawing.h
@@ -23,6 +23,7 @@
#include FT_OUTLINE_H
#include "ass.h"
+#include "ass_outline.h"
#include "ass_bitmap.h"
typedef enum {
diff --git a/libass/ass_font.c b/libass/ass_font.c
index 5889b49..95ae335 100644
--- a/libass/ass_font.c
+++ b/libass/ass_font.c
@@ -436,77 +436,6 @@ static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font,
return 0;
}
-
-int outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_contours)
-{
- 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;
- }
-
- 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;
- }
-
- 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(ASS_Outline *outline)
-{
- if (!outline)
- return;
-
- free(outline->contours);
- free(outline->points);
- free(outline->tags);
-}
-
/**
* Slightly embold a glyph without touching its metrics
*/
@@ -693,183 +622,3 @@ void ass_font_clear(ASS_Font *font)
}
free(font->desc.family);
}
-
-/**
- * \brief Calculate the cbox of a series of points
- */
-static void
-get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end)
-{
- box->xMin = box->yMin = INT_MAX;
- box->xMax = box->yMax = INT_MIN;
- int i;
-
- for (i = start; i <= end; i++) {
- box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
- box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
- box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
- box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
- }
-}
-
-/**
- * \brief Determine signed area of a contour
- * \return area doubled
- */
-static long long get_contour_area(FT_Vector *points, int start, int end)
-{
- long long area = 0;
- int x = points[end].x;
- int y = points[end].y;
- for (int i = start; i <= end; i++) {
- area += (long long)(points[i].x + x) * (points[i].y - y);
- x = points[i].x;
- y = points[i].y;
- }
- return area;
-}
-
-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.
- * At the moment, the following fixes are done:
- *
- * 1. Reverse contours that have "inside" winding direction but are not
- * contained in any other contours' cbox.
- * 2. Remove "inside" contours depending on border size, so that large
- * borders do not reverse the winding direction, which leads to "holes"
- * inside the border. The inside will be filled by the border of the
- * outside contour anyway in this case.
- *
- * \param outline FreeType outline, modified in-place
- * \param border_x border size, x direction, d6 format
- * \param border_x border size, y direction, d6 format
- */
-void fix_freetype_stroker(ASS_Outline *outline, int border_x, int border_y)
-{
- int nc = outline->n_contours;
- int begin, stop;
- char modified = 0;
- char *valid_cont = malloc(nc);
- int start = 0;
- int end = -1;
- FT_BBox *boxes = malloc(nc * sizeof(FT_BBox));
- int i, j;
-
- long long area = 0;
- // create a list of cboxes of the contours
- for (i = 0; i < nc; i++) {
- start = end + 1;
- end = outline->contours[i];
- get_contour_cbox(&boxes[i], outline->points, start, end);
- area += get_contour_area(outline->points, start, end);
- }
- int inside_direction = area < 0;
-
- // for each contour, check direction and whether it's "outside"
- // or contained in another contour
- end = -1;
- for (i = 0; i < nc; i++) {
- start = end + 1;
- end = outline->contours[i];
- int dir = get_contour_area(outline->points, start, end) > 0;
- valid_cont[i] = 1;
- if (dir == inside_direction) {
- for (j = 0; j < nc; j++) {
- if (i == j)
- continue;
- if (boxes[i].xMin >= boxes[j].xMin &&
- boxes[i].xMax <= boxes[j].xMax &&
- boxes[i].yMin >= boxes[j].yMin &&
- boxes[i].yMax <= boxes[j].yMax)
- goto check_inside;
- }
- /* "inside" contour but we can't find anything it could be
- * inside of - assume the font is buggy and it should be
- * an "outside" contour, and reverse it */
- for (j = 0; j < (end - start) / 2; j++) {
- FT_Vector temp = outline->points[start + 1 + j];
- char temp2 = outline->tags[start + 1 + j];
- outline->points[start + 1 + j] = outline->points[end - j];
- outline->points[end - j] = temp;
- outline->tags[start + 1 + 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, 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) {
- valid_cont[i] = 0;
- modified = 1;
- }
- }
- }
-
- // if we need to modify the outline, rewrite it and skip
- // the contours that we determined should be removed.
- if (modified) {
- int p = 0, c = 0;
- for (i = 0; i < nc; i++) {
- if (!valid_cont[i])
- continue;
- 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;
- outline->tags[p] = outline->tags[j];
- p++;
- }
- outline->contours[c] = p - 1;
- c++;
- }
- outline->n_points = p;
- outline->n_contours = c;
- }
-
- free(boxes);
- free(valid_cont);
-}
-
diff --git a/libass/ass_font.h b/libass/ass_font.h
index 0f02159..843a01e 100644
--- a/libass/ass_font.h
+++ b/libass/ass_font.h
@@ -76,13 +76,4 @@ FT_Glyph ass_font_get_glyph(ASS_Font *font,
ASS_Hinting hinting, int deco);
void ass_font_clear(ASS_Font *font);
-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_points, size_t n_contours);
-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_outline.c b/libass/ass_outline.c
new file mode 100644
index 0000000..7c1d784
--- /dev/null
+++ b/libass/ass_outline.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2016 Vabishchevich Nikolay <vabnick@gmail.com>
+ *
+ * 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 "config.h"
+#include "ass_compat.h"
+
+#include "ass_utils.h"
+#include "ass_outline.h"
+
+
+
+bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_contours)
+{
+ 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 false;
+
+ outline->max_contours = n_contours;
+ outline->max_points = n_points;
+ return true;
+}
+
+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;
+ }
+
+ 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;
+ }
+
+ 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(ASS_Outline *outline)
+{
+ if (!outline)
+ return;
+
+ free(outline->contours);
+ free(outline->points);
+ free(outline->tags);
+}
+
+
+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 Calculate the cbox of a series of points
+ */
+static void
+get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end)
+{
+ box->xMin = box->yMin = INT_MAX;
+ box->xMax = box->yMax = INT_MIN;
+ for (int i = start; i <= end; i++) {
+ box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
+ box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
+ box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
+ box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
+ }
+}
+
+/**
+ * \brief Determine signed area of a contour
+ * \return area doubled
+ */
+static long long get_contour_area(FT_Vector *points, int start, int end)
+{
+ long long area = 0;
+ int x = points[end].x;
+ int y = points[end].y;
+ for (int i = start; i <= end; i++) {
+ area += (long long)(points[i].x + x) * (points[i].y - y);
+ x = points[i].x;
+ y = points[i].y;
+ }
+ return area;
+}
+
+/**
+ * \brief Apply fixups to please the FreeType stroker and improve the
+ * rendering result, especially in case the outline has some anomalies.
+ * At the moment, the following fixes are done:
+ *
+ * 1. Reverse contours that have "inside" winding direction but are not
+ * contained in any other contours' cbox.
+ * 2. Remove "inside" contours depending on border size, so that large
+ * borders do not reverse the winding direction, which leads to "holes"
+ * inside the border. The inside will be filled by the border of the
+ * outside contour anyway in this case.
+ *
+ * \param outline FreeType outline, modified in-place
+ * \param border_x border size, x direction, d6 format
+ * \param border_x border size, y direction, d6 format
+ */
+void fix_freetype_stroker(ASS_Outline *outline, int border_x, int border_y)
+{
+ int nc = outline->n_contours;
+ int begin, stop;
+ char modified = 0;
+ char *valid_cont = malloc(nc);
+ int start = 0;
+ int end = -1;
+ FT_BBox *boxes = malloc(nc * sizeof(FT_BBox));
+ int i, j;
+
+ long long area = 0;
+ // create a list of cboxes of the contours
+ for (i = 0; i < nc; i++) {
+ start = end + 1;
+ end = outline->contours[i];
+ get_contour_cbox(&boxes[i], outline->points, start, end);
+ area += get_contour_area(outline->points, start, end);
+ }
+ int inside_direction = area < 0;
+
+ // for each contour, check direction and whether it's "outside"
+ // or contained in another contour
+ end = -1;
+ for (i = 0; i < nc; i++) {
+ start = end + 1;
+ end = outline->contours[i];
+ int dir = get_contour_area(outline->points, start, end) > 0;
+ valid_cont[i] = 1;
+ if (dir == inside_direction) {
+ for (j = 0; j < nc; j++) {
+ if (i == j)
+ continue;
+ if (boxes[i].xMin >= boxes[j].xMin &&
+ boxes[i].xMax <= boxes[j].xMax &&
+ boxes[i].yMin >= boxes[j].yMin &&
+ boxes[i].yMax <= boxes[j].yMax)
+ goto check_inside;
+ }
+ /* "inside" contour but we can't find anything it could be
+ * inside of - assume the font is buggy and it should be
+ * an "outside" contour, and reverse it */
+ for (j = 0; j < (end - start) / 2; j++) {
+ FT_Vector temp = outline->points[start + 1 + j];
+ char temp2 = outline->tags[start + 1 + j];
+ outline->points[start + 1 + j] = outline->points[end - j];
+ outline->points[end - j] = temp;
+ outline->tags[start + 1 + 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, 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) {
+ valid_cont[i] = 0;
+ modified = 1;
+ }
+ }
+ }
+
+ // if we need to modify the outline, rewrite it and skip
+ // the contours that we determined should be removed.
+ if (modified) {
+ int p = 0, c = 0;
+ for (i = 0; i < nc; i++) {
+ if (!valid_cont[i])
+ continue;
+ 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;
+ outline->tags[p] = outline->tags[j];
+ p++;
+ }
+ outline->contours[c] = p - 1;
+ c++;
+ }
+ outline->n_points = p;
+ outline->n_contours = c;
+ }
+
+ free(boxes);
+ free(valid_cont);
+}
+
diff --git a/libass/ass_outline.h b/libass/ass_outline.h
new file mode 100644
index 0000000..a03f082
--- /dev/null
+++ b/libass/ass_outline.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Vabishchevich Nikolay <vabnick@gmail.com>
+ *
+ * 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_OUTLINE_H
+#define LIBASS_OUTLINE_H
+
+#include <ft2build.h>
+#include FT_OUTLINE_H
+#include <stdbool.h>
+
+
+typedef struct ass_outline {
+ 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
+
+bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_contours);
+ASS_Outline *outline_convert(const FT_Outline *source);
+ASS_Outline *outline_copy(const ASS_Outline *source);
+void outline_free(ASS_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);
+
+
+#endif /* LIBASS_OUTLINE_H */
diff --git a/libass/ass_rasterizer.c b/libass/ass_rasterizer.c
index dbad5a1..41002ca 100644
--- a/libass/ass_rasterizer.c
+++ b/libass/ass_rasterizer.c
@@ -19,15 +19,16 @@
#include "config.h"
#include "ass_compat.h"
-#include "ass_utils.h"
-#include "ass_rasterizer.h"
#include <assert.h>
-
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#endif
+#include "ass_utils.h"
+#include "ass_outline.h"
+#include "ass_rasterizer.h"
+
static inline int ilog2(uint32_t n) // XXX: different compilers
diff --git a/libass/ass_rasterizer.h b/libass/ass_rasterizer.h
index 73cdba4..c7dfe61 100644
--- a/libass/ass_rasterizer.h
+++ b/libass/ass_rasterizer.h
@@ -23,7 +23,6 @@
#include <stdint.h>
#include "ass_bitmap.h"
-#include "ass_font.h"
enum {
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 93fc6e2..8e0443a 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <stdbool.h>
+#include "ass_outline.h"
#include "ass_render.h"
#include "ass_parse.h"
#include "ass_shaper.h"