/*
* 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"
/*
* \brief Initialize ASS_Outline to an empty state
* Equivalent to zeroing of outline object and doesn't free any memory.
*/
void ass_outline_clear(ASS_Outline *outline)
{
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 ass_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)) {
ass_outline_clear(outline);
return false;
}
outline->points = malloc(sizeof(ASS_Vector) * max_points);
outline->segments = malloc(max_segments);
if (!outline->points || !outline->segments) {
ass_outline_free(outline);
return false;
}
outline->max_points = max_points;
outline->max_segments = max_segments;
outline->n_points = outline->n_segments = 0;
return true;
}
/*
* \brief Free previously initialized ASS_Outline
* Outline state after the call is the same as after ass_outline_clear().
* Outline pointer can be NULL.
*/
void ass_outline_free(ASS_Outline *outline)
{
if (!outline)
return;
free(outline->points);
free(outline->segments);
ass_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 ass_outline_convert(ASS_Outline *outline, const FT_Outline *source)
{
enum Status {
S_ON, S_Q, S_C1, S_C2
};
for (int i = 0, j = 0; i < source->n_contours; i++) {
ASS_Vector pt;
int skip_last = 0;
enum Status st;
char seg;
int last = source->contours[i];
if (j > last || last >= source->n_points)
return false;
// skip degenerate 2-point contours from broken fonts
if (last - j < 2) {
j = last + 1;
continue;
}
if (!valid_point(source->points + j))
return false;
switch (FT_CURVE_TAG(source->tags[j])) {
case FT_CURVE_TAG_ON:
st = S_ON;
break;
case FT_CURVE_TAG_CONIC:
if (!valid_point(source->points + last))
return false;
pt.x = source->points[last].x;
pt.y = -source->points[last].y;
switch (FT_CURVE_TAG(source->tags[last])) {
case FT_CURVE_TAG_ON:
skip_last = 1;
last--;
break;
case FT_CURVE_TAG_CONIC:
pt.x = (pt.x + source->points[j].x) >> 1;
pt.y = (pt.y - source->points[j].y) >> 1;
break;
default:
return false;
}
assert(outline->n_points < outline->max_points);
outline->points[outline->n_points++] = pt;
st = S_Q;
break;
default:
return false;
}
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++) {
if (!valid_point(source->points + j))
return false;
switch (FT_CURVE_TAG(source->tags[j])) {
case FT_CURVE_TAG_ON:
switch (st) {
case S_ON:
seg = OUTLINE_LINE_SEGMENT;
break;
case S_Q:
seg = OUTLINE_QUADRATIC_SPLINE;
break;
case S_C2:
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;
case FT_CURVE_TAG_CONIC:
switch (st) {
case S_ON:
st = S_Q;
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;
default:
return false;
}
break;
case FT_CURVE_TAG_CUBIC:
switch (st) {
case S_ON:
st = S_C1;
break;
case S_C1:
st = S_C2;
break;
default:
return false;
}
break;
default:
return false;
}
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:
seg = OUTLINE_LINE_SEGMENT | OUTLINE_CONTOUR_END;
break;
case S_Q:
|