mirror of
https://github.com/buserror/mii_emu.git
synced 2024-12-03 13:49:56 +00:00
3fd0540a83
Changes are just too long to list... Signed-off-by: Michel Pollet <buserror@gmail.com>
2875 lines
68 KiB
C
2875 lines
68 KiB
C
/*
|
|
* xft.c
|
|
*
|
|
* Copyright(c) 2007-2023 Jianjun Jiang <8192542@qq.com>
|
|
* Mobile phone: +86-18665388956
|
|
* QQ: 8192542
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "xft.h"
|
|
|
|
/*
|
|
* type
|
|
*/
|
|
typedef int64_t XCG_FT_Int64;
|
|
typedef uint64_t XCG_FT_UInt64;
|
|
typedef int32_t XCG_FT_Int32;
|
|
typedef uint32_t XCG_FT_UInt32;
|
|
|
|
#define XCG_FT_BOOL(x) ((XCG_FT_Bool)(x))
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
/*
|
|
* math
|
|
*/
|
|
#define XCG_FT_MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
#define XCG_FT_MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
#define XCG_FT_ABS(a) ((a) < 0 ? -(a) : (a))
|
|
#define XCG_FT_HYPOT(x, y) (x = XCG_FT_ABS(x), y = XCG_FT_ABS(y), x > y ? x + (3 * y >> 3) : y + (3 * x >> 3))
|
|
#define XCG_FT_ANGLE_PI (180L << 16)
|
|
#define XCG_FT_ANGLE_2PI (XCG_FT_ANGLE_PI * 2)
|
|
#define XCG_FT_ANGLE_PI2 (XCG_FT_ANGLE_PI / 2)
|
|
#define XCG_FT_ANGLE_PI4 (XCG_FT_ANGLE_PI / 4)
|
|
|
|
#define XCG_FT_MSB(x) (31 - __builtin_clz(x))
|
|
#define XCG_FT_PAD_FLOOR(x, n) ((x) & ~((n)-1))
|
|
#define XCG_FT_PAD_ROUND(x, n) XCG_FT_PAD_FLOOR((x) + ((n) / 2), n)
|
|
#define XCG_FT_PAD_CEIL(x, n) XCG_FT_PAD_FLOOR((x) + ((n) - 1), n)
|
|
|
|
#define XCG_FT_MOVE_SIGN(x, s) \
|
|
do { \
|
|
if(x < 0) { \
|
|
x = -x; \
|
|
s = -s; \
|
|
} \
|
|
} while(0)
|
|
|
|
XCG_FT_Long XCG_FT_MulFix(XCG_FT_Long a, XCG_FT_Long b)
|
|
{
|
|
XCG_FT_Int s = 1;
|
|
XCG_FT_Long c;
|
|
|
|
XCG_FT_MOVE_SIGN(a, s);
|
|
XCG_FT_MOVE_SIGN(b, s);
|
|
c = (XCG_FT_Long)(((XCG_FT_Int64)a * b + 0x8000L) >> 16);
|
|
|
|
return (s > 0) ? c : -c;
|
|
}
|
|
|
|
XCG_FT_Long XCG_FT_MulDiv(XCG_FT_Long a, XCG_FT_Long b, XCG_FT_Long c)
|
|
{
|
|
XCG_FT_Int s = 1;
|
|
XCG_FT_Long d;
|
|
|
|
XCG_FT_MOVE_SIGN(a, s);
|
|
XCG_FT_MOVE_SIGN(b, s);
|
|
XCG_FT_MOVE_SIGN(c, s);
|
|
d = (XCG_FT_Long)(c > 0 ? ((XCG_FT_Int64)a * b + (c >> 1)) / c : 0x7FFFFFFFL);
|
|
return (s > 0) ? d : -d;
|
|
}
|
|
|
|
XCG_FT_Long XCG_FT_DivFix(XCG_FT_Long a, XCG_FT_Long b)
|
|
{
|
|
XCG_FT_Int s = 1;
|
|
XCG_FT_Long q;
|
|
|
|
XCG_FT_MOVE_SIGN(a, s);
|
|
XCG_FT_MOVE_SIGN(b, s);
|
|
q = (XCG_FT_Long)(b > 0 ? (((XCG_FT_UInt64)a << 16) + (b >> 1)) / b : 0x7FFFFFFFL);
|
|
return (s < 0 ? -q : q);
|
|
}
|
|
|
|
#define XCG_FT_TRIG_SCALE (0xDBD95B16UL)
|
|
#define XCG_FT_TRIG_SAFE_MSB (29)
|
|
#define XCG_FT_TRIG_MAX_ITERS (23)
|
|
|
|
static const XCG_FT_Fixed ft_trig_arctan_table[] = {
|
|
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L, 14668L,
|
|
7334L, 3667L, 1833L, 917L, 458L, 229L, 115L, 57L,
|
|
29L, 14L, 7L, 4L, 2L, 1L
|
|
};
|
|
|
|
static XCG_FT_Fixed ft_trig_downscale(XCG_FT_Fixed val)
|
|
{
|
|
XCG_FT_Fixed s;
|
|
XCG_FT_Int64 v;
|
|
|
|
s = val;
|
|
val = XCG_FT_ABS(val);
|
|
v = (val * (XCG_FT_Int64)XCG_FT_TRIG_SCALE) + 0x100000000UL;
|
|
val = (XCG_FT_Fixed)(v >> 32);
|
|
return (s >= 0) ? val : -val;
|
|
}
|
|
|
|
static XCG_FT_Int ft_trig_prenorm(XCG_FT_Vector *vec)
|
|
{
|
|
XCG_FT_Pos x, y;
|
|
XCG_FT_Int shift;
|
|
|
|
x = vec->x;
|
|
y = vec->y;
|
|
|
|
shift = XCG_FT_MSB(XCG_FT_ABS(x) | XCG_FT_ABS(y));
|
|
|
|
if(shift <= XCG_FT_TRIG_SAFE_MSB)
|
|
{
|
|
shift = XCG_FT_TRIG_SAFE_MSB - shift;
|
|
vec->x = (XCG_FT_Pos)((XCG_FT_ULong)x << shift);
|
|
vec->y = (XCG_FT_Pos)((XCG_FT_ULong)y << shift);
|
|
}
|
|
else
|
|
{
|
|
shift -= XCG_FT_TRIG_SAFE_MSB;
|
|
vec->x = x >> shift;
|
|
vec->y = y >> shift;
|
|
shift = -shift;
|
|
}
|
|
return shift;
|
|
}
|
|
|
|
static void ft_trig_pseudo_rotate(XCG_FT_Vector *vec, XCG_FT_Angle theta)
|
|
{
|
|
XCG_FT_Int i;
|
|
XCG_FT_Fixed x, y, xtemp, b;
|
|
const XCG_FT_Fixed *arctanptr;
|
|
|
|
x = vec->x;
|
|
y = vec->y;
|
|
while(theta < -XCG_FT_ANGLE_PI4)
|
|
{
|
|
xtemp = y;
|
|
y = -x;
|
|
x = xtemp;
|
|
theta += XCG_FT_ANGLE_PI2;
|
|
}
|
|
while(theta > XCG_FT_ANGLE_PI4)
|
|
{
|
|
xtemp = -y;
|
|
y = x;
|
|
x = xtemp;
|
|
theta -= XCG_FT_ANGLE_PI2;
|
|
}
|
|
arctanptr = ft_trig_arctan_table;
|
|
for(i = 1, b = 1; i < XCG_FT_TRIG_MAX_ITERS; b <<= 1, i++)
|
|
{
|
|
XCG_FT_Fixed v1 = ((y + b) >> i);
|
|
XCG_FT_Fixed v2 = ((x + b) >> i);
|
|
if(theta < 0)
|
|
{
|
|
xtemp = x + v1;
|
|
y = y - v2;
|
|
x = xtemp;
|
|
theta += *arctanptr++;
|
|
}
|
|
else
|
|
{
|
|
xtemp = x - v1;
|
|
y = y + v2;
|
|
x = xtemp;
|
|
theta -= *arctanptr++;
|
|
}
|
|
}
|
|
vec->x = x;
|
|
vec->y = y;
|
|
}
|
|
|
|
static void ft_trig_pseudo_polarize(XCG_FT_Vector *vec)
|
|
{
|
|
XCG_FT_Angle theta;
|
|
XCG_FT_Int i;
|
|
XCG_FT_Fixed x, y, xtemp, b;
|
|
const XCG_FT_Fixed *arctanptr;
|
|
|
|
x = vec->x;
|
|
y = vec->y;
|
|
if(y > x)
|
|
{
|
|
if(y > -x)
|
|
{
|
|
theta = XCG_FT_ANGLE_PI2;
|
|
xtemp = y;
|
|
y = -x;
|
|
x = xtemp;
|
|
}
|
|
else
|
|
{
|
|
theta = y > 0 ? XCG_FT_ANGLE_PI : -XCG_FT_ANGLE_PI;
|
|
x = -x;
|
|
y = -y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(y < -x)
|
|
{
|
|
theta = -XCG_FT_ANGLE_PI2;
|
|
xtemp = -y;
|
|
y = x;
|
|
x = xtemp;
|
|
}
|
|
else
|
|
{
|
|
theta = 0;
|
|
}
|
|
}
|
|
arctanptr = ft_trig_arctan_table;
|
|
for(i = 1, b = 1; i < XCG_FT_TRIG_MAX_ITERS; b <<= 1, i++)
|
|
{
|
|
XCG_FT_Fixed v1 = ((y + b) >> i);
|
|
XCG_FT_Fixed v2 = ((x + b) >> i);
|
|
if(y > 0)
|
|
{
|
|
xtemp = x + v1;
|
|
y = y - v2;
|
|
x = xtemp;
|
|
theta += *arctanptr++;
|
|
}
|
|
else
|
|
{
|
|
xtemp = x - v1;
|
|
y = y + v2;
|
|
x = xtemp;
|
|
theta -= *arctanptr++;
|
|
}
|
|
}
|
|
if(theta >= 0)
|
|
theta = XCG_FT_PAD_ROUND(theta, 32);
|
|
else
|
|
theta = -XCG_FT_PAD_ROUND(-theta, 32);
|
|
vec->x = x;
|
|
vec->y = theta;
|
|
}
|
|
|
|
XCG_FT_Fixed XCG_FT_Cos(XCG_FT_Angle angle)
|
|
{
|
|
XCG_FT_Vector v;
|
|
|
|
v.x = XCG_FT_TRIG_SCALE >> 8;
|
|
v.y = 0;
|
|
ft_trig_pseudo_rotate(&v, angle);
|
|
return (v.x + 0x80L) >> 8;
|
|
}
|
|
|
|
XCG_FT_Fixed XCG_FT_Sin(XCG_FT_Angle angle)
|
|
{
|
|
return XCG_FT_Cos(XCG_FT_ANGLE_PI2 - angle);
|
|
}
|
|
|
|
XCG_FT_Fixed XCG_FT_Tan(XCG_FT_Angle angle)
|
|
{
|
|
XCG_FT_Vector v;
|
|
|
|
v.x = XCG_FT_TRIG_SCALE >> 8;
|
|
v.y = 0;
|
|
ft_trig_pseudo_rotate(&v, angle);
|
|
return XCG_FT_DivFix(v.y, v.x);
|
|
}
|
|
|
|
XCG_FT_Angle XCG_FT_Atan2(XCG_FT_Fixed dx, XCG_FT_Fixed dy)
|
|
{
|
|
XCG_FT_Vector v;
|
|
|
|
if(dx == 0 && dy == 0)
|
|
return 0;
|
|
v.x = dx;
|
|
v.y = dy;
|
|
ft_trig_prenorm(&v);
|
|
ft_trig_pseudo_polarize(&v);
|
|
return v.y;
|
|
}
|
|
|
|
void XCG_FT_Vector_Unit(XCG_FT_Vector *vec, XCG_FT_Angle angle)
|
|
{
|
|
vec->x = XCG_FT_TRIG_SCALE >> 8;
|
|
vec->y = 0;
|
|
ft_trig_pseudo_rotate(vec, angle);
|
|
vec->x = (vec->x + 0x80L) >> 8;
|
|
vec->y = (vec->y + 0x80L) >> 8;
|
|
}
|
|
|
|
void XCG_FT_Vector_Rotate(XCG_FT_Vector *vec, XCG_FT_Angle angle)
|
|
{
|
|
XCG_FT_Int shift;
|
|
XCG_FT_Vector v = *vec;
|
|
|
|
if(v.x == 0 && v.y == 0)
|
|
return;
|
|
shift = ft_trig_prenorm(&v);
|
|
ft_trig_pseudo_rotate(&v, angle);
|
|
v.x = ft_trig_downscale(v.x);
|
|
v.y = ft_trig_downscale(v.y);
|
|
|
|
if(shift > 0)
|
|
{
|
|
XCG_FT_Int32 half = (XCG_FT_Int32)1L << (shift - 1);
|
|
|
|
vec->x = (v.x + half - (v.x < 0)) >> shift;
|
|
vec->y = (v.y + half - (v.y < 0)) >> shift;
|
|
}
|
|
else
|
|
{
|
|
shift = -shift;
|
|
vec->x = (XCG_FT_Pos)((XCG_FT_ULong)v.x << shift);
|
|
vec->y = (XCG_FT_Pos)((XCG_FT_ULong)v.y << shift);
|
|
}
|
|
}
|
|
|
|
XCG_FT_Fixed XCG_FT_Vector_Length(XCG_FT_Vector *vec)
|
|
{
|
|
XCG_FT_Int shift;
|
|
XCG_FT_Vector v;
|
|
|
|
v = *vec;
|
|
if(v.x == 0)
|
|
{
|
|
return XCG_FT_ABS(v.y);
|
|
}
|
|
else if(v.y == 0)
|
|
{
|
|
return XCG_FT_ABS(v.x);
|
|
}
|
|
|
|
shift = ft_trig_prenorm(&v);
|
|
ft_trig_pseudo_polarize(&v);
|
|
v.x = ft_trig_downscale(v.x);
|
|
if(shift > 0)
|
|
return (v.x + (1 << (shift - 1))) >> shift;
|
|
|
|
return (XCG_FT_Fixed)((XCG_FT_UInt32)v.x << -shift);
|
|
}
|
|
|
|
void XCG_FT_Vector_Polarize(XCG_FT_Vector *vec, XCG_FT_Fixed *length, XCG_FT_Angle *angle)
|
|
{
|
|
XCG_FT_Int shift;
|
|
XCG_FT_Vector v;
|
|
|
|
v = *vec;
|
|
if(v.x == 0 && v.y == 0)
|
|
return;
|
|
shift = ft_trig_prenorm(&v);
|
|
ft_trig_pseudo_polarize(&v);
|
|
v.x = ft_trig_downscale(v.x);
|
|
|
|
*length = (shift >= 0) ? (v.x >> shift) : (XCG_FT_Fixed)((XCG_FT_UInt32)v.x << -shift);
|
|
*angle = v.y;
|
|
}
|
|
|
|
void XCG_FT_Vector_From_Polar(XCG_FT_Vector *vec, XCG_FT_Fixed length, XCG_FT_Angle angle)
|
|
{
|
|
vec->x = length;
|
|
vec->y = 0;
|
|
|
|
XCG_FT_Vector_Rotate(vec, angle);
|
|
}
|
|
|
|
XCG_FT_Angle XCG_FT_Angle_Diff(XCG_FT_Angle angle1, XCG_FT_Angle angle2)
|
|
{
|
|
XCG_FT_Angle delta = angle2 - angle1;
|
|
|
|
while(delta <= -XCG_FT_ANGLE_PI)
|
|
delta += XCG_FT_ANGLE_2PI;
|
|
|
|
while(delta > XCG_FT_ANGLE_PI)
|
|
delta -= XCG_FT_ANGLE_2PI;
|
|
|
|
return delta;
|
|
}
|
|
|
|
/*
|
|
* raster
|
|
*/
|
|
typedef long TCoord;
|
|
typedef long TPos;
|
|
typedef long TArea;
|
|
typedef ptrdiff_t XCG_FT_PtrDist;
|
|
|
|
#define xcg_ft_setjmp setjmp
|
|
#define xcg_ft_longjmp longjmp
|
|
#define xcg_ft_jmp_buf jmp_buf
|
|
|
|
#define ErrRaster_Invalid_Mode -2
|
|
#define ErrRaster_Invalid_Outline -1
|
|
#define ErrRaster_Invalid_Argument -3
|
|
#define ErrRaster_Memory_Overflow -4
|
|
#define ErrRaster_OutOfMemory -6
|
|
|
|
#define XCG_FT_MINIMUM_POOL_SIZE 8192
|
|
#define XCG_FT_MAX_GRAY_SPANS 256
|
|
|
|
#define RAS_ARG PWorker worker
|
|
#define RAS_ARG_ PWorker worker,
|
|
#define RAS_VAR worker
|
|
#define RAS_VAR_ worker,
|
|
#define ras (*worker)
|
|
|
|
#define PIXEL_BITS 8
|
|
#define ONE_PIXEL (1L << PIXEL_BITS)
|
|
#define TRUNC(x) (TCoord)((x) >> PIXEL_BITS)
|
|
#define FRACT(x) (TCoord)((x) & (ONE_PIXEL - 1))
|
|
#if PIXEL_BITS >= 6
|
|
#define UPSCALE(x) ((x) * (ONE_PIXEL >> 6))
|
|
#define DOWNSCALE(x) ((x) >> (PIXEL_BITS - 6))
|
|
#else
|
|
#define UPSCALE(x) ((x) >> (6 - PIXEL_BITS))
|
|
#define DOWNSCALE(x) ((x) * (64 >> PIXEL_BITS))
|
|
#endif
|
|
|
|
#define XCG_FT_DIV_MOD(type, dividend, divisor, quotient, remainder) \
|
|
do { \
|
|
(quotient) = (type)((dividend) / (divisor)); \
|
|
(remainder) = (type)((dividend) % (divisor)); \
|
|
if((remainder) < 0) \
|
|
{ \
|
|
(quotient)--; \
|
|
(remainder) += (type)(divisor); \
|
|
} \
|
|
} while(0)
|
|
|
|
typedef struct TCell_ * PCell;
|
|
typedef struct TCell_ {
|
|
int x;
|
|
int cover;
|
|
TArea area;
|
|
PCell next;
|
|
} TCell;
|
|
|
|
typedef struct TWorker_ {
|
|
TCoord ex, ey;
|
|
TPos min_ex, max_ex;
|
|
TPos min_ey, max_ey;
|
|
TPos count_ex, count_ey;
|
|
TArea area;
|
|
int cover;
|
|
int invalid;
|
|
PCell cells;
|
|
XCG_FT_PtrDist max_cells;
|
|
XCG_FT_PtrDist num_cells;
|
|
TPos x, y;
|
|
XCG_FT_Outline outline;
|
|
XCG_FT_BBox clip_box;
|
|
XCG_FT_Span gray_spans[XCG_FT_MAX_GRAY_SPANS];
|
|
int num_gray_spans;
|
|
int skip_spans;
|
|
XCG_FT_Raster_Span_Func render_span;
|
|
void *render_span_data;
|
|
int band_size;
|
|
int band_shoot;
|
|
xcg_ft_jmp_buf jump_buffer;
|
|
void *buffer;
|
|
long buffer_size;
|
|
PCell *ycells;
|
|
TPos ycount;
|
|
} TWorker, *PWorker;
|
|
|
|
static void gray_init_cells(RAS_ARG_ void* buffer, long byte_size)
|
|
{
|
|
ras.buffer = buffer;
|
|
ras.buffer_size = byte_size;
|
|
ras.ycells = (PCell*)buffer;
|
|
ras.cells = NULL;
|
|
ras.max_cells = 0;
|
|
ras.num_cells = 0;
|
|
ras.area = 0;
|
|
ras.cover = 0;
|
|
ras.invalid = 1;
|
|
}
|
|
|
|
static void gray_compute_cbox( RAS_ARG)
|
|
{
|
|
XCG_FT_Outline *outline = &ras.outline;
|
|
XCG_FT_Vector *vec = outline->points;
|
|
XCG_FT_Vector *limit = vec + outline->n_points;
|
|
|
|
if(outline->n_points <= 0)
|
|
{
|
|
ras.min_ex = ras.max_ex = 0;
|
|
ras.min_ey = ras.max_ey = 0;
|
|
return;
|
|
}
|
|
ras.min_ex = ras.max_ex = vec->x;
|
|
ras.min_ey = ras.max_ey = vec->y;
|
|
vec++;
|
|
for(; vec < limit; vec++)
|
|
{
|
|
TPos x = vec->x;
|
|
TPos y = vec->y;
|
|
if(x < ras.min_ex)
|
|
ras.min_ex = x;
|
|
if(x > ras.max_ex)
|
|
ras.max_ex = x;
|
|
if(y < ras.min_ey)
|
|
ras.min_ey = y;
|
|
if(y > ras.max_ey)
|
|
ras.max_ey = y;
|
|
}
|
|
ras.min_ex = ras.min_ex >> 6;
|
|
ras.min_ey = ras.min_ey >> 6;
|
|
ras.max_ex = (ras.max_ex + 63) >> 6;
|
|
ras.max_ey = (ras.max_ey + 63) >> 6;
|
|
}
|
|
|
|
static PCell gray_find_cell(RAS_ARG)
|
|
{
|
|
PCell *pcell, cell;
|
|
TPos x = ras.ex;
|
|
|
|
if(x > ras.count_ex)
|
|
x = ras.count_ex;
|
|
pcell = &ras.ycells[ras.ey];
|
|
for(;;)
|
|
{
|
|
cell = *pcell;
|
|
if(cell == NULL || cell->x > x)
|
|
break;
|
|
if(cell->x == x)
|
|
goto Exit;
|
|
pcell = &cell->next;
|
|
}
|
|
if(ras.num_cells >= ras.max_cells)
|
|
xcg_ft_longjmp(ras.jump_buffer, 1);
|
|
cell = ras.cells + ras.num_cells++;
|
|
cell->x = x;
|
|
cell->area = 0;
|
|
cell->cover = 0;
|
|
cell->next = *pcell;
|
|
*pcell = cell;
|
|
Exit:
|
|
return cell;
|
|
}
|
|
|
|
static void gray_record_cell( RAS_ARG)
|
|
{
|
|
if( ras.area | ras.cover)
|
|
{
|
|
PCell cell = gray_find_cell( RAS_VAR);
|
|
cell->area += ras.area;
|
|
cell->cover += ras.cover;
|
|
}
|
|
}
|
|
|
|
static void gray_set_cell( RAS_ARG_ TCoord ex, TCoord ey)
|
|
{
|
|
ey -= ras.min_ey;
|
|
if(ex > ras.max_ex)
|
|
ex = ras.max_ex;
|
|
ex -= ras.min_ex;
|
|
if(ex < 0)
|
|
ex = -1;
|
|
if(ex != ras.ex || ey != ras.ey)
|
|
{
|
|
if(!ras.invalid)
|
|
gray_record_cell( RAS_VAR);
|
|
ras.area = 0;
|
|
ras.cover = 0;
|
|
ras.ex = ex;
|
|
ras.ey = ey;
|
|
}
|
|
ras.invalid = ((unsigned int)ey >= (unsigned int)ras.count_ey || ex >= ras.count_ex);
|
|
}
|
|
|
|
static void gray_start_cell( RAS_ARG_ TCoord ex, TCoord ey)
|
|
{
|
|
if(ex > ras.max_ex)
|
|
ex = (TCoord)( ras.max_ex);
|
|
if(ex < ras.min_ex)
|
|
ex = (TCoord)( ras.min_ex - 1);
|
|
ras.area = 0;
|
|
ras.cover = 0;
|
|
ras.ex = ex - ras.min_ex;
|
|
ras.ey = ey - ras.min_ey;
|
|
ras.invalid = 0;
|
|
gray_set_cell( RAS_VAR_ ex, ey);
|
|
}
|
|
|
|
static void gray_render_scanline( RAS_ARG_ TCoord ey, TPos x1, TCoord y1, TPos x2, TCoord y2)
|
|
{
|
|
TCoord ex1, ex2, fx1, fx2, first, dy, delta, mod;
|
|
TPos p, dx;
|
|
int incr;
|
|
|
|
ex1 = TRUNC(x1);
|
|
ex2 = TRUNC(x2);
|
|
if(y1 == y2)
|
|
{
|
|
gray_set_cell( RAS_VAR_ ex2, ey);
|
|
return;
|
|
}
|
|
fx1 = FRACT(x1);
|
|
fx2 = FRACT(x2);
|
|
if(ex1 == ex2)
|
|
goto End;
|
|
dx = x2 - x1;
|
|
dy = y2 - y1;
|
|
if(dx > 0)
|
|
{
|
|
p = ( ONE_PIXEL - fx1) * dy;
|
|
first = ONE_PIXEL;
|
|
incr = 1;
|
|
}
|
|
else
|
|
{
|
|
p = fx1 * dy;
|
|
first = 0;
|
|
incr = -1;
|
|
dx = -dx;
|
|
}
|
|
XCG_FT_DIV_MOD(TCoord, p, dx, delta, mod);
|
|
ras.area += (TArea)(fx1 + first) * delta;
|
|
ras.cover += delta;
|
|
y1 += delta;
|
|
ex1 += incr;
|
|
gray_set_cell( RAS_VAR_ ex1, ey);
|
|
if(ex1 != ex2)
|
|
{
|
|
TCoord lift, rem;
|
|
p = ONE_PIXEL * dy;
|
|
XCG_FT_DIV_MOD(TCoord, p, dx, lift, rem);
|
|
do {
|
|
delta = lift;
|
|
mod += rem;
|
|
if(mod >= (TCoord)dx)
|
|
{
|
|
mod -= (TCoord)dx;
|
|
delta++;
|
|
}
|
|
ras.area += (TArea)( ONE_PIXEL * delta);
|
|
ras.cover += delta;
|
|
y1 += delta;
|
|
ex1 += incr;
|
|
gray_set_cell( RAS_VAR_ ex1, ey);
|
|
} while(ex1 != ex2);
|
|
}
|
|
fx1 = ONE_PIXEL - first;
|
|
End:
|
|
dy = y2 - y1;
|
|
ras.area += (TArea)((fx1 + fx2) * dy);
|
|
ras.cover += dy;
|
|
}
|
|
|
|
static void gray_render_line( RAS_ARG_ TPos to_x, TPos to_y)
|
|
{
|
|
TCoord ey1, ey2, fy1, fy2, first, delta, mod;
|
|
TPos p, dx, dy, x, x2;
|
|
int incr;
|
|
|
|
ey1 = TRUNC(ras.y);
|
|
ey2 = TRUNC(to_y);
|
|
if((ey1 >= ras.max_ey && ey2 >= ras.max_ey) || (ey1 < ras.min_ey && ey2 < ras.min_ey))
|
|
goto End;
|
|
fy1 = FRACT(ras.y);
|
|
fy2 = FRACT(to_y);
|
|
if(ey1 == ey2)
|
|
{
|
|
gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2);
|
|
goto End;
|
|
}
|
|
dx = to_x - ras.x;
|
|
dy = to_y - ras.y;
|
|
if(dx == 0)
|
|
{
|
|
TCoord ex = TRUNC(ras.x);
|
|
TCoord two_fx = FRACT( ras.x ) << 1;
|
|
TPos area, max_ey1;
|
|
|
|
if(dy > 0)
|
|
{
|
|
first = ONE_PIXEL;
|
|
}
|
|
else
|
|
{
|
|
first = 0;
|
|
}
|
|
delta = first - fy1;
|
|
ras.area += (TArea)two_fx * delta;
|
|
ras.cover += delta;
|
|
delta = first + first - ONE_PIXEL;
|
|
area = (TArea)two_fx * delta;
|
|
max_ey1 = ras.count_ey + ras.min_ey;
|
|
if(dy < 0)
|
|
{
|
|
if(ey1 > max_ey1)
|
|
{
|
|
ey1 = (max_ey1 > ey2) ? max_ey1 : ey2;
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
else
|
|
{
|
|
ey1--;
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
while(ey1 > ey2 && ey1 >= ras.min_ey)
|
|
{
|
|
ras.area += area;
|
|
ras.cover += delta;
|
|
ey1--;
|
|
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
if(ey1 != ey2)
|
|
{
|
|
ey1 = ey2;
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(ey1 < ras.min_ey)
|
|
{
|
|
ey1 = (ras.min_ey < ey2) ? ras.min_ey : ey2;
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
else
|
|
{
|
|
ey1++;
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
while(ey1 < ey2 && ey1 < max_ey1)
|
|
{
|
|
ras.area += area;
|
|
ras.cover += delta;
|
|
ey1++;
|
|
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
if(ey1 != ey2)
|
|
{
|
|
ey1 = ey2;
|
|
gray_set_cell(&ras, ex, ey1);
|
|
}
|
|
}
|
|
delta = (int)(fy2 - ONE_PIXEL + first);
|
|
ras.area += (TArea)two_fx * delta;
|
|
ras.cover += delta;
|
|
goto End;
|
|
}
|
|
if(dy > 0)
|
|
{
|
|
p = ( ONE_PIXEL - fy1) * dx;
|
|
first = ONE_PIXEL;
|
|
incr = 1;
|
|
}
|
|
else
|
|
{
|
|
p = fy1 * dx;
|
|
first = 0;
|
|
incr = -1;
|
|
dy = -dy;
|
|
}
|
|
XCG_FT_DIV_MOD(TCoord, p, dy, delta, mod);
|
|
x = ras.x + delta;
|
|
gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first);
|
|
ey1 += incr;
|
|
gray_set_cell( RAS_VAR_ TRUNC( x ), ey1);
|
|
if(ey1 != ey2)
|
|
{
|
|
TCoord lift, rem;
|
|
p = ONE_PIXEL * dx;
|
|
XCG_FT_DIV_MOD(TCoord, p, dy, lift, rem);
|
|
do {
|
|
delta = lift;
|
|
mod += rem;
|
|
if(mod >= (TCoord)dy)
|
|
{
|
|
mod -= (TCoord)dy;
|
|
delta++;
|
|
}
|
|
x2 = x + delta;
|
|
gray_render_scanline( RAS_VAR_ ey1, x, ONE_PIXEL - first, x2, first);
|
|
x = x2;
|
|
ey1 += incr;
|
|
gray_set_cell( RAS_VAR_ TRUNC( x ), ey1);
|
|
} while(ey1 != ey2);
|
|
}
|
|
gray_render_scanline( RAS_VAR_ ey1, x, ONE_PIXEL - first, to_x, fy2);
|
|
End:
|
|
ras.x = to_x;
|
|
ras.y = to_y;
|
|
}
|
|
|
|
static void gray_split_conic(XCG_FT_Vector * base)
|
|
{
|
|
TPos a, b;
|
|
|
|
base[4].x = base[2].x;
|
|
b = base[1].x;
|
|
a = base[3].x = (base[2].x + b) / 2;
|
|
b = base[1].x = (base[0].x + b) / 2;
|
|
base[2].x = (a + b) / 2;
|
|
|
|
base[4].y = base[2].y;
|
|
b = base[1].y;
|
|
a = base[3].y = (base[2].y + b) / 2;
|
|
b = base[1].y = (base[0].y + b) / 2;
|
|
base[2].y = (a + b) / 2;
|
|
}
|
|
|
|
static void gray_render_conic( RAS_ARG_ const XCG_FT_Vector * control, const XCG_FT_Vector * to)
|
|
{
|
|
XCG_FT_Vector bez_stack[16 * 2 + 1];
|
|
XCG_FT_Vector *arc = bez_stack;
|
|
TPos dx, dy;
|
|
int draw, split;
|
|
|
|
arc[0].x = UPSCALE(to->x);
|
|
arc[0].y = UPSCALE(to->y);
|
|
arc[1].x = UPSCALE(control->x);
|
|
arc[1].y = UPSCALE(control->y);
|
|
arc[2].x = ras.x;
|
|
arc[2].y = ras.y;
|
|
|
|
if(( TRUNC( arc[0].y ) >= ras.max_ey &&
|
|
TRUNC( arc[1].y ) >= ras.max_ey &&
|
|
TRUNC( arc[2].y ) >= ras.max_ey) || ( TRUNC( arc[0].y ) < ras.min_ey &&
|
|
TRUNC( arc[1].y ) < ras.min_ey &&
|
|
TRUNC( arc[2].y ) < ras.min_ey))
|
|
{
|
|
ras.x = arc[0].x;
|
|
ras.y = arc[0].y;
|
|
return;
|
|
}
|
|
dx = XCG_FT_ABS(arc[2].x + arc[0].x - 2 * arc[1].x);
|
|
dy = XCG_FT_ABS(arc[2].y + arc[0].y - 2 * arc[1].y);
|
|
if(dx < dy)
|
|
dx = dy;
|
|
draw = 1;
|
|
while(dx > ONE_PIXEL / 4)
|
|
{
|
|
dx >>= 2;
|
|
draw <<= 1;
|
|
}
|
|
do {
|
|
split = 1;
|
|
while((draw & split) == 0)
|
|
{
|
|
gray_split_conic(arc);
|
|
arc += 2;
|
|
split <<= 1;
|
|
}
|
|
gray_render_line( RAS_VAR_ arc[0].x, arc[0].y);
|
|
arc -= 2;
|
|
} while(--draw);
|
|
}
|
|
|
|
static void gray_split_cubic(XCG_FT_Vector * base)
|
|
{
|
|
TPos a, b, c, d;
|
|
|
|
base[6].x = base[3].x;
|
|
c = base[1].x;
|
|
d = base[2].x;
|
|
base[1].x = a = (base[0].x + c) / 2;
|
|
base[5].x = b = (base[3].x + d) / 2;
|
|
c = (c + d) / 2;
|
|
base[2].x = a = (a + c) / 2;
|
|
base[4].x = b = (b + c) / 2;
|
|
base[3].x = (a + b) / 2;
|
|
|
|
base[6].y = base[3].y;
|
|
c = base[1].y;
|
|
d = base[2].y;
|
|
base[1].y = a = (base[0].y + c) / 2;
|
|
base[5].y = b = (base[3].y + d) / 2;
|
|
c = (c + d) / 2;
|
|
base[2].y = a = (a + c) / 2;
|
|
base[4].y = b = (b + c) / 2;
|
|
base[3].y = (a + b) / 2;
|
|
}
|
|
|
|
static void gray_render_cubic(RAS_ARG_ const XCG_FT_Vector * control1, const XCG_FT_Vector * control2, const XCG_FT_Vector * to)
|
|
{
|
|
XCG_FT_Vector bez_stack[16 * 3 + 1];
|
|
XCG_FT_Vector * arc = bez_stack;
|
|
TPos dx, dy, dx_, dy_;
|
|
TPos dx1, dy1, dx2, dy2;
|
|
TPos L, s, s_limit;
|
|
|
|
arc[0].x = UPSCALE(to->x);
|
|
arc[0].y = UPSCALE(to->y);
|
|
arc[1].x = UPSCALE(control2->x);
|
|
arc[1].y = UPSCALE(control2->y);
|
|
arc[2].x = UPSCALE(control1->x);
|
|
arc[2].y = UPSCALE(control1->y);
|
|
arc[3].x = ras.x;
|
|
arc[3].y = ras.y;
|
|
|
|
if(( TRUNC( arc[0].y ) >= ras.max_ey &&
|
|
TRUNC( arc[1].y ) >= ras.max_ey &&
|
|
TRUNC( arc[2].y ) >= ras.max_ey &&
|
|
TRUNC( arc[3].y ) >= ras.max_ey) || ( TRUNC( arc[0].y ) < ras.min_ey &&
|
|
TRUNC( arc[1].y ) < ras.min_ey &&
|
|
TRUNC( arc[2].y ) < ras.min_ey &&
|
|
TRUNC( arc[3].y ) < ras.min_ey))
|
|
{
|
|
ras.x = arc[0].x;
|
|
ras.y = arc[0].y;
|
|
return;
|
|
}
|
|
for(;;)
|
|
{
|
|
dx = dx_ = arc[3].x - arc[0].x;
|
|
dy = dy_ = arc[3].y - arc[0].y;
|
|
L = XCG_FT_HYPOT(dx_, dy_);
|
|
if(L >= (1 << 23))
|
|
goto Split;
|
|
s_limit = L * (TPos)( ONE_PIXEL / 6);
|
|
dx1 = arc[1].x - arc[0].x;
|
|
dy1 = arc[1].y - arc[0].y;
|
|
s = XCG_FT_ABS(dy * dx1 - dx * dy1);
|
|
if(s > s_limit)
|
|
goto Split;
|
|
dx2 = arc[2].x - arc[0].x;
|
|
dy2 = arc[2].y - arc[0].y;
|
|
s = XCG_FT_ABS(dy * dx2 - dx * dy2);
|
|
if(s > s_limit)
|
|
goto Split;
|
|
if(dx1 * (dx1 - dx) + dy1 * (dy1 - dy) > 0 || dx2 * (dx2 - dx) + dy2 * (dy2 - dy) > 0)
|
|
goto Split;
|
|
gray_render_line( RAS_VAR_ arc[0].x, arc[0].y);
|
|
if(arc == bez_stack)
|
|
return;
|
|
arc -= 3;
|
|
continue;
|
|
Split:
|
|
gray_split_cubic(arc);
|
|
arc += 3;
|
|
}
|
|
}
|
|
|
|
static int gray_move_to(const XCG_FT_Vector *to, PWorker worker)
|
|
{
|
|
TPos x, y;
|
|
|
|
if(!ras.invalid)
|
|
gray_record_cell(worker);
|
|
x = UPSCALE(to->x);
|
|
y = UPSCALE(to->y);
|
|
gray_start_cell(worker, TRUNC(x), TRUNC(y));
|
|
ras.x = x;
|
|
ras.y = y;
|
|
return 0;
|
|
}
|
|
|
|
static void gray_hline( RAS_ARG_ TCoord x, TCoord y, TPos area, int acount)
|
|
{
|
|
int coverage;
|
|
|
|
coverage = (int)(area >> ( PIXEL_BITS * 2 + 1 - 8));
|
|
if(coverage < 0)
|
|
coverage = -coverage;
|
|
if( ras.outline.flags & XCG_FT_OUTLINE_EVEN_ODD_FILL)
|
|
{
|
|
coverage &= 511;
|
|
if(coverage > 256)
|
|
coverage = 512 - coverage;
|
|
else if(coverage == 256)
|
|
coverage = 255;
|
|
}
|
|
else
|
|
{
|
|
if(coverage >= 256)
|
|
coverage = 255;
|
|
}
|
|
y += (TCoord)ras.min_ey;
|
|
x += (TCoord)ras.min_ex;
|
|
if(x >= (1 << 23))
|
|
x = (1 << 23) - 1;
|
|
if(y >= (1 << 23))
|
|
y = (1 << 23) - 1;
|
|
if(coverage)
|
|
{
|
|
XCG_FT_Span *span;
|
|
int count;
|
|
int skip;
|
|
count = ras.num_gray_spans;
|
|
span = ras.gray_spans + count - 1;
|
|
if(count > 0 && span->y == y && span->x + span->len == x && span->coverage == coverage)
|
|
{
|
|
span->len = span->len + acount;
|
|
return;
|
|
}
|
|
if(count >= XCG_FT_MAX_GRAY_SPANS)
|
|
{
|
|
if( ras.render_span && count > ras.skip_spans)
|
|
{
|
|
skip = ras.skip_spans > 0 ? ras.skip_spans : 0;
|
|
ras.render_span( ras.num_gray_spans - skip,
|
|
ras.gray_spans + skip,
|
|
ras.render_span_data);
|
|
}
|
|
ras.skip_spans -= ras.num_gray_spans;
|
|
ras.num_gray_spans = 0;
|
|
span = ras.gray_spans;
|
|
}
|
|
else
|
|
span++;
|
|
span->x = x;
|
|
span->len = acount;
|
|
span->y = y;
|
|
span->coverage = (unsigned char)coverage;
|
|
ras.num_gray_spans++;
|
|
}
|
|
}
|
|
|
|
static void gray_sweep(RAS_ARG)
|
|
{
|
|
int yindex;
|
|
|
|
if( ras.num_cells == 0)
|
|
return;
|
|
for(yindex = 0; yindex < ras.ycount; yindex++)
|
|
{
|
|
PCell cell = ras.ycells[yindex];
|
|
TCoord cover = 0;
|
|
TCoord x = 0;
|
|
|
|
for(; cell != NULL; cell = cell->next)
|
|
{
|
|
TArea area;
|
|
if(cell->x > x && cover != 0)
|
|
gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2), cell->x - x);
|
|
cover += cell->cover;
|
|
area = cover * ( ONE_PIXEL * 2) - cell->area;
|
|
if(area != 0 && cell->x >= 0)
|
|
gray_hline( RAS_VAR_ cell->x, yindex, area, 1);
|
|
x = cell->x + 1;
|
|
}
|
|
if( ras.count_ex > x && cover != 0)
|
|
gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2), ras.count_ex - x);
|
|
}
|
|
}
|
|
|
|
static int XCG_FT_Outline_Decompose(const XCG_FT_Outline * outline, void * user)
|
|
{
|
|
#undef SCALED
|
|
#define SCALED( x ) (x)
|
|
XCG_FT_Vector v_last;
|
|
XCG_FT_Vector v_control;
|
|
XCG_FT_Vector v_start;
|
|
XCG_FT_Vector * point;
|
|
XCG_FT_Vector * limit;
|
|
char * tags;
|
|
int n;
|
|
int first;
|
|
int error;
|
|
char tag;
|
|
|
|
if(!outline)
|
|
return ErrRaster_Invalid_Outline;
|
|
first = 0;
|
|
for(n = 0; n < outline->n_contours; n++)
|
|
{
|
|
int last;
|
|
last = outline->contours[n];
|
|
if(last < 0)
|
|
goto Invalid_Outline;
|
|
limit = outline->points + last;
|
|
v_start = outline->points[first];
|
|
v_start.x = SCALED(v_start.x);
|
|
v_start.y = SCALED(v_start.y);
|
|
v_last = outline->points[last];
|
|
v_last.x = SCALED(v_last.x);
|
|
v_last.y = SCALED(v_last.y);
|
|
v_control = v_start;
|
|
point = outline->points + first;
|
|
tags = outline->tags + first;
|
|
tag = XCG_FT_CURVE_TAG(tags[0]);
|
|
if(tag == XCG_FT_CURVE_TAG_CUBIC)
|
|
goto Invalid_Outline;
|
|
if(tag == XCG_FT_CURVE_TAG_CONIC)
|
|
{
|
|
if( XCG_FT_CURVE_TAG( outline->tags[last] ) == XCG_FT_CURVE_TAG_ON)
|
|
{
|
|
v_start = v_last;
|
|
limit--;
|
|
}
|
|
else
|
|
{
|
|
v_start.x = (v_start.x + v_last.x) / 2;
|
|
v_start.y = (v_start.y + v_last.y) / 2;
|
|
v_last = v_start;
|
|
}
|
|
point--;
|
|
tags--;
|
|
}
|
|
error = gray_move_to(&v_start, user);
|
|
if(error)
|
|
goto Exit;
|
|
while(point < limit)
|
|
{
|
|
point++;
|
|
tags++;
|
|
tag = XCG_FT_CURVE_TAG(tags[0]);
|
|
switch(tag)
|
|
{
|
|
case XCG_FT_CURVE_TAG_ON:
|
|
{
|
|
XCG_FT_Vector vec;
|
|
vec.x = SCALED(point->x);
|
|
vec.y = SCALED(point->y);
|
|
gray_render_line(user, UPSCALE(vec.x), UPSCALE(vec.y));
|
|
continue;
|
|
}
|
|
case XCG_FT_CURVE_TAG_CONIC:
|
|
{
|
|
v_control.x = SCALED(point->x);
|
|
v_control.y = SCALED(point->y);
|
|
Do_Conic:
|
|
if(point < limit)
|
|
{
|
|
XCG_FT_Vector vec;
|
|
XCG_FT_Vector v_middle;
|
|
point++;
|
|
tags++;
|
|
tag = XCG_FT_CURVE_TAG(tags[0]);
|
|
vec.x = SCALED(point->x);
|
|
vec.y = SCALED(point->y);
|
|
if(tag == XCG_FT_CURVE_TAG_ON)
|
|
{
|
|
gray_render_conic(user, &v_control, &vec);
|
|
continue;
|
|
}
|
|
if(tag != XCG_FT_CURVE_TAG_CONIC)
|
|
goto Invalid_Outline;
|
|
v_middle.x = (v_control.x + vec.x) / 2;
|
|
v_middle.y = (v_control.y + vec.y) / 2;
|
|
gray_render_conic(user, &v_control, &v_middle);
|
|
v_control = vec;
|
|
goto Do_Conic;
|
|
}
|
|
gray_render_conic(user, &v_control, &v_start);
|
|
goto Close;
|
|
}
|
|
default:
|
|
{
|
|
XCG_FT_Vector vec1, vec2;
|
|
if(point + 1 > limit ||
|
|
XCG_FT_CURVE_TAG( tags[1] ) != XCG_FT_CURVE_TAG_CUBIC)
|
|
goto Invalid_Outline;
|
|
point += 2;
|
|
tags += 2;
|
|
vec1.x = SCALED(point[-2].x);
|
|
vec1.y = SCALED(point[-2].y);
|
|
vec2.x = SCALED(point[-1].x);
|
|
vec2.y = SCALED(point[-1].y);
|
|
if(point <= limit)
|
|
{
|
|
XCG_FT_Vector vec;
|
|
vec.x = SCALED(point->x);
|
|
vec.y = SCALED(point->y);
|
|
gray_render_cubic(user, &vec1, &vec2, &vec);
|
|
continue;
|
|
}
|
|
gray_render_cubic(user, &vec1, &vec2, &v_start);
|
|
goto Close;
|
|
}
|
|
}
|
|
}
|
|
gray_render_line(user, UPSCALE(v_start.x), UPSCALE(v_start.y));
|
|
Close:
|
|
first = last + 1;
|
|
}
|
|
return 0;
|
|
Exit:
|
|
return error;
|
|
Invalid_Outline:
|
|
return ErrRaster_Invalid_Outline;
|
|
}
|
|
|
|
typedef struct TBand_ {
|
|
TPos min, max;
|
|
} TBand;
|
|
|
|
static int gray_convert_glyph_inner(RAS_ARG)
|
|
{
|
|
volatile int error = 0;
|
|
|
|
if( xcg_ft_setjmp( ras.jump_buffer) == 0)
|
|
{
|
|
error = XCG_FT_Outline_Decompose(&ras.outline, &ras);
|
|
if(!ras.invalid)
|
|
gray_record_cell( RAS_VAR);
|
|
}
|
|
else
|
|
{
|
|
error = ErrRaster_Memory_Overflow;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int gray_convert_glyph(RAS_ARG)
|
|
{
|
|
TBand bands[40];
|
|
TBand *volatile band;
|
|
int volatile n, num_bands;
|
|
TPos volatile min, max, max_y;
|
|
XCG_FT_BBox *clip;
|
|
int skip;
|
|
|
|
ras.num_gray_spans = 0;
|
|
gray_compute_cbox( RAS_VAR);
|
|
clip = &ras.clip_box;
|
|
if( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax || ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax)
|
|
return 0;
|
|
if( ras.min_ex < clip->xMin)
|
|
ras.min_ex = clip->xMin;
|
|
if( ras.min_ey < clip->yMin)
|
|
ras.min_ey = clip->yMin;
|
|
if( ras.max_ex > clip->xMax)
|
|
ras.max_ex = clip->xMax;
|
|
if( ras.max_ey > clip->yMax)
|
|
ras.max_ey = clip->yMax;
|
|
ras.count_ex = ras.max_ex - ras.min_ex;
|
|
ras.count_ey = ras.max_ey - ras.min_ey;
|
|
num_bands = (int)(( ras.max_ey - ras.min_ey) / ras.band_size);
|
|
if(num_bands == 0)
|
|
num_bands = 1;
|
|
if(num_bands >= 39)
|
|
num_bands = 39;
|
|
ras.band_shoot = 0;
|
|
min = ras.min_ey;
|
|
max_y = ras.max_ey;
|
|
for(n = 0; n < num_bands; n++, min = max)
|
|
{
|
|
max = min + ras.band_size;
|
|
if(n == num_bands - 1 || max > max_y)
|
|
max = max_y;
|
|
bands[0].min = min;
|
|
bands[0].max = max;
|
|
band = bands;
|
|
while(band >= bands)
|
|
{
|
|
TPos bottom, top, middle;
|
|
int error;
|
|
{
|
|
PCell cells_max;
|
|
int yindex;
|
|
int cell_start, cell_end, cell_mod;
|
|
ras.ycells = (PCell*)ras.buffer;
|
|
ras.ycount = band->max - band->min;
|
|
cell_start = sizeof(PCell) * ras.ycount;
|
|
cell_mod = cell_start % sizeof(TCell);
|
|
if(cell_mod > 0)
|
|
cell_start += sizeof(TCell) - cell_mod;
|
|
cell_end = ras.buffer_size;
|
|
cell_end -= cell_end % sizeof(TCell);
|
|
cells_max = (PCell)((char*)ras.buffer + cell_end);
|
|
ras.cells = (PCell)((char*)ras.buffer + cell_start);
|
|
if( ras.cells >= cells_max)
|
|
goto ReduceBands;
|
|
ras.max_cells = (int)(cells_max - ras.cells);
|
|
if( ras.max_cells < 2)
|
|
goto ReduceBands;
|
|
for(yindex = 0; yindex < ras.ycount; yindex++)
|
|
ras.ycells[yindex] = NULL;
|
|
}
|
|
ras.num_cells = 0;
|
|
ras.invalid = 1;
|
|
ras.min_ey = band->min;
|
|
ras.max_ey = band->max;
|
|
ras.count_ey = band->max - band->min;
|
|
error = gray_convert_glyph_inner( RAS_VAR);
|
|
if(!error)
|
|
{
|
|
gray_sweep( RAS_VAR);
|
|
band--;
|
|
continue;
|
|
}
|
|
else if(error != ErrRaster_Memory_Overflow)
|
|
return 1;
|
|
ReduceBands:
|
|
bottom = band->min;
|
|
top = band->max;
|
|
middle = bottom + ((top - bottom) >> 1);
|
|
if(middle == bottom)
|
|
{
|
|
return ErrRaster_OutOfMemory;
|
|
}
|
|
if(bottom - top >= ras.band_size)
|
|
ras.band_shoot++;
|
|
band[1].min = bottom;
|
|
band[1].max = middle;
|
|
band[0].min = middle;
|
|
band[0].max = top;
|
|
band++;
|
|
}
|
|
}
|
|
if( ras.render_span && ras.num_gray_spans > ras.skip_spans)
|
|
{
|
|
skip = ras.skip_spans > 0 ? ras.skip_spans : 0;
|
|
ras.render_span( ras.num_gray_spans - skip,
|
|
ras.gray_spans + skip,
|
|
ras.render_span_data);
|
|
}
|
|
ras.skip_spans -= ras.num_gray_spans;
|
|
if( ras.band_shoot > 8 && ras.band_size > 16)
|
|
ras.band_size = ras.band_size / 2;
|
|
return 0;
|
|
}
|
|
|
|
static int gray_raster_render(RAS_ARG_ void * buffer, long buffer_size, const XCG_FT_Raster_Params * params)
|
|
{
|
|
const XCG_FT_Outline *outline = (const XCG_FT_Outline*)params->source;
|
|
if(outline == NULL)
|
|
return ErrRaster_Invalid_Outline;
|
|
if(outline->n_points == 0 || outline->n_contours <= 0)
|
|
return 0;
|
|
if(!outline->contours || !outline->points)
|
|
return ErrRaster_Invalid_Outline;
|
|
if(outline->n_points != outline->contours[outline->n_contours - 1] + 1)
|
|
return ErrRaster_Invalid_Outline;
|
|
if(!(params->flags & XCG_FT_RASTER_FLAG_AA))
|
|
return ErrRaster_Invalid_Mode;
|
|
if(!(params->flags & XCG_FT_RASTER_FLAG_DIRECT))
|
|
return ErrRaster_Invalid_Mode;
|
|
if(params->flags & XCG_FT_RASTER_FLAG_CLIP)
|
|
{
|
|
ras.clip_box = params->clip_box;
|
|
}
|
|
else
|
|
{
|
|
ras.clip_box.xMin = -(1 << 23);
|
|
ras.clip_box.yMin = -(1 << 23);
|
|
ras.clip_box.xMax = (1 << 23) - 1;
|
|
ras.clip_box.yMax = (1 << 23) - 1;
|
|
}
|
|
gray_init_cells( RAS_VAR_ buffer, buffer_size);
|
|
ras.outline = *outline;
|
|
ras.num_cells = 0;
|
|
ras.invalid = 1;
|
|
ras.band_size = (int)(buffer_size / (long)(sizeof(TCell) * 8));
|
|
ras.render_span = (XCG_FT_Raster_Span_Func)params->gray_spans;
|
|
ras.render_span_data = params->user;
|
|
return gray_convert_glyph( RAS_VAR);
|
|
}
|
|
|
|
void XCG_FT_Raster_Render(const XCG_FT_Raster_Params * params)
|
|
{
|
|
char stack[XCG_FT_MINIMUM_POOL_SIZE];
|
|
size_t length = XCG_FT_MINIMUM_POOL_SIZE;
|
|
|
|
TWorker worker;
|
|
worker.skip_spans = 0;
|
|
int rendered_spans = 0;
|
|
int error = gray_raster_render(&worker, stack, length, params);
|
|
while(error == ErrRaster_OutOfMemory)
|
|
{
|
|
if(worker.skip_spans < 0)
|
|
rendered_spans += -worker.skip_spans;
|
|
worker.skip_spans = rendered_spans;
|
|
length *= 2;
|
|
void *heap = malloc(length);
|
|
error = gray_raster_render(&worker, heap, length, params);
|
|
free(heap);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* stroker
|
|
*/
|
|
#define XCG_FT_SMALL_CONIC_THRESHOLD (XCG_FT_ANGLE_PI / 6)
|
|
#define XCG_FT_SMALL_CUBIC_THRESHOLD (XCG_FT_ANGLE_PI / 8)
|
|
#define XCG_FT_IS_SMALL(x) ((x) > -2 && (x) < 2)
|
|
|
|
static XCG_FT_Pos ft_pos_abs(XCG_FT_Pos x)
|
|
{
|
|
return x >= 0 ? x : -x;
|
|
}
|
|
|
|
static void ft_conic_split(XCG_FT_Vector *base)
|
|
{
|
|
XCG_FT_Pos a, b;
|
|
|
|
base[4].x = base[2].x;
|
|
a = base[0].x + base[1].x;
|
|
b = base[1].x + base[2].x;
|
|
base[3].x = b >> 1;
|
|
base[2].x = (a + b) >> 2;
|
|
base[1].x = a >> 1;
|
|
|
|
base[4].y = base[2].y;
|
|
a = base[0].y + base[1].y;
|
|
b = base[1].y + base[2].y;
|
|
base[3].y = b >> 1;
|
|
base[2].y = (a + b) >> 2;
|
|
base[1].y = a >> 1;
|
|
}
|
|
|
|
static XCG_FT_Bool ft_conic_is_small_enough(XCG_FT_Vector *base, XCG_FT_Angle *angle_in, XCG_FT_Angle *angle_out)
|
|
{
|
|
XCG_FT_Vector d1, d2;
|
|
XCG_FT_Angle theta;
|
|
XCG_FT_Int close1, close2;
|
|
|
|
d1.x = base[1].x - base[2].x;
|
|
d1.y = base[1].y - base[2].y;
|
|
d2.x = base[0].x - base[1].x;
|
|
d2.y = base[0].y - base[1].y;
|
|
close1 = XCG_FT_IS_SMALL(d1.x) && XCG_FT_IS_SMALL(d1.y);
|
|
close2 = XCG_FT_IS_SMALL(d2.x) && XCG_FT_IS_SMALL(d2.y);
|
|
if(close1)
|
|
{
|
|
if(close2)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
*angle_in = *angle_out = XCG_FT_Atan2(d2.x, d2.y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(close2)
|
|
{
|
|
*angle_in = *angle_out = XCG_FT_Atan2(d1.x, d1.y);
|
|
}
|
|
else
|
|
{
|
|
*angle_in = XCG_FT_Atan2(d1.x, d1.y);
|
|
*angle_out = XCG_FT_Atan2(d2.x, d2.y);
|
|
}
|
|
}
|
|
theta = ft_pos_abs(XCG_FT_Angle_Diff(*angle_in, *angle_out));
|
|
return XCG_FT_BOOL(theta < XCG_FT_SMALL_CONIC_THRESHOLD);
|
|
}
|
|
|
|
static void ft_cubic_split(XCG_FT_Vector * base)
|
|
{
|
|
XCG_FT_Pos a, b, c;
|
|
|
|
base[6].x = base[3].x;
|
|
a = base[0].x + base[1].x;
|
|
b = base[1].x + base[2].x;
|
|
c = base[2].x + base[3].x;
|
|
base[5].x = c >> 1;
|
|
c += b;
|
|
base[4].x = c >> 2;
|
|
base[1].x = a >> 1;
|
|
a += b;
|
|
base[2].x = a >> 2;
|
|
base[3].x = (a + c) >> 3;
|
|
|
|
base[6].y = base[3].y;
|
|
a = base[0].y + base[1].y;
|
|
b = base[1].y + base[2].y;
|
|
c = base[2].y + base[3].y;
|
|
base[5].y = c >> 1;
|
|
c += b;
|
|
base[4].y = c >> 2;
|
|
base[1].y = a >> 1;
|
|
a += b;
|
|
base[2].y = a >> 2;
|
|
base[3].y = (a + c) >> 3;
|
|
}
|
|
|
|
static XCG_FT_Angle ft_angle_mean(XCG_FT_Angle angle1, XCG_FT_Angle angle2)
|
|
{
|
|
return angle1 + XCG_FT_Angle_Diff(angle1, angle2) / 2;
|
|
}
|
|
|
|
static XCG_FT_Bool ft_cubic_is_small_enough(XCG_FT_Vector *base, XCG_FT_Angle *angle_in, XCG_FT_Angle *angle_mid, XCG_FT_Angle *angle_out)
|
|
{
|
|
XCG_FT_Vector d1, d2, d3;
|
|
XCG_FT_Angle theta1, theta2;
|
|
XCG_FT_Int close1, close2, close3;
|
|
|
|
d1.x = base[2].x - base[3].x;
|
|
d1.y = base[2].y - base[3].y;
|
|
d2.x = base[1].x - base[2].x;
|
|
d2.y = base[1].y - base[2].y;
|
|
d3.x = base[0].x - base[1].x;
|
|
d3.y = base[0].y - base[1].y;
|
|
close1 = XCG_FT_IS_SMALL(d1.x) && XCG_FT_IS_SMALL(d1.y);
|
|
close2 = XCG_FT_IS_SMALL(d2.x) && XCG_FT_IS_SMALL(d2.y);
|
|
close3 = XCG_FT_IS_SMALL(d3.x) && XCG_FT_IS_SMALL(d3.y);
|
|
|
|
if(close1)
|
|
{
|
|
if(close2)
|
|
{
|
|
if(close3)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
*angle_in = *angle_mid = *angle_out = XCG_FT_Atan2(d3.x, d3.y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(close3)
|
|
{
|
|
*angle_in = *angle_mid = *angle_out = XCG_FT_Atan2(d2.x, d2.y);
|
|
}
|
|
else
|
|
{
|
|
*angle_in = *angle_mid = XCG_FT_Atan2(d2.x, d2.y);
|
|
*angle_out = XCG_FT_Atan2(d3.x, d3.y);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(close2)
|
|
{
|
|
if(close3)
|
|
{
|
|
*angle_in = *angle_mid = *angle_out = XCG_FT_Atan2(d1.x, d1.y);
|
|
}
|
|
else
|
|
{
|
|
*angle_in = XCG_FT_Atan2(d1.x, d1.y);
|
|
*angle_out = XCG_FT_Atan2(d3.x, d3.y);
|
|
*angle_mid = ft_angle_mean(*angle_in, *angle_out);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(close3)
|
|
{
|
|
*angle_in = XCG_FT_Atan2(d1.x, d1.y);
|
|
*angle_mid = *angle_out = XCG_FT_Atan2(d2.x, d2.y);
|
|
}
|
|
else
|
|
{
|
|
*angle_in = XCG_FT_Atan2(d1.x, d1.y);
|
|
*angle_mid = XCG_FT_Atan2(d2.x, d2.y);
|
|
*angle_out = XCG_FT_Atan2(d3.x, d3.y);
|
|
}
|
|
}
|
|
}
|
|
theta1 = ft_pos_abs(XCG_FT_Angle_Diff(*angle_in, *angle_mid));
|
|
theta2 = ft_pos_abs(XCG_FT_Angle_Diff(*angle_mid, *angle_out));
|
|
return XCG_FT_BOOL(theta1 < XCG_FT_SMALL_CUBIC_THRESHOLD && theta2 < XCG_FT_SMALL_CUBIC_THRESHOLD);
|
|
}
|
|
|
|
typedef enum XCG_FT_StrokeTags_ {
|
|
XCG_FT_STROKE_TAG_ON = 1,
|
|
XCG_FT_STROKE_TAG_CUBIC = 2,
|
|
XCG_FT_STROKE_TAG_BEGIN = 4,
|
|
XCG_FT_STROKE_TAG_END = 8,
|
|
} XCG_FT_StrokeTags;
|
|
|
|
#define XCG_FT_STROKE_TAG_BEGIN_END (XCG_FT_STROKE_TAG_BEGIN | XCG_FT_STROKE_TAG_END)
|
|
|
|
typedef struct XCG_FT_StrokeBorderRec_ {
|
|
XCG_FT_UInt num_points;
|
|
XCG_FT_UInt max_points;
|
|
XCG_FT_Vector * points;
|
|
XCG_FT_Byte * tags;
|
|
XCG_FT_Bool movable;
|
|
XCG_FT_Int start;
|
|
XCG_FT_Bool valid;
|
|
} XCG_FT_StrokeBorderRec, *XCG_FT_StrokeBorder;
|
|
|
|
XCG_FT_Error XCG_FT_Outline_Check(XCG_FT_Outline * outline)
|
|
{
|
|
if(outline)
|
|
{
|
|
XCG_FT_Int n_points = outline->n_points;
|
|
XCG_FT_Int n_contours = outline->n_contours;
|
|
XCG_FT_Int end0, end;
|
|
XCG_FT_Int n;
|
|
|
|
if(n_points == 0 && n_contours == 0)
|
|
return 0;
|
|
if(n_points <= 0 || n_contours <= 0)
|
|
goto Bad;
|
|
end0 = end = -1;
|
|
for(n = 0; n < n_contours; n++)
|
|
{
|
|
end = outline->contours[n];
|
|
if(end <= end0 || end >= n_points)
|
|
goto Bad;
|
|
end0 = end;
|
|
}
|
|
if(end != n_points - 1)
|
|
goto Bad;
|
|
return 0;
|
|
}
|
|
Bad:
|
|
return -1;
|
|
}
|
|
|
|
void XCG_FT_Outline_Get_CBox(const XCG_FT_Outline * outline, XCG_FT_BBox * acbox)
|
|
{
|
|
XCG_FT_Pos xMin, yMin, xMax, yMax;
|
|
|
|
if(outline && acbox)
|
|
{
|
|
if(outline->n_points == 0)
|
|
{
|
|
xMin = 0;
|
|
yMin = 0;
|
|
xMax = 0;
|
|
yMax = 0;
|
|
}
|
|
else
|
|
{
|
|
XCG_FT_Vector *vec = outline->points;
|
|
XCG_FT_Vector *limit = vec + outline->n_points;
|
|
xMin = xMax = vec->x;
|
|
yMin = yMax = vec->y;
|
|
vec++;
|
|
for(; vec < limit; vec++)
|
|
{
|
|
XCG_FT_Pos x, y;
|
|
x = vec->x;
|
|
if(x < xMin)
|
|
xMin = x;
|
|
if(x > xMax)
|
|
xMax = x;
|
|
y = vec->y;
|
|
if(y < yMin)
|
|
yMin = y;
|
|
if(y > yMax)
|
|
yMax = y;
|
|
}
|
|
}
|
|
acbox->xMin = xMin;
|
|
acbox->xMax = xMax;
|
|
acbox->yMin = yMin;
|
|
acbox->yMax = yMax;
|
|
}
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroke_border_grow(XCG_FT_StrokeBorder border, XCG_FT_UInt new_points)
|
|
{
|
|
XCG_FT_UInt old_max = border->max_points;
|
|
XCG_FT_UInt new_max = border->num_points + new_points;
|
|
XCG_FT_Error error = 0;
|
|
|
|
if(new_max > old_max)
|
|
{
|
|
XCG_FT_UInt cur_max = old_max;
|
|
while(cur_max < new_max)
|
|
cur_max += (cur_max >> 1) + 16;
|
|
border->points = (XCG_FT_Vector*)realloc(border->points, cur_max * sizeof(XCG_FT_Vector));
|
|
border->tags = (XCG_FT_Byte*)realloc(border->tags, cur_max * sizeof(XCG_FT_Byte));
|
|
if(!border->points || !border->tags)
|
|
goto Exit;
|
|
border->max_points = cur_max;
|
|
}
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
static void ft_stroke_border_close(XCG_FT_StrokeBorder border, XCG_FT_Bool reverse)
|
|
{
|
|
XCG_FT_UInt start = border->start;
|
|
XCG_FT_UInt count = border->num_points;
|
|
|
|
if(count <= start + 1U)
|
|
border->num_points = start;
|
|
else
|
|
{
|
|
border->num_points = --count;
|
|
border->points[start] = border->points[count];
|
|
border->tags[start] = border->tags[count];
|
|
if(reverse)
|
|
{
|
|
{
|
|
XCG_FT_Vector *vec1 = border->points + start + 1;
|
|
XCG_FT_Vector *vec2 = border->points + count - 1;
|
|
for(; vec1 < vec2; vec1++, vec2--)
|
|
{
|
|
XCG_FT_Vector tmp;
|
|
tmp = *vec1;
|
|
*vec1 = *vec2;
|
|
*vec2 = tmp;
|
|
}
|
|
}
|
|
{
|
|
XCG_FT_Byte *tag1 = border->tags + start + 1;
|
|
XCG_FT_Byte *tag2 = border->tags + count - 1;
|
|
|
|
for(; tag1 < tag2; tag1++, tag2--)
|
|
{
|
|
XCG_FT_Byte tmp;
|
|
tmp = *tag1;
|
|
*tag1 = *tag2;
|
|
*tag2 = tmp;
|
|
}
|
|
}
|
|
}
|
|
border->tags[start] |= XCG_FT_STROKE_TAG_BEGIN;
|
|
border->tags[count - 1] |= XCG_FT_STROKE_TAG_END;
|
|
}
|
|
border->start = -1;
|
|
border->movable = FALSE;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroke_border_lineto(XCG_FT_StrokeBorder border, XCG_FT_Vector * to, XCG_FT_Bool movable)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
|
|
if(border->movable)
|
|
{
|
|
border->points[border->num_points - 1] = *to;
|
|
}
|
|
else
|
|
{
|
|
if(border->num_points > 0&&
|
|
XCG_FT_IS_SMALL(border->points[border->num_points - 1].x - to->x) &&
|
|
XCG_FT_IS_SMALL(border->points[border->num_points - 1].y - to->y))
|
|
return error;
|
|
error = ft_stroke_border_grow(border, 1);
|
|
if(!error)
|
|
{
|
|
XCG_FT_Vector *vec = border->points + border->num_points;
|
|
XCG_FT_Byte *tag = border->tags + border->num_points;
|
|
vec[0] = *to;
|
|
tag[0] = XCG_FT_STROKE_TAG_ON;
|
|
border->num_points += 1;
|
|
}
|
|
}
|
|
border->movable = movable;
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroke_border_conicto(XCG_FT_StrokeBorder border, XCG_FT_Vector * control, XCG_FT_Vector * to)
|
|
{
|
|
XCG_FT_Error error;
|
|
|
|
error = ft_stroke_border_grow(border, 2);
|
|
if(!error)
|
|
{
|
|
XCG_FT_Vector *vec = border->points + border->num_points;
|
|
XCG_FT_Byte *tag = border->tags + border->num_points;
|
|
|
|
vec[0] = *control;
|
|
vec[1] = *to;
|
|
tag[0] = 0;
|
|
tag[1] = XCG_FT_STROKE_TAG_ON;
|
|
border->num_points += 2;
|
|
}
|
|
border->movable = FALSE;
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroke_border_cubicto(XCG_FT_StrokeBorder border, XCG_FT_Vector * control1, XCG_FT_Vector * control2, XCG_FT_Vector * to)
|
|
{
|
|
XCG_FT_Error error;
|
|
|
|
error = ft_stroke_border_grow(border, 3);
|
|
if(!error)
|
|
{
|
|
XCG_FT_Vector *vec = border->points + border->num_points;
|
|
XCG_FT_Byte *tag = border->tags + border->num_points;
|
|
|
|
vec[0] = *control1;
|
|
vec[1] = *control2;
|
|
vec[2] = *to;
|
|
tag[0] = XCG_FT_STROKE_TAG_CUBIC;
|
|
tag[1] = XCG_FT_STROKE_TAG_CUBIC;
|
|
tag[2] = XCG_FT_STROKE_TAG_ON;
|
|
border->num_points += 3;
|
|
}
|
|
border->movable = FALSE;
|
|
return error;
|
|
}
|
|
|
|
#define XCG_FT_ARC_CUBIC_ANGLE (XCG_FT_ANGLE_PI / 2)
|
|
|
|
static XCG_FT_Error ft_stroke_border_arcto(XCG_FT_StrokeBorder border, XCG_FT_Vector *center, XCG_FT_Fixed radius, XCG_FT_Angle angle_start, XCG_FT_Angle angle_diff)
|
|
{
|
|
XCG_FT_Fixed coef;
|
|
XCG_FT_Vector a0, a1, a2, a3;
|
|
XCG_FT_Int i, arcs = 1;
|
|
XCG_FT_Error error = 0;
|
|
|
|
while(angle_diff > XCG_FT_ARC_CUBIC_ANGLE * arcs || -angle_diff > XCG_FT_ARC_CUBIC_ANGLE * arcs)
|
|
arcs++;
|
|
coef = XCG_FT_Tan(angle_diff / (4 * arcs));
|
|
coef += coef / 3;
|
|
XCG_FT_Vector_From_Polar(&a0, radius, angle_start);
|
|
a1.x = XCG_FT_MulFix(-a0.y, coef);
|
|
a1.y = XCG_FT_MulFix(a0.x, coef);
|
|
a0.x += center->x;
|
|
a0.y += center->y;
|
|
a1.x += a0.x;
|
|
a1.y += a0.y;
|
|
for(i = 1; i <= arcs; i++)
|
|
{
|
|
XCG_FT_Vector_From_Polar(&a3, radius, angle_start + i * angle_diff / arcs);
|
|
a2.x = XCG_FT_MulFix(a3.y, coef);
|
|
a2.y = XCG_FT_MulFix(-a3.x, coef);
|
|
a3.x += center->x;
|
|
a3.y += center->y;
|
|
a2.x += a3.x;
|
|
a2.y += a3.y;
|
|
error = ft_stroke_border_cubicto(border, &a1, &a2, &a3);
|
|
if(error)
|
|
break;
|
|
a1.x = a3.x - a2.x + a3.x;
|
|
a1.y = a3.y - a2.y + a3.y;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroke_border_moveto(XCG_FT_StrokeBorder border, XCG_FT_Vector * to)
|
|
{
|
|
if(border->start >= 0)
|
|
ft_stroke_border_close(border, FALSE);
|
|
border->start = border->num_points;
|
|
border->movable = FALSE;
|
|
return ft_stroke_border_lineto(border, to, FALSE);
|
|
}
|
|
|
|
static void ft_stroke_border_init(XCG_FT_StrokeBorder border)
|
|
{
|
|
border->points = NULL;
|
|
border->tags = NULL;
|
|
border->num_points = 0;
|
|
border->max_points = 0;
|
|
border->start = -1;
|
|
border->valid = FALSE;
|
|
}
|
|
|
|
static void ft_stroke_border_reset(XCG_FT_StrokeBorder border)
|
|
{
|
|
border->num_points = 0;
|
|
border->start = -1;
|
|
border->valid = FALSE;
|
|
}
|
|
|
|
static void ft_stroke_border_done(XCG_FT_StrokeBorder border)
|
|
{
|
|
free(border->points);
|
|
free(border->tags);
|
|
|
|
border->num_points = 0;
|
|
border->max_points = 0;
|
|
border->start = -1;
|
|
border->valid = FALSE;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroke_border_get_counts(XCG_FT_StrokeBorder border, XCG_FT_UInt * anum_points, XCG_FT_UInt * anum_contours)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_UInt num_points = 0;
|
|
XCG_FT_UInt num_contours = 0;
|
|
XCG_FT_UInt count = border->num_points;
|
|
XCG_FT_Vector *point = border->points;
|
|
XCG_FT_Byte *tags = border->tags;
|
|
XCG_FT_Int in_contour = 0;
|
|
|
|
for(; count > 0; count--, num_points++, point++, tags++)
|
|
{
|
|
if(tags[0] & XCG_FT_STROKE_TAG_BEGIN)
|
|
{
|
|
if(in_contour != 0)
|
|
goto Fail;
|
|
in_contour = 1;
|
|
}
|
|
else if(in_contour == 0)
|
|
goto Fail;
|
|
if(tags[0] & XCG_FT_STROKE_TAG_END)
|
|
{
|
|
in_contour = 0;
|
|
num_contours++;
|
|
}
|
|
}
|
|
if(in_contour != 0)
|
|
goto Fail;
|
|
border->valid = TRUE;
|
|
Exit:
|
|
*anum_points = num_points;
|
|
*anum_contours = num_contours;
|
|
return error;
|
|
Fail:
|
|
num_points = 0;
|
|
num_contours = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
static void ft_stroke_border_export(XCG_FT_StrokeBorder border, XCG_FT_Outline * outline)
|
|
{
|
|
if(outline->points != NULL && border->points != NULL)
|
|
memcpy(outline->points + outline->n_points, border->points, border->num_points * sizeof(XCG_FT_Vector));
|
|
if(outline->tags)
|
|
{
|
|
XCG_FT_UInt count = border->num_points;
|
|
XCG_FT_Byte *read = border->tags;
|
|
XCG_FT_Byte *write = (XCG_FT_Byte*)outline->tags + outline->n_points;
|
|
for(; count > 0; count--, read++, write++)
|
|
{
|
|
if(*read & XCG_FT_STROKE_TAG_ON)
|
|
*write = XCG_FT_CURVE_TAG_ON;
|
|
else if(*read & XCG_FT_STROKE_TAG_CUBIC)
|
|
*write = XCG_FT_CURVE_TAG_CUBIC;
|
|
else
|
|
*write = XCG_FT_CURVE_TAG_CONIC;
|
|
}
|
|
}
|
|
if(outline->contours)
|
|
{
|
|
XCG_FT_UInt count = border->num_points;
|
|
XCG_FT_Byte *tags = border->tags;
|
|
XCG_FT_Int *write = outline->contours + outline->n_contours;
|
|
XCG_FT_Int idx = (XCG_FT_Int)outline->n_points;
|
|
for(; count > 0; count--, tags++, idx++)
|
|
{
|
|
if(*tags & XCG_FT_STROKE_TAG_END)
|
|
{
|
|
*write++ = idx;
|
|
outline->n_contours++;
|
|
}
|
|
}
|
|
}
|
|
outline->n_points = (int)(outline->n_points + border->num_points);
|
|
XCG_FT_Outline_Check(outline);
|
|
}
|
|
|
|
#define XCG_FT_SIDE_TO_ROTATE(s) (XCG_FT_ANGLE_PI2 - (s) * XCG_FT_ANGLE_PI)
|
|
|
|
typedef struct XCG_FT_StrokerRec_ {
|
|
XCG_FT_Angle angle_in;
|
|
XCG_FT_Angle angle_out;
|
|
XCG_FT_Vector center;
|
|
XCG_FT_Fixed line_length;
|
|
XCG_FT_Bool first_point;
|
|
XCG_FT_Bool subpath_open;
|
|
XCG_FT_Angle subpath_angle;
|
|
XCG_FT_Vector subpath_start;
|
|
XCG_FT_Fixed subpath_line_length;
|
|
XCG_FT_Bool handle_wide_strokes;
|
|
XCG_FT_Stroker_LineCap line_cap;
|
|
XCG_FT_Stroker_LineJoin line_join;
|
|
XCG_FT_Stroker_LineJoin line_join_saved;
|
|
XCG_FT_Fixed miter_limit;
|
|
XCG_FT_Fixed radius;
|
|
XCG_FT_StrokeBorderRec borders[2];
|
|
} XCG_FT_StrokerRec;
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_New(XCG_FT_Stroker * astroker)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_Stroker stroker = NULL;
|
|
stroker = (XCG_FT_StrokerRec*)calloc(1, sizeof(XCG_FT_StrokerRec));
|
|
if(stroker)
|
|
{
|
|
ft_stroke_border_init(&stroker->borders[0]);
|
|
ft_stroke_border_init(&stroker->borders[1]);
|
|
}
|
|
*astroker = stroker;
|
|
return error;
|
|
}
|
|
|
|
void XCG_FT_Stroker_Rewind(XCG_FT_Stroker stroker)
|
|
{
|
|
if(stroker)
|
|
{
|
|
ft_stroke_border_reset(&stroker->borders[0]);
|
|
ft_stroke_border_reset(&stroker->borders[1]);
|
|
}
|
|
}
|
|
|
|
void XCG_FT_Stroker_Set(XCG_FT_Stroker stroker, XCG_FT_Fixed radius, XCG_FT_Stroker_LineCap line_cap, XCG_FT_Stroker_LineJoin line_join, XCG_FT_Fixed miter_limit)
|
|
{
|
|
stroker->radius = radius;
|
|
stroker->line_cap = line_cap;
|
|
stroker->line_join = line_join;
|
|
stroker->miter_limit = miter_limit;
|
|
if(stroker->miter_limit < 0x10000)
|
|
stroker->miter_limit = 0x10000;
|
|
stroker->line_join_saved = line_join;
|
|
XCG_FT_Stroker_Rewind(stroker);
|
|
}
|
|
|
|
void XCG_FT_Stroker_Done(XCG_FT_Stroker stroker)
|
|
{
|
|
if(stroker)
|
|
{
|
|
ft_stroke_border_done(&stroker->borders[0]);
|
|
ft_stroke_border_done(&stroker->borders[1]);
|
|
free(stroker);
|
|
}
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroker_arcto(XCG_FT_Stroker stroker, XCG_FT_Int side)
|
|
{
|
|
XCG_FT_Angle total, rotate;
|
|
XCG_FT_Fixed radius = stroker->radius;
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_StrokeBorder border = stroker->borders + side;
|
|
|
|
rotate = XCG_FT_SIDE_TO_ROTATE(side);
|
|
total = XCG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out);
|
|
if(total == XCG_FT_ANGLE_PI)
|
|
total = -rotate * 2;
|
|
error = ft_stroke_border_arcto(border, &stroker->center, radius, stroker->angle_in + rotate, total);
|
|
border->movable = FALSE;
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroker_cap(XCG_FT_Stroker stroker, XCG_FT_Angle angle, XCG_FT_Int side)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
|
|
if(stroker->line_cap == XCG_FT_STROKER_LINECAP_ROUND)
|
|
{
|
|
stroker->angle_in = angle;
|
|
stroker->angle_out = angle + XCG_FT_ANGLE_PI;
|
|
error = ft_stroker_arcto(stroker, side);
|
|
}
|
|
else
|
|
{
|
|
XCG_FT_Vector middle, delta;
|
|
XCG_FT_Fixed radius = stroker->radius;
|
|
XCG_FT_StrokeBorder border = stroker->borders + side;
|
|
XCG_FT_Vector_From_Polar(&middle, radius, angle);
|
|
delta.x = side ? middle.y : -middle.y;
|
|
delta.y = side ? -middle.x : middle.x;
|
|
if(stroker->line_cap == XCG_FT_STROKER_LINECAP_SQUARE)
|
|
{
|
|
middle.x += stroker->center.x;
|
|
middle.y += stroker->center.y;
|
|
}
|
|
else
|
|
{
|
|
middle.x = stroker->center.x;
|
|
middle.y = stroker->center.y;
|
|
}
|
|
delta.x += middle.x;
|
|
delta.y += middle.y;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
delta.x = middle.x - delta.x + middle.x;
|
|
delta.y = middle.y - delta.y + middle.y;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
}
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroker_inside(XCG_FT_Stroker stroker, XCG_FT_Int side, XCG_FT_Fixed line_length)
|
|
{
|
|
XCG_FT_StrokeBorder border = stroker->borders + side;
|
|
XCG_FT_Angle phi, theta, rotate;
|
|
XCG_FT_Fixed length;
|
|
XCG_FT_Vector sigma = { 0, 0 };
|
|
XCG_FT_Vector delta;
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_Bool intersect;
|
|
|
|
rotate = XCG_FT_SIDE_TO_ROTATE(side);
|
|
theta = XCG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out) / 2;
|
|
if(!border->movable || line_length == 0 || theta > 0x59C000 || theta < -0x59C000)
|
|
intersect = FALSE;
|
|
else
|
|
{
|
|
XCG_FT_Fixed min_length;
|
|
XCG_FT_Vector_Unit(&sigma, theta);
|
|
min_length = ft_pos_abs(XCG_FT_MulDiv(stroker->radius, sigma.y, sigma.x));
|
|
intersect = XCG_FT_BOOL(min_length && stroker->line_length >= min_length && line_length >= min_length);
|
|
}
|
|
if(!intersect)
|
|
{
|
|
XCG_FT_Vector_From_Polar(&delta, stroker->radius, stroker->angle_out + rotate);
|
|
delta.x += stroker->center.x;
|
|
delta.y += stroker->center.y;
|
|
border->movable = FALSE;
|
|
}
|
|
else
|
|
{
|
|
phi = stroker->angle_in + theta + rotate;
|
|
length = XCG_FT_DivFix(stroker->radius, sigma.x);
|
|
XCG_FT_Vector_From_Polar(&delta, length, phi);
|
|
delta.x += stroker->center.x;
|
|
delta.y += stroker->center.y;
|
|
}
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroker_outside(XCG_FT_Stroker stroker, XCG_FT_Int side, XCG_FT_Fixed line_length)
|
|
{
|
|
XCG_FT_StrokeBorder border = stroker->borders + side;
|
|
XCG_FT_Error error;
|
|
XCG_FT_Angle rotate;
|
|
|
|
if(stroker->line_join == XCG_FT_STROKER_LINEJOIN_ROUND)
|
|
error = ft_stroker_arcto(stroker, side);
|
|
else
|
|
{
|
|
XCG_FT_Fixed radius = stroker->radius;
|
|
XCG_FT_Vector sigma = { 0, 0 };
|
|
XCG_FT_Angle theta = 0, phi = 0;
|
|
XCG_FT_Bool bevel, fixed_bevel;
|
|
|
|
rotate = XCG_FT_SIDE_TO_ROTATE(side);
|
|
bevel = XCG_FT_BOOL(stroker->line_join == XCG_FT_STROKER_LINEJOIN_BEVEL);
|
|
fixed_bevel = XCG_FT_BOOL(stroker->line_join != XCG_FT_STROKER_LINEJOIN_MITER_VARIABLE);
|
|
|
|
if(!bevel)
|
|
{
|
|
theta = XCG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out) / 2;
|
|
if(theta == XCG_FT_ANGLE_PI2)
|
|
theta = -rotate;
|
|
phi = stroker->angle_in + theta + rotate;
|
|
XCG_FT_Vector_From_Polar(&sigma, stroker->miter_limit, theta);
|
|
if(sigma.x < 0x10000L)
|
|
{
|
|
if(fixed_bevel || ft_pos_abs(theta) > 57)
|
|
bevel = TRUE;
|
|
}
|
|
}
|
|
if(bevel)
|
|
{
|
|
if(fixed_bevel)
|
|
{
|
|
XCG_FT_Vector delta;
|
|
XCG_FT_Vector_From_Polar(&delta, radius, stroker->angle_out + rotate);
|
|
delta.x += stroker->center.x;
|
|
delta.y += stroker->center.y;
|
|
border->movable = FALSE;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
}
|
|
else
|
|
{
|
|
XCG_FT_Vector middle, delta;
|
|
XCG_FT_Fixed coef;
|
|
|
|
XCG_FT_Vector_From_Polar(&middle, XCG_FT_MulFix(radius, stroker->miter_limit), phi);
|
|
coef = XCG_FT_DivFix(0x10000L - sigma.x, sigma.y);
|
|
delta.x = XCG_FT_MulFix(middle.y, coef);
|
|
delta.y = XCG_FT_MulFix(-middle.x, coef);
|
|
middle.x += stroker->center.x;
|
|
middle.y += stroker->center.y;
|
|
delta.x += middle.x;
|
|
delta.y += middle.y;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
delta.x = middle.x - delta.x + middle.x;
|
|
delta.y = middle.y - delta.y + middle.y;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
if(line_length == 0)
|
|
{
|
|
XCG_FT_Vector_From_Polar(&delta, radius, stroker->angle_out + rotate);
|
|
delta.x += stroker->center.x;
|
|
delta.y += stroker->center.y;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
XCG_FT_Fixed length;
|
|
XCG_FT_Vector delta;
|
|
length = XCG_FT_MulDiv(stroker->radius, stroker->miter_limit, sigma.x);
|
|
XCG_FT_Vector_From_Polar(&delta, length, phi);
|
|
delta.x += stroker->center.x;
|
|
delta.y += stroker->center.y;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
if(line_length == 0)
|
|
{
|
|
XCG_FT_Vector_From_Polar(&delta, stroker->radius, stroker->angle_out + rotate);
|
|
delta.x += stroker->center.x;
|
|
delta.y += stroker->center.y;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroker_process_corner(XCG_FT_Stroker stroker, XCG_FT_Fixed line_length)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_Angle turn;
|
|
XCG_FT_Int inside_side;
|
|
|
|
turn = XCG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out);
|
|
if(turn == 0)
|
|
goto Exit;
|
|
inside_side = 0;
|
|
if(turn < 0)
|
|
inside_side = 1;
|
|
error = ft_stroker_inside(stroker, inside_side, line_length);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroker_outside(stroker, 1 - inside_side, line_length);
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroker_subpath_start(XCG_FT_Stroker stroker, XCG_FT_Angle start_angle, XCG_FT_Fixed line_length)
|
|
{
|
|
XCG_FT_Vector delta;
|
|
XCG_FT_Vector point;
|
|
XCG_FT_Error error;
|
|
XCG_FT_StrokeBorder border;
|
|
|
|
XCG_FT_Vector_From_Polar(&delta, stroker->radius, start_angle + XCG_FT_ANGLE_PI2);
|
|
point.x = stroker->center.x + delta.x;
|
|
point.y = stroker->center.y + delta.y;
|
|
border = stroker->borders;
|
|
error = ft_stroke_border_moveto(border, &point);
|
|
if(error)
|
|
goto Exit;
|
|
point.x = stroker->center.x - delta.x;
|
|
point.y = stroker->center.y - delta.y;
|
|
border++;
|
|
error = ft_stroke_border_moveto(border, &point);
|
|
stroker->subpath_angle = start_angle;
|
|
stroker->first_point = FALSE;
|
|
stroker->subpath_line_length = line_length;
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_LineTo(XCG_FT_Stroker stroker, XCG_FT_Vector * to)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_StrokeBorder border;
|
|
XCG_FT_Vector delta;
|
|
XCG_FT_Angle angle;
|
|
XCG_FT_Int side;
|
|
XCG_FT_Fixed line_length;
|
|
|
|
delta.x = to->x - stroker->center.x;
|
|
delta.y = to->y - stroker->center.y;
|
|
if(delta.x == 0 && delta.y == 0)
|
|
goto Exit;
|
|
line_length = XCG_FT_Vector_Length(&delta);
|
|
angle = XCG_FT_Atan2(delta.x, delta.y);
|
|
XCG_FT_Vector_From_Polar(&delta, stroker->radius, angle + XCG_FT_ANGLE_PI2);
|
|
if(stroker->first_point)
|
|
{
|
|
error = ft_stroker_subpath_start(stroker, angle, line_length);
|
|
if(error)
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
stroker->angle_out = angle;
|
|
error = ft_stroker_process_corner(stroker, line_length);
|
|
if(error)
|
|
goto Exit;
|
|
}
|
|
for(border = stroker->borders, side = 1; side >= 0; side--, border++)
|
|
{
|
|
XCG_FT_Vector point;
|
|
point.x = to->x + delta.x;
|
|
point.y = to->y + delta.y;
|
|
error = ft_stroke_border_lineto(border, &point, TRUE);
|
|
if(error)
|
|
goto Exit;
|
|
delta.x = -delta.x;
|
|
delta.y = -delta.y;
|
|
}
|
|
stroker->angle_in = angle;
|
|
stroker->center = *to;
|
|
stroker->line_length = line_length;
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_ConicTo(XCG_FT_Stroker stroker, XCG_FT_Vector * control, XCG_FT_Vector * to)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_Vector bez_stack[34];
|
|
XCG_FT_Vector *arc;
|
|
XCG_FT_Vector *limit = bez_stack + 30;
|
|
XCG_FT_Bool first_arc = TRUE;
|
|
|
|
if(XCG_FT_IS_SMALL(stroker->center.x - control->x) &&
|
|
XCG_FT_IS_SMALL(stroker->center.y - control->y) &&
|
|
XCG_FT_IS_SMALL(control->x - to->x) &&
|
|
XCG_FT_IS_SMALL(control->y - to->y))
|
|
{
|
|
stroker->center = *to;
|
|
goto Exit;
|
|
}
|
|
arc = bez_stack;
|
|
arc[0] = *to;
|
|
arc[1] = *control;
|
|
arc[2] = stroker->center;
|
|
while(arc >= bez_stack)
|
|
{
|
|
XCG_FT_Angle angle_in, angle_out;
|
|
angle_in = angle_out = stroker->angle_in;
|
|
if(arc < limit && !ft_conic_is_small_enough(arc, &angle_in, &angle_out))
|
|
{
|
|
if(stroker->first_point)
|
|
stroker->angle_in = angle_in;
|
|
ft_conic_split(arc);
|
|
arc += 2;
|
|
continue;
|
|
}
|
|
if(first_arc)
|
|
{
|
|
first_arc = FALSE;
|
|
if(stroker->first_point)
|
|
error = ft_stroker_subpath_start(stroker, angle_in, 0);
|
|
else
|
|
{
|
|
stroker->angle_out = angle_in;
|
|
error = ft_stroker_process_corner(stroker, 0);
|
|
}
|
|
}
|
|
else if(ft_pos_abs(XCG_FT_Angle_Diff(stroker->angle_in, angle_in)) > XCG_FT_SMALL_CONIC_THRESHOLD / 4)
|
|
{
|
|
stroker->center = arc[2];
|
|
stroker->angle_out = angle_in;
|
|
stroker->line_join = XCG_FT_STROKER_LINEJOIN_ROUND;
|
|
error = ft_stroker_process_corner(stroker, 0);
|
|
stroker->line_join = stroker->line_join_saved;
|
|
}
|
|
if(error)
|
|
goto Exit;
|
|
{
|
|
XCG_FT_Vector ctrl, end;
|
|
XCG_FT_Angle theta, phi, rotate, alpha0 = 0;
|
|
XCG_FT_Fixed length;
|
|
XCG_FT_StrokeBorder border;
|
|
XCG_FT_Int side;
|
|
|
|
theta = XCG_FT_Angle_Diff(angle_in, angle_out) / 2;
|
|
phi = angle_in + theta;
|
|
length = XCG_FT_DivFix(stroker->radius, XCG_FT_Cos(theta));
|
|
if(stroker->handle_wide_strokes)
|
|
alpha0 = XCG_FT_Atan2(arc[0].x - arc[2].x, arc[0].y - arc[2].y);
|
|
for(border = stroker->borders, side = 0; side <= 1; side++, border++)
|
|
{
|
|
rotate = XCG_FT_SIDE_TO_ROTATE(side);
|
|
XCG_FT_Vector_From_Polar(&ctrl, length, phi + rotate);
|
|
ctrl.x += arc[1].x;
|
|
ctrl.y += arc[1].y;
|
|
XCG_FT_Vector_From_Polar(&end, stroker->radius, angle_out + rotate);
|
|
end.x += arc[0].x;
|
|
end.y += arc[0].y;
|
|
|
|
if(stroker->handle_wide_strokes)
|
|
{
|
|
XCG_FT_Vector start;
|
|
XCG_FT_Angle alpha1;
|
|
|
|
start = border->points[border->num_points - 1];
|
|
alpha1 = XCG_FT_Atan2(end.x - start.x, end.y - start.y);
|
|
if(ft_pos_abs(XCG_FT_Angle_Diff(alpha0, alpha1)) >
|
|
XCG_FT_ANGLE_PI / 2)
|
|
{
|
|
XCG_FT_Angle beta, gamma;
|
|
XCG_FT_Vector bvec, delta;
|
|
XCG_FT_Fixed blen, sinA, sinB, alen;
|
|
|
|
beta = XCG_FT_Atan2(arc[2].x - start.x, arc[2].y - start.y);
|
|
gamma = XCG_FT_Atan2(arc[0].x - end.x, arc[0].y - end.y);
|
|
|
|
bvec.x = end.x - start.x;
|
|
bvec.y = end.y - start.y;
|
|
blen = XCG_FT_Vector_Length(&bvec);
|
|
sinA = ft_pos_abs(XCG_FT_Sin(alpha1 - gamma));
|
|
sinB = ft_pos_abs(XCG_FT_Sin(beta - gamma));
|
|
alen = XCG_FT_MulDiv(blen, sinA, sinB);
|
|
XCG_FT_Vector_From_Polar(&delta, alen, beta);
|
|
delta.x += start.x;
|
|
delta.y += start.y;
|
|
border->movable = FALSE;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroke_border_lineto(border, &end, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroke_border_conicto(border, &ctrl, &start);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroke_border_lineto(border, &end, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
continue;
|
|
}
|
|
}
|
|
error = ft_stroke_border_conicto(border, &ctrl, &end);
|
|
if(error)
|
|
goto Exit;
|
|
}
|
|
}
|
|
arc -= 2;
|
|
stroker->angle_in = angle_out;
|
|
}
|
|
stroker->center = *to;
|
|
stroker->line_length = 0;
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_CubicTo(XCG_FT_Stroker stroker, XCG_FT_Vector *control1, XCG_FT_Vector *control2, XCG_FT_Vector *to)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
XCG_FT_Vector bez_stack[37];
|
|
XCG_FT_Vector *arc;
|
|
XCG_FT_Vector *limit = bez_stack + 32;
|
|
XCG_FT_Bool first_arc = TRUE;
|
|
|
|
if(XCG_FT_IS_SMALL(stroker->center.x - control1->x) &&
|
|
XCG_FT_IS_SMALL(stroker->center.y - control1->y) &&
|
|
XCG_FT_IS_SMALL(control1->x - control2->x) &&
|
|
XCG_FT_IS_SMALL(control1->y - control2->y) &&
|
|
XCG_FT_IS_SMALL(control2->x - to->x) &&
|
|
XCG_FT_IS_SMALL(control2->y - to->y))
|
|
{
|
|
stroker->center = *to;
|
|
goto Exit;
|
|
}
|
|
arc = bez_stack;
|
|
arc[0] = *to;
|
|
arc[1] = *control2;
|
|
arc[2] = *control1;
|
|
arc[3] = stroker->center;
|
|
while(arc >= bez_stack)
|
|
{
|
|
XCG_FT_Angle angle_in, angle_mid, angle_out;
|
|
angle_in = angle_out = angle_mid = stroker->angle_in;
|
|
if(arc < limit && !ft_cubic_is_small_enough(arc, &angle_in, &angle_mid, &angle_out))
|
|
{
|
|
if(stroker->first_point)
|
|
stroker->angle_in = angle_in;
|
|
ft_cubic_split(arc);
|
|
arc += 3;
|
|
continue;
|
|
}
|
|
if(first_arc)
|
|
{
|
|
first_arc = FALSE;
|
|
if(stroker->first_point)
|
|
error = ft_stroker_subpath_start(stroker, angle_in, 0);
|
|
else
|
|
{
|
|
stroker->angle_out = angle_in;
|
|
error = ft_stroker_process_corner(stroker, 0);
|
|
}
|
|
}
|
|
else if(ft_pos_abs(XCG_FT_Angle_Diff(stroker->angle_in, angle_in)) > XCG_FT_SMALL_CUBIC_THRESHOLD / 4)
|
|
{
|
|
stroker->center = arc[3];
|
|
stroker->angle_out = angle_in;
|
|
stroker->line_join = XCG_FT_STROKER_LINEJOIN_ROUND;
|
|
error = ft_stroker_process_corner(stroker, 0);
|
|
stroker->line_join = stroker->line_join_saved;
|
|
}
|
|
if(error)
|
|
goto Exit;
|
|
{
|
|
XCG_FT_Vector ctrl1, ctrl2, end;
|
|
XCG_FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0;
|
|
XCG_FT_Fixed length1, length2;
|
|
XCG_FT_StrokeBorder border;
|
|
XCG_FT_Int side;
|
|
|
|
theta1 = XCG_FT_Angle_Diff(angle_in, angle_mid) / 2;
|
|
theta2 = XCG_FT_Angle_Diff(angle_mid, angle_out) / 2;
|
|
phi1 = ft_angle_mean(angle_in, angle_mid);
|
|
phi2 = ft_angle_mean(angle_mid, angle_out);
|
|
length1 = XCG_FT_DivFix(stroker->radius, XCG_FT_Cos(theta1));
|
|
length2 = XCG_FT_DivFix(stroker->radius, XCG_FT_Cos(theta2));
|
|
if(stroker->handle_wide_strokes)
|
|
alpha0 = XCG_FT_Atan2(arc[0].x - arc[3].x, arc[0].y - arc[3].y);
|
|
for(border = stroker->borders, side = 0; side <= 1; side++, border++)
|
|
{
|
|
rotate = XCG_FT_SIDE_TO_ROTATE(side);
|
|
XCG_FT_Vector_From_Polar(&ctrl1, length1, phi1 + rotate);
|
|
ctrl1.x += arc[2].x;
|
|
ctrl1.y += arc[2].y;
|
|
XCG_FT_Vector_From_Polar(&ctrl2, length2, phi2 + rotate);
|
|
ctrl2.x += arc[1].x;
|
|
ctrl2.y += arc[1].y;
|
|
XCG_FT_Vector_From_Polar(&end, stroker->radius, angle_out + rotate);
|
|
end.x += arc[0].x;
|
|
end.y += arc[0].y;
|
|
if(stroker->handle_wide_strokes)
|
|
{
|
|
XCG_FT_Vector start;
|
|
XCG_FT_Angle alpha1;
|
|
start = border->points[border->num_points - 1];
|
|
alpha1 = XCG_FT_Atan2(end.x - start.x, end.y - start.y);
|
|
if(ft_pos_abs(XCG_FT_Angle_Diff(alpha0, alpha1)) >
|
|
XCG_FT_ANGLE_PI / 2)
|
|
{
|
|
XCG_FT_Angle beta, gamma;
|
|
XCG_FT_Vector bvec, delta;
|
|
XCG_FT_Fixed blen, sinA, sinB, alen;
|
|
beta = XCG_FT_Atan2(arc[3].x - start.x, arc[3].y - start.y);
|
|
gamma = XCG_FT_Atan2(arc[0].x - end.x, arc[0].y - end.y);
|
|
bvec.x = end.x - start.x;
|
|
bvec.y = end.y - start.y;
|
|
blen = XCG_FT_Vector_Length(&bvec);
|
|
sinA = ft_pos_abs(XCG_FT_Sin(alpha1 - gamma));
|
|
sinB = ft_pos_abs(XCG_FT_Sin(beta - gamma));
|
|
alen = XCG_FT_MulDiv(blen, sinA, sinB);
|
|
XCG_FT_Vector_From_Polar(&delta, alen, beta);
|
|
delta.x += start.x;
|
|
delta.y += start.y;
|
|
border->movable = FALSE;
|
|
error = ft_stroke_border_lineto(border, &delta, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroke_border_lineto(border, &end, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroke_border_cubicto(border, &ctrl2, &ctrl1, &start);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroke_border_lineto(border, &end, FALSE);
|
|
if(error)
|
|
goto Exit;
|
|
continue;
|
|
}
|
|
}
|
|
error = ft_stroke_border_cubicto(border, &ctrl1, &ctrl2, &end);
|
|
if(error)
|
|
goto Exit;
|
|
}
|
|
}
|
|
arc -= 3;
|
|
stroker->angle_in = angle_out;
|
|
}
|
|
stroker->center = *to;
|
|
stroker->line_length = 0;
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_BeginSubPath(XCG_FT_Stroker stroker, XCG_FT_Vector * to, XCG_FT_Bool open)
|
|
{
|
|
stroker->first_point = TRUE;
|
|
stroker->center = *to;
|
|
stroker->subpath_open = open;
|
|
stroker->handle_wide_strokes = XCG_FT_BOOL(stroker->line_join != XCG_FT_STROKER_LINEJOIN_ROUND || (stroker->subpath_open && stroker->line_cap == XCG_FT_STROKER_LINECAP_BUTT));
|
|
stroker->subpath_start = *to;
|
|
stroker->angle_in = 0;
|
|
return 0;
|
|
}
|
|
|
|
static XCG_FT_Error ft_stroker_add_reverse_left(XCG_FT_Stroker stroker, XCG_FT_Bool open)
|
|
{
|
|
XCG_FT_StrokeBorder right = stroker->borders + 0;
|
|
XCG_FT_StrokeBorder left = stroker->borders + 1;
|
|
XCG_FT_Int new_points;
|
|
XCG_FT_Error error = 0;
|
|
|
|
new_points = left->num_points - left->start;
|
|
if(new_points > 0)
|
|
{
|
|
error = ft_stroke_border_grow(right, (XCG_FT_UInt)new_points);
|
|
if(error)
|
|
goto Exit;
|
|
{
|
|
XCG_FT_Vector *dst_point = right->points + right->num_points;
|
|
XCG_FT_Byte *dst_tag = right->tags + right->num_points;
|
|
XCG_FT_Vector *src_point = left->points + left->num_points - 1;
|
|
XCG_FT_Byte *src_tag = left->tags + left->num_points - 1;
|
|
while(src_point >= left->points + left->start)
|
|
{
|
|
*dst_point = *src_point;
|
|
*dst_tag = *src_tag;
|
|
if(open)
|
|
dst_tag[0] &= ~XCG_FT_STROKE_TAG_BEGIN_END;
|
|
else
|
|
{
|
|
XCG_FT_Byte ttag = (XCG_FT_Byte)(dst_tag[0] & XCG_FT_STROKE_TAG_BEGIN_END);
|
|
if(ttag == XCG_FT_STROKE_TAG_BEGIN || ttag == XCG_FT_STROKE_TAG_END)
|
|
dst_tag[0] ^= XCG_FT_STROKE_TAG_BEGIN_END;
|
|
}
|
|
src_point--;
|
|
src_tag--;
|
|
dst_point++;
|
|
dst_tag++;
|
|
}
|
|
}
|
|
left->num_points = left->start;
|
|
right->num_points += new_points;
|
|
right->movable = FALSE;
|
|
left->movable = FALSE;
|
|
}
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_EndSubPath(XCG_FT_Stroker stroker)
|
|
{
|
|
XCG_FT_Error error = 0;
|
|
|
|
if(stroker->subpath_open)
|
|
{
|
|
XCG_FT_StrokeBorder right = stroker->borders;
|
|
error = ft_stroker_cap(stroker, stroker->angle_in, 0);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroker_add_reverse_left(stroker, TRUE);
|
|
if(error)
|
|
goto Exit;
|
|
stroker->center = stroker->subpath_start;
|
|
error = ft_stroker_cap(stroker, stroker->subpath_angle + XCG_FT_ANGLE_PI, 0);
|
|
if(error)
|
|
goto Exit;
|
|
ft_stroke_border_close(right, FALSE);
|
|
}
|
|
else
|
|
{
|
|
XCG_FT_Angle turn;
|
|
XCG_FT_Int inside_side;
|
|
if(stroker->center.x != stroker->subpath_start.x || stroker->center.y != stroker->subpath_start.y)
|
|
{
|
|
error = XCG_FT_Stroker_LineTo(stroker, &stroker->subpath_start);
|
|
if(error)
|
|
goto Exit;
|
|
}
|
|
stroker->angle_out = stroker->subpath_angle;
|
|
turn = XCG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out);
|
|
if(turn != 0)
|
|
{
|
|
inside_side = 0;
|
|
if(turn < 0)
|
|
inside_side = 1;
|
|
error = ft_stroker_inside(stroker, inside_side, stroker->subpath_line_length);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroker_outside(stroker, 1 - inside_side, stroker->subpath_line_length);
|
|
if(error)
|
|
goto Exit;
|
|
}
|
|
ft_stroke_border_close(stroker->borders + 0, FALSE);
|
|
ft_stroke_border_close(stroker->borders + 1, TRUE);
|
|
}
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_GetBorderCounts(XCG_FT_Stroker stroker, XCG_FT_StrokerBorder border, XCG_FT_UInt * anum_points, XCG_FT_UInt * anum_contours)
|
|
{
|
|
XCG_FT_UInt num_points = 0, num_contours = 0;
|
|
XCG_FT_Error error;
|
|
|
|
if(!stroker || border > 1)
|
|
{
|
|
error = -1;
|
|
goto Exit;
|
|
}
|
|
error = ft_stroke_border_get_counts(stroker->borders + border, &num_points, &num_contours);
|
|
Exit:
|
|
if(anum_points)
|
|
*anum_points = num_points;
|
|
if(anum_contours)
|
|
*anum_contours = num_contours;
|
|
return error;
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_GetCounts(XCG_FT_Stroker stroker, XCG_FT_UInt * anum_points, XCG_FT_UInt * anum_contours)
|
|
{
|
|
XCG_FT_UInt count1, count2, num_points = 0;
|
|
XCG_FT_UInt count3, count4, num_contours = 0;
|
|
XCG_FT_Error error;
|
|
|
|
error = ft_stroke_border_get_counts(stroker->borders + 0, &count1, &count2);
|
|
if(error)
|
|
goto Exit;
|
|
error = ft_stroke_border_get_counts(stroker->borders + 1, &count3, &count4);
|
|
if(error)
|
|
goto Exit;
|
|
num_points = count1 + count3;
|
|
num_contours = count2 + count4;
|
|
Exit:
|
|
*anum_points = num_points;
|
|
*anum_contours = num_contours;
|
|
return error;
|
|
}
|
|
|
|
void XCG_FT_Stroker_ExportBorder(XCG_FT_Stroker stroker, XCG_FT_StrokerBorder border, XCG_FT_Outline * outline)
|
|
{
|
|
if(border == XCG_FT_STROKER_BORDER_LEFT || border == XCG_FT_STROKER_BORDER_RIGHT)
|
|
{
|
|
XCG_FT_StrokeBorder sborder = &stroker->borders[border];
|
|
if(sborder->valid)
|
|
ft_stroke_border_export(sborder, outline);
|
|
}
|
|
}
|
|
|
|
void XCG_FT_Stroker_Export(XCG_FT_Stroker stroker, XCG_FT_Outline * outline)
|
|
{
|
|
XCG_FT_Stroker_ExportBorder(stroker, XCG_FT_STROKER_BORDER_LEFT, outline);
|
|
XCG_FT_Stroker_ExportBorder(stroker, XCG_FT_STROKER_BORDER_RIGHT, outline);
|
|
}
|
|
|
|
XCG_FT_Error XCG_FT_Stroker_ParseOutline(XCG_FT_Stroker stroker, const XCG_FT_Outline *outline)
|
|
{
|
|
XCG_FT_Vector v_last;
|
|
XCG_FT_Vector v_control;
|
|
XCG_FT_Vector v_start;
|
|
XCG_FT_Vector * point;
|
|
XCG_FT_Vector * limit;
|
|
char * tags;
|
|
XCG_FT_Error error;
|
|
XCG_FT_Int n;
|
|
XCG_FT_UInt first;
|
|
XCG_FT_Int tag;
|
|
|
|
if(!outline || !stroker)
|
|
return -1;
|
|
XCG_FT_Stroker_Rewind(stroker);
|
|
first = 0;
|
|
for(n = 0; n < outline->n_contours; n++)
|
|
{
|
|
XCG_FT_UInt last;
|
|
last = outline->contours[n];
|
|
limit = outline->points + last;
|
|
if(last <= first)
|
|
{
|
|
first = last + 1;
|
|
continue;
|
|
}
|
|
v_start = outline->points[first];
|
|
v_last = outline->points[last];
|
|
v_control = v_start;
|
|
point = outline->points + first;
|
|
tags = outline->tags + first;
|
|
tag = XCG_FT_CURVE_TAG(tags[0]);
|
|
if(tag == XCG_FT_CURVE_TAG_CUBIC)
|
|
goto Invalid_Outline;
|
|
if(tag == XCG_FT_CURVE_TAG_CONIC)
|
|
{
|
|
if(XCG_FT_CURVE_TAG(outline->tags[last]) == XCG_FT_CURVE_TAG_ON)
|
|
{
|
|
v_start = v_last;
|
|
limit--;
|
|
}
|
|
else
|
|
{
|
|
v_start.x = (v_start.x + v_last.x) / 2;
|
|
v_start.y = (v_start.y + v_last.y) / 2;
|
|
}
|
|
point--;
|
|
tags--;
|
|
}
|
|
error = XCG_FT_Stroker_BeginSubPath(stroker, &v_start, outline->contours_flag[n]);
|
|
if(error)
|
|
goto Exit;
|
|
while(point < limit)
|
|
{
|
|
point++;
|
|
tags++;
|
|
tag = XCG_FT_CURVE_TAG(tags[0]);
|
|
switch(tag)
|
|
{
|
|
case XCG_FT_CURVE_TAG_ON:
|
|
{
|
|
XCG_FT_Vector vec;
|
|
vec.x = point->x;
|
|
vec.y = point->y;
|
|
error = XCG_FT_Stroker_LineTo(stroker, &vec);
|
|
if(error)
|
|
goto Exit;
|
|
continue;
|
|
}
|
|
|
|
case XCG_FT_CURVE_TAG_CONIC:
|
|
v_control.x = point->x;
|
|
v_control.y = point->y;
|
|
Do_Conic:
|
|
if(point < limit)
|
|
{
|
|
XCG_FT_Vector vec;
|
|
XCG_FT_Vector v_middle;
|
|
point++;
|
|
tags++;
|
|
tag = XCG_FT_CURVE_TAG(tags[0]);
|
|
vec = point[0];
|
|
if(tag == XCG_FT_CURVE_TAG_ON)
|
|
{
|
|
error = XCG_FT_Stroker_ConicTo(stroker, &v_control, &vec);
|
|
if(error)
|
|
goto Exit;
|
|
continue;
|
|
}
|
|
if(tag != XCG_FT_CURVE_TAG_CONIC)
|
|
goto Invalid_Outline;
|
|
v_middle.x = (v_control.x + vec.x) / 2;
|
|
v_middle.y = (v_control.y + vec.y) / 2;
|
|
error = XCG_FT_Stroker_ConicTo(stroker, &v_control, &v_middle);
|
|
if(error)
|
|
goto Exit;
|
|
v_control = vec;
|
|
goto Do_Conic;
|
|
}
|
|
error = XCG_FT_Stroker_ConicTo(stroker, &v_control, &v_start);
|
|
goto Close;
|
|
|
|
default:
|
|
{
|
|
XCG_FT_Vector vec1, vec2;
|
|
if(point + 1 > limit ||
|
|
XCG_FT_CURVE_TAG(tags[1]) != XCG_FT_CURVE_TAG_CUBIC)
|
|
goto Invalid_Outline;
|
|
point += 2;
|
|
tags += 2;
|
|
vec1 = point[-2];
|
|
vec2 = point[-1];
|
|
if(point <= limit)
|
|
{
|
|
XCG_FT_Vector vec;
|
|
vec = point[0];
|
|
error = XCG_FT_Stroker_CubicTo(stroker, &vec1, &vec2, &vec);
|
|
if(error)
|
|
goto Exit;
|
|
continue;
|
|
}
|
|
error = XCG_FT_Stroker_CubicTo(stroker, &vec1, &vec2, &v_start);
|
|
goto Close;
|
|
}
|
|
}
|
|
}
|
|
Close:
|
|
if(error)
|
|
goto Exit;
|
|
if(stroker->first_point)
|
|
{
|
|
stroker->subpath_open = TRUE;
|
|
error = ft_stroker_subpath_start(stroker, 0, 0);
|
|
if(error)
|
|
goto Exit;
|
|
}
|
|
error = XCG_FT_Stroker_EndSubPath(stroker);
|
|
if(error)
|
|
goto Exit;
|
|
first = last + 1;
|
|
}
|
|
return 0;
|
|
Exit:
|
|
return error;
|
|
Invalid_Outline:
|
|
return -2;
|
|
}
|