From 891ec145857c717105deb2481d224174b1c966ac Mon Sep 17 00:00:00 2001 From: Subhransu Sekhar Mohanty Date: Fri, 3 Apr 2015 16:31:45 +0200 Subject: [PATCH] ector: add software backend using FreeType rasterizer. --- src/lib/ector/software/Ector_Software.h | 15 + src/lib/ector/software/ector_blend_private.h | 117 + .../software/ector_renderer_software_base.eo | 12 + .../ector_renderer_software_gradient_linear.c | 109 + ...ector_renderer_software_gradient_linear.eo | 14 + .../ector_renderer_software_gradient_radial.c | 117 + ...ector_renderer_software_gradient_radial.eo | 14 + .../software/ector_renderer_software_shape.c | 373 +++ .../software/ector_renderer_software_shape.eo | 13 + .../ector/software/ector_software_gradient.c | 269 ++ .../ector/software/ector_software_private.h | 150 ++ .../software/ector_software_rasterizer.c | 437 ++++ .../ector/software/ector_software_surface.c | 94 + .../ector/software/ector_software_surface.eo | 33 + src/lib/ector/software/sw_ft_math.c | 528 ++++ src/lib/ector/software/sw_ft_math.h | 438 ++++ src/lib/ector/software/sw_ft_raster.c | 1846 +++++++++++++ src/lib/ector/software/sw_ft_raster.h | 607 +++++ src/lib/ector/software/sw_ft_stroker.c | 2292 +++++++++++++++++ src/lib/ector/software/sw_ft_stroker.h | 325 +++ src/lib/ector/software/sw_ft_types.h | 160 ++ 21 files changed, 7963 insertions(+) create mode 100644 src/lib/ector/software/Ector_Software.h create mode 100644 src/lib/ector/software/ector_blend_private.h create mode 100644 src/lib/ector/software/ector_renderer_software_base.eo create mode 100644 src/lib/ector/software/ector_renderer_software_gradient_linear.c create mode 100644 src/lib/ector/software/ector_renderer_software_gradient_linear.eo create mode 100644 src/lib/ector/software/ector_renderer_software_gradient_radial.c create mode 100644 src/lib/ector/software/ector_renderer_software_gradient_radial.eo create mode 100644 src/lib/ector/software/ector_renderer_software_shape.c create mode 100644 src/lib/ector/software/ector_renderer_software_shape.eo create mode 100644 src/lib/ector/software/ector_software_gradient.c create mode 100644 src/lib/ector/software/ector_software_private.h create mode 100644 src/lib/ector/software/ector_software_rasterizer.c create mode 100644 src/lib/ector/software/ector_software_surface.c create mode 100644 src/lib/ector/software/ector_software_surface.eo create mode 100755 src/lib/ector/software/sw_ft_math.c create mode 100755 src/lib/ector/software/sw_ft_math.h create mode 100644 src/lib/ector/software/sw_ft_raster.c create mode 100755 src/lib/ector/software/sw_ft_raster.h create mode 100755 src/lib/ector/software/sw_ft_stroker.c create mode 100755 src/lib/ector/software/sw_ft_stroker.h create mode 100755 src/lib/ector/software/sw_ft_types.h diff --git a/src/lib/ector/software/Ector_Software.h b/src/lib/ector/software/Ector_Software.h new file mode 100644 index 0000000000..7d003cc3ff --- /dev/null +++ b/src/lib/ector/software/Ector_Software.h @@ -0,0 +1,15 @@ +#ifndef ECTOR_SOFTWARE_H_ +#define ECTOR_SOFTWARE_H_ + +#include + +typedef Eo Ector_Software_Surface; +typedef struct _Software_Rasterizer Software_Rasterizer; + +#include "software/ector_software_surface.eo.h" +#include "software/ector_renderer_software_base.eo.h" +#include "software/ector_renderer_software_shape.eo.h" +#include "software/ector_renderer_software_gradient_linear.eo.h" +#include "software/ector_renderer_software_gradient_radial.eo.h" + +#endif diff --git a/src/lib/ector/software/ector_blend_private.h b/src/lib/ector/software/ector_blend_private.h new file mode 100644 index 0000000000..ba48a03349 --- /dev/null +++ b/src/lib/ector/software/ector_blend_private.h @@ -0,0 +1,117 @@ +#ifndef ECTOR_BLEND_PRIVATE_H +#define ECTOR_BLEND_PRIVATE_H + +#ifndef MIN +#define MIN( a, b ) ( (a) < (b) ? (a) : (b) ) +#endif + +#ifndef MAX +#define MAX( a, b ) ( (a) > (b) ? (a) : (b) ) +#endif + +#define ECTOR_ARGB_JOIN(a,r,g,b) \ + (((a) << 24) + ((r) << 16) + ((g) << 8) + (b)) + +#define ECTOR_MUL4_SYM(x, y) \ + ( ((((((x) >> 16) & 0xff00) * (((y) >> 16) & 0xff00)) + 0xff0000) & 0xff000000) + \ + ((((((x) >> 8) & 0xff00) * (((y) >> 16) & 0xff)) + 0xff00) & 0xff0000) + \ + ((((((x) & 0xff00) * ((y) & 0xff00)) + 0xff0000) >> 16) & 0xff00) + \ + (((((x) & 0xff) * ((y) & 0xff)) + 0xff) >> 8) ) + +#define ECTOR_MUL_256(c, a) \ + ( (((((c) >> 8) & 0x00ff00ff) * (a)) & 0xff00ff00) + \ + (((((c) & 0x00ff00ff) * (a)) >> 8) & 0x00ff00ff) ) + + +static inline void +_ector_memfill(DATA32 *dest, uint value, int count) +{ + if (!count) + return; + + int n = (count + 7) / 8; + switch (count & 0x07) + { + case 0: do { *dest++ = value; + case 7: *dest++ = value; + case 6: *dest++ = value; + case 5: *dest++ = value; + case 4: *dest++ = value; + case 3: *dest++ = value; + case 2: *dest++ = value; + case 1: *dest++ = value; + } while (--n > 0); + } +} + + +static inline void +_ector_comp_func_source_over_mul_c(uint *dest, uint *src, DATA32 c, int length, uint const_alpha) +{ + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + uint s = src[i]; + DATA32 sc = ECTOR_MUL4_SYM(c, s); + uint a = (~sc) >> 24; + dest[i] = sc + ECTOR_MUL_256(dest[i], a); + } + } else { + for (int i = 0; i < length; ++i) { + uint s = src[i]; + DATA32 sc = ECTOR_MUL4_SYM(c, s); + sc = ECTOR_MUL_256(sc, const_alpha); + uint a = (~sc) >> 24; + dest[i] = sc + ECTOR_MUL_256(dest[i], a); + } + } +} + + +static inline void +_ector_comp_func_source_over(uint *dest, uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + uint s = src[i]; + if (s >= 0xff000000) + dest[i] = s; + else if (s != 0) { + uint a = (~s) >> 24; + dest[i] = s + ECTOR_MUL_256(dest[i], a); + } + } + } else { + for (int i = 0; i < length; ++i) { + uint s = ECTOR_MUL_256(src[i], const_alpha); + uint a = (~s) >> 24; + dest[i] = s + ECTOR_MUL_256(dest[i], a); + } + } +} + + +static inline uint +_ector_premultiply(uint data) +{ + DATA32 a = 1 + (data >> 24); + data = ( data & 0xff000000) + + (((((data) >> 8) & 0xff) * a) & 0xff00) + + (((((data) & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); + + return data; +} + +static inline uint +INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) { + uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; + t >>= 8; + t &= 0xff00ff; + + x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; + x &= 0xff00ff00; + x |= t; + return x; +} + + +#endif \ No newline at end of file diff --git a/src/lib/ector/software/ector_renderer_software_base.eo b/src/lib/ector/software/ector_renderer_software_base.eo new file mode 100644 index 0000000000..6055be585c --- /dev/null +++ b/src/lib/ector/software/ector_renderer_software_base.eo @@ -0,0 +1,12 @@ +class Ector.Renderer.Software.Base (Ector.Renderer.Generic.Base) +{ + legacy_prefix: null; + methods { + fill { + return: bool; + } + } + implements { + @virtual .fill; + } +} diff --git a/src/lib/ector/software/ector_renderer_software_gradient_linear.c b/src/lib/ector/software/ector_renderer_software_gradient_linear.c new file mode 100644 index 0000000000..8bbf23d73a --- /dev/null +++ b/src/lib/ector/software/ector_renderer_software_gradient_linear.c @@ -0,0 +1,109 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "ector_private.h" +#include "ector_software_private.h" + + +static void +_update_linear_data(Ector_Renderer_Software_Gradient_Data *gdata) +{ + update_color_table(gdata); + gdata->linear.x1 = gdata->gld->start.x; + gdata->linear.y1 = gdata->gld->start.y; + + gdata->linear.x2 = gdata->gld->end.x; + gdata->linear.y2 = gdata->gld->end.y; + + gdata->linear.dx = gdata->linear.x2 - gdata->linear.x1; + gdata->linear.dy = gdata->linear.y2 - gdata->linear.y1; + gdata->linear.l = gdata->linear.dx * gdata->linear.dx + gdata->linear.dy * gdata->linear.dy; + gdata->linear.off = 0; + + if (gdata->linear.l != 0) + { + gdata->linear.dx /= gdata->linear.l; + gdata->linear.dy /= gdata->linear.l; + gdata->linear.off = -gdata->linear.dx * gdata->linear.x1 - gdata->linear.dy * gdata->linear.y1; + } +} + + +static Eina_Bool +_ector_renderer_software_gradient_linear_ector_renderer_generic_base_prepare(Eo *obj, + Ector_Renderer_Software_Gradient_Data *pd) +{ + if (!pd->surface) + { + Eo *parent; + + eo_do(obj, parent = eo_parent_get()); + if (!parent) return EINA_FALSE; + pd->surface = eo_data_xref(parent, ECTOR_SOFTWARE_SURFACE_CLASS, obj); + } + + _update_linear_data(pd); + + + return EINA_FALSE; +} + +static Eina_Bool +_ector_renderer_software_gradient_linear_ector_renderer_generic_base_draw(Eo *obj EINA_UNUSED, + Ector_Renderer_Software_Gradient_Data *pd EINA_UNUSED, + Ector_Rop op EINA_UNUSED, Eina_Array *clips EINA_UNUSED, + int x EINA_UNUSED, int y EINA_UNUSED, unsigned int mul_col EINA_UNUSED) +{ + return EINA_TRUE; +} + +static Eina_Bool +_ector_renderer_software_gradient_linear_ector_renderer_software_base_fill(Eo *obj EINA_UNUSED, + Ector_Renderer_Software_Gradient_Data *pd) +{ + ector_software_rasterizer_linear_gradient_set(pd->surface->software, pd); + + return EINA_TRUE; +} + +void +_ector_renderer_software_gradient_linear_eo_base_constructor(Eo *obj, + Ector_Renderer_Software_Gradient_Data *pd) +{ + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_GRADIENT_LINEAR_CLASS, eo_constructor()); + pd->gd = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_GRADIENT_MIXIN, obj); + pd->gld = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_GRADIENT_LINEAR_MIXIN, obj); +} + +void +_ector_renderer_software_gradient_linear_eo_base_destructor(Eo *obj, + Ector_Renderer_Software_Gradient_Data *pd) +{ + Eo *parent; + + destroy_color_table(pd); + + eo_do(obj, parent = eo_parent_get()); + eo_data_xunref(parent, pd->surface, obj); + + eo_data_xunref(obj, pd->gd, obj); + eo_data_xunref(obj, pd->gld, obj); + + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_GRADIENT_LINEAR_CLASS, eo_destructor()); +} + +void +_ector_renderer_software_gradient_linear_efl_gfx_gradient_base_stop_set(Eo *obj, Ector_Renderer_Software_Gradient_Data *pd, const Efl_Gfx_Gradient_Stop *colors, unsigned int length) +{ + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_GRADIENT_LINEAR_CLASS, + efl_gfx_gradient_stop_set(colors, length)); + + destroy_color_table(pd); +} + +#include "ector_renderer_software_gradient_linear.eo.c" diff --git a/src/lib/ector/software/ector_renderer_software_gradient_linear.eo b/src/lib/ector/software/ector_renderer_software_gradient_linear.eo new file mode 100644 index 0000000000..32f83df016 --- /dev/null +++ b/src/lib/ector/software/ector_renderer_software_gradient_linear.eo @@ -0,0 +1,14 @@ +class Ector.Renderer.Software.Gradient_Linear (Ector.Renderer.Software.Base, Ector.Renderer.Generic.Gradient, Ector.Renderer.Generic.Gradient_Linear) +{ + eo_prefix: ector_renderer_software_gradient_linear; + legacy_prefix: null; + data: Ector_Renderer_Software_Gradient_Data; + implements { + Ector.Renderer.Generic.Base.prepare; + Ector.Renderer.Generic.Base.draw; + Ector.Renderer.Software.Base.fill; + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Gfx.Gradient.Base.stop.set; + } +} diff --git a/src/lib/ector/software/ector_renderer_software_gradient_radial.c b/src/lib/ector/software/ector_renderer_software_gradient_radial.c new file mode 100644 index 0000000000..4bcf2a1594 --- /dev/null +++ b/src/lib/ector/software/ector_renderer_software_gradient_radial.c @@ -0,0 +1,117 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "ector_private.h" +#include "ector_software_private.h" + +static void +_update_radial_data(Ector_Renderer_Software_Gradient_Data *gdata) +{ + update_color_table(gdata); + + gdata->radial.cx = gdata->grd->radial.x; + gdata->radial.cy = gdata->grd->radial.y; + gdata->radial.cradius = gdata->grd->radius; + + if (!gdata->grd->focal.x) + gdata->radial.fx = gdata->grd->radial.x; + else + gdata->radial.fx = gdata->grd->focal.x; + + if (!gdata->grd->focal.y) + gdata->radial.fy = gdata->grd->radial.y; + else + gdata->radial.fy = gdata->grd->focal.y; + + gdata->radial.fradius = 0; + + gdata->radial.dx = gdata->radial.cx - gdata->radial.fx; + gdata->radial.dy = gdata->radial.cy - gdata->radial.fy; + + gdata->radial.dr = gdata->radial.cradius - gdata->radial.fradius; + gdata->radial.sqrfr = gdata->radial.fradius * gdata->radial.fradius; + + gdata->radial.a = gdata->radial.dr * gdata->radial.dr - + gdata->radial.dx * gdata->radial.dx - + gdata->radial.dy * gdata->radial.dy; + gdata->radial.inv2a = 1 / (2 * gdata->radial.a); + + gdata->radial.extended = (gdata->radial.fradius >= 0.00001f) || gdata->radial.a >= 0.00001f; +} + + +static Eina_Bool +_ector_renderer_software_gradient_radial_ector_renderer_generic_base_prepare(Eo *obj, Ector_Renderer_Software_Gradient_Data *pd) +{ + if (!pd->surface) + { + Eo *parent; + + eo_do(obj, parent = eo_parent_get()); + if (!parent) return EINA_FALSE; + pd->surface = eo_data_xref(parent, ECTOR_SOFTWARE_SURFACE_CLASS, obj); + } + + _update_radial_data(pd); + return EINA_FALSE; +} + +// Clearly duplicated and should be in a common place... +static Eina_Bool +_ector_renderer_software_gradient_radial_ector_renderer_generic_base_draw(Eo *obj EINA_UNUSED, + Ector_Renderer_Software_Gradient_Data *pd EINA_UNUSED, + Ector_Rop op EINA_UNUSED, Eina_Array *clips EINA_UNUSED, + int x EINA_UNUSED, int y EINA_UNUSED, unsigned int mul_col EINA_UNUSED) +{ + return EINA_TRUE; +} + +// Clearly duplicated and should be in a common place... +static Eina_Bool +_ector_renderer_software_gradient_radial_ector_renderer_software_base_fill(Eo *obj EINA_UNUSED, Ector_Renderer_Software_Gradient_Data *pd) +{ + ector_software_rasterizer_radial_gradient_set(pd->surface->software, pd); + return EINA_TRUE; +} + +void +_ector_renderer_software_gradient_radial_eo_base_constructor(Eo *obj, + Ector_Renderer_Software_Gradient_Data *pd) +{ + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_GRADIENT_RADIAL_CLASS, eo_constructor()); + pd->gd = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_GRADIENT_MIXIN, obj); + pd->gld = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_GRADIENT_RADIAL_MIXIN, obj); +} + +void +_ector_renderer_software_gradient_radial_eo_base_destructor(Eo *obj, + Ector_Renderer_Software_Gradient_Data *pd) +{ + Eo *parent; + + destroy_color_table(pd); + + eo_do(obj, parent = eo_parent_get()); + eo_data_xunref(parent, pd->surface, obj); + + eo_data_xunref(obj, pd->gd, obj); + eo_data_xunref(obj, pd->gld, obj); + + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_GRADIENT_RADIAL_CLASS, eo_destructor()); +} + +void +_ector_renderer_software_gradient_radial_efl_gfx_gradient_base_stop_set(Eo *obj, Ector_Renderer_Software_Gradient_Data *pd, const Efl_Gfx_Gradient_Stop *colors, unsigned int length) +{ + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_GRADIENT_RADIAL_CLASS, + efl_gfx_gradient_stop_set(colors, length)); + + destroy_color_table(pd); +} + +#include "ector_renderer_software_gradient_radial.eo.c" diff --git a/src/lib/ector/software/ector_renderer_software_gradient_radial.eo b/src/lib/ector/software/ector_renderer_software_gradient_radial.eo new file mode 100644 index 0000000000..85625f341f --- /dev/null +++ b/src/lib/ector/software/ector_renderer_software_gradient_radial.eo @@ -0,0 +1,14 @@ +class Ector.Renderer.Software.Gradient_Radial (Ector.Renderer.Software.Base, Ector.Renderer.Generic.Gradient, Ector.Renderer.Generic.Gradient_Radial) +{ + eo_prefix: ector_renderer_software_gradient_radial; + legacy_prefix: null; + data: Ector_Renderer_Software_Gradient_Data; + implements { + Ector.Renderer.Generic.Base.prepare; + Ector.Renderer.Generic.Base.draw; + Ector.Renderer.Software.Base.fill; + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Gfx.Gradient.Base.stop.set; + } +} diff --git a/src/lib/ector/software/ector_renderer_software_shape.c b/src/lib/ector/software/ector_renderer_software_shape.c new file mode 100644 index 0000000000..cdecce9e87 --- /dev/null +++ b/src/lib/ector/software/ector_renderer_software_shape.c @@ -0,0 +1,373 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include +#include + +#include "ector_private.h" +#include "ector_software_private.h" + + +typedef struct _Ector_Renderer_Software_Shape_Data Ector_Renderer_Software_Shape_Data; +struct _Ector_Renderer_Software_Shape_Data +{ + Ector_Software_Surface_Data *surface; + Ector_Renderer_Generic_Shape_Data *shape; + Ector_Renderer_Generic_Base_Data *base; + Shape_Rle_Data *shape_data; + Shape_Rle_Data *outline_data; +}; + +typedef struct _Outline +{ + SW_FT_Outline ft_outline; + int points_alloc; + int contours_alloc; +}Outline; + +static Outline * +_outline_create() +{ + Outline *outline = (Outline *) calloc(1, sizeof(Outline)); + + outline->ft_outline.points = (SW_FT_Vector *) calloc(50, sizeof(SW_FT_Vector)); + outline->ft_outline.tags = (char *) calloc(50, sizeof(char)); + + outline->ft_outline.contours = (short *) calloc(5, sizeof(short)); + + outline->points_alloc = 50; + outline->contours_alloc = 5; + return outline; +} + +static +void _outline_destroy(Outline *outline) +{ + if (outline) + { + free(outline->ft_outline.points); + free(outline->ft_outline.tags); + free(outline->ft_outline.contours); + free(outline); + outline = NULL; + } +} + +static void +_outline_move_to(Outline *outline, double x, double y) +{ + SW_FT_Outline *ft_outline = &outline->ft_outline; + + if (ft_outline->n_contours == outline->contours_alloc) + { + outline->contours_alloc += 5; + ft_outline->contours = (short *) realloc(ft_outline->contours, outline->contours_alloc * sizeof(short)); + } + ft_outline->points[ft_outline->n_points].x = x; + ft_outline->points[ft_outline->n_points].y = y; + ft_outline->tags[ft_outline->n_points] = SW_FT_CURVE_TAG_ON; + + if (ft_outline->n_points) + { + ft_outline->contours[ft_outline->n_contours] = ft_outline->n_points - 1; + ft_outline->n_contours++; + } + + ft_outline->n_points++; +} + +static void +_outline_end(Outline *outline) +{ + SW_FT_Outline *ft_outline = &outline->ft_outline; + + if (ft_outline->n_contours == outline->contours_alloc) + { + outline->contours_alloc += 1; + ft_outline->contours = (short *) realloc(ft_outline->contours, outline->contours_alloc * sizeof(short)); + } + + if (ft_outline->n_points) + { + ft_outline->contours[ft_outline->n_contours] = ft_outline->n_points - 1; + ft_outline->n_contours++; + } +} + + +static void _outline_line_to(Outline *outline, double x, double y) +{ + SW_FT_Outline *ft_outline = &outline->ft_outline; + + if (ft_outline->n_points == outline->points_alloc) + { + outline->points_alloc += 50; + ft_outline->points = (SW_FT_Vector *) realloc(ft_outline->points, outline->points_alloc * sizeof(SW_FT_Vector)); + ft_outline->tags = (char *) realloc(ft_outline->tags, outline->points_alloc * sizeof(char)); + } + ft_outline->points[ft_outline->n_points].x = x; + ft_outline->points[ft_outline->n_points].y = y; + ft_outline->tags[ft_outline->n_points] = SW_FT_CURVE_TAG_ON; + ft_outline->n_points++; +} + + +static Eina_Bool +_outline_close_path(Outline *outline) +{ + SW_FT_Outline *ft_outline = &outline->ft_outline; + int index ; + + if (ft_outline->n_contours) + { + index = ft_outline->contours[ft_outline->n_contours - 1] + 1; + } + else + { + // first path + index = 0; + } + + // make sure there is atleast one point in the current path + if (ft_outline->n_points == index) return EINA_FALSE; + + _outline_line_to(outline, ft_outline->points[index].x, ft_outline->points[index].y); + return EINA_TRUE; +} + + +static void _outline_cubic_to(Outline *outline, double cx1, double cy1, double cx2, double cy2, double x, double y) +{ + SW_FT_Outline *ft_outline = &outline->ft_outline; + + if (ft_outline->n_points == outline->points_alloc) + { + outline->points_alloc += 50; + ft_outline->points = (SW_FT_Vector *) realloc(ft_outline->points, outline->points_alloc * sizeof(SW_FT_Vector)); + ft_outline->tags = (char *) realloc(ft_outline->tags, outline->points_alloc * sizeof(char)); + } + + ft_outline->points[ft_outline->n_points].x = cx1; + ft_outline->points[ft_outline->n_points].y = cy1; + ft_outline->tags[ft_outline->n_points] = SW_FT_CURVE_TAG_CUBIC; + ft_outline->n_points++; + + ft_outline->points[ft_outline->n_points].x = cx2; + ft_outline->points[ft_outline->n_points].y = cy2; + ft_outline->tags[ft_outline->n_points] = SW_FT_CURVE_TAG_CUBIC; + ft_outline->n_points++; + + ft_outline->points[ft_outline->n_points].x = x; + ft_outline->points[ft_outline->n_points].y = y; + ft_outline->tags[ft_outline->n_points] = SW_FT_CURVE_TAG_ON; + ft_outline->n_points++; +} + +static void _outline_transform(Outline *outline, Eina_Matrix3 *m) +{ + int i; + SW_FT_Outline *ft_outline = &outline->ft_outline; + + if (m) + { + double x, y; + for (i = 0; i < ft_outline->n_points ; i++) + { + eina_matrix3_point_transform(m, ft_outline->points[i].x, ft_outline->points[i].y, &x, &y); + ft_outline->points[i].x = (int)(x * 64);// to freetype 26.6 coordinate. + ft_outline->points[i].y = (int)(y * 64); + } + } + else + { + for (i = 0; i < ft_outline->n_points ; i++) + { + ft_outline->points[i].x = ft_outline->points[i].x <<6;// to freetype 26.6 coordinate. + ft_outline->points[i].y = ft_outline->points[i].y <<6; + } + } +} + + +static Eina_Bool +_ector_renderer_software_shape_ector_renderer_generic_base_prepare(Eo *obj, Ector_Renderer_Software_Shape_Data *pd) +{ + const Efl_Gfx_Path_Command *cmds = NULL; + const double *pts = NULL; + + // FIXME: shouldn't that be part of the shape generic implementation ? + if (pd->shape->fill) + eo_do(pd->shape->fill, ector_renderer_prepare()); + if (pd->shape->stroke.fill) + eo_do(pd->shape->stroke.fill, ector_renderer_prepare()); + if (pd->shape->stroke.marker) + eo_do(pd->shape->stroke.marker, ector_renderer_prepare()); + + // shouldn't that be moved to the software base object + if (!pd->surface) + { + Eo *parent; + eo_do(obj, parent = eo_parent_get()); + if (!parent) return EINA_FALSE; + pd->surface = eo_data_xref(parent, ECTOR_SOFTWARE_SURFACE_CLASS, obj); + if (!pd->surface) return EINA_FALSE; + } + + eo_do(obj, efl_gfx_shape_path_get(&cmds, &pts)); + if (!pd->shape_data && cmds) + { + Eina_Bool close_path = EINA_FALSE; + Outline * outline = _outline_create(); + + for (; *cmds != EFL_GFX_PATH_COMMAND_TYPE_END; cmds++) + { + switch (*cmds) + { + case EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO: + + _outline_move_to(outline, pts[0], pts[1]); + + pts += 2; + break; + case EFL_GFX_PATH_COMMAND_TYPE_LINE_TO: + + _outline_line_to(outline, pts[0], pts[1]); + + pts += 2; + break; + case EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO: + + // Be careful, we do have a different order than + // cairo, first is destination point, followed by + // the control point. The opposite of cairo. + _outline_cubic_to(outline, + pts[2], pts[3], pts[4], pts[5], // control points + pts[0], pts[1]); // destination point + pts += 6; + break; + + case EFL_GFX_PATH_COMMAND_TYPE_CLOSE: + + close_path = _outline_close_path(outline); + break; + + case EFL_GFX_PATH_COMMAND_TYPE_LAST: + case EFL_GFX_PATH_COMMAND_TYPE_END: + break; + } + } + + _outline_end(outline); + _outline_transform(outline, pd->base->m); + + // generate the shape data. + pd->shape_data = ector_software_rasterizer_generate_rle_data(pd->surface->software, &outline->ft_outline); + if (!pd->outline_data) + { + ector_software_rasterizer_stroke_set(pd->surface->software, (pd->shape->stroke.width * pd->shape->stroke.scale), pd->shape->stroke.cap, + pd->shape->stroke.join); + pd->outline_data = ector_software_rasterizer_generate_stroke_rle_data(pd->surface->software, &outline->ft_outline, close_path); + } + + _outline_destroy(outline); + } + + return EINA_TRUE; +} + +static Eina_Bool +_ector_renderer_software_shape_ector_renderer_generic_base_draw(Eo *obj EINA_UNUSED, Ector_Renderer_Software_Shape_Data *pd, Ector_Rop op, Eina_Array *clips, int x, int y, unsigned int mul_col) +{ + // adjust the offset + x = x + (int)pd->base->origin.x; + y = y + (int)pd->base->origin.y; + + // fill the span_data structure + ector_software_rasterizer_clip_rect_set(pd->surface->software, clips); + ector_software_rasterizer_transform_set(pd->surface->software, pd->base->m); + + if (pd->shape->fill) + { + eo_do(pd->shape->fill, ector_renderer_software_base_fill()); + ector_software_rasterizer_draw_rle_data(pd->surface->software, x, y, mul_col, op, pd->shape_data); + } + else + { + if (pd->base->color.a > 0) + { + ector_software_rasterizer_color_set(pd->surface->software, pd->base->color.r, pd->base->color.g, pd->base->color.b, pd->base->color.a); + ector_software_rasterizer_draw_rle_data(pd->surface->software, x, y, mul_col, op, pd->shape_data); + } + } + + if (pd->shape->stroke.fill) + { + eo_do(pd->shape->stroke.fill, ector_renderer_software_base_fill()); + ector_software_rasterizer_draw_rle_data(pd->surface->software, x, y, mul_col, op, pd->outline_data); + } + else + { + if (pd->shape->stroke.color.a > 0) + { + ector_software_rasterizer_color_set(pd->surface->software, + pd->shape->stroke.color.r, pd->shape->stroke.color.g, + pd->shape->stroke.color.b, pd->shape->stroke.color.a); + ector_software_rasterizer_draw_rle_data(pd->surface->software, x, y, mul_col, op, pd->outline_data); + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_ector_renderer_software_shape_ector_renderer_software_base_fill(Eo *obj EINA_UNUSED, Ector_Renderer_Software_Shape_Data *pd EINA_UNUSED) +{ + // FIXME: let's find out how to fill a shape with a shape later. + // I need to read SVG specification and see how to map that with software. + return EINA_FALSE; +} + +static void +_ector_renderer_software_shape_efl_gfx_shape_path_set(Eo *obj, Ector_Renderer_Software_Shape_Data *pd, + const Efl_Gfx_Path_Command *op, const double *points) +{ + if(pd->shape_data) ector_software_rasterizer_destroy_rle_data(pd->shape_data); + if(pd->outline_data) ector_software_rasterizer_destroy_rle_data(pd->outline_data); + pd->shape_data = NULL; + pd->outline_data = NULL; + + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_SHAPE_CLASS, efl_gfx_shape_path_set(op, points)); +} + + +void +_ector_renderer_software_shape_eo_base_constructor(Eo *obj, Ector_Renderer_Software_Shape_Data *pd) +{ + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_SHAPE_CLASS, eo_constructor()); + pd->shape = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_SHAPE_MIXIN, obj); + pd->base = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_BASE_CLASS, obj); +} + +void +_ector_renderer_software_shape_eo_base_destructor(Eo *obj, Ector_Renderer_Software_Shape_Data *pd) +{ + Eo *parent; + + if(pd->shape_data) ector_software_rasterizer_destroy_rle_data(pd->shape_data); + if(pd->outline_data) ector_software_rasterizer_destroy_rle_data(pd->outline_data); + + eo_do(obj, parent = eo_parent_get()); + eo_data_xunref(parent, pd->surface, obj); + + eo_data_xunref(obj, pd->shape, obj); + eo_data_xunref(obj, pd->base, obj); + eo_do_super(obj, ECTOR_RENDERER_SOFTWARE_SHAPE_CLASS, eo_destructor()); +} + + +#include "ector_renderer_software_shape.eo.c" diff --git a/src/lib/ector/software/ector_renderer_software_shape.eo b/src/lib/ector/software/ector_renderer_software_shape.eo new file mode 100644 index 0000000000..267ef7cc7e --- /dev/null +++ b/src/lib/ector/software/ector_renderer_software_shape.eo @@ -0,0 +1,13 @@ +class Ector.Renderer.Software.Shape (Ector.Renderer.Software.Base, Ector.Renderer.Generic.Shape) +{ + eo_prefix: ector_renderer_software_shape; + legacy_prefix: null; + implements { + Ector.Renderer.Generic.Base.prepare; + Ector.Renderer.Generic.Base.draw; + Ector.Renderer.Software.Base.fill; + Efl.Gfx.Shape.path.set; + Eo.Base.constructor; + Eo.Base.destructor; + } +} diff --git a/src/lib/ector/software/ector_software_gradient.c b/src/lib/ector/software/ector_software_gradient.c new file mode 100644 index 0000000000..ed2b9fe985 --- /dev/null +++ b/src/lib/ector/software/ector_software_gradient.c @@ -0,0 +1,269 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +//Remove +#include + +#include +#include + +#include +#include +#include + +#include "ector_private.h" +#include "ector_software_private.h" +#include "ector_blend_private.h" + + +#define GRADIENT_STOPTABLE_SIZE 1024 +#define FIXPT_BITS 8 +#define FIXPT_SIZE (1<gd->s == EFL_GFX_GRADIENT_SPREAD_REPEAT) + { + ipos = ipos % GRADIENT_STOPTABLE_SIZE; + ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos; + } + else if (data->gd->s == EFL_GFX_GRADIENT_SPREAD_REFLECT) + { + const int limit = GRADIENT_STOPTABLE_SIZE * 2; + ipos = ipos % limit; + ipos = ipos < 0 ? limit + ipos : ipos; + ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - 1 - ipos : ipos; + } + else + { + if (ipos < 0) + ipos = 0; + else if (ipos >= GRADIENT_STOPTABLE_SIZE) + ipos = GRADIENT_STOPTABLE_SIZE-1; + } + + return ipos; +} + + +static uint +_gradient_pixel_fixed(const Ector_Renderer_Software_Gradient_Data *data, int fixed_pos) +{ + int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; + return data->colorTable[_gradient_clamp(data, ipos)]; +} + +static inline uint +_gradient_pixel(const Ector_Renderer_Software_Gradient_Data *data, float pos) +{ + int ipos = (int)(pos * (GRADIENT_STOPTABLE_SIZE - 1) + (float)(0.5)); + return data->colorTable[_gradient_clamp(data, ipos)]; +} + +typedef double (*BLEND_FUNC)(double progress); + +static double +_ease_linear(double t) +{ + return t; +} + +static void +_generate_gradient_color_table(Efl_Gfx_Gradient_Stop *gradient_stops, int stop_count, uint *colorTable, int size) +{ + int pos = 0; + Efl_Gfx_Gradient_Stop *curr, *next; + assert(stop_count > 0); + + curr = gradient_stops; + uint current_color = ECTOR_ARGB_JOIN(curr->a, curr->r, curr->g, curr->b); + double incr = 1.0 / (double)size; + double fpos = 1.5 * incr; + current_color = _ector_premultiply(current_color); + + colorTable[pos++] = current_color; + + while (fpos <= curr->offset) + { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + for (int i = 0; i < stop_count - 1; ++i) + { + curr = (gradient_stops + i); + next = (gradient_stops + i + 1); + double delta = 1/(next->offset - curr->offset); + uint next_color = ECTOR_ARGB_JOIN(next->a, next->r, next->g, next->b); + next_color = _ector_premultiply(next_color); + BLEND_FUNC func = &_ease_linear; + while (fpos < next->offset && pos < size) + { + double t = func((fpos - curr->offset) * delta); + int dist = (int)(256 * t); + int idist = 256 - dist; + colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + uint last_color = _ector_premultiply(current_color); + for (;pos < size; ++pos) + colorTable[pos] = last_color; + + // Make sure the last color stop is represented at the end of the table + colorTable[size-1] = last_color; +} + + +void +update_color_table(Ector_Renderer_Software_Gradient_Data *gdata) +{ + if(gdata->colorTable) return; + + gdata->colorTable = malloc(GRADIENT_STOPTABLE_SIZE * 4); + _generate_gradient_color_table(gdata->gd->colors, gdata->gd->colors_count, gdata->colorTable, GRADIENT_STOPTABLE_SIZE); +} + +void +destroy_color_table(Ector_Renderer_Software_Gradient_Data *gdata) +{ + if (gdata->colorTable) + { + free(gdata->colorTable); + gdata->colorTable = NULL; + } +} + + +void +fetch_linear_gradient(uint *buffer, Span_Data *data, int y, int x, int length) +{ + Ector_Renderer_Software_Gradient_Data *g_data = data->gradient; + float t, inc; + float rx=0, ry=0; + + if (g_data->linear.l == 0) + { + t = inc = 0; + } + else + { + rx = data->inv.xy * (y + (float)0.5) + data->inv.xz + data->inv.xx * (x + (float)0.5); + ry = data->inv.yy * (y + (float)0.5) + data->inv.yz + data->inv.yx * (x + (float)0.5); + t = g_data->linear.dx*rx + g_data->linear.dy*ry + g_data->linear.off; + inc = g_data->linear.dx * data->inv.xx + g_data->linear.dx * data->inv.yx; + + t *= (GRADIENT_STOPTABLE_SIZE - 1); + inc *= (GRADIENT_STOPTABLE_SIZE - 1); + } + + uint *end = buffer + length; + if (inc > (float)(-1e-5) && inc < (float)(1e-5)) + { + _ector_memfill(buffer, _gradient_pixel_fixed(g_data, (int)(t * FIXPT_SIZE)), length); + } + else + { + if (t + inc*length < (float)(INT_MAX >> (FIXPT_BITS + 1)) && + t+inc*length > (float)(INT_MIN >> (FIXPT_BITS + 1))) + { + // we can use fixed point math + int t_fixed = (int)(t * FIXPT_SIZE); + int inc_fixed = (int)(inc * FIXPT_SIZE); + // #ifdef BUILD_SSE3 + // if (evas_common_cpu_has_feature(CPU_FEATURE_SSE3)) { + // _fetch_linear_sse3(buffer, length, g_data, t_fixed, inc_fixed); + // } else + // #endif + { + while (buffer < end) + { + *buffer++ = _gradient_pixel_fixed(g_data, t_fixed); + t_fixed += inc_fixed; + } + } + } + else + { + // we have to fall back to float math + while (buffer < end) { + *buffer++ = _gradient_pixel(g_data, t/GRADIENT_STOPTABLE_SIZE); + t += inc; + } + } + } +} + +static void +_radial_helper_generic(uint *buffer, int length, Ector_Renderer_Software_Gradient_Data *g_data, float det, + float delta_det, float delta_delta_det, float b, float delta_b) +{ + for (int i = 0 ; i < length ; i++) + { + *buffer++ = _gradient_pixel(g_data, sqrt(det) - b); + det += delta_det; + delta_det += delta_delta_det; + b += delta_b; + } +} + +void +fetch_radial_gradient(uint *buffer, Span_Data *data, int y, int x, int length) +{ + Ector_Renderer_Software_Gradient_Data *g_data = data->gradient; + + // avoid division by zero + if (abs(g_data->radial.a) <= 0.00001f) + { + _ector_memfill(buffer, 0, length); + return; + } + + float rx = data->inv.xy * (y + (float)0.5) + data->inv.xz + data->inv.xx * (x + (float)0.5); + float ry = data->inv.yy * (y + (float)0.5) + data->inv.yz + data->inv.yx * (x + (float)0.5); + + rx -= g_data->radial.fx; + ry -= g_data->radial.fy; + + float inv_a = 1 / (float)(2 * g_data->radial.a); + + const float delta_rx = data->inv.xx; + const float delta_ry = data->inv.yx; + + float b = 2*(g_data->radial.dr*g_data->radial.fradius + rx * g_data->radial.dx + ry * g_data->radial.dy); + float delta_b = 2*(delta_rx * g_data->radial.dx + delta_ry * g_data->radial.dy); + const float b_delta_b = 2 * b * delta_b; + const float delta_b_delta_b = 2 * delta_b * delta_b; + + const float bb = b * b; + const float delta_bb = delta_b * delta_b; + b *= inv_a; + delta_b *= inv_a; + + const float rxrxryry = rx * rx + ry * ry; + const float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry; + const float rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry); + const float delta_rx_plus_ry = 2 * delta_rxrxryry; + + inv_a *= inv_a; + + float det = (bb - 4 * g_data->radial.a * (g_data->radial.sqrfr - rxrxryry)) * inv_a; + float delta_det = (b_delta_b + delta_bb + 4 * g_data->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a; + const float delta_delta_det = (delta_b_delta_b + 4 * g_data->radial.a * delta_rx_plus_ry) * inv_a; + + // #ifdef BUILD_SSE3 + // if (evas_common_cpu_has_feature(CPU_FEATURE_SSE3)) { + // _radial_helper_sse3(buffer, length, g_data, det, delta_det, delta_delta_det, b, delta_b); + // } else + // #endif + { // generic fallback + _radial_helper_generic(buffer, length, g_data, det, delta_det, delta_delta_det, b, delta_b); + } +} diff --git a/src/lib/ector/software/ector_software_private.h b/src/lib/ector/software/ector_software_private.h new file mode 100644 index 0000000000..b43dcfea06 --- /dev/null +++ b/src/lib/ector/software/ector_software_private.h @@ -0,0 +1,150 @@ +#ifndef ECTOR_SOFTWARE_PRIVATE_H_ +# define ECTOR_SOFTWARE_PRIVATE_H_ + +#include "sw_ft_raster.h" +#include "sw_ft_stroker.h" + +#ifndef DATA32 +typedef unsigned int DATA32; +#endif + +#ifndef uint +typedef unsigned int uint; +#endif + +typedef struct _Ector_Software_Surface_Data Ector_Software_Surface_Data; + +#define CHECK_SOFTWARE(Parent) (!(Parent && Parent->software)) + +// Gradient related structure +typedef struct _Software_Gradient_Linear_Data +{ + float x1, y1, x2, y2; + float dx, dy, l, off; +} Software_Gradient_Linear_Data; + +typedef struct _Software_Gradient_Radial_Data +{ + float cx, cy, fx, fy, cradius, fradius; + float dx, dy, dr, sqrfr, a, inv2a; + Eina_Bool extended; +} Software_Gradient_Radial_Data; + +typedef struct _Ector_Renderer_Software_Gradient_Data +{ + Ector_Software_Surface_Data *surface; + Ector_Renderer_Generic_Gradient_Data *gd; + union { + Ector_Renderer_Generic_Gradient_Linear_Data *gld; + Ector_Renderer_Generic_Gradient_Radial_Data *grd; + }; + union { + Software_Gradient_Linear_Data linear; + Software_Gradient_Radial_Data radial; + }; + uint* colorTable; +} Ector_Renderer_Software_Gradient_Data; + + +// Rasterizer related structure +typedef struct _Raster_Buffer +{ + int width; + int height; + DATA32 *buffer; +} Raster_Buffer; + +typedef struct _Shape_Rle_Data +{ + unsigned short alloc; + unsigned short size; + SW_FT_Span *spans;// array of Scanlines. +} Shape_Rle_Data; + +typedef struct _Clip_Data +{ + Eina_Array *clips; //Eina_Rectangle + Shape_Rle_Data *path; + unsigned int enabled : 1; + unsigned int hasRectClip : 1; + unsigned int hasPathClip : 1; +} Clip_Data; + + +typedef enum _Span_Data_Type { + None, + Solid, + LinearGradient, + RadialGradient, + Image +} Span_Data_Type; + +typedef struct _Span_Data +{ + Raster_Buffer raster_buffer; + + SW_FT_SpanFunc blend; + SW_FT_SpanFunc unclipped_blend; + + int offx, offy; + Clip_Data clip; + Eina_Matrix3 inv; + Span_Data_Type type; + Eina_Bool fast_matrix ; + DATA32 mul_col; + Ector_Rop op; + union { + DATA32 color; + Ector_Renderer_Software_Gradient_Data *gradient; + //ImageData texture; + }; +} Span_Data; + +typedef struct _Software_Rasterizer +{ + SW_FT_Raster raster; + SW_FT_Stroker stroker; + + Span_Data fillData; + Eina_Matrix3 *transform; + Eina_Rectangle systemClip; + +} Software_Rasterizer; + +struct _Ector_Software_Surface_Data +{ + Software_Rasterizer *software; +}; + + +void ector_software_rasterizer_init(Software_Rasterizer *rasterizer); +void ector_software_rasterizer_done(Software_Rasterizer *rasterizer); + +void ector_software_rasterizer_stroke_set(Software_Rasterizer *rasterizer, double width, + Efl_Gfx_Cap cap_style, Efl_Gfx_Join join_style); + +void ector_software_rasterizer_transform_set(Software_Rasterizer *rasterizer, Eina_Matrix3 *t); +void ector_software_rasterizer_color_set(Software_Rasterizer *rasterizer, int r, int g, int b, int a); +void ector_software_rasterizer_linear_gradient_set(Software_Rasterizer *rasterizer, Ector_Renderer_Software_Gradient_Data *linear); +void ector_software_rasterizer_radial_gradient_set(Software_Rasterizer *rasterizer, Ector_Renderer_Software_Gradient_Data *radial); +void ector_software_rasterizer_clip_rect_set(Software_Rasterizer *rasterizer, Eina_Array *clips); +void ector_software_rasterizer_clip_shape_set(Software_Rasterizer *rasterizer, Shape_Rle_Data *clip); + + + +Shape_Rle_Data * ector_software_rasterizer_generate_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline); +Shape_Rle_Data * ector_software_rasterizer_generate_stroke_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline, Eina_Bool closePath); + +void ector_software_rasterizer_draw_rle_data(Software_Rasterizer *rasterizer, int x, int y, uint mul_col, Ector_Rop op, Shape_Rle_Data* rle); + +void ector_software_rasterizer_destroy_rle_data(Shape_Rle_Data *rle); + + + +// Gradient Api +void update_color_table(Ector_Renderer_Software_Gradient_Data *gdata); +void destroy_color_table(Ector_Renderer_Software_Gradient_Data *gdata); +void fetch_linear_gradient(uint *buffer, Span_Data *data, int y, int x, int length); +void fetch_radial_gradient(uint *buffer, Span_Data *data, int y, int x, int length); + +#endif diff --git a/src/lib/ector/software/ector_software_rasterizer.c b/src/lib/ector/software/ector_software_rasterizer.c new file mode 100644 index 0000000000..c1f6cae026 --- /dev/null +++ b/src/lib/ector/software/ector_software_rasterizer.c @@ -0,0 +1,437 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "ector_private.h" +#include "ector_software_private.h" +#include "ector_blend_private.h" + +static void +_blend_color_argb(int count, const SW_FT_Span *spans, void *userData) +{ + Span_Data *data = (Span_Data *)(userData); + + // multiply the color with mul_col if any + uint color = ECTOR_MUL4_SYM(data->color, data->mul_col); + Eina_Bool solidSource = ((color >> 24) == 255); + + // move to the offset location + uint *buffer = data->raster_buffer.buffer + (data->raster_buffer.width * data->offy + data->offx); + + if (solidSource) + { + while (count--) + { + uint *target = buffer + (data->raster_buffer.width * spans->y + spans->x); + if (spans->coverage == 255) + { + _ector_memfill(target, color, spans->len); + } + else + { + uint c = ECTOR_MUL_256(color, spans->coverage); + int ialpha = 255 - spans->coverage; + for (int i = 0; i < spans->len; ++i) + target[i] = c + ECTOR_MUL_256(target[i], ialpha); + } + ++spans; + } + return; + } + + while (count--) + { + uint *target = buffer + (data->raster_buffer.width * spans->y + spans->x); + uint c = ECTOR_MUL_256(color, spans->coverage); + int ialpha = (~c) >> 24; + + for (int i = 0; i < spans->len; ++i) + target[i] = c + ECTOR_MUL_256(target[i], ialpha); + ++spans; + } +} + +int buffer_size = 2048; + +typedef void (*src_fetch) (unsigned int *buffer, Span_Data *data, int y, int x, int length); + +static void +_blend_gradient(int count, const SW_FT_Span *spans, void *userData) +{ + Span_Data *data = (Span_Data *)(userData); + src_fetch fetchfunc = NULL; + + if(data->type == LinearGradient) fetchfunc = &fetch_linear_gradient; + if(data->type == RadialGradient) fetchfunc = &fetch_radial_gradient; + + unsigned int buffer[buffer_size]; + + // move to the offset location + unsigned int *destbuffer = data->raster_buffer.buffer + (data->raster_buffer.width * data->offy + data->offx); + + while (count--) + { + unsigned int *target = destbuffer + (data->raster_buffer.width * spans->y + spans->x); + int length = spans->len; + while (length) + { + int l = MIN(length, buffer_size); + fetchfunc(buffer, data, spans->y, spans->x, l); + if (data->mul_col == 0xffffffff) + _ector_comp_func_source_over(target, buffer, l, spans->coverage); // TODO use proper composition func + else + _ector_comp_func_source_over_mul_c(target, buffer, data->mul_col, l, spans->coverage); + target += l; + length -= l; + } + ++spans; + } +} + + +/*! + \internal + spans must be sorted on y +*/ +static const +SW_FT_Span *_intersect_spans_rect(const Eina_Rectangle *clip, const SW_FT_Span *spans, const SW_FT_Span *end, + SW_FT_Span **outSpans, int available) +{ + SW_FT_Span *out = *outSpans; + const short minx = clip->x; + const short miny = clip->y; + const short maxx = minx + clip->w - 1; + const short maxy = miny + clip->h - 1; + + while (available && spans < end ) + { + if (spans->y > maxy) + { + spans = end;// update spans so that we can breakout + break; + } + if (spans->y < miny + || spans->x > maxx + || spans->x + spans->len <= minx) + { + ++spans; + continue; + } + if (spans->x < minx) + { + out->len = MIN(spans->len - (minx - spans->x), maxx - minx + 1); + out->x = minx; + } + else + { + out->x = spans->x; + out->len = MIN(spans->len, (maxx - spans->x + 1)); + } + if (out->len != 0) + { + out->y = spans->y; + out->coverage = spans->coverage; + ++out; + } + + ++spans; + --available; + } + + *outSpans = out; + + return spans; +} + +static void +_span_fill_clipRect(int spanCount, const SW_FT_Span *spans, void *userData) +{ + const int NSPANS = 256; + int clip_count, i; + SW_FT_Span cspans[NSPANS]; + Span_Data *fillData = (Span_Data *) userData; + Clip_Data clip = fillData->clip; + + clip_count = eina_array_count(clip.clips); + for (i = 0; i < clip_count ; i ++) + { + Eina_Rectangle *rect = (Eina_Rectangle *)eina_array_data_get(clip.clips, i); + Eina_Rectangle tmpRect; + + // invert transform the offset + tmpRect.x = rect->x - fillData->offx; + tmpRect.y = rect->y - fillData->offy; + tmpRect.w = rect->w; + tmpRect.h = rect->h; + //printf("Clip after Offset : %d , %d ,%d , %d\n",tmpRect.x, tmpRect.y, tmpRect.w, tmpRect.h); + //printf("Offset = %d , %d \n", fillData->offx, fillData->offy); + const SW_FT_Span *end = spans + spanCount; + + while (spans < end) + { + SW_FT_Span *clipped = cspans; + spans = _intersect_spans_rect(&tmpRect,spans, end, &clipped, NSPANS); + if (clipped - cspans) + fillData->unclipped_blend(clipped - cspans, cspans, fillData); + } + } +} + +static void +_adjust_span_fill_methods(Span_Data *spdata) +{ + switch(spdata->type) + { + case None: + spdata->unclipped_blend = 0; + break; + case Solid: + spdata->unclipped_blend = &_blend_color_argb; + break; + case LinearGradient: + case RadialGradient: + spdata->unclipped_blend = &_blend_gradient; + break; + case Image: + spdata->unclipped_blend = 0;//&_blend_image; + break; + } + + // setup clipping + if (!spdata->unclipped_blend) + { + spdata->blend = 0; + } + else if (!spdata->clip.enabled) + { + spdata->blend = spdata->unclipped_blend; + } + else if (spdata->clip.hasRectClip) + { + spdata->blend = &_span_fill_clipRect; + } + else + { + spdata->blend = &_span_fill_clipRect; //TODO change when do path clipping + } +} + + + +void ector_software_rasterizer_init(Software_Rasterizer *rasterizer) +{ + // initialize the rasterizer and stroker + unsigned char* renderPool = (unsigned char*) malloc(1024 * 100); + sw_ft_grays_raster.raster_new(&rasterizer->raster); + sw_ft_grays_raster.raster_reset(rasterizer->raster, renderPool, 1024*100); + + SW_FT_Stroker_New(&rasterizer->stroker); + SW_FT_Stroker_Set(rasterizer->stroker, 1<<6,SW_FT_STROKER_LINECAP_BUTT,SW_FT_STROKER_LINEJOIN_MITER,0); + + //initialize the span data. + rasterizer->fillData.raster_buffer.buffer = NULL; + rasterizer->fillData.clip.enabled = EINA_FALSE; + rasterizer->fillData.unclipped_blend = 0; + rasterizer->fillData.blend = 0; +} + +void ector_software_rasterizer_done(Software_Rasterizer *rasterizer) +{ + sw_ft_grays_raster.raster_done(rasterizer->raster); + SW_FT_Stroker_Done(rasterizer->stroker); + //TODO free the pool memory +} + + +void ector_software_rasterizer_stroke_set(Software_Rasterizer *rasterizer, double width, + Efl_Gfx_Cap cap_style, Efl_Gfx_Join join_style) +{ + SW_FT_Stroker_LineCap cap; + SW_FT_Stroker_LineJoin join; + + switch (cap_style) + { + case EFL_GFX_CAP_SQUARE: + cap = SW_FT_STROKER_LINECAP_SQUARE; + break; + case EFL_GFX_CAP_ROUND: + cap = SW_FT_STROKER_LINECAP_ROUND; + break; + default: + cap = SW_FT_STROKER_LINECAP_BUTT; + break; + } + + switch (join_style) + { + case EFL_GFX_JOIN_BEVEL: + join = SW_FT_STROKER_LINEJOIN_BEVEL; + break; + case EFL_GFX_JOIN_ROUND: + join = SW_FT_STROKER_LINEJOIN_ROUND; + break; + default: + join = SW_FT_STROKER_LINEJOIN_MITER; + break; + } + + int stroke_width = (int)(width * 64); + SW_FT_Stroker_Set(rasterizer->stroker, stroke_width, cap, join, 0); +} + +static void +_rle_generation_cb( int count, const SW_FT_Span* spans,void *user) +{ + Shape_Rle_Data *rle = (Shape_Rle_Data *) user; + int newsize = rle->size + count; + + // allocate enough memory for new spans + // alloc is required to prevent free and reallocation + // when the rle needs to be regenerated because of attribute change. + if(rle->alloc < newsize) + { + rle->spans = (SW_FT_Span *) realloc(rle->spans, newsize * sizeof(SW_FT_Span)); + rle->alloc = newsize; + } + + // copy the new spans to the allocated memory + SW_FT_Span *lastspan = (rle->spans + rle->size); + memcpy(lastspan,spans, count * sizeof(SW_FT_Span)); + + // update the size + rle->size = newsize; +} + +Shape_Rle_Data * +ector_software_rasterizer_generate_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline) +{ + Shape_Rle_Data *rle_data = (Shape_Rle_Data *) calloc(1, sizeof(Shape_Rle_Data)); + SW_FT_Raster_Params params; + + params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ; + params.gray_spans = &_rle_generation_cb; + params.user = rle_data; + params.source = outline; + + sw_ft_grays_raster.raster_render(rasterizer->raster, ¶ms); + + return rle_data; +} + +Shape_Rle_Data * +ector_software_rasterizer_generate_stroke_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline, Eina_Bool closePath) +{ + uint points,contors; + + SW_FT_Stroker_ParseOutline(rasterizer->stroker, outline, !closePath); + SW_FT_Stroker_GetCounts(rasterizer->stroker,&points, &contors); + + SW_FT_Outline strokeOutline = {0}; + strokeOutline.points = (SW_FT_Vector *) calloc(points, sizeof(SW_FT_Vector)); + strokeOutline.tags = (char *) calloc(points, sizeof(char)); + strokeOutline.contours = (short *) calloc(contors, sizeof(short)); + + SW_FT_Stroker_Export(rasterizer->stroker, &strokeOutline); + + Shape_Rle_Data *rle_data = ector_software_rasterizer_generate_rle_data(rasterizer, &strokeOutline); + + // cleanup the outline data. + free(strokeOutline.points); + free(strokeOutline.tags); + free(strokeOutline.contours); + + return rle_data; +} + +void ector_software_rasterizer_destroy_rle_data(Shape_Rle_Data *rle) +{ + if (rle) + { + if (rle->spans) + free(rle->spans); + free(rle); + } +} + +static +void _setup_span_fill_matrix(Software_Rasterizer *rasterizer) +{ + if (rasterizer->transform) + { + eina_matrix3_inverse(rasterizer->transform, &rasterizer->fillData.inv); + } + else + { + eina_matrix3_identity(&rasterizer->fillData.inv); + eina_matrix3_identity(&rasterizer->fillData.inv); + } +} + +void ector_software_rasterizer_transform_set(Software_Rasterizer *rasterizer, Eina_Matrix3 *t) +{ + rasterizer->transform = t; +} + +void ector_software_rasterizer_clip_rect_set(Software_Rasterizer *rasterizer, Eina_Array *clips) +{ + if (clips) + { + rasterizer->fillData.clip.clips = clips; + rasterizer->fillData.clip.hasRectClip = EINA_TRUE; + rasterizer->fillData.clip.enabled = EINA_TRUE; + } + else + { + rasterizer->fillData.clip.clips = NULL; + rasterizer->fillData.clip.hasRectClip = EINA_FALSE; + rasterizer->fillData.clip.enabled = EINA_FALSE; + } +} + +void ector_software_rasterizer_clip_shape_set(Software_Rasterizer *rasterizer, Shape_Rle_Data *clip) +{ + rasterizer->fillData.clip.path = clip; + rasterizer->fillData.clip.hasPathClip = EINA_TRUE; + rasterizer->fillData.clip.enabled = EINA_TRUE; +} + +void ector_software_rasterizer_color_set(Software_Rasterizer *rasterizer, int r, int g, int b, int a) +{ + uint color = ECTOR_ARGB_JOIN(a, r, g, b); + + rasterizer->fillData.color = _ector_premultiply(color); + rasterizer->fillData.type = Solid; +} +void ector_software_rasterizer_linear_gradient_set(Software_Rasterizer *rasterizer, Ector_Renderer_Software_Gradient_Data *linear) +{ + rasterizer->fillData.gradient = linear; + rasterizer->fillData.type = LinearGradient; +} +void ector_software_rasterizer_radial_gradient_set(Software_Rasterizer *rasterizer, Ector_Renderer_Software_Gradient_Data *radial) +{ + rasterizer->fillData.gradient = radial; + rasterizer->fillData.type = RadialGradient; +} + + +void ector_software_rasterizer_draw_rle_data(Software_Rasterizer *rasterizer, + int x, int y, uint mul_col, Ector_Rop op, Shape_Rle_Data* rle) +{ + // check for NULL rle data + if (!rle) return; + + rasterizer->fillData.offx = x; + rasterizer->fillData.offy = y; + rasterizer->fillData.mul_col = mul_col; + rasterizer->fillData.op = op; + + _setup_span_fill_matrix(rasterizer); + _adjust_span_fill_methods(&rasterizer->fillData); + + if(rasterizer->fillData.blend) + rasterizer->fillData.blend(rle->size, rle->spans, &rasterizer->fillData); +} diff --git a/src/lib/ector/software/ector_software_surface.c b/src/lib/ector/software/ector_software_surface.c new file mode 100644 index 0000000000..e32477ecc0 --- /dev/null +++ b/src/lib/ector/software/ector_software_surface.c @@ -0,0 +1,94 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "ector_private.h" +#include "ector_software_private.h" + +static unsigned int _software_count = 0; + +typedef struct _Ector_Renderer_Software_Base_Data Ector_Renderer_Software_Base_Data; +struct _Ector_Renderer_Software_Base_Data +{ +}; + +static Ector_Renderer * +_ector_software_surface_ector_generic_surface_renderer_factory_new(Eo *obj, + Ector_Software_Surface_Data *pd EINA_UNUSED, + const Eo_Class *type) +{ + if (type == ECTOR_RENDERER_GENERIC_SHAPE_MIXIN) + return eo_add(ECTOR_RENDERER_SOFTWARE_SHAPE_CLASS, obj); + else if (type == ECTOR_RENDERER_GENERIC_GRADIENT_LINEAR_MIXIN) + return eo_add(ECTOR_RENDERER_SOFTWARE_GRADIENT_LINEAR_CLASS, obj); + else if (type == ECTOR_RENDERER_GENERIC_GRADIENT_RADIAL_MIXIN) + return eo_add(ECTOR_RENDERER_SOFTWARE_GRADIENT_RADIAL_CLASS, obj); + ERR("Couldn't find class for type: %s\n", eo_class_name_get(type)); + return NULL; +} + +static void +_ector_software_surface_context_set(Eo *obj EINA_UNUSED, + Ector_Software_Surface_Data *pd, + Software_Rasterizer *ctx) +{ + pd->software = ctx; +} + +static Software_Rasterizer * +_ector_software_surface_context_get(Eo *obj EINA_UNUSED, + Ector_Software_Surface_Data *pd) +{ + return pd->software; +} + +void +_ector_software_surface_surface_set(Eo *obj EINA_UNUSED, + Ector_Software_Surface_Data *pd, + void *pixels, unsigned int width, unsigned int height) +{ + pd->software->fillData.raster_buffer.buffer = pixels; + pd->software->fillData.raster_buffer.width = width; + pd->software->fillData.raster_buffer.height = height; +} + +void +_ector_software_surface_surface_get(Eo *obj EINA_UNUSED, + Ector_Software_Surface_Data *pd, + void **pixels, unsigned int *width, unsigned int *height) +{ + *pixels = pd->software->fillData.raster_buffer.buffer; + *width = pd->software->fillData.raster_buffer.width; + *height = pd->software->fillData.raster_buffer.height; +} + +static void +_ector_software_surface_eo_base_constructor(Eo *obj, + Ector_Software_Surface_Data *pd EINA_UNUSED) +{ + eo_do_super(obj, ECTOR_SOFTWARE_SURFACE_CLASS, eo_constructor()); + if(_software_count == 0) + { + pd->software = (Software_Rasterizer *) calloc(1, sizeof(Software_Rasterizer)); + ector_software_rasterizer_init(pd->software); + } + _software_count++; +} + +static void +_ector_software_surface_eo_base_destructor(Eo *obj EINA_UNUSED, + Ector_Software_Surface_Data *pd EINA_UNUSED) +{ + --_software_count; + if (_software_count > 0) return; + ector_software_rasterizer_done(pd->software); + free(pd->software); + pd->software = NULL; + eo_do_super(obj, ECTOR_SOFTWARE_SURFACE_CLASS, eo_destructor()); +} + +#include "ector_software_surface.eo.c" +#include "ector_renderer_software_base.eo.c" diff --git a/src/lib/ector/software/ector_software_surface.eo b/src/lib/ector/software/ector_software_surface.eo new file mode 100644 index 0000000000..58a6a771f6 --- /dev/null +++ b/src/lib/ector/software/ector_software_surface.eo @@ -0,0 +1,33 @@ +class Ector.Software.Surface (Ector.Generic.Surface) +{ + eo_prefix: ector_software_surface; + legacy_prefix: null; + properties { + context { + set { + } + get { + } + values { + Software_Rasterizer *ctx; + } + } + surface { + set { + } + get { + } + values { + void *pixels; + uint width; + uint height; + } + } + } + + implements { + Ector.Generic.Surface.renderer_factory_new; + Eo.Base.destructor; + Eo.Base.constructor; + } +} diff --git a/src/lib/ector/software/sw_ft_math.c b/src/lib/ector/software/sw_ft_math.c new file mode 100755 index 0000000000..9b3894ff8d --- /dev/null +++ b/src/lib/ector/software/sw_ft_math.c @@ -0,0 +1,528 @@ +/***************************************************************************/ +/* */ +/* fttrigon.c */ +/* */ +/* FreeType trigonometric functions (body). */ +/* */ +/* Copyright 2001-2005, 2012-2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include +#include "sw_ft_math.h" + + +#define SW_FT_MSB( x ) ( 31 - __builtin_clz( x ) ) + +#define SW_FT_PAD_FLOOR( x, n ) ( (x) & ~((n)-1) ) +#define SW_FT_PAD_ROUND( x, n ) SW_FT_PAD_FLOOR( (x) + ((n)/2), n ) +#define SW_FT_PAD_CEIL( x, n ) SW_FT_PAD_FLOOR( (x) + ((n)-1), n ) + + +#define SW_FT_BEGIN_STMNT do { +#define SW_FT_END_STMNT } while ( 0 ) +/* transfer sign leaving a positive number */ +#define SW_FT_MOVE_SIGN( x, s ) \ +SW_FT_BEGIN_STMNT \ + if ( x < 0 ) \ + { \ + x = -x; \ + s = -s; \ + } \ +SW_FT_END_STMNT + + + + +SW_FT_Long +SW_FT_MulFix( SW_FT_Long a, + SW_FT_Long b ) +{ + SW_FT_Int s = 1; + SW_FT_Long c; + + + SW_FT_MOVE_SIGN( a, s ); + SW_FT_MOVE_SIGN( b, s ); + + c = (SW_FT_Long)( ( (SW_FT_Int64)a * b + 0x8000L ) >> 16 ); + + return ( s > 0 ) ? c : -c; +} + +SW_FT_Long +SW_FT_MulDiv( SW_FT_Long a, + SW_FT_Long b, + SW_FT_Long c ) +{ + SW_FT_Int s = 1; + SW_FT_Long d; + + + SW_FT_MOVE_SIGN( a, s ); + SW_FT_MOVE_SIGN( b, s ); + SW_FT_MOVE_SIGN( c, s ); + + d = (SW_FT_Long)( c > 0 ? ( (SW_FT_Int64)a * b + ( c >> 1 ) ) / c + : 0x7FFFFFFFL ); + + return ( s > 0 ) ? d : -d; +} + +SW_FT_Long +SW_FT_DivFix( SW_FT_Long a, + SW_FT_Long b ) +{ + SW_FT_Int s = 1; + SW_FT_Long q; + + + SW_FT_MOVE_SIGN( a, s ); + SW_FT_MOVE_SIGN( b, s ); + + q = (SW_FT_Long)( b > 0 ? ( ( (SW_FT_UInt64)a << 16 ) + ( b >> 1 ) ) / b + : 0x7FFFFFFFL ); + + return ( s < 0 ? -q : q ); +} + + +/*************************************************************************/ +/* */ +/* This is a fixed-point CORDIC implementation of trigonometric */ +/* functions as well as transformations between Cartesian and polar */ +/* coordinates. The angles are represented as 16.16 fixed-point values */ +/* in degrees, i.e., the angular resolution is 2^-16 degrees. Note that */ +/* only vectors longer than 2^16*180/pi (or at least 22 bits) on a */ +/* discrete Cartesian grid can have the same or better angular */ +/* resolution. Therefore, to maintain this precision, some functions */ +/* require an interim upscaling of the vectors, whereas others operate */ +/* with 24-bit long vectors directly. */ +/* */ +/*************************************************************************/ + + /* the Cordic shrink factor 0.858785336480436 * 2^32 */ +#define SW_FT_TRIG_SCALE 0xDBD95B16UL + + /* the highest bit in overflow-safe vector components, */ + /* MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */ +#define SW_FT_TRIG_SAFE_MSB 29 + + /* this table was generated for SW_FT_PI = 180L << 16, i.e. degrees */ +#define SW_FT_TRIG_MAX_ITERS 23 + + static const SW_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 + }; + + /* multiply a given value by the CORDIC shrink factor */ + static SW_FT_Fixed + ft_trig_downscale( SW_FT_Fixed val ) + { + SW_FT_Fixed s; + SW_FT_Int64 v; + + + s = val; + val = SW_FT_ABS( val ); + + v = ( val * (SW_FT_Int64)SW_FT_TRIG_SCALE ) + 0x100000000UL; + val = (SW_FT_Fixed)( v >> 32 ); + + return ( s >= 0 ) ? val : -val; + } + + + + /* undefined and never called for zero vector */ + static SW_FT_Int + ft_trig_prenorm( SW_FT_Vector* vec ) + { + SW_FT_Pos x, y; + SW_FT_Int shift; + + + x = vec->x; + y = vec->y; + + shift = SW_FT_MSB( SW_FT_ABS( x ) | SW_FT_ABS( y ) ); + + if ( shift <= SW_FT_TRIG_SAFE_MSB ) + { + shift = SW_FT_TRIG_SAFE_MSB - shift; + vec->x = (SW_FT_Pos)( (SW_FT_ULong)x << shift ); + vec->y = (SW_FT_Pos)( (SW_FT_ULong)y << shift ); + } + else + { + shift -= SW_FT_TRIG_SAFE_MSB; + vec->x = x >> shift; + vec->y = y >> shift; + shift = -shift; + } + + return shift; + } + + + static void + ft_trig_pseudo_rotate( SW_FT_Vector* vec, + SW_FT_Angle theta ) + { + SW_FT_Int i; + SW_FT_Fixed x, y, xtemp, b; + const SW_FT_Fixed *arctanptr; + + + x = vec->x; + y = vec->y; + + /* Rotate inside [-PI/4,PI/4] sector */ + while ( theta < -SW_FT_ANGLE_PI4 ) + { + xtemp = y; + y = -x; + x = xtemp; + theta += SW_FT_ANGLE_PI2; + } + + while ( theta > SW_FT_ANGLE_PI4 ) + { + xtemp = -y; + y = x; + x = xtemp; + theta -= SW_FT_ANGLE_PI2; + } + + arctanptr = ft_trig_arctan_table; + + /* Pseudorotations, with right shifts */ + for ( i = 1, b = 1; i < SW_FT_TRIG_MAX_ITERS; b <<= 1, i++ ) + { + if ( theta < 0 ) + { + xtemp = x + ( ( y + b ) >> i ); + y = y - ( ( x + b ) >> i ); + x = xtemp; + theta += *arctanptr++; + } + else + { + xtemp = x - ( ( y + b ) >> i ); + y = y + ( ( x + b ) >> i ); + x = xtemp; + theta -= *arctanptr++; + } + } + + vec->x = x; + vec->y = y; + } + + + static void + ft_trig_pseudo_polarize( SW_FT_Vector* vec ) + { + SW_FT_Angle theta; + SW_FT_Int i; + SW_FT_Fixed x, y, xtemp, b; + const SW_FT_Fixed *arctanptr; + + + x = vec->x; + y = vec->y; + + /* Get the vector into [-PI/4,PI/4] sector */ + if ( y > x ) + { + if ( y > -x ) + { + theta = SW_FT_ANGLE_PI2; + xtemp = y; + y = -x; + x = xtemp; + } + else + { + theta = y > 0 ? SW_FT_ANGLE_PI : -SW_FT_ANGLE_PI; + x = -x; + y = -y; + } + } + else + { + if ( y < -x ) + { + theta = -SW_FT_ANGLE_PI2; + xtemp = -y; + y = x; + x = xtemp; + } + else + { + theta = 0; + } + } + + arctanptr = ft_trig_arctan_table; + + /* Pseudorotations, with right shifts */ + for ( i = 1, b = 1; i < SW_FT_TRIG_MAX_ITERS; b <<= 1, i++ ) + { + if ( y > 0 ) + { + xtemp = x + ( ( y + b ) >> i ); + y = y - ( ( x + b ) >> i ); + x = xtemp; + theta += *arctanptr++; + } + else + { + xtemp = x - ( ( y + b ) >> i ); + y = y + ( ( x + b ) >> i ); + x = xtemp; + theta -= *arctanptr++; + } + } + + /* round theta */ + if ( theta >= 0 ) + theta = SW_FT_PAD_ROUND( theta, 32 ); + else + theta = -SW_FT_PAD_ROUND( -theta, 32 ); + + vec->x = x; + vec->y = theta; + } + + + /* documentation is in fttrigon.h */ + + SW_FT_Fixed + SW_FT_Cos( SW_FT_Angle angle ) + { + SW_FT_Vector v; + + + v.x = SW_FT_TRIG_SCALE >> 8; + v.y = 0; + ft_trig_pseudo_rotate( &v, angle ); + + return ( v.x + 0x80L ) >> 8; + } + + + /* documentation is in fttrigon.h */ + + SW_FT_Fixed + SW_FT_Sin( SW_FT_Angle angle ) + { + return SW_FT_Cos( SW_FT_ANGLE_PI2 - angle ); + } + + + /* documentation is in fttrigon.h */ + + SW_FT_Fixed + SW_FT_Tan( SW_FT_Angle angle ) + { + SW_FT_Vector v; + + + v.x = SW_FT_TRIG_SCALE >> 8; + v.y = 0; + ft_trig_pseudo_rotate( &v, angle ); + + return SW_FT_DivFix( v.y, v.x ); + } + + + /* documentation is in fttrigon.h */ + + SW_FT_Angle + SW_FT_Atan2( SW_FT_Fixed dx, + SW_FT_Fixed dy ) + { + SW_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; + } + + + /* documentation is in fttrigon.h */ + + void + SW_FT_Vector_Unit( SW_FT_Vector* vec, + SW_FT_Angle angle ) + { + vec->x = SW_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; + } + + + /* these macros return 0 for positive numbers, + and -1 for negative ones */ +#define SW_FT_SIGN_LONG( x ) ( (x) >> ( SW_FT_SIZEOF_LONG * 8 - 1 ) ) +#define SW_FT_SIGN_INT( x ) ( (x) >> ( SW_FT_SIZEOF_INT * 8 - 1 ) ) +#define SW_FT_SIGN_INT32( x ) ( (x) >> 31 ) +#define SW_FT_SIGN_INT16( x ) ( (x) >> 15 ) + + + /* documentation is in fttrigon.h */ + + void + SW_FT_Vector_Rotate( SW_FT_Vector* vec, + SW_FT_Angle angle ) + { + SW_FT_Int shift; + SW_FT_Vector v; + + + v.x = vec->x; + v.y = vec->y; + + if ( angle && ( v.x != 0 || v.y != 0 ) ) + { + 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 ) + { + SW_FT_Int32 half = (SW_FT_Int32)1L << ( shift - 1 ); + + + vec->x = ( v.x + half + SW_FT_SIGN_LONG( v.x ) ) >> shift; + vec->y = ( v.y + half + SW_FT_SIGN_LONG( v.y ) ) >> shift; + } + else + { + shift = -shift; + vec->x = (SW_FT_Pos)( (SW_FT_ULong)v.x << shift ); + vec->y = (SW_FT_Pos)( (SW_FT_ULong)v.y << shift ); + } + } + } + + + /* documentation is in fttrigon.h */ + + SW_FT_Fixed + SW_FT_Vector_Length( SW_FT_Vector* vec ) + { + SW_FT_Int shift; + SW_FT_Vector v; + + + v = *vec; + + /* handle trivial cases */ + if ( v.x == 0 ) + { + return SW_FT_ABS( v.y ); + } + else if ( v.y == 0 ) + { + return SW_FT_ABS( v.x ); + } + + /* general case */ + 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 (SW_FT_Fixed)( (SW_FT_UInt32)v.x << -shift ); + } + + + /* documentation is in fttrigon.h */ + + void + SW_FT_Vector_Polarize( SW_FT_Vector* vec, + SW_FT_Fixed *length, + SW_FT_Angle *angle ) + { + SW_FT_Int shift; + SW_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 ) + : (SW_FT_Fixed)( (SW_FT_UInt32)v.x << -shift ); + *angle = v.y; + } + + + /* documentation is in fttrigon.h */ + + void + SW_FT_Vector_From_Polar( SW_FT_Vector* vec, + SW_FT_Fixed length, + SW_FT_Angle angle ) + { + vec->x = length; + vec->y = 0; + + SW_FT_Vector_Rotate( vec, angle ); + } + + + /* documentation is in fttrigon.h */ + + SW_FT_Angle + SW_FT_Angle_Diff( SW_FT_Angle angle1, + SW_FT_Angle angle2 ) + { + SW_FT_Angle delta = angle2 - angle1; + + + delta %= SW_FT_ANGLE_2PI; + if ( delta < 0 ) + delta += SW_FT_ANGLE_2PI; + + if ( delta > SW_FT_ANGLE_PI ) + delta -= SW_FT_ANGLE_2PI; + + return delta; + } + + +/* END */ + diff --git a/src/lib/ector/software/sw_ft_math.h b/src/lib/ector/software/sw_ft_math.h new file mode 100755 index 0000000000..b844834a3b --- /dev/null +++ b/src/lib/ector/software/sw_ft_math.h @@ -0,0 +1,438 @@ +#ifndef SW_FT_MATH_H +#define SW_FT_MATH_H + +/***************************************************************************/ +/* */ +/* fttrigon.h */ +/* */ +/* FreeType trigonometric functions (specification). */ +/* */ +/* Copyright 2001, 2003, 2005, 2007, 2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include "sw_ft_types.h" + + +/*************************************************************************/ +/* */ +/* The min and max functions missing in C. As usual, be careful not to */ +/* write things like SW_FT_MIN( a++, b++ ) to avoid side effects. */ +/* */ +#define SW_FT_MIN( a, b ) ( (a) < (b) ? (a) : (b) ) +#define SW_FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) + +#define SW_FT_ABS( a ) ( (a) < 0 ? -(a) : (a) ) + +/* + * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' + * algorithm. We use alpha = 1, beta = 3/8, giving us results with a + * largest error less than 7% compared to the exact value. + */ +#define SW_FT_HYPOT( x, y ) \ + ( x = SW_FT_ABS( x ), \ + y = SW_FT_ABS( y ), \ + x > y ? x + ( 3 * y >> 3 ) \ + : y + ( 3 * x >> 3 ) ) + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_MulFix */ +/* */ +/* */ +/* A very simple function used to perform the computation */ +/* `(a*b)/0x10000' with maximum accuracy. Most of the time this is */ +/* used to multiply a given value by a 16.16 fixed-point factor. */ +/* */ +/* */ +/* a :: The first multiplier. */ +/* b :: The second multiplier. Use a 16.16 factor here whenever */ +/* possible (see note below). */ +/* */ +/* */ +/* The result of `(a*b)/0x10000'. */ +/* */ +/* */ +/* This function has been optimized for the case where the absolute */ +/* value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */ +/* As this happens mainly when scaling from notional units to */ +/* fractional pixels in FreeType, it resulted in noticeable speed */ +/* improvements between versions 2.x and 1.x. */ +/* */ +/* As a conclusion, always try to place a 16.16 factor as the */ +/* _second_ argument of this function; this can make a great */ +/* difference. */ +/* */ +SW_FT_Long +SW_FT_MulFix( SW_FT_Long a, + SW_FT_Long b ); + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_MulDiv */ +/* */ +/* */ +/* A very simple function used to perform the computation `(a*b)/c' */ +/* with maximum accuracy (it uses a 64-bit intermediate integer */ +/* whenever necessary). */ +/* */ +/* This function isn't necessarily as fast as some processor specific */ +/* operations, but is at least completely portable. */ +/* */ +/* */ +/* a :: The first multiplier. */ +/* b :: The second multiplier. */ +/* c :: The divisor. */ +/* */ +/* */ +/* The result of `(a*b)/c'. This function never traps when trying to */ +/* divide by zero; it simply returns `MaxInt' or `MinInt' depending */ +/* on the signs of `a' and `b'. */ +/* */ +SW_FT_Long +SW_FT_MulDiv( SW_FT_Long a, + SW_FT_Long b, + SW_FT_Long c ); + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_DivFix */ +/* */ +/* */ +/* A very simple function used to perform the computation */ +/* `(a*0x10000)/b' with maximum accuracy. Most of the time, this is */ +/* used to divide a given value by a 16.16 fixed-point factor. */ +/* */ +/* */ +/* a :: The numerator. */ +/* b :: The denominator. Use a 16.16 factor here. */ +/* */ +/* */ +/* The result of `(a*0x10000)/b'. */ +/* */ +SW_FT_Long +SW_FT_DivFix( SW_FT_Long a, + SW_FT_Long b ); + + + + /*************************************************************************/ + /* */ + /*
*/ + /* computations */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * SW_FT_Angle + * + * @description: + * This type is used to model angle values in FreeType. Note that the + * angle is a 16.16 fixed-point value expressed in degrees. + * + */ + typedef SW_FT_Fixed SW_FT_Angle; + + + /************************************************************************* + * + * @macro: + * SW_FT_ANGLE_PI + * + * @description: + * The angle pi expressed in @SW_FT_Angle units. + * + */ +#define SW_FT_ANGLE_PI ( 180L << 16 ) + + + /************************************************************************* + * + * @macro: + * SW_FT_ANGLE_2PI + * + * @description: + * The angle 2*pi expressed in @SW_FT_Angle units. + * + */ +#define SW_FT_ANGLE_2PI ( SW_FT_ANGLE_PI * 2 ) + + + /************************************************************************* + * + * @macro: + * SW_FT_ANGLE_PI2 + * + * @description: + * The angle pi/2 expressed in @SW_FT_Angle units. + * + */ +#define SW_FT_ANGLE_PI2 ( SW_FT_ANGLE_PI / 2 ) + + + /************************************************************************* + * + * @macro: + * SW_FT_ANGLE_PI4 + * + * @description: + * The angle pi/4 expressed in @SW_FT_Angle units. + * + */ +#define SW_FT_ANGLE_PI4 ( SW_FT_ANGLE_PI / 4 ) + + + /************************************************************************* + * + * @function: + * SW_FT_Sin + * + * @description: + * Return the sinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The sinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @SW_FT_Vector_Unit. + * + */ + SW_FT_Fixed + SW_FT_Sin( SW_FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * SW_FT_Cos + * + * @description: + * Return the cosinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The cosinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @SW_FT_Vector_Unit. + * + */ + SW_FT_Fixed + SW_FT_Cos( SW_FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * SW_FT_Tan + * + * @description: + * Return the tangent of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The tangent value. + * + */ + SW_FT_Fixed + SW_FT_Tan( SW_FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * SW_FT_Atan2 + * + * @description: + * Return the arc-tangent corresponding to a given vector (x,y) in + * the 2d plane. + * + * @input: + * x :: + * The horizontal vector coordinate. + * + * y :: + * The vertical vector coordinate. + * + * @return: + * The arc-tangent value (i.e. angle). + * + */ + SW_FT_Angle + SW_FT_Atan2( SW_FT_Fixed x, + SW_FT_Fixed y ); + + + /************************************************************************* + * + * @function: + * SW_FT_Angle_Diff + * + * @description: + * Return the difference between two angles. The result is always + * constrained to the ]-PI..PI] interval. + * + * @input: + * angle1 :: + * First angle. + * + * angle2 :: + * Second angle. + * + * @return: + * Constrained value of `value2-value1'. + * + */ + SW_FT_Angle + SW_FT_Angle_Diff( SW_FT_Angle angle1, + SW_FT_Angle angle2 ); + + + /************************************************************************* + * + * @function: + * SW_FT_Vector_Unit + * + * @description: + * Return the unit vector corresponding to a given angle. After the + * call, the value of `vec.x' will be `sin(angle)', and the value of + * `vec.y' will be `cos(angle)'. + * + * This function is useful to retrieve both the sinus and cosinus of a + * given angle quickly. + * + * @output: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ + void + SW_FT_Vector_Unit( SW_FT_Vector* vec, + SW_FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * SW_FT_Vector_Rotate + * + * @description: + * Rotate a vector by a given angle. + * + * @inout: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ + void + SW_FT_Vector_Rotate( SW_FT_Vector* vec, + SW_FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * SW_FT_Vector_Length + * + * @description: + * Return the length of a given vector. + * + * @input: + * vec :: + * The address of target vector. + * + * @return: + * The vector length, expressed in the same units that the original + * vector coordinates. + * + */ + SW_FT_Fixed + SW_FT_Vector_Length( SW_FT_Vector* vec ); + + + /************************************************************************* + * + * @function: + * SW_FT_Vector_Polarize + * + * @description: + * Compute both the length and angle of a given vector. + * + * @input: + * vec :: + * The address of source vector. + * + * @output: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ + void + SW_FT_Vector_Polarize( SW_FT_Vector* vec, + SW_FT_Fixed *length, + SW_FT_Angle *angle ); + + + /************************************************************************* + * + * @function: + * SW_FT_Vector_From_Polar + * + * @description: + * Compute vector coordinates from a length and angle. + * + * @output: + * vec :: + * The address of source vector. + * + * @input: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ + void + SW_FT_Vector_From_Polar( SW_FT_Vector* vec, + SW_FT_Fixed length, + SW_FT_Angle angle ); + + +#endif // SW_FT_MATH_H diff --git a/src/lib/ector/software/sw_ft_raster.c b/src/lib/ector/software/sw_ft_raster.c new file mode 100644 index 0000000000..2956123b10 --- /dev/null +++ b/src/lib/ector/software/sw_ft_raster.c @@ -0,0 +1,1846 @@ +/***************************************************************************/ +/* */ +/* ftgrays.c */ +/* */ +/* A new `perfect' anti-aliasing renderer (body). */ +/* */ +/* Copyright 2000-2003, 2005-2014 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This is a new anti-aliasing scan-converter for FreeType 2. The */ + /* algorithm used here is _very_ different from the one in the standard */ + /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */ + /* coverage of the outline on each pixel cell. */ + /* */ + /* It is based on ideas that I initially found in Raph Levien's */ + /* excellent LibArt graphics library (see http://www.levien.com/libart */ + /* for more information, though the web pages do not tell anything */ + /* about the renderer; you'll have to dive into the source code to */ + /* understand how it works). */ + /* */ + /* Note, however, that this is a _very_ different implementation */ + /* compared to Raph's. Coverage information is stored in a very */ + /* different way, and I don't use sorted vector paths. Also, it doesn't */ + /* use floating point values. */ + /* */ + /* This renderer has the following advantages: */ + /* */ + /* - It doesn't need an intermediate bitmap. Instead, one can supply a */ + /* callback function that will be called by the renderer to draw gray */ + /* spans on any target surface. You can thus do direct composition on */ + /* any kind of bitmap, provided that you give the renderer the right */ + /* callback. */ + /* */ + /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */ + /* each pixel cell. */ + /* */ + /* - It performs a single pass on the outline (the `standard' FT2 */ + /* renderer makes two passes). */ + /* */ + /* - It can easily be modified to render to _any_ number of gray levels */ + /* cheaply. */ + /* */ + /* - For small (< 20) pixel sizes, it is faster than the standard */ + /* renderer. */ + /* */ + /*************************************************************************/ + + +#include "sw_ft_raster.h" +#include "sw_ft_math.h" + + /* Auxiliary macros for token concatenation. */ +#define SW_FT_ERR_XCAT( x, y ) x ## y +#define SW_FT_ERR_CAT( x, y ) SW_FT_ERR_XCAT( x, y ) + +#define SW_FT_BEGIN_STMNT do { +#define SW_FT_END_STMNT } while ( 0 ) + + +#include +#include +#include +#include +#define SW_FT_UINT_MAX UINT_MAX +#define SW_FT_INT_MAX INT_MAX + +#define ft_memset memset + +#define ft_setjmp setjmp +#define ft_longjmp longjmp +#define ft_jmp_buf jmp_buf + +typedef ptrdiff_t SW_FT_PtrDist; + + +#define ErrRaster_Invalid_Mode -2 +#define ErrRaster_Invalid_Outline -1 +#define ErrRaster_Invalid_Argument -3 +#define ErrRaster_Memory_Overflow -4 + +#define SW_FT_BEGIN_HEADER +#define SW_FT_END_HEADER + + + /* This macro is used to indicate that a function parameter is unused. */ + /* Its purpose is simply to reduce compiler warnings. Note also that */ + /* simply defining it as `(void)x' doesn't avoid warnings with certain */ + /* ANSI compilers (e.g. LCC). */ +#define SW_FT_UNUSED( x ) (x) = (x) + + +#define SW_FT_TRACE5( x ) do { } while ( 0 ) /* nothing */ +#define SW_FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ +#define SW_FT_ERROR( x ) do { } while ( 0 ) /* nothing */ +#define SW_FT_THROW( e ) SW_FT_ERR_CAT( ErrRaster_, e ) + + + +typedef int +(*SW_FT_Outline_MoveToFunc)( const SW_FT_Vector* to, + void* user ); + +#define SW_FT_Outline_MoveTo_Func SW_FT_Outline_MoveToFunc + +typedef int +(*SW_FT_Outline_LineToFunc)( const SW_FT_Vector* to, + void* user ); + +#define SW_FT_Outline_LineTo_Func SW_FT_Outline_LineToFunc + + +typedef int +(*SW_FT_Outline_ConicToFunc)( const SW_FT_Vector* control, + const SW_FT_Vector* to, + void* user ); + +#define SW_FT_Outline_ConicTo_Func SW_FT_Outline_ConicToFunc + +typedef int +(*SW_FT_Outline_CubicToFunc)( const SW_FT_Vector* control1, + const SW_FT_Vector* control2, + const SW_FT_Vector* to, + void* user ); + +#define SW_FT_Outline_CubicTo_Func SW_FT_Outline_CubicToFunc + +typedef struct SW_FT_Outline_Funcs_ +{ + SW_FT_Outline_MoveToFunc move_to; + SW_FT_Outline_LineToFunc line_to; + SW_FT_Outline_ConicToFunc conic_to; + SW_FT_Outline_CubicToFunc cubic_to; + + int shift; + SW_FT_Pos delta; + +} SW_FT_Outline_Funcs; + + + +#define SW_FT_DEFINE_OUTLINE_FUNCS( class_, \ + move_to_, line_to_, \ + conic_to_, cubic_to_, \ + shift_, delta_ ) \ + static const SW_FT_Outline_Funcs class_ = \ + { \ + move_to_, \ + line_to_, \ + conic_to_, \ + cubic_to_, \ + shift_, \ + delta_ \ + }; + +#define SW_FT_DEFINE_RASTER_FUNCS( class_, \ + raster_new_, raster_reset_, \ + raster_render_, \ + raster_done_ ) \ + const SW_FT_Raster_Funcs class_ = \ + { \ + raster_new_, \ + raster_reset_, \ + raster_render_, \ + raster_done_ \ + }; + + +#ifndef SW_FT_MEM_SET +#define SW_FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) +#endif + +#ifndef SW_FT_MEM_ZERO +#define SW_FT_MEM_ZERO( dest, count ) SW_FT_MEM_SET( dest, 0, count ) +#endif + + /* as usual, for the speed hungry :-) */ + +#undef RAS_ARG +#undef RAS_ARG_ +#undef RAS_VAR +#undef RAS_VAR_ + +#ifndef SW_FT_STATIC_RASTER + +#define RAS_ARG gray_PWorker worker +#define RAS_ARG_ gray_PWorker worker, + +#define RAS_VAR worker +#define RAS_VAR_ worker, + +#else /* SW_FT_STATIC_RASTER */ + +#define RAS_ARG /* empty */ +#define RAS_ARG_ /* empty */ +#define RAS_VAR /* empty */ +#define RAS_VAR_ /* empty */ + +#endif /* SW_FT_STATIC_RASTER */ + + + /* must be at least 6 bits! */ +#define PIXEL_BITS 5 + +#undef FLOOR +#undef CEILING +#undef TRUNC +#undef SCALED + +#define ONE_PIXEL ( 1L << PIXEL_BITS ) +#define PIXEL_MASK ( -1L << PIXEL_BITS ) +#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) ) +#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS ) +#define FLOOR( x ) ( (x) & -ONE_PIXEL ) +#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL ) +#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL ) + +#if PIXEL_BITS >= 6 +#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) ) +#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) ) +#else +#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) ) +#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) ) +#endif + + + /* Compute `dividend / divisor' and return both its quotient and */ + /* remainder, cast to a specific type. This macro also ensures that */ + /* the remainder is always positive. */ +#define SW_FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ + SW_FT_BEGIN_STMNT \ + (quotient) = (type)( (dividend) / (divisor) ); \ + (remainder) = (type)( (dividend) % (divisor) ); \ + if ( (remainder) < 0 ) \ + { \ + (quotient)--; \ + (remainder) += (type)(divisor); \ + } \ + SW_FT_END_STMNT + +#ifdef __arm__ + /* Work around a bug specific to GCC which make the compiler fail to */ + /* optimize a division and modulo operation on the same parameters */ + /* into a single call to `__aeabi_idivmod'. See */ + /* */ + /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721 */ +#undef SW_FT_DIV_MOD +#define SW_FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ + SW_FT_BEGIN_STMNT \ + (quotient) = (type)( (dividend) / (divisor) ); \ + (remainder) = (type)( (dividend) - (quotient) * (divisor) ); \ + if ( (remainder) < 0 ) \ + { \ + (quotient)--; \ + (remainder) += (type)(divisor); \ + } \ + SW_FT_END_STMNT +#endif /* __arm__ */ + + + /*************************************************************************/ + /* */ + /* TYPE DEFINITIONS */ + /* */ + + /* don't change the following types to SW_FT_Int or SW_FT_Pos, since we might */ + /* need to define them to "float" or "double" when experimenting with */ + /* new algorithms */ + + typedef long TCoord; /* integer scanline/pixel coordinate */ + typedef long TPos; /* sub-pixel coordinate */ + + /* determine the type used to store cell areas. This normally takes at */ + /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */ + /* `long' instead of `int', otherwise bad things happen */ + +#if PIXEL_BITS <= 7 + + typedef int TArea; + +#else /* PIXEL_BITS >= 8 */ + + /* approximately determine the size of integers using an ANSI-C header */ +#if SW_FT_UINT_MAX == 0xFFFFU + typedef long TArea; +#else + typedef int TArea; +#endif + +#endif /* PIXEL_BITS >= 8 */ + + + /* maximum number of gray spans in a call to the span callback */ +#define SW_FT_MAX_GRAY_SPANS 256 + + + typedef struct TCell_* PCell; + + typedef struct TCell_ + { + TPos x; /* same with gray_TWorker.ex */ + TCoord cover; /* same with gray_TWorker.cover */ + TArea area; + PCell next; + + } TCell; + + +#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ + /* We disable the warning `structure was padded due to */ + /* __declspec(align())' in order to compile cleanly with */ + /* the maximum level of warnings. */ +#pragma warning( push ) +#pragma warning( disable : 4324 ) +#endif /* _MSC_VER */ + + typedef struct gray_TWorker_ + { + TCoord ex, ey; + TPos min_ex, max_ex; + TPos min_ey, max_ey; + TPos count_ex, count_ey; + + TArea area; + TCoord cover; + int invalid; + + PCell cells; + SW_FT_PtrDist max_cells; + SW_FT_PtrDist num_cells; + + TCoord cx, cy; + TPos x, y; + + TPos last_ey; + + SW_FT_Vector bez_stack[32 * 3 + 1]; + int lev_stack[32]; + + SW_FT_Outline outline; + SW_FT_BBox clip_box; + + SW_FT_Span gray_spans[SW_FT_MAX_GRAY_SPANS]; + int num_gray_spans; + + SW_FT_Raster_Span_Func render_span; + void* render_span_data; + int span_y; + + int band_size; + int band_shoot; + + ft_jmp_buf jump_buffer; + + void* buffer; + long buffer_size; + + PCell* ycells; + TPos ycount; + + } gray_TWorker, *gray_PWorker; + +#if defined( _MSC_VER ) +#pragma warning( pop ) +#endif + + +#ifndef SW_FT_STATIC_RASTER +#define ras (*worker) +#else + static gray_TWorker ras; +#endif + + + typedef struct gray_TRaster_ + { + void* buffer; + long buffer_size; + int band_size; + void* memory; + gray_PWorker worker; + + } gray_TRaster, *gray_PRaster; + + + + /*************************************************************************/ + /* */ + /* Initialize the cells table. */ + /* */ + 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; + } + + + /*************************************************************************/ + /* */ + /* Compute the outline bounding box. */ + /* */ + static void + gray_compute_cbox( RAS_ARG ) + { + SW_FT_Outline* outline = &ras.outline; + SW_FT_Vector* vec = outline->points; + SW_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; + } + + /* truncate the bounding box to integer pixels */ + 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; + } + + + /*************************************************************************/ + /* */ + /* Record the current cell in the table. */ + /* */ + 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 ) + 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; + } + } + + + /*************************************************************************/ + /* */ + /* Set the current cell to a new position. */ + /* */ + static void + gray_set_cell( RAS_ARG_ TCoord ex, + TCoord ey ) + { + /* Move the cell pointer to a new position. We set the `invalid' */ + /* flag to indicate that the cell isn't part of those we're interested */ + /* in during the render phase. This means that: */ + /* */ + /* . the new vertical position must be within min_ey..max_ey-1. */ + /* . the new horizontal position must be strictly less than max_ex */ + /* */ + /* Note that if a cell is to the left of the clipping region, it is */ + /* actually set to the (min_ex-1) horizontal position. */ + + /* All cells that are on the left of the clipping region go to the */ + /* min_ex - 1 horizontal position. */ + ey -= ras.min_ey; + + if ( ex > ras.max_ex ) + ex = ras.max_ex; + + ex -= ras.min_ex; + if ( ex < 0 ) + ex = -1; + + /* are we moving to a different cell ? */ + if ( ex != ras.ex || ey != ras.ey ) + { + /* record the current one if it is valid */ + if ( !ras.invalid ) + gray_record_cell( RAS_VAR ); + + ras.area = 0; + ras.cover = 0; + ras.ex = ex; + ras.ey = ey; + } + + ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey || + ex >= ras.count_ex ); + } + + + /*************************************************************************/ + /* */ + /* Start a new contour at a given cell. */ + /* */ + 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.last_ey = SUBPIXELS( ey ); + ras.invalid = 0; + + gray_set_cell( RAS_VAR_ ex, ey ); + } + + + /*************************************************************************/ + /* */ + /* Render a scanline as one or more cells. */ + /* */ + static void + gray_render_scanline( RAS_ARG_ TCoord ey, + TPos x1, + TCoord y1, + TPos x2, + TCoord y2 ) + { + TCoord ex1, ex2, fx1, fx2, delta, mod; + long p, first, dx; + int incr; + + + dx = x2 - x1; + + ex1 = TRUNC( x1 ); + ex2 = TRUNC( x2 ); + fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) ); + fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) ); + + /* trivial case. Happens often */ + if ( y1 == y2 ) + { + gray_set_cell( RAS_VAR_ ex2, ey ); + return; + } + + /* everything is located in a single cell. That is easy! */ + /* */ + if ( ex1 == ex2 ) + { + delta = y2 - y1; + ras.area += (TArea)(( fx1 + fx2 ) * delta); + ras.cover += delta; + return; + } + + /* ok, we'll have to render a run of adjacent cells on the same */ + /* scanline... */ + /* */ + p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 ); + first = ONE_PIXEL; + incr = 1; + + if ( dx < 0 ) + { + p = fx1 * ( y2 - y1 ); + first = 0; + incr = -1; + dx = -dx; + } + + SW_FT_DIV_MOD( TCoord, p, dx, delta, mod ); + + ras.area += (TArea)(( fx1 + first ) * delta); + ras.cover += delta; + + ex1 += incr; + gray_set_cell( RAS_VAR_ ex1, ey ); + y1 += delta; + + if ( ex1 != ex2 ) + { + TCoord lift, rem; + + + p = ONE_PIXEL * ( y2 - y1 + delta ); + SW_FT_DIV_MOD( TCoord, p, dx, lift, rem ); + + mod -= (int)dx; + + while ( ex1 != ex2 ) + { + delta = lift; + mod += rem; + if ( mod >= 0 ) + { + mod -= (TCoord)dx; + delta++; + } + + ras.area += (TArea)(ONE_PIXEL * delta); + ras.cover += delta; + y1 += delta; + ex1 += incr; + gray_set_cell( RAS_VAR_ ex1, ey ); + } + } + + delta = y2 - y1; + ras.area += (TArea)(( fx2 + ONE_PIXEL - first ) * delta); + ras.cover += delta; + } + + + /*************************************************************************/ + /* */ + /* Render a given line as a series of scanlines. */ + /* */ + static void + gray_render_line( RAS_ARG_ TPos to_x, + TPos to_y ) + { + TCoord ey1, ey2, fy1, fy2, mod; + TPos dx, dy, x, x2; + long p, first; + int delta, rem, lift, incr; + + + ey1 = TRUNC( ras.last_ey ); + ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ + fy1 = (TCoord)( ras.y - ras.last_ey ); + fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) ); + + dx = to_x - ras.x; + dy = to_y - ras.y; + + /* perform vertical clipping */ + { + TCoord min, max; + + + min = ey1; + max = ey2; + if ( ey1 > ey2 ) + { + min = ey2; + max = ey1; + } + if ( min >= ras.max_ey || max < ras.min_ey ) + goto End; + } + + /* everything is on a single scanline */ + if ( ey1 == ey2 ) + { + gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ); + goto End; + } + + /* vertical line - avoid calling gray_render_scanline */ + incr = 1; + + if ( dx == 0 ) + { + TCoord ex = TRUNC( ras.x ); + TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 ); + TArea area; + + + first = ONE_PIXEL; + if ( dy < 0 ) + { + first = 0; + incr = -1; + } + + delta = (int)( first - fy1 ); + ras.area += (TArea)two_fx * delta; + ras.cover += delta; + ey1 += incr; + + gray_set_cell( RAS_VAR_ ex, ey1 ); + + delta = (int)( first + first - ONE_PIXEL ); + area = (TArea)two_fx * delta; + while ( ey1 != ey2 ) + { + ras.area += area; + ras.cover += delta; + ey1 += incr; + + gray_set_cell( RAS_VAR_ ex, ey1 ); + } + + delta = (int)( fy2 - ONE_PIXEL + first ); + ras.area += (TArea)two_fx * delta; + ras.cover += delta; + + goto End; + } + + /* ok, we have to render several scanlines */ + p = ( ONE_PIXEL - fy1 ) * dx; + first = ONE_PIXEL; + incr = 1; + + if ( dy < 0 ) + { + p = fy1 * dx; + first = 0; + incr = -1; + dy = -dy; + } + + SW_FT_DIV_MOD( int, 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 ) + { + p = ONE_PIXEL * dx; + SW_FT_DIV_MOD( int, p, dy, lift, rem ); + mod -= (int)dy; + + while ( ey1 != ey2 ) + { + delta = lift; + mod += rem; + if ( mod >= 0 ) + { + mod -= (int)dy; + delta++; + } + + x2 = x + delta; + gray_render_scanline( RAS_VAR_ ey1, x, + (TCoord)( ONE_PIXEL - first ), x2, + (TCoord)first ); + x = x2; + + ey1 += incr; + gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); + } + } + + gray_render_scanline( RAS_VAR_ ey1, x, + (TCoord)( ONE_PIXEL - first ), to_x, + fy2 ); + + End: + ras.x = to_x; + ras.y = to_y; + ras.last_ey = SUBPIXELS( ey2 ); + } + + + static void + gray_split_conic( SW_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 SW_FT_Vector* control, + const SW_FT_Vector* to ) + { + TPos dx, dy; + TPos min, max, y; + int top, level; + int* levels; + SW_FT_Vector* arc; + + + levels = ras.lev_stack; + + arc = ras.bez_stack; + 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; + top = 0; + + dx = SW_FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x ); + dy = SW_FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y ); + if ( dx < dy ) + dx = dy; + + if ( dx < ONE_PIXEL / 4 ) + goto Draw; + + /* short-cut the arc that crosses the current band */ + min = max = arc[0].y; + + y = arc[1].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + + y = arc[2].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + + if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey ) + goto Draw; + + level = 0; + do + { + dx >>= 2; + level++; + } while ( dx > ONE_PIXEL / 4 ); + + levels[0] = level; + + do + { + level = levels[top]; + if ( level > 0 ) + { + gray_split_conic( arc ); + arc += 2; + top++; + levels[top] = levels[top - 1] = level - 1; + continue; + } + + Draw: + gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + top--; + arc -= 2; + + } while ( top >= 0 ); + } + + + static void + gray_split_cubic( SW_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 SW_FT_Vector* control1, + const SW_FT_Vector* control2, + const SW_FT_Vector* to ) + { + SW_FT_Vector* arc; + TPos min, max, y; + + + arc = ras.bez_stack; + 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; + + /* Short-cut the arc that crosses the current band. */ + min = max = arc[0].y; + + y = arc[1].y; + if ( y < min ) + min = y; + if ( y > max ) + max = y; + + y = arc[2].y; + if ( y < min ) + min = y; + if ( y > max ) + max = y; + + y = arc[3].y; + if ( y < min ) + min = y; + if ( y > max ) + max = y; + + if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey ) + goto Draw; + + for (;;) + { + /* Decide whether to split or draw. See `Rapid Termination */ + /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */ + /* F. Hain, at */ + /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */ + + { + TPos dx, dy, dx_, dy_; + TPos dx1, dy1, dx2, dy2; + TPos L, s, s_limit; + + + /* dx and dy are x and y components of the P0-P3 chord vector. */ + dx = dx_ = arc[3].x - arc[0].x; + dy = dy_ = arc[3].y - arc[0].y; + + L = SW_FT_HYPOT( dx_, dy_ ); + + /* Avoid possible arithmetic overflow below by splitting. */ + if ( L > 32767 ) + goto Split; + + /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */ + s_limit = L * (TPos)( ONE_PIXEL / 6 ); + + /* s is L * the perpendicular distance from P1 to the line P0-P3. */ + dx1 = arc[1].x - arc[0].x; + dy1 = arc[1].y - arc[0].y; + s = SW_FT_ABS( dy * dx1 - dx * dy1 ); + + if ( s > s_limit ) + goto Split; + + /* s is L * the perpendicular distance from P2 to the line P0-P3. */ + dx2 = arc[2].x - arc[0].x; + dy2 = arc[2].y - arc[0].y; + s = SW_FT_ABS( dy * dx2 - dx * dy2 ); + + if ( s > s_limit ) + goto Split; + + /* Split super curvy segments where the off points are so far + from the chord that the angles P0-P1-P3 or P0-P2-P3 become + acute as detected by appropriate dot products. */ + if ( dx1 * ( dx1 - dx ) + dy1 * ( dy1 - dy ) > 0 || + dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 ) + goto Split; + + /* No reason to split. */ + goto Draw; + } + + Split: + gray_split_cubic( arc ); + arc += 3; + continue; + + Draw: + gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + + if ( arc == ras.bez_stack ) + return; + + arc -= 3; + } + } + + + static int + gray_move_to( const SW_FT_Vector* to, + gray_PWorker worker ) + { + TPos x, y; + + + /* record current cell, if any */ + if ( !ras.invalid ) + gray_record_cell( RAS_VAR ); + + /* start to a new position */ + x = UPSCALE( to->x ); + y = UPSCALE( to->y ); + + gray_start_cell( RAS_VAR_ TRUNC( x ), TRUNC( y ) ); + + worker->x = x; + worker->y = y; + return 0; + } + + + static int + gray_line_to( const SW_FT_Vector* to, + gray_PWorker worker ) + { + gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) ); + return 0; + } + + + static int + gray_conic_to( const SW_FT_Vector* control, + const SW_FT_Vector* to, + gray_PWorker worker ) + { + gray_render_conic( RAS_VAR_ control, to ); + return 0; + } + + + static int + gray_cubic_to( const SW_FT_Vector* control1, + const SW_FT_Vector* control2, + const SW_FT_Vector* to, + gray_PWorker worker ) + { + gray_render_cubic( RAS_VAR_ control1, control2, to ); + return 0; + } + + + static void + gray_hline( RAS_ARG_ TCoord x, + TCoord y, + TPos area, + TCoord acount ) + { + int coverage; + + + /* compute the coverage line's coverage, depending on the */ + /* outline fill rule */ + /* */ + /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ + /* */ + coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) ); + /* use range 0..256 */ + if ( coverage < 0 ) + coverage = -coverage; + + if ( ras.outline.flags & SW_FT_OUTLINE_EVEN_ODD_FILL ) + { + coverage &= 511; + + if ( coverage > 256 ) + coverage = 512 - coverage; + else if ( coverage == 256 ) + coverage = 255; + } + else + { + /* normal non-zero winding rule */ + if ( coverage >= 256 ) + coverage = 255; + } + + y += (TCoord)ras.min_ey; + x += (TCoord)ras.min_ex; + + /* SW_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */ + if ( x >= 32767 ) + x = 32767; + + /* SW_FT_Span.y is an integer, so limit our coordinates appropriately */ + if ( y >= SW_FT_INT_MAX ) + y = SW_FT_INT_MAX; + + if ( coverage ) + { + SW_FT_Span* span; + int count; + + + /* see whether we can add this span to the current list */ + count = ras.num_gray_spans; + span = ras.gray_spans + count - 1; + if ( count > 0 && + ras.span_y == y && + (int)span->x + span->len == (int)x && + span->coverage == coverage ) + { + span->len = (unsigned short)( span->len + acount ); + return; + } + + if ( count >= SW_FT_MAX_GRAY_SPANS ) + { + if ( ras.render_span && count > 0 ) + ras.render_span(count, ras.gray_spans, + ras.render_span_data ); + + #ifdef DEBUG_GRAYS + + if ( 1 ) + { + int n; + + + fprintf( stderr, "count = %3d ", count ); + span = ras.gray_spans; + for ( n = 0; n < count; n++, span++ ) + fprintf( stderr, "[%d , %d..%d] : %d ", + span->y, span->x, span->x + span->len - 1, span->coverage ); + fprintf( stderr, "\n" ); + } + + #endif /* DEBUG_GRAYS */ + + ras.num_gray_spans = 0; + //ras.span_y = (int)y; + + span = ras.gray_spans; + } + else + span++; + + /* add a gray span to the current list */ + span->x = (short)x; + span->y = (short)y; + span->len = (unsigned short)acount; + span->coverage = (unsigned char)coverage; + + ras.num_gray_spans++; + } + } + + static void + gray_sweep( RAS_ARG) + { + int yindex; + + if ( ras.num_cells == 0 ) + return; + + ras.num_gray_spans = 0; + + for ( yindex = 0; yindex < ras.ycount; yindex++ ) + { + PCell cell = ras.ycells[yindex]; + TCoord cover = 0; + TCoord x = 0; + + + for ( ; cell != NULL; cell = cell->next ) + { + TPos 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 ( cover != 0 ) + gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), + ras.count_ex - x ); + } + + if ( ras.render_span && ras.num_gray_spans > 0 ) + ras.render_span(ras.num_gray_spans, + ras.gray_spans, ras.render_span_data ); + } + + + /*************************************************************************/ + /* */ + /* The following function should only compile in stand-alone mode, */ + /* i.e., when building this component without the rest of FreeType. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Outline_Decompose */ + /* */ + /* */ + /* Walk over an outline's structure to decompose it into individual */ + /* segments and Bézier arcs. This function is also able to emit */ + /* `move to' and `close to' operations to indicate the start and end */ + /* of new contours in the outline. */ + /* */ + /* */ + /* outline :: A pointer to the source target. */ + /* */ + /* func_interface :: A table of `emitters', i.e., function pointers */ + /* called during decomposition to indicate path */ + /* operations. */ + /* */ + /* */ + /* user :: A typeless pointer which is passed to each */ + /* emitter during the decomposition. It can be */ + /* used to store the state during the */ + /* decomposition. */ + /* */ + /* */ + /* Error code. 0 means success. */ + /* */ + static int + SW_FT_Outline_Decompose( const SW_FT_Outline* outline, + const SW_FT_Outline_Funcs* func_interface, + void* user ) + { +#undef SCALED +#define SCALED( x ) ( ( (x) << shift ) - delta ) + + SW_FT_Vector v_last; + SW_FT_Vector v_control; + SW_FT_Vector v_start; + + SW_FT_Vector* point; + SW_FT_Vector* limit; + char* tags; + + int error; + + int n; /* index of contour in outline */ + int first; /* index of first point in contour */ + char tag; /* current point's state */ + + int shift; + TPos delta; + + + if ( !outline || !func_interface ) + return SW_FT_THROW( Invalid_Argument ); + + shift = func_interface->shift; + delta = func_interface->delta; + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) + { + int last; /* index of last point in contour */ + + + SW_FT_TRACE5(( "SW_FT_Outline_Decompose: Outline %d\n", n )); + + 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 = SW_FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == SW_FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == SW_FT_CURVE_TAG_CONIC ) + { + /* first point is conic control. Yes, this happens. */ + if ( SW_FT_CURVE_TAG( outline->tags[last] ) == SW_FT_CURVE_TAG_ON ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle and record its position */ + /* for closure */ + 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--; + } + + SW_FT_TRACE5(( " move to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); + error = func_interface->move_to( &v_start, user ); + if ( error ) + goto Exit; + + while ( point < limit ) + { + point++; + tags++; + + tag = SW_FT_CURVE_TAG( tags[0] ); + switch ( tag ) + { + case SW_FT_CURVE_TAG_ON: /* emit a single line_to */ + { + SW_FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + SW_FT_TRACE5(( " line to (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0 )); + error = func_interface->line_to( &vec, user ); + if ( error ) + goto Exit; + continue; + } + + case SW_FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = SCALED( point->x ); + v_control.y = SCALED( point->y ); + + Do_Conic: + if ( point < limit ) + { + SW_FT_Vector vec; + SW_FT_Vector v_middle; + + + point++; + tags++; + tag = SW_FT_CURVE_TAG( tags[0] ); + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + if ( tag == SW_FT_CURVE_TAG_ON ) + { + SW_FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + if ( tag != SW_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; + + SW_FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_middle.x / 64.0, v_middle.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &v_middle, user ); + if ( error ) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + SW_FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &v_start, user ); + goto Close; + + default: /* SW_FT_CURVE_TAG_CUBIC */ + { + SW_FT_Vector vec1, vec2; + + + if ( point + 1 > limit || + SW_FT_CURVE_TAG( tags[1] ) != SW_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 ) + { + SW_FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + SW_FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); + error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + SW_FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); + error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); + goto Close; + } + } + } + + /* close the contour with a line segment */ + SW_FT_TRACE5(( " line to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); + error = func_interface->line_to( &v_start, user ); + + Close: + if ( error ) + goto Exit; + + first = last + 1; + } + + SW_FT_TRACE5(( "SW_FT_Outline_Decompose: Done\n", n )); + return 0; + + Exit: + SW_FT_TRACE5(( "SW_FT_Outline_Decompose: Error %d\n", error )); + return error; + + Invalid_Outline: + return SW_FT_THROW( Invalid_Outline ); + } + + + typedef struct gray_TBand_ + { + TPos min, max; + + } gray_TBand; + + + + SW_FT_DEFINE_OUTLINE_FUNCS(func_interface, + (SW_FT_Outline_MoveTo_Func) gray_move_to, + (SW_FT_Outline_LineTo_Func) gray_line_to, + (SW_FT_Outline_ConicTo_Func)gray_conic_to, + (SW_FT_Outline_CubicTo_Func)gray_cubic_to, + 0, + 0 + ) + + static int + gray_convert_glyph_inner( RAS_ARG ) + { + + volatile int error = 0; + + if ( ft_setjmp( ras.jump_buffer ) == 0 ) + { + error = SW_FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); + if ( !ras.invalid ) + gray_record_cell( RAS_VAR ); + } + else + error = SW_FT_THROW( Memory_Overflow ); + + return error; + } + + + static int + gray_convert_glyph( RAS_ARG ) + { + gray_TBand bands[40]; + gray_TBand* volatile band; + int volatile n, num_bands; + TPos volatile min, max, max_y; + SW_FT_BBox* clip; + + + /* Set up state in the raster object */ + gray_compute_cbox( RAS_VAR ); + + /* clip to target bitmap, exit if nothing to do */ + 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; + + /* set up vertical bands */ + 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; + long 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 = 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: + /* render pool overflow; we will reduce the render band by half */ + bottom = band->min; + top = band->max; + middle = bottom + ( ( top - bottom ) >> 1 ); + + /* This is too complex for a single scanline; there must */ + /* be some problems. */ + if ( middle == bottom ) + { +#ifdef SW_FT_DEBUG_LEVEL_TRACE + SW_FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" )); +#endif + return 1; + } + + 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.band_shoot > 8 && ras.band_size > 16 ) + ras.band_size = ras.band_size / 2; + + return 0; + } + + + static int + gray_raster_render( gray_PRaster raster, + const SW_FT_Raster_Params* params ) + { + const SW_FT_Outline* outline = (const SW_FT_Outline*)params->source; + gray_PWorker worker; + + + if ( !raster || !raster->buffer || !raster->buffer_size ) + return SW_FT_THROW( Invalid_Argument ); + + if ( !outline ) + return SW_FT_THROW( Invalid_Outline ); + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return 0; + + if ( !outline->contours || !outline->points ) + return SW_FT_THROW( Invalid_Outline ); + + if ( outline->n_points != + outline->contours[outline->n_contours - 1] + 1 ) + return SW_FT_THROW( Invalid_Outline ); + + worker = raster->worker; + + /* this version does not support monochrome rendering */ + if ( !( params->flags & SW_FT_RASTER_FLAG_AA ) ) + return SW_FT_THROW( Invalid_Mode ); + + if ( params->flags & SW_FT_RASTER_FLAG_CLIP ) + ras.clip_box = params->clip_box; + else + { + ras.clip_box.xMin = -32768L; + ras.clip_box.yMin = -32768L; + ras.clip_box.xMax = 32767L; + ras.clip_box.yMax = 32767L; + } + + gray_init_cells( RAS_VAR_ raster->buffer, raster->buffer_size ); + + ras.outline = *outline; + ras.num_cells = 0; + ras.invalid = 1; + ras.band_size = raster->band_size; + ras.num_gray_spans = 0; + + ras.render_span = (SW_FT_Raster_Span_Func)params->gray_spans; + ras.render_span_data = params->user; + + return gray_convert_glyph( RAS_VAR ); + } + + + /**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/ + /**** a static object. *****/ + + static int + gray_raster_new(SW_FT_Raster* araster ) + { + static gray_TRaster the_raster; + + *araster = (SW_FT_Raster)&the_raster; + SW_FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) ); + + return 0; + } + + + static void + gray_raster_done( SW_FT_Raster raster ) + { + /* nothing */ + SW_FT_UNUSED( raster ); + } + + static void + gray_raster_reset( SW_FT_Raster raster, + char* pool_base, + long pool_size ) + { + gray_PRaster rast = (gray_PRaster)raster; + + + if ( raster ) + { + if ( pool_base && pool_size >= (long)sizeof ( gray_TWorker ) + 2048 ) + { + gray_PWorker worker = (gray_PWorker)pool_base; + + + rast->worker = worker; + rast->buffer = pool_base + + ( ( sizeof ( gray_TWorker ) + + sizeof ( TCell ) - 1 ) & + ~( sizeof ( TCell ) - 1 ) ); + rast->buffer_size = (long)( ( pool_base + pool_size ) - + (char*)rast->buffer ) & + ~( sizeof ( TCell ) - 1 ); + rast->band_size = (int)( rast->buffer_size / + ( sizeof ( TCell ) * 8 ) ); + } + else + { + rast->buffer = NULL; + rast->buffer_size = 0; + rast->worker = NULL; + } + } + } + + + SW_FT_DEFINE_RASTER_FUNCS(sw_ft_grays_raster, + + (SW_FT_Raster_New_Func) gray_raster_new, + (SW_FT_Raster_Reset_Func) gray_raster_reset, + (SW_FT_Raster_Render_Func) gray_raster_render, + (SW_FT_Raster_Done_Func) gray_raster_done + ) + + +/* END */ diff --git a/src/lib/ector/software/sw_ft_raster.h b/src/lib/ector/software/sw_ft_raster.h new file mode 100755 index 0000000000..cb323d030c --- /dev/null +++ b/src/lib/ector/software/sw_ft_raster.h @@ -0,0 +1,607 @@ +#ifndef SW_FT_IMG_H +#define SW_FT_IMG_H +/***************************************************************************/ +/* */ +/* ftimage.h */ +/* */ +/* FreeType glyph image formats and default raster interface */ +/* (specification). */ +/* */ +/* Copyright 1996-2010, 2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Note: A `raster' is simply a scan-line converter, used to render */ + /* SW_FT_Outlines into SW_FT_Bitmaps. */ + /* */ + /*************************************************************************/ + +#include "sw_ft_types.h" + + /*************************************************************************/ + /* */ + /* */ + /* FT_BBox */ + /* */ + /* */ + /* A structure used to hold an outline's bounding box, i.e., the */ + /* coordinates of its extrema in the horizontal and vertical */ + /* directions. */ + /* */ + /* */ + /* xMin :: The horizontal minimum (left-most). */ + /* */ + /* yMin :: The vertical minimum (bottom-most). */ + /* */ + /* xMax :: The horizontal maximum (right-most). */ + /* */ + /* yMax :: The vertical maximum (top-most). */ + /* */ + /* */ + /* The bounding box is specified with the coordinates of the lower */ + /* left and the upper right corner. In PostScript, those values are */ + /* often called (llx,lly) and (urx,ury), respectively. */ + /* */ + /* If `yMin' is negative, this value gives the glyph's descender. */ + /* Otherwise, the glyph doesn't descend below the baseline. */ + /* Similarly, if `ymax' is positive, this value gives the glyph's */ + /* ascender. */ + /* */ + /* `xMin' gives the horizontal distance from the glyph's origin to */ + /* the left edge of the glyph's bounding box. If `xMin' is negative, */ + /* the glyph extends to the left of the origin. */ + /* */ + typedef struct SW_FT_BBox_ + { + SW_FT_Pos xMin, yMin; + SW_FT_Pos xMax, yMax; + + } SW_FT_BBox; + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Outline */ +/* */ +/* */ +/* This structure is used to describe an outline to the scan-line */ +/* converter. */ +/* */ +/* */ +/* n_contours :: The number of contours in the outline. */ +/* */ +/* n_points :: The number of points in the outline. */ +/* */ +/* points :: A pointer to an array of `n_points' @SW_FT_Vector */ +/* elements, giving the outline's point coordinates. */ +/* */ +/* tags :: A pointer to an array of `n_points' chars, giving */ +/* each outline point's type. */ +/* */ +/* If bit~0 is unset, the point is `off' the curve, */ +/* i.e., a Bézier control point, while it is `on' if */ +/* set. */ +/* */ +/* Bit~1 is meaningful for `off' points only. If set, */ +/* it indicates a third-order Bézier arc control point; */ +/* and a second-order control point if unset. */ +/* */ +/* If bit~2 is set, bits 5-7 contain the drop-out mode */ +/* (as defined in the OpenType specification; the value */ +/* is the same as the argument to the SCANMODE */ +/* instruction). */ +/* */ +/* Bits 3 and~4 are reserved for internal purposes. */ +/* */ +/* contours :: An array of `n_contours' shorts, giving the end */ +/* point of each contour within the outline. For */ +/* example, the first contour is defined by the points */ +/* `0' to `contours[0]', the second one is defined by */ +/* the points `contours[0]+1' to `contours[1]', etc. */ +/* */ +/* flags :: A set of bit flags used to characterize the outline */ +/* and give hints to the scan-converter and hinter on */ +/* how to convert/grid-fit it. See @SW_FT_OUTLINE_FLAGS.*/ +/* */ +typedef struct SW_FT_Outline_ +{ + short n_contours; /* number of contours in glyph */ + short n_points; /* number of points in the glyph */ + + SW_FT_Vector* points; /* the outline's points */ + char* tags; /* the points flags */ + short* contours; /* the contour end points */ + + int flags; /* outline masks */ + +} SW_FT_Outline; + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_OUTLINE_FLAGS */ + /* */ + /* */ + /* A list of bit-field constants use for the flags in an outline's */ + /* `flags' field. */ + /* */ + /* */ + /* SW_FT_OUTLINE_NONE :: */ + /* Value~0 is reserved. */ + /* */ + /* SW_FT_OUTLINE_OWNER :: */ + /* If set, this flag indicates that the outline's field arrays */ + /* (i.e., `points', `flags', and `contours') are `owned' by the */ + /* outline object, and should thus be freed when it is destroyed. */ + /* */ + /* SW_FT_OUTLINE_EVEN_ODD_FILL :: */ + /* By default, outlines are filled using the non-zero winding rule. */ + /* If set to 1, the outline will be filled using the even-odd fill */ + /* rule (only works with the smooth rasterizer). */ + /* */ + /* SW_FT_OUTLINE_REVERSE_FILL :: */ + /* By default, outside contours of an outline are oriented in */ + /* clock-wise direction, as defined in the TrueType specification. */ + /* This flag is set if the outline uses the opposite direction */ + /* (typically for Type~1 fonts). This flag is ignored by the scan */ + /* converter. */ + /* */ + /* */ + /* */ + /* There exists a second mechanism to pass the drop-out mode to the */ + /* B/W rasterizer; see the `tags' field in @SW_FT_Outline. */ + /* */ + /* Please refer to the description of the `SCANTYPE' instruction in */ + /* the OpenType specification (in file `ttinst1.doc') how simple */ + /* drop-outs, smart drop-outs, and stubs are defined. */ + /* */ +#define SW_FT_OUTLINE_NONE 0x0 +#define SW_FT_OUTLINE_OWNER 0x1 +#define SW_FT_OUTLINE_EVEN_ODD_FILL 0x2 +#define SW_FT_OUTLINE_REVERSE_FILL 0x4 + + /* */ + +#define SW_FT_CURVE_TAG( flag ) ( flag & 3 ) + +#define SW_FT_CURVE_TAG_ON 1 +#define SW_FT_CURVE_TAG_CONIC 0 +#define SW_FT_CURVE_TAG_CUBIC 2 + + +#define SW_FT_Curve_Tag_On SW_FT_CURVE_TAG_ON +#define SW_FT_Curve_Tag_Conic SW_FT_CURVE_TAG_CONIC +#define SW_FT_Curve_Tag_Cubic SW_FT_CURVE_TAG_CUBIC + + /*************************************************************************/ + /* */ + /* A raster is a scan converter, in charge of rendering an outline into */ + /* a a bitmap. This section contains the public API for rasters. */ + /* */ + /* Note that in FreeType 2, all rasters are now encapsulated within */ + /* specific modules called `renderers'. See `ftrender.h' for more */ + /* details on renderers. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Raster */ + /* */ + /* */ + /* A handle (pointer) to a raster object. Each object can be used */ + /* independently to convert an outline into a bitmap or pixmap. */ + /* */ + typedef struct SW_FT_RasterRec_* SW_FT_Raster; + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Span */ + /* */ + /* */ + /* A structure used to model a single span of gray (or black) pixels */ + /* when rendering a monochrome or anti-aliased bitmap. */ + /* */ + /* */ + /* x :: The span's horizontal start position. */ + /* */ + /* len :: The span's length in pixels. */ + /* */ + /* coverage :: The span color/coverage, ranging from 0 (background) */ + /* to 255 (foreground). Only used for anti-aliased */ + /* rendering. */ + /* */ + /* */ + /* This structure is used by the span drawing callback type named */ + /* @SW_FT_SpanFunc that takes the y~coordinate of the span as a */ + /* parameter. */ + /* */ + /* The coverage value is always between 0 and 255. If you want less */ + /* gray values, the callback function has to reduce them. */ + /* */ + typedef struct SW_FT_Span_ + { + short x; + short y; + unsigned short len; + unsigned char coverage; + + } SW_FT_Span; + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_SpanFunc */ + /* */ + /* */ + /* A function used as a call-back by the anti-aliased renderer in */ + /* order to let client applications draw themselves the gray pixel */ + /* spans on each scan line. */ + /* */ + /* */ + /* y :: The scanline's y~coordinate. */ + /* */ + /* count :: The number of spans to draw on this scanline. */ + /* */ + /* spans :: A table of `count' spans to draw on the scanline. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* */ + /* This callback allows client applications to directly render the */ + /* gray spans of the anti-aliased bitmap to any kind of surfaces. */ + /* */ + /* This can be used to write anti-aliased outlines directly to a */ + /* given background bitmap, and even perform translucency. */ + /* */ + /* Note that the `count' field cannot be greater than a fixed value */ + /* defined by the `SW_FT_MAX_GRAY_SPANS' configuration macro in */ + /* `ftoption.h'. By default, this value is set to~32, which means */ + /* that if there are more than 32~spans on a given scanline, the */ + /* callback is called several times with the same `y' parameter in */ + /* order to draw all callbacks. */ + /* */ + /* Otherwise, the callback is only called once per scan-line, and */ + /* only for those scanlines that do have `gray' pixels on them. */ + /* */ + typedef void + (*SW_FT_SpanFunc)( int count, + const SW_FT_Span* spans, + void* user ); + +#define SW_FT_Raster_Span_Func SW_FT_SpanFunc + + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_RASTER_FLAG_XXX */ + /* */ + /* */ + /* A list of bit flag constants as used in the `flags' field of a */ + /* @SW_FT_Raster_Params structure. */ + /* */ + /* */ + /* SW_FT_RASTER_FLAG_DEFAULT :: This value is 0. */ + /* */ + /* SW_FT_RASTER_FLAG_AA :: This flag is set to indicate that an */ + /* anti-aliased glyph image should be */ + /* generated. Otherwise, it will be */ + /* monochrome (1-bit). */ + /* */ + /* SW_FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */ + /* rendering. In this mode, client */ + /* applications must provide their own span */ + /* callback. This lets them directly */ + /* draw or compose over an existing bitmap. */ + /* If this bit is not set, the target */ + /* pixmap's buffer _must_ be zeroed before */ + /* rendering. */ + /* */ + /* Note that for now, direct rendering is */ + /* only possible with anti-aliased glyphs. */ + /* */ + /* SW_FT_RASTER_FLAG_CLIP :: This flag is only used in direct */ + /* rendering mode. If set, the output will */ + /* be clipped to a box specified in the */ + /* `clip_box' field of the */ + /* @SW_FT_Raster_Params structure. */ + /* */ + /* Note that by default, the glyph bitmap */ + /* is clipped to the target pixmap, except */ + /* in direct rendering mode where all spans */ + /* are generated if no clipping box is set. */ + /* */ +#define SW_FT_RASTER_FLAG_DEFAULT 0x0 +#define SW_FT_RASTER_FLAG_AA 0x1 +#define SW_FT_RASTER_FLAG_DIRECT 0x2 +#define SW_FT_RASTER_FLAG_CLIP 0x4 + + /* deprecated */ +#define ft_raster_flag_default SW_FT_RASTER_FLAG_DEFAULT +#define ft_raster_flag_aa SW_FT_RASTER_FLAG_AA +#define ft_raster_flag_direct SW_FT_RASTER_FLAG_DIRECT +#define ft_raster_flag_clip SW_FT_RASTER_FLAG_CLIP + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Raster_Params */ + /* */ + /* */ + /* A structure to hold the arguments used by a raster's render */ + /* function. */ + /* */ + /* */ + /* target :: The target bitmap. */ + /* */ + /* source :: A pointer to the source glyph image (e.g., an */ + /* @SW_FT_Outline). */ + /* */ + /* flags :: The rendering flags. */ + /* */ + /* gray_spans :: The gray span drawing callback. */ + /* */ + /* black_spans :: The black span drawing callback. UNIMPLEMENTED! */ + /* */ + /* bit_test :: The bit test callback. UNIMPLEMENTED! */ + /* */ + /* bit_set :: The bit set callback. UNIMPLEMENTED! */ + /* */ + /* user :: User-supplied data that is passed to each drawing */ + /* callback. */ + /* */ + /* clip_box :: An optional clipping box. It is only used in */ + /* direct rendering mode. Note that coordinates here */ + /* should be expressed in _integer_ pixels (and not in */ + /* 26.6 fixed-point units). */ + /* */ + /* */ + /* An anti-aliased glyph bitmap is drawn if the @SW_FT_RASTER_FLAG_AA */ + /* bit flag is set in the `flags' field, otherwise a monochrome */ + /* bitmap is generated. */ + /* */ + /* If the @SW_FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */ + /* raster will call the `gray_spans' callback to draw gray pixel */ + /* spans, in the case of an aa glyph bitmap, it will call */ + /* `black_spans', and `bit_test' and `bit_set' in the case of a */ + /* monochrome bitmap. This allows direct composition over a */ + /* pre-existing bitmap through user-provided callbacks to perform the */ + /* span drawing/composition. */ + /* */ + /* Note that the `bit_test' and `bit_set' callbacks are required when */ + /* rendering a monochrome bitmap, as they are crucial to implement */ + /* correct drop-out control as defined in the TrueType specification. */ + /* */ + typedef struct SW_FT_Raster_Params_ + { + const void* source; + int flags; + SW_FT_SpanFunc gray_spans; + void* user; + SW_FT_BBox clip_box; + + } SW_FT_Raster_Params; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Outline_Check */ +/* */ +/* */ +/* Check the contents of an outline descriptor. */ +/* */ +/* */ +/* outline :: A handle to a source outline. */ +/* */ +/* */ +/* FreeType error code. 0~means success. */ +/* */ +SW_FT_Error +SW_FT_Outline_Check( SW_FT_Outline* outline ); + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Outline_Get_CBox */ +/* */ +/* */ +/* Return an outline's `control box'. The control box encloses all */ +/* the outline's points, including Bézier control points. Though it */ +/* coincides with the exact bounding box for most glyphs, it can be */ +/* slightly larger in some situations (like when rotating an outline */ +/* that contains Bézier outside arcs). */ +/* */ +/* Computing the control box is very fast, while getting the bounding */ +/* box can take much more time as it needs to walk over all segments */ +/* and arcs in the outline. To get the latter, you can use the */ +/* `ftbbox' component, which is dedicated to this single task. */ +/* */ +/* */ +/* outline :: A pointer to the source outline descriptor. */ +/* */ +/* */ +/* acbox :: The outline's control box. */ +/* */ +/* */ +/* See @SW_FT_Glyph_Get_CBox for a discussion of tricky fonts. */ +/* */ +void +SW_FT_Outline_Get_CBox( const SW_FT_Outline* outline, + SW_FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Raster_NewFunc */ + /* */ + /* */ + /* A function used to create a new raster object. */ + /* */ + /* */ + /* memory :: A handle to the memory allocator. */ + /* */ + /* */ + /* raster :: A handle to the new raster object. */ + /* */ + /* */ + /* Error code. 0~means success. */ + /* */ + /* */ + /* The `memory' parameter is a typeless pointer in order to avoid */ + /* un-wanted dependencies on the rest of the FreeType code. In */ + /* practice, it is an @SW_FT_Memory object, i.e., a handle to the */ + /* standard FreeType memory allocator. However, this field can be */ + /* completely ignored by a given raster implementation. */ + /* */ + typedef int + (*SW_FT_Raster_NewFunc)( SW_FT_Raster* raster ); + +#define SW_FT_Raster_New_Func SW_FT_Raster_NewFunc + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Raster_DoneFunc */ + /* */ + /* */ + /* A function used to destroy a given raster object. */ + /* */ + /* */ + /* raster :: A handle to the raster object. */ + /* */ + typedef void + (*SW_FT_Raster_DoneFunc)( SW_FT_Raster raster ); + +#define SW_FT_Raster_Done_Func SW_FT_Raster_DoneFunc + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Raster_ResetFunc */ + /* */ + /* */ + /* FreeType provides an area of memory called the `render pool', */ + /* available to all registered rasters. This pool can be freely used */ + /* during a given scan-conversion but is shared by all rasters. Its */ + /* content is thus transient. */ + /* */ + /* This function is called each time the render pool changes, or just */ + /* after a new raster object is created. */ + /* */ + /* */ + /* raster :: A handle to the new raster object. */ + /* */ + /* pool_base :: The address in memory of the render pool. */ + /* */ + /* pool_size :: The size in bytes of the render pool. */ + /* */ + /* */ + /* Rasters can ignore the render pool and rely on dynamic memory */ + /* allocation if they want to (a handle to the memory allocator is */ + /* passed to the raster constructor). However, this is not */ + /* recommended for efficiency purposes. */ + /* */ + typedef void + (*SW_FT_Raster_ResetFunc)( SW_FT_Raster raster, + unsigned char* pool_base, + unsigned long pool_size ); + +#define SW_FT_Raster_Reset_Func SW_FT_Raster_ResetFunc + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Raster_RenderFunc */ + /* */ + /* */ + /* Invoke a given raster to scan-convert a given glyph image into a */ + /* target bitmap. */ + /* */ + /* */ + /* raster :: A handle to the raster object. */ + /* */ + /* params :: A pointer to an @SW_FT_Raster_Params structure used to */ + /* store the rendering parameters. */ + /* */ + /* */ + /* Error code. 0~means success. */ + /* */ + /* */ + /* The exact format of the source image depends on the raster's glyph */ + /* format defined in its @SW_FT_Raster_Funcs structure. It can be an */ + /* @SW_FT_Outline or anything else in order to support a large array of */ + /* glyph formats. */ + /* */ + /* Note also that the render function can fail and return a */ + /* `SW_FT_Err_Unimplemented_Feature' error code if the raster used does */ + /* not support direct composition. */ + /* */ + /* XXX: For now, the standard raster doesn't support direct */ + /* composition but this should change for the final release (see */ + /* the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c' */ + /* for examples of distinct implementations that support direct */ + /* composition). */ + /* */ + typedef int + (*SW_FT_Raster_RenderFunc)( SW_FT_Raster raster, + const SW_FT_Raster_Params* params ); + +#define SW_FT_Raster_Render_Func SW_FT_Raster_RenderFunc + + + /*************************************************************************/ + /* */ + /* */ + /* SW_FT_Raster_Funcs */ + /* */ + /* */ + /* A structure used to describe a given raster class to the library. */ + /* */ + /* */ + /* glyph_format :: The supported glyph format for this raster. */ + /* */ + /* raster_new :: The raster constructor. */ + /* */ + /* raster_reset :: Used to reset the render pool within the raster. */ + /* */ + /* raster_render :: A function to render a glyph into a given bitmap. */ + /* */ + /* raster_done :: The raster destructor. */ + /* */ + typedef struct SW_FT_Raster_Funcs_ + { + SW_FT_Raster_NewFunc raster_new; + SW_FT_Raster_ResetFunc raster_reset; + SW_FT_Raster_RenderFunc raster_render; + SW_FT_Raster_DoneFunc raster_done; + + } SW_FT_Raster_Funcs; + + +extern const SW_FT_Raster_Funcs sw_ft_grays_raster; + +#endif // SW_FT_IMG_H diff --git a/src/lib/ector/software/sw_ft_stroker.c b/src/lib/ector/software/sw_ft_stroker.c new file mode 100755 index 0000000000..d1c31e8b10 --- /dev/null +++ b/src/lib/ector/software/sw_ft_stroker.c @@ -0,0 +1,2292 @@ + +/***************************************************************************/ +/* */ +/* ftstroke.c */ +/* */ +/* FreeType path stroker (body). */ +/* */ +/* Copyright 2002-2006, 2008-2011, 2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "sw_ft_math.h" +#include "sw_ft_stroker.h" +#include +#include +#include + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BEZIER COMPUTATIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define SW_FT_SMALL_CONIC_THRESHOLD ( SW_FT_ANGLE_PI / 6 ) +#define SW_FT_SMALL_CUBIC_THRESHOLD ( SW_FT_ANGLE_PI / 8 ) + +#define SW_FT_EPSILON 2 + +#define SW_FT_IS_SMALL( x ) ( (x) > -SW_FT_EPSILON && (x) < SW_FT_EPSILON ) + + + static SW_FT_Pos + ft_pos_abs( SW_FT_Pos x ) + { + return x >= 0 ? x : -x; + } + + + static void + ft_conic_split( SW_FT_Vector* base ) + { + SW_FT_Pos 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 SW_FT_Bool + ft_conic_is_small_enough( SW_FT_Vector* base, + SW_FT_Angle *angle_in, + SW_FT_Angle *angle_out ) + { + SW_FT_Vector d1, d2; + SW_FT_Angle theta; + SW_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 = SW_FT_IS_SMALL( d1.x ) && SW_FT_IS_SMALL( d1.y ); + close2 = SW_FT_IS_SMALL( d2.x ) && SW_FT_IS_SMALL( d2.y ); + + if ( close1 ) + { + if ( close2 ) + { + /* basically a point; */ + /* do nothing to retain original direction */ + } + else + { + *angle_in = + *angle_out = SW_FT_Atan2( d2.x, d2.y ); + } + } + else /* !close1 */ + { + if ( close2 ) + { + *angle_in = + *angle_out = SW_FT_Atan2( d1.x, d1.y ); + } + else + { + *angle_in = SW_FT_Atan2( d1.x, d1.y ); + *angle_out = SW_FT_Atan2( d2.x, d2.y ); + } + } + + theta = ft_pos_abs( SW_FT_Angle_Diff( *angle_in, *angle_out ) ); + + return SW_FT_BOOL( theta < SW_FT_SMALL_CONIC_THRESHOLD ); + } + + + static void + ft_cubic_split( SW_FT_Vector* base ) + { + SW_FT_Pos 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; + } + + + /* Return the average of `angle1' and `angle2'. */ + /* This gives correct result even if `angle1' and `angle2' */ + /* have opposite signs. */ + static SW_FT_Angle + ft_angle_mean( SW_FT_Angle angle1, + SW_FT_Angle angle2 ) + { + return angle1 + SW_FT_Angle_Diff( angle1, angle2 ) / 2; + } + + + static SW_FT_Bool + ft_cubic_is_small_enough( SW_FT_Vector* base, + SW_FT_Angle *angle_in, + SW_FT_Angle *angle_mid, + SW_FT_Angle *angle_out ) + { + SW_FT_Vector d1, d2, d3; + SW_FT_Angle theta1, theta2; + SW_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 = SW_FT_IS_SMALL( d1.x ) && SW_FT_IS_SMALL( d1.y ); + close2 = SW_FT_IS_SMALL( d2.x ) && SW_FT_IS_SMALL( d2.y ); + close3 = SW_FT_IS_SMALL( d3.x ) && SW_FT_IS_SMALL( d3.y ); + + if ( close1 ) + { + if ( close2 ) + { + if ( close3 ) + { + /* basically a point; */ + /* do nothing to retain original direction */ + } + else /* !close3 */ + { + *angle_in = + *angle_mid = + *angle_out = SW_FT_Atan2( d3.x, d3.y ); + } + } + else /* !close2 */ + { + if ( close3 ) + { + *angle_in = + *angle_mid = + *angle_out = SW_FT_Atan2( d2.x, d2.y ); + } + else /* !close3 */ + { + *angle_in = + *angle_mid = SW_FT_Atan2( d2.x, d2.y ); + *angle_out = SW_FT_Atan2( d3.x, d3.y ); + } + } + } + else /* !close1 */ + { + if ( close2 ) + { + if ( close3 ) + { + *angle_in = + *angle_mid = + *angle_out = SW_FT_Atan2( d1.x, d1.y ); + } + else /* !close3 */ + { + *angle_in = SW_FT_Atan2( d1.x, d1.y ); + *angle_out = SW_FT_Atan2( d3.x, d3.y ); + *angle_mid = ft_angle_mean( *angle_in, *angle_out ); + } + } + else /* !close2 */ + { + if ( close3 ) + { + *angle_in = SW_FT_Atan2( d1.x, d1.y ); + *angle_mid = + *angle_out = SW_FT_Atan2( d2.x, d2.y ); + } + else /* !close3 */ + { + *angle_in = SW_FT_Atan2( d1.x, d1.y ); + *angle_mid = SW_FT_Atan2( d2.x, d2.y ); + *angle_out = SW_FT_Atan2( d3.x, d3.y ); + } + } + } + + theta1 = ft_pos_abs( SW_FT_Angle_Diff( *angle_in, *angle_mid ) ); + theta2 = ft_pos_abs( SW_FT_Angle_Diff( *angle_mid, *angle_out ) ); + + return SW_FT_BOOL( theta1 < SW_FT_SMALL_CUBIC_THRESHOLD && + theta2 < SW_FT_SMALL_CUBIC_THRESHOLD ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STROKE BORDERS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef enum SW_FT_StrokeTags_ + { + SW_FT_STROKE_TAG_ON = 1, /* on-curve point */ + SW_FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ + SW_FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ + SW_FT_STROKE_TAG_END = 8 /* sub-path end */ + + } SW_FT_StrokeTags; + +#define SW_FT_STROKE_TAG_BEGIN_END ( SW_FT_STROKE_TAG_BEGIN | SW_FT_STROKE_TAG_END ) + + typedef struct SW_FT_StrokeBorderRec_ + { + SW_FT_UInt num_points; + SW_FT_UInt max_points; + SW_FT_Vector* points; + SW_FT_Byte* tags; + SW_FT_Bool movable; /* TRUE for ends of lineto borders */ + SW_FT_Int start; /* index of current sub-path start point */ + SW_FT_Bool valid; + + } SW_FT_StrokeBorderRec, *SW_FT_StrokeBorder; + + + + SW_FT_Error + SW_FT_Outline_Check( SW_FT_Outline* outline ) + { + if ( outline ) + { + SW_FT_Int n_points = outline->n_points; + SW_FT_Int n_contours = outline->n_contours; + SW_FT_Int end0, end; + SW_FT_Int n; + + + /* empty glyph? */ + if ( n_points == 0 && n_contours == 0 ) + return 0; + + /* check point and contour counts */ + if ( n_points <= 0 || n_contours <= 0 ) + goto Bad; + + end0 = end = -1; + for ( n = 0; n < n_contours; n++ ) + { + end = outline->contours[n]; + + /* note that we don't accept empty contours */ + if ( end <= end0 || end >= n_points ) + goto Bad; + + end0 = end; + } + + if ( end != n_points - 1 ) + goto Bad; + + /* XXX: check the tags array */ + return 0; + } + + Bad: + return -1;//SW_FT_THROW( Invalid_Argument ); + } + + + + void + SW_FT_Outline_Get_CBox( const SW_FT_Outline* outline, + SW_FT_BBox *acbox ) + { + SW_FT_Pos xMin, yMin, xMax, yMax; + + + if ( outline && acbox ) + { + if ( outline->n_points == 0 ) + { + xMin = 0; + yMin = 0; + xMax = 0; + yMax = 0; + } + else + { + SW_FT_Vector* vec = outline->points; + SW_FT_Vector* limit = vec + outline->n_points; + + + xMin = xMax = vec->x; + yMin = yMax = vec->y; + vec++; + + for ( ; vec < limit; vec++ ) + { + SW_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 SW_FT_Error + ft_stroke_border_grow( SW_FT_StrokeBorder border, + SW_FT_UInt new_points ) + { + SW_FT_UInt old_max = border->max_points; + SW_FT_UInt new_max = border->num_points + new_points; + SW_FT_Error error = 0; + + + if ( new_max > old_max ) + { + SW_FT_UInt cur_max = old_max; + + + while ( cur_max < new_max ) + cur_max += ( cur_max >> 1 ) + 16; + + border->points = (SW_FT_Vector *) realloc(border->points, cur_max * sizeof(SW_FT_Vector)); + border->tags = (SW_FT_Byte *) realloc(border->tags, cur_max * sizeof(SW_FT_Byte)); + + if ( !border->points || !border->tags) + goto Exit; + + border->max_points = cur_max; + } + + Exit: + return error; + } + + + static void + ft_stroke_border_close( SW_FT_StrokeBorder border, + SW_FT_Bool reverse ) + { + SW_FT_UInt start = border->start; + SW_FT_UInt count = border->num_points; + + + assert( border->start >= 0 ); + + /* don't record empty paths! */ + if ( count <= start + 1U ) + border->num_points = start; + else + { + /* copy the last point to the start of this sub-path, since */ + /* it contains the `adjusted' starting coordinates */ + border->num_points = --count; + border->points[start] = border->points[count]; + + if ( reverse ) + { + /* reverse the points */ + { + SW_FT_Vector* vec1 = border->points + start + 1; + SW_FT_Vector* vec2 = border->points + count - 1; + + + for ( ; vec1 < vec2; vec1++, vec2-- ) + { + SW_FT_Vector tmp; + + + tmp = *vec1; + *vec1 = *vec2; + *vec2 = tmp; + } + } + + /* then the tags */ + { + SW_FT_Byte* tag1 = border->tags + start + 1; + SW_FT_Byte* tag2 = border->tags + count - 1; + + + for ( ; tag1 < tag2; tag1++, tag2-- ) + { + SW_FT_Byte tmp; + + + tmp = *tag1; + *tag1 = *tag2; + *tag2 = tmp; + } + } + } + + border->tags[start ] |= SW_FT_STROKE_TAG_BEGIN; + border->tags[count - 1] |= SW_FT_STROKE_TAG_END; + } + + border->start = -1; + border->movable = FALSE; + } + + + static SW_FT_Error + ft_stroke_border_lineto( SW_FT_StrokeBorder border, + SW_FT_Vector* to, + SW_FT_Bool movable ) + { + SW_FT_Error error = 0; + + + assert( border->start >= 0 ); + + if ( border->movable ) + { + /* move last point */ + border->points[border->num_points - 1] = *to; + } + else + { + /* don't add zero-length lineto */ + if ( border->num_points > 0 && + SW_FT_IS_SMALL( border->points[border->num_points - 1].x - to->x ) && + SW_FT_IS_SMALL( border->points[border->num_points - 1].y - to->y ) ) + return error; + + /* add one point */ + error = ft_stroke_border_grow( border, 1 ); + if ( !error ) + { + SW_FT_Vector* vec = border->points + border->num_points; + SW_FT_Byte* tag = border->tags + border->num_points; + + + vec[0] = *to; + tag[0] = SW_FT_STROKE_TAG_ON; + + border->num_points += 1; + } + } + border->movable = movable; + return error; + } + + + static SW_FT_Error + ft_stroke_border_conicto( SW_FT_StrokeBorder border, + SW_FT_Vector* control, + SW_FT_Vector* to ) + { + SW_FT_Error error; + + + assert( border->start >= 0 ); + + error = ft_stroke_border_grow( border, 2 ); + if ( !error ) + { + SW_FT_Vector* vec = border->points + border->num_points; + SW_FT_Byte* tag = border->tags + border->num_points; + + + vec[0] = *control; + vec[1] = *to; + + tag[0] = 0; + tag[1] = SW_FT_STROKE_TAG_ON; + + border->num_points += 2; + } + + border->movable = FALSE; + + return error; + } + + + static SW_FT_Error + ft_stroke_border_cubicto( SW_FT_StrokeBorder border, + SW_FT_Vector* control1, + SW_FT_Vector* control2, + SW_FT_Vector* to ) + { + SW_FT_Error error; + + + assert( border->start >= 0 ); + + error = ft_stroke_border_grow( border, 3 ); + if ( !error ) + { + SW_FT_Vector* vec = border->points + border->num_points; + SW_FT_Byte* tag = border->tags + border->num_points; + + + vec[0] = *control1; + vec[1] = *control2; + vec[2] = *to; + + tag[0] = SW_FT_STROKE_TAG_CUBIC; + tag[1] = SW_FT_STROKE_TAG_CUBIC; + tag[2] = SW_FT_STROKE_TAG_ON; + + border->num_points += 3; + } + + border->movable = FALSE; + + return error; + } + + +#define SW_FT_ARC_CUBIC_ANGLE ( SW_FT_ANGLE_PI / 2 ) + + + static SW_FT_Error + ft_stroke_border_arcto( SW_FT_StrokeBorder border, + SW_FT_Vector* center, + SW_FT_Fixed radius, + SW_FT_Angle angle_start, + SW_FT_Angle angle_diff ) + { + SW_FT_Angle total, angle, step, rotate, next, theta; + SW_FT_Vector a, b, a2, b2; + SW_FT_Fixed length; + SW_FT_Error error = 0; + + + /* compute start point */ + SW_FT_Vector_From_Polar( &a, radius, angle_start ); + a.x += center->x; + a.y += center->y; + + total = angle_diff; + angle = angle_start; + rotate = ( angle_diff >= 0 ) ? SW_FT_ANGLE_PI2 : -SW_FT_ANGLE_PI2; + + while ( total != 0 ) + { + step = total; + if ( step > SW_FT_ARC_CUBIC_ANGLE ) + step = SW_FT_ARC_CUBIC_ANGLE; + + else if ( step < -SW_FT_ARC_CUBIC_ANGLE ) + step = -SW_FT_ARC_CUBIC_ANGLE; + + next = angle + step; + theta = step; + if ( theta < 0 ) + theta = -theta; + + theta >>= 1; + + /* compute end point */ + SW_FT_Vector_From_Polar( &b, radius, next ); + b.x += center->x; + b.y += center->y; + + /* compute first and second control points */ + length = SW_FT_MulDiv( radius, SW_FT_Sin( theta ) * 4, + ( 0x10000L + SW_FT_Cos( theta ) ) * 3 ); + + SW_FT_Vector_From_Polar( &a2, length, angle + rotate ); + a2.x += a.x; + a2.y += a.y; + + SW_FT_Vector_From_Polar( &b2, length, next - rotate ); + b2.x += b.x; + b2.y += b.y; + + /* add cubic arc */ + error = ft_stroke_border_cubicto( border, &a2, &b2, &b ); + if ( error ) + break; + + /* process the rest of the arc ?? */ + a = b; + total -= step; + angle = next; + } + + return error; + } + + + static SW_FT_Error + ft_stroke_border_moveto( SW_FT_StrokeBorder border, + SW_FT_Vector* to ) + { + /* close current open path if any ? */ + 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( SW_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( SW_FT_StrokeBorder border ) + { + border->num_points = 0; + border->start = -1; + border->valid = FALSE; + } + + + static void + ft_stroke_border_done( SW_FT_StrokeBorder border ) + { + + free( border->points ); + free( border->tags ); + + border->num_points = 0; + border->max_points = 0; + border->start = -1; + border->valid = FALSE; + } + + + static SW_FT_Error + ft_stroke_border_get_counts( SW_FT_StrokeBorder border, + SW_FT_UInt *anum_points, + SW_FT_UInt *anum_contours ) + { + SW_FT_Error error = 0; + SW_FT_UInt num_points = 0; + SW_FT_UInt num_contours = 0; + + SW_FT_UInt count = border->num_points; + SW_FT_Vector* point = border->points; + SW_FT_Byte* tags = border->tags; + SW_FT_Int in_contour = 0; + + + for ( ; count > 0; count--, num_points++, point++, tags++ ) + { + if ( tags[0] & SW_FT_STROKE_TAG_BEGIN ) + { + if ( in_contour != 0 ) + goto Fail; + + in_contour = 1; + } + else if ( in_contour == 0 ) + goto Fail; + + if ( tags[0] & SW_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( SW_FT_StrokeBorder border, + SW_FT_Outline* outline ) + { + /* copy point locations */ + memcpy( outline->points + outline->n_points, + border->points, + border->num_points * sizeof(SW_FT_Vector)); + + /* copy tags */ + { + SW_FT_UInt count = border->num_points; + SW_FT_Byte* read = border->tags; + SW_FT_Byte* write = (SW_FT_Byte*)outline->tags + outline->n_points; + + + for ( ; count > 0; count--, read++, write++ ) + { + if ( *read & SW_FT_STROKE_TAG_ON ) + *write = SW_FT_CURVE_TAG_ON; + else if ( *read & SW_FT_STROKE_TAG_CUBIC ) + *write = SW_FT_CURVE_TAG_CUBIC; + else + *write = SW_FT_CURVE_TAG_CONIC; + } + } + + /* copy contours */ + { + SW_FT_UInt count = border->num_points; + SW_FT_Byte* tags = border->tags; + SW_FT_Short* write = outline->contours + outline->n_contours; + SW_FT_Short idx = (SW_FT_Short)outline->n_points; + + + for ( ; count > 0; count--, tags++, idx++ ) + { + if ( *tags & SW_FT_STROKE_TAG_END ) + { + *write++ = idx; + outline->n_contours++; + } + } + } + + outline->n_points = (short)( outline->n_points + border->num_points ); + + assert( SW_FT_Outline_Check( outline ) == 0 ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STROKER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define SW_FT_SIDE_TO_ROTATE( s ) ( SW_FT_ANGLE_PI2 - (s) * SW_FT_ANGLE_PI ) + + typedef struct SW_FT_StrokerRec_ + { + SW_FT_Angle angle_in; /* direction into curr join */ + SW_FT_Angle angle_out; /* direction out of join */ + SW_FT_Vector center; /* current position */ + SW_FT_Fixed line_length; /* length of last lineto */ + SW_FT_Bool first_point; /* is this the start? */ + SW_FT_Bool subpath_open; /* is the subpath open? */ + SW_FT_Angle subpath_angle; /* subpath start direction */ + SW_FT_Vector subpath_start; /* subpath start position */ + SW_FT_Fixed subpath_line_length; /* subpath start lineto len */ + SW_FT_Bool handle_wide_strokes; /* use wide strokes logic? */ + + SW_FT_Stroker_LineCap line_cap; + SW_FT_Stroker_LineJoin line_join; + SW_FT_Stroker_LineJoin line_join_saved; + SW_FT_Fixed miter_limit; + SW_FT_Fixed radius; + + SW_FT_StrokeBorderRec borders[2]; + } SW_FT_StrokerRec; + + + /* documentation is in ftstroke.h */ + + SW_FT_Error + SW_FT_Stroker_New( SW_FT_Stroker *astroker ) + { + SW_FT_Error error = 0; /* assigned in SW_FT_NEW */ + SW_FT_Stroker stroker = NULL; + + + stroker = (SW_FT_StrokerRec *) calloc(1, sizeof(SW_FT_StrokerRec)); + if ( stroker ) + { + + ft_stroke_border_init( &stroker->borders[0]); + ft_stroke_border_init( &stroker->borders[1]); + } + + *astroker = stroker; + + return error; + } + + void + SW_FT_Stroker_Rewind( SW_FT_Stroker stroker ) + { + if ( stroker ) + { + ft_stroke_border_reset( &stroker->borders[0] ); + ft_stroke_border_reset( &stroker->borders[1] ); + } + } + + + /* documentation is in ftstroke.h */ + + void + SW_FT_Stroker_Set( SW_FT_Stroker stroker, + SW_FT_Fixed radius, + SW_FT_Stroker_LineCap line_cap, + SW_FT_Stroker_LineJoin line_join, + SW_FT_Fixed miter_limit ) + { + stroker->radius = radius; + stroker->line_cap = line_cap; + stroker->line_join = line_join; + stroker->miter_limit = miter_limit; + + /* ensure miter limit has sensible value */ + if ( stroker->miter_limit < 0x10000 ) + stroker->miter_limit = 0x10000; + + /* save line join style: */ + /* line join style can be temporarily changed when stroking curves */ + stroker->line_join_saved = line_join; + + SW_FT_Stroker_Rewind( stroker ); + } + + /* documentation is in ftstroke.h */ + + void + SW_FT_Stroker_Done( SW_FT_Stroker stroker ) + { + if ( stroker ) + { + + ft_stroke_border_done( &stroker->borders[0] ); + ft_stroke_border_done( &stroker->borders[1] ); + + free( stroker ); + } + } + + + /* create a circular arc at a corner or cap */ + static SW_FT_Error + ft_stroker_arcto( SW_FT_Stroker stroker, + SW_FT_Int side ) + { + SW_FT_Angle total, rotate; + SW_FT_Fixed radius = stroker->radius; + SW_FT_Error error = 0; + SW_FT_StrokeBorder border = stroker->borders + side; + + + rotate = SW_FT_SIDE_TO_ROTATE( side ); + + total = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + if ( total == SW_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; + } + + + /* add a cap at the end of an opened path */ + static SW_FT_Error + ft_stroker_cap( SW_FT_Stroker stroker, + SW_FT_Angle angle, + SW_FT_Int side ) + { + SW_FT_Error error = 0; + + + if ( stroker->line_cap == SW_FT_STROKER_LINECAP_ROUND ) + { + /* add a round cap */ + stroker->angle_in = angle; + stroker->angle_out = angle + SW_FT_ANGLE_PI; + + error = ft_stroker_arcto( stroker, side ); + } + else if ( stroker->line_cap == SW_FT_STROKER_LINECAP_SQUARE ) + { + /* add a square cap */ + SW_FT_Vector delta, delta2; + SW_FT_Angle rotate = SW_FT_SIDE_TO_ROTATE( side ); + SW_FT_Fixed radius = stroker->radius; + SW_FT_StrokeBorder border = stroker->borders + side; + + + SW_FT_Vector_From_Polar( &delta2, radius, angle + rotate ); + SW_FT_Vector_From_Polar( &delta, radius, angle ); + + delta.x += stroker->center.x + delta2.x; + delta.y += stroker->center.y + delta2.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + SW_FT_Vector_From_Polar( &delta2, radius, angle - rotate ); + SW_FT_Vector_From_Polar( &delta, radius, angle ); + + delta.x += delta2.x + stroker->center.x; + delta.y += delta2.y + stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + else if ( stroker->line_cap == SW_FT_STROKER_LINECAP_BUTT ) + { + /* add a butt ending */ + SW_FT_Vector delta; + SW_FT_Angle rotate = SW_FT_SIDE_TO_ROTATE( side ); + SW_FT_Fixed radius = stroker->radius; + SW_FT_StrokeBorder border = stroker->borders + side; + + + SW_FT_Vector_From_Polar( &delta, radius, angle + rotate ); + + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + SW_FT_Vector_From_Polar( &delta, radius, angle - rotate ); + + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + + Exit: + return error; + } + + + /* process an inside corner, i.e. compute intersection */ + static SW_FT_Error + ft_stroker_inside( SW_FT_Stroker stroker, + SW_FT_Int side, + SW_FT_Fixed line_length ) + { + SW_FT_StrokeBorder border = stroker->borders + side; + SW_FT_Angle phi, theta, rotate; + SW_FT_Fixed length, thcos; + SW_FT_Vector delta; + SW_FT_Error error = 0; + SW_FT_Bool intersect; /* use intersection of lines? */ + + + rotate = SW_FT_SIDE_TO_ROTATE( side ); + + theta = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2; + + /* Only intersect borders if between two lineto's and both */ + /* lines are long enough (line_length is zero for curves). */ + if ( !border->movable || line_length == 0 ) + intersect = FALSE; + else + { + /* compute minimum required length of lines */ + SW_FT_Fixed min_length = ft_pos_abs( SW_FT_MulFix( stroker->radius, + SW_FT_Tan( theta ) ) ); + + + intersect = SW_FT_BOOL( stroker->line_length >= min_length && + line_length >= min_length ); + } + + if ( !intersect ) + { + SW_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 + { + /* compute median angle */ + phi = stroker->angle_in + theta; + + thcos = SW_FT_Cos( theta ); + + length = SW_FT_DivFix( stroker->radius, thcos ); + + SW_FT_Vector_From_Polar( &delta, length, phi + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + } + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + + return error; + } + + + /* process an outside corner, i.e. compute bevel/miter/round */ + static SW_FT_Error + ft_stroker_outside( SW_FT_Stroker stroker, + SW_FT_Int side, + SW_FT_Fixed line_length ) + { + SW_FT_StrokeBorder border = stroker->borders + side; + SW_FT_Error error; + SW_FT_Angle rotate; + + + if ( stroker->line_join == SW_FT_STROKER_LINEJOIN_ROUND ) + error = ft_stroker_arcto( stroker, side ); + else + { + /* this is a mitered (pointed) or beveled (truncated) corner */ + SW_FT_Fixed sigma = 0, radius = stroker->radius; + SW_FT_Angle theta = 0, phi = 0; + SW_FT_Fixed thcos = 0; + SW_FT_Bool bevel, fixed_bevel; + + + rotate = SW_FT_SIDE_TO_ROTATE( side ); + + bevel = + SW_FT_BOOL( stroker->line_join == SW_FT_STROKER_LINEJOIN_BEVEL ); + + fixed_bevel = + SW_FT_BOOL( stroker->line_join != SW_FT_STROKER_LINEJOIN_MITER_VARIABLE ); + + if ( !bevel ) + { + theta = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + + if ( theta == SW_FT_ANGLE_PI ) + { + theta = rotate; + phi = stroker->angle_in; + } + else + { + theta /= 2; + phi = stroker->angle_in + theta + rotate; + } + + thcos = SW_FT_Cos( theta ); + sigma = SW_FT_MulFix( stroker->miter_limit, thcos ); + + /* is miter limit exceeded? */ + if ( sigma < 0x10000L ) + { + /* don't create variable bevels for very small deviations; */ + /* SW_FT_Sin(x) = 0 for x <= 57 */ + if ( fixed_bevel || ft_pos_abs( theta ) > 57 ) + bevel = TRUE; + } + } + + if ( bevel ) /* this is a bevel (broken angle) */ + { + if ( fixed_bevel ) + { + /* the outer corners are simply joined together */ + SW_FT_Vector delta; + + + /* add bevel */ + SW_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 /* variable bevel */ + { + /* the miter is truncated */ + SW_FT_Vector middle, delta; + SW_FT_Fixed length; + + + /* compute middle point */ + SW_FT_Vector_From_Polar( &middle, + SW_FT_MulFix( radius, stroker->miter_limit ), + phi ); + middle.x += stroker->center.x; + middle.y += stroker->center.y; + + /* compute first angle point */ + length = SW_FT_MulDiv( radius, 0x10000L - sigma, + ft_pos_abs( SW_FT_Sin( theta ) ) ); + + SW_FT_Vector_From_Polar( &delta, length, phi + rotate ); + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + /* compute second angle point */ + SW_FT_Vector_From_Polar( &delta, length, phi - rotate ); + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + /* finally, add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if ( line_length == 0 ) + { + SW_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 /* this is a miter (intersection) */ + { + SW_FT_Fixed length; + SW_FT_Vector delta; + + + length = SW_FT_DivFix( stroker->radius, thcos ); + + SW_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; + + /* now add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if ( line_length == 0 ) + { + SW_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 SW_FT_Error + ft_stroker_process_corner( SW_FT_Stroker stroker, + SW_FT_Fixed line_length ) + { + SW_FT_Error error = 0; + SW_FT_Angle turn; + SW_FT_Int inside_side; + + + turn = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if ( turn == 0 ) + goto Exit; + + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + + /* otherwise, the inside side is 1 */ + if ( turn < 0 ) + inside_side = 1; + + /* process the inside side */ + error = ft_stroker_inside( stroker, inside_side, line_length ); + if ( error ) + goto Exit; + + /* process the outside side */ + error = ft_stroker_outside( stroker, 1 - inside_side, line_length ); + + Exit: + return error; + } + + + /* add two points to the left and right borders corresponding to the */ + /* start of the subpath */ + static SW_FT_Error + ft_stroker_subpath_start( SW_FT_Stroker stroker, + SW_FT_Angle start_angle, + SW_FT_Fixed line_length ) + { + SW_FT_Vector delta; + SW_FT_Vector point; + SW_FT_Error error; + SW_FT_StrokeBorder border; + + + SW_FT_Vector_From_Polar( &delta, stroker->radius, + start_angle + SW_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 ); + + /* save angle, position, and line length for last join */ + /* (line_length is zero for curves) */ + stroker->subpath_angle = start_angle; + stroker->first_point = FALSE; + stroker->subpath_line_length = line_length; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + SW_FT_Error + SW_FT_Stroker_LineTo( SW_FT_Stroker stroker, + SW_FT_Vector* to ) + { + SW_FT_Error error = 0; + SW_FT_StrokeBorder border; + SW_FT_Vector delta; + SW_FT_Angle angle; + SW_FT_Int side; + SW_FT_Fixed line_length; + + + delta.x = to->x - stroker->center.x; + delta.y = to->y - stroker->center.y; + + /* a zero-length lineto is a no-op; avoid creating a spurious corner */ + if ( delta.x == 0 && delta.y == 0 ) + goto Exit; + + /* compute length of line */ + line_length = SW_FT_Vector_Length( &delta ); + + angle = SW_FT_Atan2( delta.x, delta.y ); + SW_FT_Vector_From_Polar( &delta, stroker->radius, angle + SW_FT_ANGLE_PI2 ); + + /* process corner if necessary */ + if ( stroker->first_point ) + { + /* This is the first segment of a subpath. We need to */ + /* add a point to each border at their respective starting */ + /* point locations. */ + error = ft_stroker_subpath_start( stroker, angle, line_length ); + if ( error ) + goto Exit; + } + else + { + /* process the current corner */ + stroker->angle_out = angle; + error = ft_stroker_process_corner( stroker, line_length ); + if ( error ) + goto Exit; + } + + /* now add a line segment to both the `inside' and `outside' paths */ + for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) + { + SW_FT_Vector point; + + + point.x = to->x + delta.x; + point.y = to->y + delta.y; + + /* the ends of lineto borders are movable */ + 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; + } + + + /* documentation is in ftstroke.h */ + + SW_FT_Error + SW_FT_Stroker_ConicTo( SW_FT_Stroker stroker, + SW_FT_Vector* control, + SW_FT_Vector* to ) + { + SW_FT_Error error = 0; + SW_FT_Vector bez_stack[34]; + SW_FT_Vector* arc; + SW_FT_Vector* limit = bez_stack + 30; + SW_FT_Bool first_arc = TRUE; + + + /* if all control points are coincident, this is a no-op; */ + /* avoid creating a spurious corner */ + if ( SW_FT_IS_SMALL( stroker->center.x - control->x ) && + SW_FT_IS_SMALL( stroker->center.y - control->y ) && + SW_FT_IS_SMALL( control->x - to->x ) && + SW_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 ) + { + SW_FT_Angle angle_in, angle_out; + + + /* initialize with current direction */ + 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; + + /* process corner if necessary */ + 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( SW_FT_Angle_Diff( stroker->angle_in, angle_in ) ) > + SW_FT_SMALL_CONIC_THRESHOLD / 4 ) + { + /* if the deviation from one arc to the next is too great, */ + /* add a round corner */ + stroker->center = arc[2]; + stroker->angle_out = angle_in; + stroker->line_join = SW_FT_STROKER_LINEJOIN_ROUND; + + error = ft_stroker_process_corner( stroker, 0 ); + + /* reinstate line join style */ + stroker->line_join = stroker->line_join_saved; + } + + if ( error ) + goto Exit; + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + SW_FT_Vector ctrl, end; + SW_FT_Angle theta, phi, rotate, alpha0 = 0; + SW_FT_Fixed length; + SW_FT_StrokeBorder border; + SW_FT_Int side; + + + theta = SW_FT_Angle_Diff( angle_in, angle_out ) / 2; + phi = angle_in + theta; + length = SW_FT_DivFix( stroker->radius, SW_FT_Cos( theta ) ); + + /* compute direction of original arc */ + if ( stroker->handle_wide_strokes ) + alpha0 = SW_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 = SW_FT_SIDE_TO_ROTATE( side ); + + /* compute control point */ + SW_FT_Vector_From_Polar( &ctrl, length, phi + rotate ); + ctrl.x += arc[1].x; + ctrl.y += arc[1].y; + + /* compute end point */ + SW_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 ) + { + SW_FT_Vector start; + SW_FT_Angle alpha1; + + + /* determine whether the border radius is greater than the */ + /* radius of curvature of the original arc */ + start = border->points[border->num_points - 1]; + + alpha1 = SW_FT_Atan2( end.x - start.x, end.y - start.y ); + + /* is the direction of the border arc opposite to */ + /* that of the original arc? */ + if ( ft_pos_abs( SW_FT_Angle_Diff( alpha0, alpha1 ) ) > + SW_FT_ANGLE_PI / 2 ) + { + SW_FT_Angle beta, gamma; + SW_FT_Vector bvec, delta; + SW_FT_Fixed blen, sinA, sinB, alen; + + + /* use the sine rule to find the intersection point */ + beta = SW_FT_Atan2( arc[2].x - start.x, arc[2].y - start.y ); + gamma = SW_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 = SW_FT_Vector_Length( &bvec ); + + sinA = ft_pos_abs( SW_FT_Sin( alpha1 - gamma ) ); + sinB = ft_pos_abs( SW_FT_Sin( beta - gamma ) ); + + alen = SW_FT_MulDiv( blen, sinA, sinB ); + + SW_FT_Vector_From_Polar( &delta, alen, beta ); + delta.x += start.x; + delta.y += start.y; + + /* circumnavigate the negative sector backwards */ + 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; + /* and then move to the endpoint */ + error = ft_stroke_border_lineto( border, &end, FALSE ); + if ( error ) + goto Exit; + + continue; + } + + /* else fall through */ + } + + /* simply add an arc */ + error = ft_stroke_border_conicto( border, &ctrl, &end ); + if ( error ) + goto Exit; + } + } + + arc -= 2; + + stroker->angle_in = angle_out; + } + + stroker->center = *to; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + SW_FT_Error + SW_FT_Stroker_CubicTo( SW_FT_Stroker stroker, + SW_FT_Vector* control1, + SW_FT_Vector* control2, + SW_FT_Vector* to ) + { + SW_FT_Error error = 0; + SW_FT_Vector bez_stack[37]; + SW_FT_Vector* arc; + SW_FT_Vector* limit = bez_stack + 32; + SW_FT_Bool first_arc = TRUE; + + + /* if all control points are coincident, this is a no-op; */ + /* avoid creating a spurious corner */ + if ( SW_FT_IS_SMALL( stroker->center.x - control1->x ) && + SW_FT_IS_SMALL( stroker->center.y - control1->y ) && + SW_FT_IS_SMALL( control1->x - control2->x ) && + SW_FT_IS_SMALL( control1->y - control2->y ) && + SW_FT_IS_SMALL( control2->x - to->x ) && + SW_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 ) + { + SW_FT_Angle angle_in, angle_mid, angle_out; + + + /* initialize with current direction */ + 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; + + /* process corner if necessary */ + 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( SW_FT_Angle_Diff( stroker->angle_in, angle_in ) ) > + SW_FT_SMALL_CUBIC_THRESHOLD / 4 ) + { + /* if the deviation from one arc to the next is too great, */ + /* add a round corner */ + stroker->center = arc[3]; + stroker->angle_out = angle_in; + stroker->line_join = SW_FT_STROKER_LINEJOIN_ROUND; + + error = ft_stroker_process_corner( stroker, 0 ); + + /* reinstate line join style */ + stroker->line_join = stroker->line_join_saved; + } + + if ( error ) + goto Exit; + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + SW_FT_Vector ctrl1, ctrl2, end; + SW_FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0; + SW_FT_Fixed length1, length2; + SW_FT_StrokeBorder border; + SW_FT_Int side; + + + theta1 = SW_FT_Angle_Diff( angle_in, angle_mid ) / 2; + theta2 = SW_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 = SW_FT_DivFix( stroker->radius, SW_FT_Cos( theta1 ) ); + length2 = SW_FT_DivFix( stroker->radius, SW_FT_Cos( theta2 ) ); + + /* compute direction of original arc */ + if ( stroker->handle_wide_strokes ) + alpha0 = SW_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 = SW_FT_SIDE_TO_ROTATE( side ); + + /* compute control points */ + SW_FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); + ctrl1.x += arc[2].x; + ctrl1.y += arc[2].y; + + SW_FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); + ctrl2.x += arc[1].x; + ctrl2.y += arc[1].y; + + /* compute end point */ + SW_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 ) + { + SW_FT_Vector start; + SW_FT_Angle alpha1; + + + /* determine whether the border radius is greater than the */ + /* radius of curvature of the original arc */ + start = border->points[border->num_points - 1]; + + alpha1 = SW_FT_Atan2( end.x - start.x, end.y - start.y ); + + /* is the direction of the border arc opposite to */ + /* that of the original arc? */ + if ( ft_pos_abs( SW_FT_Angle_Diff( alpha0, alpha1 ) ) > + SW_FT_ANGLE_PI / 2 ) + { + SW_FT_Angle beta, gamma; + SW_FT_Vector bvec, delta; + SW_FT_Fixed blen, sinA, sinB, alen; + + + /* use the sine rule to find the intersection point */ + beta = SW_FT_Atan2( arc[3].x - start.x, arc[3].y - start.y ); + gamma = SW_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 = SW_FT_Vector_Length( &bvec ); + + sinA = ft_pos_abs( SW_FT_Sin( alpha1 - gamma ) ); + sinB = ft_pos_abs( SW_FT_Sin( beta - gamma ) ); + + alen = SW_FT_MulDiv( blen, sinA, sinB ); + + SW_FT_Vector_From_Polar( &delta, alen, beta ); + delta.x += start.x; + delta.y += start.y; + + /* circumnavigate the negative sector backwards */ + 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; + /* and then move to the endpoint */ + error = ft_stroke_border_lineto( border, &end, FALSE ); + if ( error ) + goto Exit; + + continue; + } + + /* else fall through */ + } + + /* simply add an arc */ + error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end ); + if ( error ) + goto Exit; + } + } + + arc -= 3; + + stroker->angle_in = angle_out; + } + + stroker->center = *to; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + SW_FT_Error + SW_FT_Stroker_BeginSubPath( SW_FT_Stroker stroker, + SW_FT_Vector* to, + SW_FT_Bool open ) + { + /* We cannot process the first point, because there is not enough */ + /* information regarding its corner/cap. The latter will be processed */ + /* in the `SW_FT_Stroker_EndSubPath' routine. */ + /* */ + stroker->first_point = TRUE; + stroker->center = *to; + stroker->subpath_open = open; + + /* Determine if we need to check whether the border radius is greater */ + /* than the radius of curvature of a curve, to handle this case */ + /* specially. This is only required if bevel joins or butt caps may */ + /* be created, because round & miter joins and round & square caps */ + /* cover the negative sector created with wide strokes. */ + stroker->handle_wide_strokes = + SW_FT_BOOL( stroker->line_join != SW_FT_STROKER_LINEJOIN_ROUND || + ( stroker->subpath_open && + stroker->line_cap == SW_FT_STROKER_LINECAP_BUTT ) ); + + /* record the subpath start point for each border */ + stroker->subpath_start = *to; + + stroker->angle_in = 0; + + return 0; + } + + + static SW_FT_Error + ft_stroker_add_reverse_left( SW_FT_Stroker stroker, + SW_FT_Bool open ) + { + SW_FT_StrokeBorder right = stroker->borders + 0; + SW_FT_StrokeBorder left = stroker->borders + 1; + SW_FT_Int new_points; + SW_FT_Error error = 0; + + + assert( left->start >= 0 ); + + new_points = left->num_points - left->start; + if ( new_points > 0 ) + { + error = ft_stroke_border_grow( right, (SW_FT_UInt)new_points ); + if ( error ) + goto Exit; + + { + SW_FT_Vector* dst_point = right->points + right->num_points; + SW_FT_Byte* dst_tag = right->tags + right->num_points; + SW_FT_Vector* src_point = left->points + left->num_points - 1; + SW_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] &= ~SW_FT_STROKE_TAG_BEGIN_END; + else + { + SW_FT_Byte ttag = + (SW_FT_Byte)( dst_tag[0] & SW_FT_STROKE_TAG_BEGIN_END ); + + + /* switch begin/end tags if necessary */ + if ( ttag == SW_FT_STROKE_TAG_BEGIN || + ttag == SW_FT_STROKE_TAG_END ) + dst_tag[0] ^= SW_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; + } + + + /* documentation is in ftstroke.h */ + + /* there's a lot of magic in this function! */ + SW_FT_Error + SW_FT_Stroker_EndSubPath( SW_FT_Stroker stroker ) + { + SW_FT_Error error = 0; + + + if ( stroker->subpath_open ) + { + SW_FT_StrokeBorder right = stroker->borders; + + + /* All right, this is an opened path, we need to add a cap between */ + /* right & left, add the reverse of left, then add a final cap */ + /* between left & right. */ + error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); + if ( error ) + goto Exit; + + /* add reversed points from `left' to `right' */ + error = ft_stroker_add_reverse_left( stroker, TRUE ); + if ( error ) + goto Exit; + + /* now add the final cap */ + stroker->center = stroker->subpath_start; + error = ft_stroker_cap( stroker, + stroker->subpath_angle + SW_FT_ANGLE_PI, 0 ); + if ( error ) + goto Exit; + + /* Now end the right subpath accordingly. The left one is */ + /* rewind and doesn't need further processing. */ + ft_stroke_border_close( right, FALSE ); + } + else + { + SW_FT_Angle turn; + SW_FT_Int inside_side; + + + /* close the path if needed */ + if ( stroker->center.x != stroker->subpath_start.x || + stroker->center.y != stroker->subpath_start.y ) + { + error = SW_FT_Stroker_LineTo( stroker, &stroker->subpath_start ); + if ( error ) + goto Exit; + } + + /* process the corner */ + stroker->angle_out = stroker->subpath_angle; + turn = SW_FT_Angle_Diff( stroker->angle_in, + stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if ( turn != 0 ) + { + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + + /* otherwise, the inside side is 1 */ + if ( turn < 0 ) + inside_side = 1; + + error = ft_stroker_inside( stroker, + inside_side, + stroker->subpath_line_length ); + if ( error ) + goto Exit; + + /* process the outside side */ + error = ft_stroker_outside( stroker, + 1 - inside_side, + stroker->subpath_line_length ); + if ( error ) + goto Exit; + } + + /* then end our two subpaths */ + ft_stroke_border_close( stroker->borders + 0, FALSE ); + ft_stroke_border_close( stroker->borders + 1, TRUE ); + } + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + SW_FT_Error + SW_FT_Stroker_GetBorderCounts( SW_FT_Stroker stroker, + SW_FT_StrokerBorder border, + SW_FT_UInt *anum_points, + SW_FT_UInt *anum_contours ) + { + SW_FT_UInt num_points = 0, num_contours = 0; + SW_FT_Error error; + + + if ( !stroker || border > 1 ) + { + error = -1;//SW_FT_THROW( Invalid_Argument ); + 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; + } + + + /* documentation is in ftstroke.h */ + + SW_FT_Error + SW_FT_Stroker_GetCounts( SW_FT_Stroker stroker, + SW_FT_UInt *anum_points, + SW_FT_UInt *anum_contours ) + { + SW_FT_UInt count1, count2, num_points = 0; + SW_FT_UInt count3, count4, num_contours = 0; + SW_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; + } + + + /* documentation is in ftstroke.h */ + + void + SW_FT_Stroker_ExportBorder( SW_FT_Stroker stroker, + SW_FT_StrokerBorder border, + SW_FT_Outline* outline ) + { + if ( border == SW_FT_STROKER_BORDER_LEFT || + border == SW_FT_STROKER_BORDER_RIGHT ) + { + SW_FT_StrokeBorder sborder = & stroker->borders[border]; + + + if ( sborder->valid ) + ft_stroke_border_export( sborder, outline ); + } + } + + + /* documentation is in ftstroke.h */ + + void + SW_FT_Stroker_Export( SW_FT_Stroker stroker, + SW_FT_Outline* outline ) + { + SW_FT_Stroker_ExportBorder( stroker, SW_FT_STROKER_BORDER_LEFT, outline ); + SW_FT_Stroker_ExportBorder( stroker, SW_FT_STROKER_BORDER_RIGHT, outline ); + } + + + /* documentation is in ftstroke.h */ + + /* + * The following is very similar to SW_FT_Outline_Decompose, except + * that we do support opened paths, and do not scale the outline. + */ + SW_FT_Error + SW_FT_Stroker_ParseOutline( SW_FT_Stroker stroker, + SW_FT_Outline* outline, + SW_FT_Bool opened ) + { + SW_FT_Vector v_last; + SW_FT_Vector v_control; + SW_FT_Vector v_start; + + SW_FT_Vector* point; + SW_FT_Vector* limit; + char* tags; + + SW_FT_Error error; + + SW_FT_Int n; /* index of contour in outline */ + SW_FT_UInt first; /* index of first point in contour */ + SW_FT_Int tag; /* current point's state */ + + + if ( !outline || !stroker ) + return -1;//SW_FT_THROW( Invalid_Argument ); + + SW_FT_Stroker_Rewind( stroker ); + + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) + { + SW_FT_UInt last; /* index of last point in contour */ + + + last = outline->contours[n]; + limit = outline->points + last; + + /* skip empty points; we don't stroke these */ + 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 = SW_FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == SW_FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == SW_FT_CURVE_TAG_CONIC ) + { + /* First point is conic control. Yes, this happens. */ + if ( SW_FT_CURVE_TAG( outline->tags[last] ) == SW_FT_CURVE_TAG_ON ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + } + point--; + tags--; + } + + error = SW_FT_Stroker_BeginSubPath( stroker, &v_start, opened ); + if ( error ) + goto Exit; + + while ( point < limit ) + { + point++; + tags++; + + tag = SW_FT_CURVE_TAG( tags[0] ); + switch ( tag ) + { + case SW_FT_CURVE_TAG_ON: /* emit a single line_to */ + { + SW_FT_Vector vec; + + + vec.x = point->x; + vec.y = point->y; + + error = SW_FT_Stroker_LineTo( stroker, &vec ); + if ( error ) + goto Exit; + continue; + } + + case SW_FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = point->x; + v_control.y = point->y; + + Do_Conic: + if ( point < limit ) + { + SW_FT_Vector vec; + SW_FT_Vector v_middle; + + + point++; + tags++; + tag = SW_FT_CURVE_TAG( tags[0] ); + + vec = point[0]; + + if ( tag == SW_FT_CURVE_TAG_ON ) + { + error = SW_FT_Stroker_ConicTo( stroker, &v_control, &vec ); + if ( error ) + goto Exit; + continue; + } + + if ( tag != SW_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 = SW_FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); + if ( error ) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + error = SW_FT_Stroker_ConicTo( stroker, &v_control, &v_start ); + goto Close; + + default: /* SW_FT_CURVE_TAG_CUBIC */ + { + SW_FT_Vector vec1, vec2; + + + if ( point + 1 > limit || + SW_FT_CURVE_TAG( tags[1] ) != SW_FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1 = point[-2]; + vec2 = point[-1]; + + if ( point <= limit ) + { + SW_FT_Vector vec; + + + vec = point[0]; + + error = SW_FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); + if ( error ) + goto Exit; + continue; + } + + error = SW_FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); + goto Close; + } + } + } + + Close: + if ( error ) + goto Exit; + + /* don't try to end the path if no segments have been generated */ + if ( !stroker->first_point ) + { + error = SW_FT_Stroker_EndSubPath( stroker ); + if ( error ) + goto Exit; + } + + first = last + 1; + } + + return 0; + + Exit: + return error; + + Invalid_Outline: + return -2;//SW_FT_THROW( Invalid_Outline ); + } + + +/* END */ diff --git a/src/lib/ector/software/sw_ft_stroker.h b/src/lib/ector/software/sw_ft_stroker.h new file mode 100755 index 0000000000..75830fb25c --- /dev/null +++ b/src/lib/ector/software/sw_ft_stroker.h @@ -0,0 +1,325 @@ +#ifndef SW_FT_STROKER_H +#define SW_FT_STROKER_H +/***************************************************************************/ +/* */ +/* ftstroke.h */ +/* */ +/* FreeType path stroker (specification). */ +/* */ +/* Copyright 2002-2006, 2008, 2009, 2011-2012 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include "sw_ft_raster.h" + + /************************************************************** + * + * @type: + * SW_FT_Stroker + * + * @description: + * Opaque handler to a path stroker object. + */ + typedef struct SW_FT_StrokerRec_* SW_FT_Stroker; + + + /************************************************************** + * + * @enum: + * SW_FT_Stroker_LineJoin + * + * @description: + * These values determine how two joining lines are rendered + * in a stroker. + * + * @values: + * SW_FT_STROKER_LINEJOIN_ROUND :: + * Used to render rounded line joins. Circular arcs are used + * to join two lines smoothly. + * + * SW_FT_STROKER_LINEJOIN_BEVEL :: + * Used to render beveled line joins. The outer corner of + * the joined lines is filled by enclosing the triangular + * region of the corner with a straight line between the + * outer corners of each stroke. + * + * SW_FT_STROKER_LINEJOIN_MITER_FIXED :: + * Used to render mitered line joins, with fixed bevels if the + * miter limit is exceeded. The outer edges of the strokes + * for the two segments are extended until they meet at an + * angle. If the segments meet at too sharp an angle (such + * that the miter would extend from the intersection of the + * segments a distance greater than the product of the miter + * limit value and the border radius), then a bevel join (see + * above) is used instead. This prevents long spikes being + * created. SW_FT_STROKER_LINEJOIN_MITER_FIXED generates a miter + * line join as used in PostScript and PDF. + * + * SW_FT_STROKER_LINEJOIN_MITER_VARIABLE :: + * SW_FT_STROKER_LINEJOIN_MITER :: + * Used to render mitered line joins, with variable bevels if + * the miter limit is exceeded. The intersection of the + * strokes is clipped at a line perpendicular to the bisector + * of the angle between the strokes, at the distance from the + * intersection of the segments equal to the product of the + * miter limit value and the border radius. This prevents + * long spikes being created. + * SW_FT_STROKER_LINEJOIN_MITER_VARIABLE generates a mitered line + * join as used in XPS. SW_FT_STROKER_LINEJOIN_MITER is an alias + * for SW_FT_STROKER_LINEJOIN_MITER_VARIABLE, retained for + * backwards compatibility. + */ + typedef enum SW_FT_Stroker_LineJoin_ + { + SW_FT_STROKER_LINEJOIN_ROUND = 0, + SW_FT_STROKER_LINEJOIN_BEVEL = 1, + SW_FT_STROKER_LINEJOIN_MITER_VARIABLE = 2, + SW_FT_STROKER_LINEJOIN_MITER = SW_FT_STROKER_LINEJOIN_MITER_VARIABLE, + SW_FT_STROKER_LINEJOIN_MITER_FIXED = 3 + + } SW_FT_Stroker_LineJoin; + + + /************************************************************** + * + * @enum: + * SW_FT_Stroker_LineCap + * + * @description: + * These values determine how the end of opened sub-paths are + * rendered in a stroke. + * + * @values: + * SW_FT_STROKER_LINECAP_BUTT :: + * The end of lines is rendered as a full stop on the last + * point itself. + * + * SW_FT_STROKER_LINECAP_ROUND :: + * The end of lines is rendered as a half-circle around the + * last point. + * + * SW_FT_STROKER_LINECAP_SQUARE :: + * The end of lines is rendered as a square around the + * last point. + */ + typedef enum SW_FT_Stroker_LineCap_ + { + SW_FT_STROKER_LINECAP_BUTT = 0, + SW_FT_STROKER_LINECAP_ROUND, + SW_FT_STROKER_LINECAP_SQUARE + + } SW_FT_Stroker_LineCap; + + + /************************************************************** + * + * @enum: + * SW_FT_StrokerBorder + * + * @description: + * These values are used to select a given stroke border + * in @SW_FT_Stroker_GetBorderCounts and @SW_FT_Stroker_ExportBorder. + * + * @values: + * SW_FT_STROKER_BORDER_LEFT :: + * Select the left border, relative to the drawing direction. + * + * SW_FT_STROKER_BORDER_RIGHT :: + * Select the right border, relative to the drawing direction. + * + * @note: + * Applications are generally interested in the `inside' and `outside' + * borders. However, there is no direct mapping between these and the + * `left' and `right' ones, since this really depends on the glyph's + * drawing orientation, which varies between font formats. + * + * You can however use @SW_FT_Outline_GetInsideBorder and + * @SW_FT_Outline_GetOutsideBorder to get these. + */ + typedef enum SW_FT_StrokerBorder_ + { + SW_FT_STROKER_BORDER_LEFT = 0, + SW_FT_STROKER_BORDER_RIGHT + + } SW_FT_StrokerBorder; + + + /************************************************************** + * + * @function: + * SW_FT_Stroker_New + * + * @description: + * Create a new stroker object. + * + * @input: + * library :: + * FreeType library handle. + * + * @output: + * astroker :: + * A new stroker object handle. NULL in case of error. + * + * @return: + * FreeType error code. 0~means success. + */ + SW_FT_Error + SW_FT_Stroker_New( SW_FT_Stroker *astroker ); + + + /************************************************************** + * + * @function: + * SW_FT_Stroker_Set + * + * @description: + * Reset a stroker object's attributes. + * + * @input: + * stroker :: + * The target stroker handle. + * + * radius :: + * The border radius. + * + * line_cap :: + * The line cap style. + * + * line_join :: + * The line join style. + * + * miter_limit :: + * The miter limit for the SW_FT_STROKER_LINEJOIN_MITER_FIXED and + * SW_FT_STROKER_LINEJOIN_MITER_VARIABLE line join styles, + * expressed as 16.16 fixed-point value. + * + * @note: + * The radius is expressed in the same units as the outline + * coordinates. + */ + void + SW_FT_Stroker_Set( SW_FT_Stroker stroker, + SW_FT_Fixed radius, + SW_FT_Stroker_LineCap line_cap, + SW_FT_Stroker_LineJoin line_join, + SW_FT_Fixed miter_limit ); + + /************************************************************** + * + * @function: + * SW_FT_Stroker_ParseOutline + * + * @description: + * A convenience function used to parse a whole outline with + * the stroker. The resulting outline(s) can be retrieved + * later by functions like @SW_FT_Stroker_GetCounts and @SW_FT_Stroker_Export. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The source outline. + * + * opened :: + * A boolean. If~1, the outline is treated as an open path instead + * of a closed one. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `opened' is~0 (the default), the outline is treated as a closed + * path, and the stroker generates two distinct `border' outlines. + * + * If `opened' is~1, the outline is processed as an open path, and the + * stroker generates a single `stroke' outline. + * + * This function calls @SW_FT_Stroker_Rewind automatically. + */ + SW_FT_Error + SW_FT_Stroker_ParseOutline( SW_FT_Stroker stroker, + SW_FT_Outline* outline, + SW_FT_Bool opened ); + + + /************************************************************** + * + * @function: + * SW_FT_Stroker_GetCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It returns the number of points and + * contours necessary to export all points/borders from the stroked + * outline/path. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0~means success. + */ + SW_FT_Error + SW_FT_Stroker_GetCounts( SW_FT_Stroker stroker, + SW_FT_UInt *anum_points, + SW_FT_UInt *anum_contours ); + + + /************************************************************** + * + * @function: + * SW_FT_Stroker_Export + * + * @description: + * Call this function after @SW_FT_Stroker_GetBorderCounts to + * export all borders to your own @SW_FT_Outline structure. + * + * Note that this function appends the border points and + * contours to your outline, but does not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The target outline handle. + */ + void + SW_FT_Stroker_Export( SW_FT_Stroker stroker, + SW_FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * SW_FT_Stroker_Done + * + * @description: + * Destroy a stroker object. + * + * @input: + * stroker :: + * A stroker handle. Can be NULL. + */ + void + SW_FT_Stroker_Done( SW_FT_Stroker stroker ); + + +#endif // SW_FT_STROKER_H diff --git a/src/lib/ector/software/sw_ft_types.h b/src/lib/ector/software/sw_ft_types.h new file mode 100755 index 0000000000..1e8b865233 --- /dev/null +++ b/src/lib/ector/software/sw_ft_types.h @@ -0,0 +1,160 @@ +#ifndef SW_FT_TYPES_H +#define SW_FT_TYPES_H + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Fixed */ +/* */ +/* */ +/* This type is used to store 16.16 fixed-point values, like scaling */ +/* values or matrix coefficients. */ +/* */ +typedef signed long SW_FT_Fixed; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Int */ +/* */ +/* */ +/* A typedef for the int type. */ +/* */ +typedef signed int SW_FT_Int; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_UInt */ +/* */ +/* */ +/* A typedef for the unsigned int type. */ +/* */ +typedef unsigned int SW_FT_UInt; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Long */ +/* */ +/* */ +/* A typedef for signed long. */ +/* */ +typedef signed long SW_FT_Long; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_ULong */ +/* */ +/* */ +/* A typedef for unsigned long. */ +/* */ +typedef unsigned long SW_FT_ULong; + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Short */ +/* */ +/* */ +/* A typedef for signed short. */ +/* */ +typedef signed short SW_FT_Short; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Byte */ +/* */ +/* */ +/* A simple typedef for the _unsigned_ char type. */ +/* */ +typedef unsigned char SW_FT_Byte; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Bool */ +/* */ +/* */ +/* A typedef of unsigned char, used for simple booleans. As usual, */ +/* values 1 and~0 represent true and false, respectively. */ +/* */ +typedef unsigned char SW_FT_Bool; + + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Error */ +/* */ +/* */ +/* The FreeType error code type. A value of~0 is always interpreted */ +/* as a successful operation. */ +/* */ +typedef int SW_FT_Error; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Pos */ +/* */ +/* */ +/* The type SW_FT_Pos is used to store vectorial coordinates. Depending */ +/* on the context, these can represent distances in integer font */ +/* units, or 16.16, or 26.6 fixed-point pixel coordinates. */ +/* */ +typedef signed long SW_FT_Pos; + + +/*************************************************************************/ +/* */ +/* */ +/* SW_FT_Vector */ +/* */ +/* */ +/* A simple structure used to store a 2D vector; coordinates are of */ +/* the SW_FT_Pos type. */ +/* */ +/* */ +/* x :: The horizontal coordinate. */ +/* y :: The vertical coordinate. */ +/* */ +typedef struct SW_FT_Vector_ +{ + SW_FT_Pos x; + SW_FT_Pos y; + +} SW_FT_Vector; + + +typedef long long int SW_FT_Int64; +typedef unsigned long long int SW_FT_UInt64; + +typedef signed int SW_FT_Int32; +typedef unsigned int SW_FT_UInt32; + + +#define SW_FT_BOOL( x ) ( (SW_FT_Bool)( x ) ) + +#define SW_FT_SIZEOF_LONG 4 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + + +#endif // SW_FT_TYPES_H