FT_Vector and FT_BBox types are based on FT_Pos, which is alias of long.
FreeType treats it as 32-bit integer, but on some platforms long can be
64-bit. That leads to wasted memory and suboptimal performance.
#include FT_GLYPH_H
#include "ass.h"
+#include "ass_outline.h"
struct segment;
extern const BitmapEngine ass_bitmap_engine_avx2;
-typedef struct ass_outline ASS_Outline;
-
typedef struct {
int left, top;
int w, h; // width, height
bool valid;
ASS_Outline outline;
ASS_Outline border[2];
- FT_BBox bbox_scaled; // bbox after scaling, but before rotation
- FT_Vector advance; // 26.6, advance distance to the next outline in line
+ ASS_Rect bbox_scaled; // bbox after scaling, but before rotation
+ ASS_Vector advance; // 26.6, advance distance to the next outline in line
int asc, desc; // ascender/descender
} OutlineHashValue;
type member;
#define STRING(member) \
char *member;
-#define FTVECTOR(member) \
- FT_Vector member;
+#define VECTOR(member) \
+ ASS_Vector member;
#define BITMAPHASHKEY(member) \
BitmapHashKey member;
#define END(typedefnamename) \
a->member == b->member &&
#define STRING(member) \
strcmp(a->member, b->member) == 0 &&
-#define FTVECTOR(member) \
+#define VECTOR(member) \
a->member.x == b->member.x && a->member.y == b->member.y &&
#define BITMAPHASHKEY(member) \
bitmap_compare(&a->member, &b->member, sizeof(a->member)) &&
hval = fnv_32a_buf(&p->member, sizeof(p->member), hval);
#define STRING(member) \
hval = fnv_32a_str(p->member, hval);
-#define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y);
+#define VECTOR(member) GENERIC(, member.x); GENERIC(, member.y);
#define BITMAPHASHKEY(member) { \
unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \
hval = fnv_32a_buf(&temp, sizeof(temp), hval); \
// = (glyph base point) - (rotation origin), otherwise
GENERIC(int, shift_x)
GENERIC(int, shift_y)
- FTVECTOR(advance) // subpixel shift vector
+ VECTOR(advance) // subpixel shift vector
END(OutlineBitmapHashKey)
// describe a clip mask bitmap
GENERIC(int, italic)
GENERIC(unsigned, scale_x) // 16.16
GENERIC(unsigned, scale_y) // 16.16
- FTVECTOR(outline) // border width, 26.6
+ VECTOR(outline) // border width, 26.6
GENERIC(unsigned, flags) // glyph decoration flags
GENERIC(unsigned, border_style)
GENERIC(int, hspacing) // 16.16
GENERIC(unsigned, scale_x)
GENERIC(unsigned, scale_y)
GENERIC(int, pbo)
- FTVECTOR(outline)
+ VECTOR(outline)
GENERIC(unsigned, border_style)
GENERIC(int, hspacing)
GENERIC(int, scale)
GENERIC(int, flags)
GENERIC(int, be)
GENERIC(double, blur)
- FTVECTOR(shadow)
+ VECTOR(shadow)
END(FilterDesc)
#undef START
#undef GENERIC
#undef STRING
-#undef FTVECTOR
+#undef VECTOR
#undef BITMAPHASHKEY
#undef END
#include <ft2build.h>
#include FT_OUTLINE_H
-#include FT_BBOX_H
#include <math.h>
#include <stdbool.h>
#include <limits.h>
#include "ass_drawing.h"
#include "ass_font.h"
-#define CURVE_ACCURACY 64.0
#define GLYPH_INITIAL_POINTS 100
#define GLYPH_INITIAL_CONTOURS 5
* \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, int raw_mode)
+static void drawing_finish(ASS_Drawing *drawing, bool raw_mode)
{
- int i;
- double pbo;
- FT_BBox bbox = drawing->cbox;
+ ASS_Rect bbox = drawing->cbox;
ASS_Outline *ol = &drawing->outline;
if (drawing->library)
if (raw_mode)
return;
- drawing->advance.x = bbox.xMax - bbox.xMin;
+ drawing->advance.x = bbox.x_max - bbox.x_min;
- pbo = drawing->pbo / (1 << (drawing->scale - 1));
+ double pbo = drawing->pbo / (1 << (drawing->scale - 1));
drawing->desc = double_to_d6(pbo * drawing->scale_y);
- drawing->asc = bbox.yMax - bbox.yMin - drawing->desc;
+ drawing->asc = bbox.y_max - bbox.y_min - drawing->desc;
// Place it onto the baseline
- for (i = 0; i < ol->n_points; i++)
+ for (size_t i = 0; i < ol->n_points; i++)
ol->points[i].y += drawing->asc;
}
*/
static int token_check_values(ASS_DrawingToken *token, int i, int type)
{
- int j;
- for (j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
if (!token || token->type != type) return 0;
token = token->next;
}
static ASS_DrawingToken *drawing_tokenize(char *str)
{
char *p = str;
- int i, type = -1, is_set = 0;
+ int type = -1, is_set = 0;
double val;
- FT_Vector point = {0, 0};
+ ASS_Vector point = {0, 0};
ASS_DrawingToken *root = NULL, *tail = NULL, *spline_start = NULL;
// 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++) {
+ for (int i = 0; i < 3; i++) {
tail->next = calloc(1, sizeof(ASS_DrawingToken));
tail->next->prev = tail;
tail = tail->next;
/*
* \brief Update drawing cbox
*/
-static inline void update_cbox(ASS_Drawing *drawing, FT_Vector *point)
+static inline void update_cbox(ASS_Drawing *drawing, ASS_Vector *point)
{
- FT_BBox *box = &drawing->cbox;
+ ASS_Rect *box = &drawing->cbox;
- box->xMin = FFMIN(box->xMin, point->x);
- box->xMax = FFMAX(box->xMax, point->x);
- box->yMin = FFMIN(box->yMin, point->y);
- box->yMax = FFMAX(box->yMax, point->y);
+ box->x_min = FFMIN(box->x_min, point->x);
+ box->x_max = FFMAX(box->x_max, point->x);
+ box->y_min = FFMIN(box->y_min, point->y);
+ box->y_max = FFMAX(box->y_max, point->y);
}
/*
* \brief Translate and scale a point coordinate according to baseline
* offset and scale.
*/
-static inline void translate_point(ASS_Drawing *drawing, FT_Vector *point)
+static inline void translate_point(ASS_Drawing *drawing, ASS_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.
+ * \brief Add curve to drawing
*/
-static bool drawing_evaluate_curve(ASS_Drawing *drawing,
- ASS_DrawingToken *token, char spline,
- int started)
+static bool drawing_add_curve(ASS_Drawing *drawing, ASS_DrawingToken *token,
+ bool spline, int started)
{
- FT_Vector p[4];
+ ASS_Vector p[4];
for (int i = 0; i < 4; ++i) {
p[i] = token->point;
translate_point(drawing, &p[i]);
/*
* \brief Create and initialize a new drawing and return it
*/
-ASS_Drawing *ass_drawing_new(ASS_Library *lib, FT_Library ftlib)
+ASS_Drawing *ass_drawing_new(ASS_Library *lib)
{
ASS_Drawing *drawing = calloc(1, sizeof(*drawing));
if (!drawing)
return NULL;
- drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX;
- drawing->cbox.xMax = drawing->cbox.yMax = INT_MIN;
- drawing->library = lib;
+ drawing->cbox.x_min = drawing->cbox.y_min = INT32_MAX;
+ drawing->cbox.x_max = drawing->cbox.y_max = INT32_MIN;
+ drawing->library = lib;
drawing->scale_x = 1.;
drawing->scale_y = 1.;
/*
* \brief Free a drawing
*/
-void ass_drawing_free(ASS_Drawing* drawing)
+void ass_drawing_free(ASS_Drawing *drawing)
{
if (drawing) {
free(drawing->text);
/*
* \brief Copy an ASCII string to the drawing text buffer
*/
-void ass_drawing_set_text(ASS_Drawing* drawing, char *str, size_t len)
+void ass_drawing_set_text(ASS_Drawing *drawing, char *str, size_t len)
{
free(drawing->text);
drawing->text = strndup(str, len);
* \brief Create a hashcode for the drawing
* XXX: To avoid collisions a better hash algorithm might be useful.
*/
-void ass_drawing_hash(ASS_Drawing* drawing)
+void ass_drawing_hash(ASS_Drawing *drawing)
{
if (!drawing->text)
return;
/*
* \brief Convert token list to outline. Calls the line and curve evaluators.
*/
-ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode)
+ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, bool raw_mode)
{
- int started = 0;
+ bool started = false;
ASS_DrawingToken *token;
- FT_Vector pen = {0, 0};
+ ASS_Vector pen = {0, 0};
drawing->tokens = drawing_tokenize(drawing->text);
drawing_prepare(drawing);
if (started) {
if (!outline_close_contour(&drawing->outline))
goto error;
- started = 0;
+ started = false;
}
token = token->next;
break;
case TOKEN_LINE: {
- FT_Vector to;
- to = token->point;
+ ASS_Vector to = token->point;
translate_point(drawing, &to);
if (!started && !outline_add_point(&drawing->outline, pen, FT_CURVE_TAG_ON))
goto error;
if (!outline_add_point(&drawing->outline, to, FT_CURVE_TAG_ON))
goto error;
- started = 1;
+ started = true;
token = token->next;
break;
}
case TOKEN_CUBIC_BEZIER:
if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) &&
token->prev) {
- if (!drawing_evaluate_curve(drawing, token->prev, 0, started))
+ if (!drawing_add_curve(drawing, token->prev, false, started))
goto error;
token = token->next;
token = token->next;
token = token->next;
- started = 1;
+ started = true;
} else
token = token->next;
break;
case TOKEN_B_SPLINE:
if (token_check_values(token, 3, TOKEN_B_SPLINE) &&
token->prev) {
- if (!drawing_evaluate_curve(drawing, token->prev, 1, started))
+ if (!drawing_add_curve(drawing, token->prev, true, started))
goto error;
token = token->next;
- started = 1;
+ started = true;
} else
token = token->next;
break;
#ifndef LIBASS_DRAWING_H
#define LIBASS_DRAWING_H
-#include <ft2build.h>
-#include FT_OUTLINE_H
-
#include "ass.h"
#include "ass_outline.h"
#include "ass_bitmap.h"
typedef struct ass_drawing_token {
ASS_TokenType type;
- FT_Vector point;
+ ASS_Vector point;
struct ass_drawing_token *next;
struct ass_drawing_token *prev;
} ASS_DrawingToken;
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
+ double scale_x; // FontScaleX
+ double scale_y; // FontScaleY
+ int asc; // ascender
+ int desc; // descender
ASS_Outline outline; // target outline
- FT_Vector advance; // advance (from cbox)
- int hash; // hash value (for caching)
+ ASS_Vector advance; // advance (from cbox)
+ int hash; // hash value (for caching)
// private
ASS_Library *library;
ASS_DrawingToken *tokens; // tokenized drawing
double point_scale_x;
double point_scale_y;
- FT_BBox cbox; // bounding box, or let's say... VSFilter's idea of it
+ ASS_Rect cbox; // bounding box, or let's say... VSFilter's idea of it
} ASS_Drawing;
-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);
-ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode);
+ASS_Drawing *ass_drawing_new(ASS_Library *lib);
+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);
+ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, bool raw_mode);
#endif /* LIBASS_DRAWING_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->points = malloc(sizeof(ASS_Vector) * n_points);
outline->tags = malloc(n_points);
if (!outline->contours || !outline->points || !outline->tags) {
outline_free(outline);
size_t n = source->contours[i] - start + 1;
// skip degenerate 2-point contours from broken fonts
if (n >= 3) {
- memcpy(outline->points + outline->n_points, source->points + start, sizeof(FT_Vector) * n);
+ for (size_t k = 0; k < n; k++) {
+ outline->points[outline->n_points + k].x = source->points[start + k].x;
+ outline->points[outline->n_points + k].y = source->points[start + k].y;
+ }
memcpy(outline->tags + outline->n_points, source->tags + start, n);
outline->n_points += n;
return false;
memcpy(outline->contours, source->contours, sizeof(size_t) * source->n_contours);
- memcpy(outline->points, source->points, sizeof(FT_Vector) * source->n_points);
+ memcpy(outline->points, source->points, sizeof(ASS_Vector) * source->n_points);
memcpy(outline->tags, source->tags, source->n_points);
outline->n_contours = source->n_contours;
outline->n_points = source->n_points;
/*
* \brief Add a single point to a contour.
*/
-bool outline_add_point(ASS_Outline *outline, FT_Vector pt, char tag)
+bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char tag)
{
if (outline->n_points >= outline->max_points) {
size_t new_size = 2 * outline->max_points;
}
-void outline_translate(const ASS_Outline *outline, FT_Pos dx, FT_Pos dy)
+void outline_translate(const ASS_Outline *outline, int32_t dx, int32_t dy)
{
for (size_t i = 0; i < outline->n_points; i++) {
outline->points[i].x += dx;
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);
+ int32_t x = FT_MulFix(outline->points[i].x, matrix->xx) +
+ FT_MulFix(outline->points[i].y, matrix->xy);
+ int32_t 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_update_cbox(const ASS_Outline *outline, FT_BBox *cbox)
-{
- if (!outline)
- return;
-
- for (size_t i = 0; 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);
- }
-}
-
-void outline_get_cbox(const ASS_Outline *outline, FT_BBox *cbox)
+void outline_get_cbox(const ASS_Outline *outline, ASS_Rect *cbox)
{
if (!outline->n_points) {
- cbox->xMin = cbox->xMax = 0;
- cbox->yMin = cbox->yMax = 0;
+ cbox->x_min = cbox->x_max = 0;
+ cbox->y_min = cbox->y_max = 0;
return;
}
- cbox->xMin = cbox->xMax = outline->points[0].x;
- cbox->yMin = cbox->yMax = outline->points[0].y;
+ cbox->x_min = cbox->x_max = outline->points[0].x;
+ cbox->y_min = cbox->y_max = 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);
+ cbox->x_min = FFMIN(cbox->x_min, outline->points[i].x);
+ cbox->x_max = FFMAX(cbox->x_max, outline->points[i].x);
+ cbox->y_min = FFMIN(cbox->y_min, outline->points[i].y);
+ cbox->y_max = FFMAX(cbox->y_max, outline->points[i].y);
}
}
typedef struct {
- int32_t x, y;
-} OutlinePoint;
-
-typedef struct {
- double x, y;
-} Vector;
-
-typedef struct {
- Vector v;
+ ASS_DVector v;
double len;
} Normal;
// skip flags for first and last point
int first_skip, last_skip;
// normal at first and last point
- Vector first_normal, last_normal;
+ ASS_DVector first_normal, last_normal;
// first and last point of current contour
- OutlinePoint first_point, last_point;
+ ASS_Vector first_point, last_point;
// cosinus of maximal angle that do not require cap
double merge_cos;
/**
* \brief 2D vector dot product
*/
-static inline double vec_dot(Vector vec1, Vector vec2)
+static inline double vec_dot(ASS_DVector vec1, ASS_DVector vec2)
{
return vec1.x * vec2.x + vec1.y * vec2.y;
}
/**
* \brief 2D vector cross product
*/
-static inline double vec_crs(Vector vec1, Vector vec2)
+static inline double vec_crs(ASS_DVector vec1, ASS_DVector vec2)
{
return vec1.x * vec2.y - vec1.y * vec2.x;
}
/**
* \brief 2D vector length
*/
-static inline double vec_len(Vector vec)
+static inline double vec_len(ASS_DVector vec)
{
return sqrt(vec.x * vec.x + vec.y * vec.y);
}
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool emit_point(StrokerState *str, OutlinePoint pt,
- Vector offs, char tag, int dir)
+static bool emit_point(StrokerState *str, ASS_Vector pt,
+ ASS_DVector offs, char tag, int dir)
{
int32_t dx = (int32_t) (str->xbord * offs.x);
int32_t dy = (int32_t) (str->ybord * offs.y);
if (dir & 1) {
- FT_Vector res = { pt.x + dx, pt.y + dy };
+ ASS_Vector res = { pt.x + dx, pt.y + dy };
res.y = -res.y;
if (!outline_add_point(str->result[0], res, tag))
return false;
}
if (dir & 2) {
- FT_Vector res = { pt.x - dx, pt.y - dy };
+ ASS_Vector res = { pt.x - dx, pt.y - dy };
res.y = -res.y;
if (!outline_add_point(str->result[1], res, tag))
return false;
* \param offs offset in normal space
* \param dir destination outline flags
*/
-static void fix_first_point(StrokerState *str, OutlinePoint pt,
- Vector offs, int dir)
+static void fix_first_point(StrokerState *str, ASS_Vector pt,
+ ASS_DVector offs, int dir)
{
int32_t dx = (int32_t) (str->xbord * offs.x);
int32_t dy = (int32_t) (str->ybord * offs.y);
if (dir & 1) {
- FT_Vector res = { pt.x + dx, pt.y + dy };
+ ASS_Vector res = { pt.x + dx, pt.y + dy };
res.y = -res.y;
ASS_Outline *ol = str->result[0];
size_t first = ol->n_contours ?
ol->points[first] = res;
}
if (dir & 2) {
- FT_Vector res = { pt.x - dx, pt.y - dy };
+ ASS_Vector res = { pt.x - dx, pt.y - dy };
res.y = -res.y;
ASS_Outline *ol = str->result[1];
size_t first = ol->n_contours ?
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool process_arc(StrokerState *str, OutlinePoint pt,
- Vector normal0, Vector normal1,
+static bool process_arc(StrokerState *str, ASS_Vector pt,
+ ASS_DVector normal0, ASS_DVector normal1,
const double *mul, int level, int dir)
{
- Vector center;
+ ASS_DVector center;
center.x = (normal0.x + normal1.x) * mul[level];
center.y = (normal0.y + normal1.y) * mul[level];
if (level)
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool draw_arc(StrokerState *str, OutlinePoint pt,
- Vector normal0, Vector normal1, double c, int dir)
+static bool draw_arc(StrokerState *str, ASS_Vector pt,
+ ASS_DVector normal0, ASS_DVector normal1, double c, int dir)
{
const int max_subdiv = 15;
double mul[max_subdiv + 1];
- Vector center;
+ ASS_DVector center;
bool small_angle = true;
if (c < 0) {
double mul = dir & 2 ? -sqrt(0.5) : sqrt(0.5);
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool draw_circle(StrokerState *str, OutlinePoint pt, int dir)
+static bool draw_circle(StrokerState *str, ASS_Vector pt, int dir)
{
const int max_subdiv = 15;
double mul[max_subdiv + 1], c = 0;
}
mul[pos] = 1 / (1 + c);
- Vector normal[4] = {
+ ASS_DVector normal[4] = {
{ 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }
};
return process_arc(str, pt, normal[0], normal[1], mul + pos, max_subdiv - pos, dir) &&
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool start_segment(StrokerState *str, OutlinePoint pt,
- Vector normal, int dir)
+static bool start_segment(StrokerState *str, ASS_Vector pt,
+ ASS_DVector normal, int dir)
{
if (str->contour_start) {
str->contour_start = false;
return true;
}
- Vector prev = str->last_normal;
+ ASS_DVector prev = str->last_normal;
double c = vec_dot(prev, normal);
if (c > str->merge_cos) { // merge without cap
double mul = 1 / (1 + c);
if (dir & skip_dir) {
if (!emit_point(str, pt, prev, FT_CURVE_TAG_ON, ~str->last_skip & skip_dir))
return false;
- Vector zero_normal = {0, 0};
+ ASS_DVector zero_normal = {0, 0};
if (!emit_point(str, pt, zero_normal, FT_CURVE_TAG_ON, skip_dir))
return false;
}
/**
* \brief Same as emit_point() but also updates skip flags
*/
-static bool emit_first_point(StrokerState *str, OutlinePoint pt, int dir)
+static bool emit_first_point(StrokerState *str, ASS_Vector pt, int dir)
{
str->last_skip &= ~dir;
return emit_point(str, pt, str->last_normal, FT_CURVE_TAG_ON, dir);
* \param first true if the skipped part is at start of the segment
* \return false on allocation failure
*/
-static bool prepare_skip(StrokerState *str, OutlinePoint pt, int dir, bool first)
+static bool prepare_skip(StrokerState *str, ASS_Vector pt, int dir, bool first)
{
if (first)
str->first_skip |= dir;
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool add_line(StrokerState *str, OutlinePoint pt, int dir)
+static bool add_line(StrokerState *str, ASS_Vector pt, int dir)
{
int32_t dx = pt.x - str->last_point.x;
int32_t dy = pt.y - str->last_point.y;
if (dx > -str->eps && dx < str->eps && dy > -str->eps && dy < str->eps)
return true;
- Vector deriv = { dy * str->yscale, -dx * str->xscale };
+ ASS_DVector deriv = { dy * str->yscale, -dx * str->xscale };
double scale = 1 / vec_len(deriv);
- Vector normal = { deriv.x * scale, deriv.y * scale };
+ ASS_DVector normal = { deriv.x * scale, deriv.y * scale };
if (!start_segment(str, str->last_point, normal, dir))
return false;
if (!emit_first_point(str, str->last_point, dir))
* \return false if error is too large
*/
static bool estimate_quadratic_error(StrokerState *str, double c, double s,
- const Normal *normal, Vector *result)
+ const Normal *normal, ASS_DVector *result)
{
// check radial error
if (!((3 + c) * (3 + c) < str->err_q * (1 + c)))
* \param first true if the current part is at start of the segment
* \return false on allocation failure
*/
-static bool process_quadratic(StrokerState *str, const OutlinePoint *pt,
- const Vector *deriv, const Normal *normal,
+static bool process_quadratic(StrokerState *str, const ASS_Vector *pt,
+ const ASS_DVector *deriv, const Normal *normal,
int dir, bool first)
{
double c = vec_dot(normal[0].v, normal[1].v);
if (!prepare_skip(str, pt[0], skip_dir, first))
return false;
if (f0 < 0 || f1 < 0) {
- Vector zero_normal = {0, 0};
+ ASS_DVector zero_normal = {0, 0};
if (!emit_point(str, pt[0], zero_normal, FT_CURVE_TAG_ON, skip_dir) ||
!emit_point(str, pt[2], zero_normal, FT_CURVE_TAG_ON, skip_dir))
return false;
} else {
double mul = f0 / abs_s;
- Vector offs = { normal[0].v.x * mul, normal[0].v.y * mul };
+ ASS_DVector offs = { normal[0].v.x * mul, normal[0].v.y * mul };
if (!emit_point(str, pt[0], offs, FT_CURVE_TAG_ON, skip_dir))
return false;
}
check_dir ^= skip_dir;
}
- Vector result;
+ ASS_DVector result;
if (check_dir && estimate_quadratic_error(str, c, s, normal, &result)) {
if (!emit_first_point(str, pt[0], check_dir))
return false;
}
}
- OutlinePoint next[5];
+ ASS_Vector next[5];
next[1].x = pt[0].x + pt[1].x;
next[1].y = pt[0].y + pt[1].y;
next[3].x = pt[1].x + pt[2].x;
next[0] = pt[0];
next[4] = pt[2];
- Vector next_deriv[3];
+ ASS_DVector next_deriv[3];
next_deriv[0].x = deriv[0].x / 2;
next_deriv[0].y = deriv[0].y / 2;
next_deriv[2].x = deriv[1].x / 2;
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool add_quadratic(StrokerState *str, const OutlinePoint *pt, int dir)
+static bool add_quadratic(StrokerState *str, const ASS_Vector *pt, int dir)
{
int32_t dx0 = pt[1].x - pt[0].x;
int32_t dy0 = pt[1].y - pt[0].y;
if (dx1 > -str->eps && dx1 < str->eps && dy1 > -str->eps && dy1 < str->eps)
return add_line(str, pt[2], dir);
- Vector deriv[2] = {
+ ASS_DVector deriv[2] = {
{ dy0 * str->yscale, -dx0 * str->xscale },
{ dy1 * str->yscale, -dx1 * str->xscale }
};
*/
static int estimate_cubic_error(StrokerState *str, double c, double s,
const double *dc, const double *ds,
- const Normal *normal, Vector *result,
+ const Normal *normal, ASS_DVector *result,
int check_flags, int dir)
{
double t = (ds[0] + ds[1]) / (dc[0] + dc[1]), c1 = 1 + c, ss = s * s;
* \param first true if the current part is at start of the segment
* \return false on allocation failure
*/
-static bool process_cubic(StrokerState *str, const OutlinePoint *pt,
- const Vector *deriv, const Normal *normal,
+static bool process_cubic(StrokerState *str, const ASS_Vector *pt,
+ const ASS_DVector *deriv, const Normal *normal,
int dir, bool first)
{
double c = vec_dot(normal[0].v, normal[1].v);
if (!prepare_skip(str, pt[0], skip_dir, first))
return false;
if (f0 < 0 || f1 < 0) {
- Vector zero_normal = {0, 0};
+ ASS_DVector zero_normal = {0, 0};
if (!emit_point(str, pt[0], zero_normal, FT_CURVE_TAG_ON, skip_dir) ||
!emit_point(str, pt[3], zero_normal, FT_CURVE_TAG_ON, skip_dir))
return false;
} else {
double mul = f0 / abs_s;
- Vector offs = { normal[0].v.x * mul, normal[0].v.y * mul };
+ ASS_DVector offs = { normal[0].v.x * mul, normal[0].v.y * mul };
if (!emit_point(str, pt[0], offs, FT_CURVE_TAG_ON, skip_dir))
return false;
}
}
}
- Vector result[2];
+ ASS_DVector result[2];
if (check_dir)
check_dir = estimate_cubic_error(str, c, s, dc, ds,
normal, result, flags, check_dir);
}
}
- OutlinePoint next[7], center;
+ ASS_Vector next[7], center;
next[1].x = pt[0].x + pt[1].x;
next[1].y = pt[0].y + pt[1].y;
center.x = pt[1].x + pt[2].x + 2;
next[0] = pt[0];
next[6] = pt[3];
- Vector next_deriv[5], center_deriv;
+ ASS_DVector next_deriv[5], center_deriv;
next_deriv[0].x = deriv[0].x / 2;
next_deriv[0].y = deriv[0].y / 2;
center_deriv.x = deriv[1].x / 2;
* \param dir destination outline flags
* \return false on allocation failure
*/
-static bool add_cubic(StrokerState *str, const OutlinePoint *pt, int dir)
+static bool add_cubic(StrokerState *str, const ASS_Vector *pt, int dir)
{
int flags = 9;
int32_t dx1 = pt[flags >> 2].x - pt[flags & 3].x;
int32_t dy1 = pt[flags >> 2].y - pt[flags & 3].y;
- Vector deriv[3] = {
+ ASS_DVector deriv[3] = {
{ dy0 * str->yscale, -dx0 * str->xscale },
{ dy1 * str->yscale, -dx1 * str->xscale },
{ dy2 * str->yscale, -dx2 * str->xscale }
const int dir = 3;
for (size_t i = 0, j = 0; i < path->n_contours; i++) {
- OutlinePoint start, p[4];
+ ASS_Vector start, p[4];
int process_end = 1;
enum Status st;
#include <ft2build.h>
#include FT_OUTLINE_H
#include <stdbool.h>
+#include <stdint.h>
+typedef struct {
+ int32_t x, y;
+} ASS_Vector;
+
+typedef struct {
+ double x, y;
+} ASS_DVector;
+
+typedef struct {
+ int32_t x_min, y_min, x_max, y_max;
+} ASS_Rect;
+
typedef struct ass_outline {
size_t n_contours, max_contours;
size_t *contours;
size_t n_points, max_points;
- FT_Vector *points;
+ ASS_Vector *points;
char *tags;
} ASS_Outline;
bool outline_copy(ASS_Outline *outline, const ASS_Outline *source);
void outline_free(ASS_Outline *outline);
-bool outline_add_point(ASS_Outline *outline, FT_Vector pt, char tag);
+bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char tag);
bool outline_close_contour(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_update_cbox(const ASS_Outline *outline, FT_BBox *cbox);
-void outline_get_cbox(const ASS_Outline *outline, FT_BBox *cbox);
+void outline_translate(const ASS_Outline *outline, int32_t dx, int32_t dy);
+void outline_transform(const ASS_Outline *outline, const FT_Matrix *matrix); // XXX: replace with outline_scale
+void outline_get_cbox(const ASS_Outline *outline, ASS_Rect *cbox);
bool outline_stroke(ASS_Outline *result, ASS_Outline *result1,
const ASS_Outline *path, int xbord, int ybord, int eps);
struct arg text = args[nargs - 1];
ass_drawing_free(drawing);
- render_priv->state.clip_drawing =
- ass_drawing_new(render_priv->library, render_priv->ftlibrary);
+ render_priv->state.clip_drawing = ass_drawing_new(render_priv->library);
drawing = render_priv->state.clip_drawing;
if (drawing) {
drawing->scale = scale;
x_start = 1000000;
x_end = -1000000;
for (cur2 = s1; cur2 <= e1; ++cur2) {
- x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x));
- x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x));
+ x_start = FFMIN(x_start, d6_to_int(cur2->bbox.x_min + cur2->pos.x));
+ x_end = FFMAX(x_end, d6_to_int(cur2->bbox.x_max + cur2->pos.x));
}
dt = (tm_current - tm_start);
*/
-typedef struct {
- int32_t x, y;
-} OutlinePoint;
-
// Helper struct for spline split decision
typedef struct {
- OutlinePoint r;
+ ASS_Vector r;
int64_t r2, er;
} OutlineSegment;
static inline void segment_init(OutlineSegment *seg,
- OutlinePoint beg, OutlinePoint end,
+ ASS_Vector beg, ASS_Vector end,
int32_t outline_error)
{
int32_t x = end.x - beg.x;
}
static inline bool segment_subdivide(const OutlineSegment *seg,
- OutlinePoint beg, OutlinePoint pt)
+ ASS_Vector beg, ASS_Vector pt)
{
int32_t x = pt.x - beg.x;
int32_t y = pt.y - beg.y;
/**
* \brief Add new segment to polyline
*/
-static bool add_line(RasterizerData *rst, OutlinePoint pt0, OutlinePoint pt1)
+static bool add_line(RasterizerData *rst, ASS_Vector pt0, ASS_Vector pt1)
{
int32_t x = pt1.x - pt0.x;
int32_t y = pt1.y - pt0.y;
* \brief Add quadratic spline to polyline
* Performs recursive subdivision if necessary.
*/
-static bool add_quadratic(RasterizerData *rst, const OutlinePoint *pt)
+static bool add_quadratic(RasterizerData *rst, const ASS_Vector *pt)
{
OutlineSegment seg;
segment_init(&seg, pt[0], pt[2], rst->outline_error);
if (!segment_subdivide(&seg, pt[0], pt[1]))
return add_line(rst, pt[0], pt[2]);
- OutlinePoint next[5];
+ ASS_Vector next[5];
next[1].x = pt[0].x + pt[1].x;
next[1].y = pt[0].y + pt[1].y;
next[3].x = pt[1].x + pt[2].x;
* \brief Add cubic spline to polyline
* Performs recursive subdivision if necessary.
*/
-static bool add_cubic(RasterizerData *rst, const OutlinePoint *pt)
+static bool add_cubic(RasterizerData *rst, const ASS_Vector *pt)
{
OutlineSegment seg;
segment_init(&seg, pt[0], pt[3], rst->outline_error);
if (!segment_subdivide(&seg, pt[0], pt[1]) && !segment_subdivide(&seg, pt[0], pt[2]))
return add_line(rst, pt[0], pt[3]);
- OutlinePoint next[7], center;
+ ASS_Vector next[7], center;
next[1].x = pt[0].x + pt[1].x;
next[1].y = pt[0].y + pt[1].y;
center.x = pt[1].x + pt[2].x + 2;
}
rst->size[0] = rst->n_first;
for (size_t i = 0, j = 0; i < path->n_contours; i++) {
- OutlinePoint start, p[4];
+ ASS_Vector start, p[4];
int process_end = 1;
enum Status st;
// We need to translate the clip according to screen borders
if (render_priv->settings.left_margin != 0 ||
render_priv->settings.top_margin != 0) {
- FT_Vector trans = {
+ ASS_Vector trans = {
.x = int_to_d6(render_priv->settings.left_margin),
.y = -int_to_d6(render_priv->settings.top_margin),
};
*/
static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info,
int asc, int desc, ASS_Outline *ol,
- FT_Vector advance, int sx, int sy)
+ ASS_Vector advance, int sx, int sy)
{
int adv = advance.x;
double scale_y = info->orig_scale_y;
desc *= scale_y;
desc += asc * (scale_y - 1.0);
- FT_Vector points[4] = {
+ ASS_Vector points[4] = {
{ .x = -sx, .y = asc + sy },
{ .x = adv + sx, .y = asc + sy },
{ .x = adv + sx, .y = -desc - sy },
return;
for (int i = 0; i < 4; ++i) {
ol->points[ol->n_points] = points[i];
- ol->tags[ol->n_points++] = 1;
+ ol->tags[ol->n_points++] = FT_CURVE_TAG_ON;
}
ol->contours[ol->n_contours++] = ol->n_points - 1;
}
outline_get_cbox(&val->outline, &val->bbox_scaled);
if (info->border_style == 3) {
- FT_Vector advance;
+ ASS_Vector advance;
if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing)
advance = val->advance;
else
* onto the screen plane.
*/
static void
-transform_3d_points(FT_Vector shift, ASS_Outline *outline, double frx, double fry,
+transform_3d_points(ASS_Vector shift, ASS_Outline *outline, double frx, double fry,
double frz, double fax, double fay, double scale,
int yshift)
{
double cx = cos(frx);
double cy = cos(fry);
double cz = cos(frz);
- FT_Vector *p = outline->points;
+ ASS_Vector *p = outline->points;
double x, y, z, xx, yy, zz;
int dist;
* 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, ASS_Outline *outline, int n_outlines,
+transform_3d(ASS_Vector shift, ASS_Outline *outline, int n_outlines,
double frx, double fry, double frz, double fax, double fay,
double scale, int yshift)
{
outline_copy(&outline[2], info->border[1]);
// calculating rotation shift vector (from rotation origin to the glyph basepoint)
- FT_Vector shift = { key->shift_x, key->shift_y };
+ ASS_Vector shift = { key->shift_x, key->shift_y };
double scale_x = render_priv->font_scale_x;
double fax_scaled = info->fax / info->scale_y * info->scale_x;
double fay_scaled = info->fay / info->scale_x * info->scale_y;
int break_at = -1;
double s_offset, len;
cur = text_info->glyphs + i;
- s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x);
- len = d6_to_double(cur->bbox.xMax + cur->pos.x) - s_offset;
+ s_offset = d6_to_double(s1->bbox.x_min + s1->pos.x);
+ len = d6_to_double(cur->bbox.x_max + cur->pos.x) - s_offset;
if (cur->symbol == '\n') {
break_type = 2;
if (w->symbol == ' ')
++w;
- l1 = d6_to_double(((s2 - 1)->bbox.xMax + (s2 - 1)->pos.x) -
- (s1->bbox.xMin + s1->pos.x));
- l2 = d6_to_double(((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) -
- (s2->bbox.xMin + s2->pos.x));
+ l1 = d6_to_double(((s2 - 1)->bbox.x_max + (s2 - 1)->pos.x) -
+ (s1->bbox.x_min + s1->pos.x));
+ l2 = d6_to_double(((s3 - 1)->bbox.x_max + (s3 - 1)->pos.x) -
+ (s2->bbox.x_min + s2->pos.x));
l1_new = d6_to_double(
- (e1->bbox.xMax + e1->pos.x) -
- (s1->bbox.xMin + s1->pos.x));
+ (e1->bbox.x_max + e1->pos.x) -
+ (s1->bbox.x_min + s1->pos.x));
l2_new = d6_to_double(
- ((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) -
- (w->bbox.xMin + w->pos.x));
+ ((s3 - 1)->bbox.x_max + (s3 - 1)->pos.x) -
+ (w->bbox.x_min + w->pos.x));
if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) {
if (w->linebreak || w == text_info->glyphs)
while ((*q != '{') && (*q != 0))
q++;
if (!drawing) {
- drawing = ass_drawing_new(render_priv->library,
- render_priv->ftlibrary);
+ drawing = ass_drawing_new(render_priv->library);
if (!drawing)
return 1;
}
if (i && glyphs[i - 1].italic && !info->italic) {
int back = i - 1;
GlyphInfo *og = &glyphs[back];
- while (back && og->bbox.xMax - og->bbox.xMin == 0
+ while (back && og->bbox.x_max - og->bbox.x_min == 0
&& og->italic)
og = &glyphs[--back];
- if (og->bbox.xMax > og->cluster_advance.x)
- og->cluster_advance.x = og->bbox.xMax;
+ if (og->bbox.x_max > og->cluster_advance.x)
+ og->cluster_advance.x = og->bbox.x_max;
}
// add horizontal letter spacing
// Preliminary layout (for line wrapping)
static void preliminary_layout(ASS_Renderer *render_priv)
{
- FT_Vector pen;
- int i;
-
- pen.x = 0;
- pen.y = 0;
- for (i = 0; i < render_priv->text_info.length; i++) {
+ ASS_Vector pen = { 0, 0 };
+ for (int i = 0; i < render_priv->text_info.length; i++) {
GlyphInfo *info = render_priv->text_info.glyphs + i;
- FT_Vector cluster_pen = pen;
+ ASS_Vector cluster_pen = pen;
while (info) {
info->pos.x = cluster_pen.x;
info->pos.y = cluster_pen.y;
static void reorder_text(ASS_Renderer *render_priv)
{
TextInfo *text_info = &render_priv->text_info;
- FT_Vector pen;
- int i;
-
FriBidiStrIndex *cmap = ass_shaper_reorder(render_priv->shaper, text_info);
if (!cmap) {
ass_msg(render_priv->library, MSGL_ERR, "Failed to reorder text");
}
// Reposition according to the map
- pen.x = 0;
- pen.y = 0;
+ ASS_Vector pen = { 0, 0 };
int lineno = 1;
double last_pen_x = 0;
double last_fay = 0;
- for (i = 0; i < text_info->length; i++) {
+ for (int i = 0; i < text_info->length; i++) {
GlyphInfo *info = text_info->glyphs + cmap[i];
if (text_info->glyphs[i].linebreak) {
pen.y -= (last_fay / info->scale_x * info->scale_y) * (pen.x - last_pen_x);
}
last_fay = info->fay;
if (info->skip) continue;
- FT_Vector cluster_pen = pen;
+ ASS_Vector cluster_pen = pen;
while (info) {
info->pos.x = info->offset.x + cluster_pen.x;
info->pos.y = info->offset.y + cluster_pen.y;
double device_x, double device_y)
{
TextInfo *text_info = &render_priv->text_info;
- DVector center;
+ ASS_DVector center;
int i;
if (render_priv->state.have_origin) {
}
-static inline void rectangle_reset(Rectangle *rect)
+static inline void rectangle_reset(ASS_Rect *rect)
{
- rect->x_min = rect->y_min = INT_MAX;
- rect->x_max = rect->y_max = INT_MIN;
+ rect->x_min = rect->y_min = INT32_MAX;
+ rect->x_max = rect->y_max = INT32_MIN;
}
-static inline void rectangle_combine(Rectangle *rect, const Bitmap *bm, int x, int y)
+static inline void rectangle_combine(ASS_Rect *rect, const Bitmap *bm, int x, int y)
{
rect->x_min = FFMIN(rect->x_min, x + bm->left);
rect->y_min = FFMIN(rect->y_min, y + bm->top);
memcpy(¤t_info->c, &info->c, sizeof(info->c));
current_info->effect_type = info->effect_type;
current_info->effect_timing = info->effect_timing;
- current_info->first_pos_x = info->bbox.xMax >> 6;
+ current_info->first_pos_x = info->bbox.x_max >> 6;
current_info->filter.flags = 0;
if (info->border_style == 3)
double yMax;
} DBBox;
-typedef struct {
- double x;
- double y;
-} DVector;
-
typedef struct {
ASS_Image result;
CompositeHashValue *source;
EF_KARAOKE_KO
} Effect;
-typedef struct
-{
- int x_min, y_min, x_max, y_max;
-} Rectangle;
-
// describes a combined bitmap
typedef struct {
FilterDesc filter;
BitmapRef *bitmaps;
int x, y;
- Rectangle rect, rect_o;
+ ASS_Rect rect, rect_o;
size_t n_bm, n_bm_o;
Bitmap *bm, *bm_o, *bm_s; // glyphs, outline, shadow bitmaps
ASS_Drawing *drawing;
ASS_Outline *outline;
ASS_Outline *border[2];
- FT_BBox bbox;
- FT_Vector pos;
- FT_Vector offset;
+ ASS_Rect bbox;
+ ASS_Vector pos;
+ ASS_Vector offset;
char linebreak; // the first (leading) glyph of some line ?
uint32_t c[4]; // colors
- FT_Vector advance; // 26.6
- FT_Vector cluster_advance;
+ ASS_Vector advance; // 26.6
+ ASS_Vector cluster_advance;
char effect; // the first (leading) glyph of some effect ?
Effect effect_type;
int effect_timing; // time duration of current karaoke word