forked from enlightenment/efl
ector: first implementation of an Cairo shape renderer.
Note: still a lot on the todo, but it should display something that follow the correct path at least. Thanks librsvg for helping a lot.
This commit is contained in:
parent
7ba0f46a39
commit
74b2eed158
|
@ -2,6 +2,9 @@
|
|||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#include <Eina.h>
|
||||
#include <Ector.h>
|
||||
#include <cairo/Ector_Cairo.h>
|
||||
|
@ -9,29 +12,477 @@
|
|||
#include "ector_private.h"
|
||||
#include "ector_cairo_private.h"
|
||||
|
||||
typedef struct _cairo_path_t cairo_path_t;
|
||||
|
||||
static void (*cairo_move_to)(cairo_t *cr, double x, double y) = NULL;
|
||||
static void (*cairo_line_to)(cairo_t *cr, double x, double y) = NULL;
|
||||
static void (*cairo_curve_to)(cairo_t *cr,
|
||||
double x1, double y1,
|
||||
double x2, double y2,
|
||||
double x3, double y3) = NULL;
|
||||
static void (*cairo_close_path)(cairo_t *cr) = NULL;
|
||||
|
||||
static void (*cairo_fill)(cairo_t *cr) = NULL;
|
||||
static void (*cairo_fill_preserve)(cairo_t *cr) = NULL;
|
||||
static void (*cairo_stroke)(cairo_t *cr) = NULL;
|
||||
|
||||
static void (*cairo_set_source_rgba)(cairo_t *cr,
|
||||
double red, double green,
|
||||
double blue, double alpha) = NULL;
|
||||
|
||||
|
||||
static cairo_path_t *(*cairo_copy_path)(cairo_t *cr) = NULL;
|
||||
static void (*cairo_path_destroy)(cairo_path_t *path) = NULL;
|
||||
static void (*cairo_new_path)(cairo_t *cr) = NULL;
|
||||
static void (*cairo_append_path)(cairo_t *cr, const cairo_path_t *path) = NULL;
|
||||
|
||||
typedef struct _Ector_Renderer_Cairo_Shape_Data Ector_Renderer_Cairo_Shape_Data;
|
||||
struct _Ector_Renderer_Cairo_Shape_Data
|
||||
{
|
||||
Ector_Cairo_Surface_Data *parent;
|
||||
Ector_Renderer_Generic_Shape_Data *shape;
|
||||
Ector_Renderer_Generic_Base_Data *base;
|
||||
cairo_path_t *path;
|
||||
};
|
||||
|
||||
// This function come from librsvg rsvg-path.c
|
||||
static void
|
||||
_ector_arc_segment(Eo *obj, cairo_t* ctx,
|
||||
double xc, double yc,
|
||||
double th0, double th1, double rx, double ry,
|
||||
double x_axis_rotation)
|
||||
{
|
||||
double x1, y1, x2, y2, x3, y3;
|
||||
double t;
|
||||
double th_half;
|
||||
double f, sinf, cosf;
|
||||
|
||||
f = x_axis_rotation * M_PI / 180.0;
|
||||
sinf = sin(f);
|
||||
cosf = cos(f);
|
||||
|
||||
th_half = 0.5 * (th1 - th0);
|
||||
t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
|
||||
x1 = rx * (cos(th0) - t * sin(th0));
|
||||
y1 = ry * (sin(th0) + t * cos(th0));
|
||||
x3 = rx* cos(th1);
|
||||
y3 = ry* sin(th1);
|
||||
x2 = x3 + rx * (t * sin(th1));
|
||||
y2 = y3 + ry * (-t * cos(th1));
|
||||
|
||||
USE(obj, cairo_curve_to, );
|
||||
|
||||
cairo_curve_to(ctx,
|
||||
xc + cosf * x1 - sinf * y1,
|
||||
yc + sinf * x1 + cosf * y1,
|
||||
xc + cosf * x2 - sinf * y2,
|
||||
yc + sinf * x2 + cosf * y2,
|
||||
xc + cosf * x3 - sinf * y3,
|
||||
yc + sinf * x3 + cosf * y3);
|
||||
}
|
||||
|
||||
// This function come from librsvg rsvg-path.c
|
||||
static void
|
||||
_ector_arc_to(Eo *obj, cairo_t* ctx,
|
||||
double *current_x, double *current_y,
|
||||
double rx, double ry, double x_axis_rotation,
|
||||
Eina_Bool large_arc_flag, Eina_Bool sweep_flag,
|
||||
double x, double y)
|
||||
{
|
||||
/* See Appendix F.6 Elliptical arc implementation notes
|
||||
http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes */
|
||||
|
||||
double f, sinf, cosf;
|
||||
double x1, y1, x2, y2;
|
||||
double x1_, y1_;
|
||||
double cx_, cy_, cx, cy;
|
||||
double gamma;
|
||||
double theta1, delta_theta;
|
||||
double k1, k2, k3, k4, k5;
|
||||
|
||||
int i, n_segs;
|
||||
|
||||
/* Start and end of path segment */
|
||||
x1 = *current_x;
|
||||
y1 = *current_y;
|
||||
|
||||
x2 = x;
|
||||
y2 = y;
|
||||
|
||||
if (x1 == x2 && y1 == y2)
|
||||
return;
|
||||
|
||||
/* X-axis */
|
||||
f = x_axis_rotation * M_PI / 180.0;
|
||||
sinf = sin(f);
|
||||
cosf = cos(f);
|
||||
|
||||
/* Check the radius against floading point underflow.
|
||||
See http://bugs.debian.org/508443 */
|
||||
if ((fabs(rx) < DBL_EPSILON) || (fabs(ry) < DBL_EPSILON)) {
|
||||
USE(obj, cairo_line_to, );
|
||||
|
||||
cairo_line_to(ctx, x, y);
|
||||
|
||||
*current_x = x;
|
||||
*current_y = y;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rx < 0) rx = -rx;
|
||||
if (ry < 0) ry = -ry;
|
||||
|
||||
k1 = (x1 - x2) / 2;
|
||||
k2 = (y1 - y2) / 2;
|
||||
|
||||
x1_ = cosf * k1 + sinf * k2;
|
||||
y1_ = -sinf * k1 + cosf * k2;
|
||||
|
||||
gamma = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
|
||||
if (gamma > 1) {
|
||||
rx *= sqrt(gamma);
|
||||
ry *= sqrt(gamma);
|
||||
}
|
||||
|
||||
/* Compute the center */
|
||||
k1 = rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_;
|
||||
if (k1 == 0)
|
||||
return;
|
||||
|
||||
k1 = sqrt(fabs((rx * rx * ry * ry) / k1 - 1));
|
||||
if (sweep_flag == large_arc_flag)
|
||||
k1 = -k1;
|
||||
|
||||
cx_ = k1 * rx * y1_ / ry;
|
||||
cy_ = -k1 * ry * x1_ / rx;
|
||||
|
||||
cx = cosf * cx_ - sinf * cy_ + (x1 + x2) / 2;
|
||||
cy = sinf * cx_ + cosf * cy_ + (y1 + y2) / 2;
|
||||
|
||||
/* Compute start angle */
|
||||
k1 = (x1_ - cx_) / rx;
|
||||
k2 = (y1_ - cy_) / ry;
|
||||
k3 = (-x1_ - cx_) / rx;
|
||||
k4 = (-y1_ - cy_) / ry;
|
||||
|
||||
k5 = sqrt(fabs(k1 * k1 + k2 * k2));
|
||||
if (k5 == 0) return;
|
||||
|
||||
k5 = k1 / k5;
|
||||
if (k5 < -1) k5 = -1;
|
||||
else if(k5 > 1) k5 = 1;
|
||||
|
||||
theta1 = acos(k5);
|
||||
if(k2 < 0) theta1 = -theta1;
|
||||
|
||||
/* Compute delta_theta */
|
||||
k5 = sqrt(fabs((k1 * k1 + k2 * k2) * (k3 * k3 + k4 * k4)));
|
||||
if (k5 == 0) return;
|
||||
|
||||
k5 = (k1 * k3 + k2 * k4) / k5;
|
||||
if (k5 < -1) k5 = -1;
|
||||
else if (k5 > 1) k5 = 1;
|
||||
delta_theta = acos(k5);
|
||||
if(k1 * k4 - k3 * k2 < 0) delta_theta = -delta_theta;
|
||||
|
||||
if (sweep_flag && delta_theta < 0)
|
||||
delta_theta += M_PI*2;
|
||||
else if (!sweep_flag && delta_theta > 0)
|
||||
delta_theta -= M_PI*2;
|
||||
|
||||
/* Now draw the arc */
|
||||
n_segs = ceil (fabs (delta_theta / (M_PI * 0.5 + 0.001)));
|
||||
|
||||
for (i = 0; i < n_segs; i++)
|
||||
_ector_arc_segment(obj, ctx,
|
||||
cx, cy,
|
||||
theta1 + i * delta_theta / n_segs,
|
||||
theta1 + (i + 1) * delta_theta / n_segs,
|
||||
rx, ry, x_axis_rotation);
|
||||
|
||||
*current_x = x;
|
||||
*current_y = y;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_ector_renderer_cairo_shape_ector_renderer_generic_base_prepare(Eo *obj, Ector_Renderer_Cairo_Shape_Data *pd, Ector_Surface *s)
|
||||
{
|
||||
// FIXME: shouldn't that be part of the shape generic implementation ?
|
||||
if (pd->shape->fill)
|
||||
eo_do(pd->shape->fill, ector_renderer_prepare(s));
|
||||
if (pd->shape->stroke.fill)
|
||||
eo_do(pd->shape->stroke.fill, ector_renderer_prepare(s));
|
||||
if (pd->shape->stroke.marker)
|
||||
eo_do(pd->shape->stroke.marker, ector_renderer_prepare(s));
|
||||
eo_do_super(obj, ECTOR_RENDERER_CAIRO_SHAPE_CLASS, ector_renderer_prepare(s));
|
||||
|
||||
// shouldn't that be moved to the cairo base object
|
||||
if (!pd->parent)
|
||||
{
|
||||
Eo *parent;
|
||||
|
||||
eo_do(obj, parent = eo_parent_get());
|
||||
if (!parent) return EINA_FALSE;
|
||||
pd->parent = eo_data_xref(parent, ECTOR_CAIRO_SURFACE_CLASS, obj);
|
||||
if (!pd->parent) return EINA_FALSE;
|
||||
}
|
||||
|
||||
if (!pd->path && pd->shape->path.cmd)
|
||||
{
|
||||
double *pts;
|
||||
double current_x = 0, current_y = 0;
|
||||
double current_ctrl_x = 0, current_ctrl_y = 0;
|
||||
unsigned int i;
|
||||
|
||||
USE(obj, cairo_new_path, EINA_FALSE);
|
||||
|
||||
cairo_new_path(pd->parent->cairo);
|
||||
|
||||
pts = pd->shape->path.pts;
|
||||
for (i = 0; pd->shape->path.cmd[i] != EFL_GRAPHICS_PATH_COMMAND_TYPE_END; i++)
|
||||
{
|
||||
switch (pd->shape->path.cmd[i])
|
||||
{
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_MOVE_TO:
|
||||
USE(obj, cairo_move_to, EINA_FALSE);
|
||||
|
||||
cairo_move_to(pd->parent->cairo, pts[0], pts[1]);
|
||||
|
||||
current_ctrl_x = current_x = pts[0];
|
||||
current_ctrl_y = current_y = pts[1];
|
||||
|
||||
pts += 2;
|
||||
break;
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_LINE_TO:
|
||||
USE(obj, cairo_line_to, EINA_FALSE);
|
||||
|
||||
cairo_line_to(pd->parent->cairo, pts[0], pts[1]);
|
||||
|
||||
current_ctrl_x = current_x = pts[0];
|
||||
current_ctrl_y = current_y = pts[1];
|
||||
|
||||
pts += 2;
|
||||
break;
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_CUBIC_TO:
|
||||
USE(obj, cairo_curve_to, EINA_FALSE);
|
||||
|
||||
// Be careful, we do have a different order than
|
||||
// cairo, first is destination point, followed by
|
||||
// the control point. The opposite of cairo.
|
||||
cairo_curve_to(pd->parent->cairo,
|
||||
pts[2], pts[3], pts[4], pts[5], // control points
|
||||
pts[0], pts[1]); // destination point
|
||||
|
||||
current_ctrl_x = pts[4];
|
||||
current_ctrl_y = pts[5];
|
||||
current_x = pts[0];
|
||||
current_y = pts[1];
|
||||
|
||||
pts += 6;
|
||||
break;
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_ARC_TO:
|
||||
_ector_arc_to(obj, pd->parent->cairo,
|
||||
¤t_x, ¤t_y,
|
||||
pts[2], pts[3], pts[4],
|
||||
0, 0, // FIXME: need to get the large arc and sweep flag
|
||||
pts[0], pts[1]);
|
||||
|
||||
pts += 5;
|
||||
break;
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_CLOSE:
|
||||
USE(obj, cairo_close_path, EINA_FALSE);
|
||||
|
||||
cairo_close_path(pd->parent->cairo);
|
||||
break;
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_QUADRATIC_TO:
|
||||
{
|
||||
double x1, y1, x2, y2, x3, y3;
|
||||
// This code come from librsvg rsvg-path.c
|
||||
// Be careful, we do have a different order than
|
||||
// cairo, first is destination point, followed by
|
||||
// the control point. The opposite of cairo.
|
||||
/* raise quadratic bezier to cubic */
|
||||
x1 = (current_x + 2 * pts[2]) * (1.0 / 3.0);
|
||||
y1 = (current_y + 2 * pts[3]) * (1.0 / 3.0);
|
||||
x3 = pts[0];
|
||||
y3 = pts[1];
|
||||
x2 = (x3 + 2 * pts[2]) * (1.0 / 3.0);
|
||||
y2 = (y3 + 2 * pts[3]) * (1.0 / 3.0);
|
||||
|
||||
cairo_curve_to(pd->parent->cairo,
|
||||
x1, y1, x2, y2, // control points
|
||||
x3, y3); // destination point
|
||||
|
||||
current_ctrl_x = pts[2];
|
||||
current_ctrl_y = pts[3];
|
||||
|
||||
current_x = x3;
|
||||
current_y = y3;
|
||||
break;
|
||||
}
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_SQUADRATIC_TO:
|
||||
{
|
||||
// This code come from librsvg rsvg-path.c
|
||||
// Smooth quadratic basically reusing the last control
|
||||
// point in a meaningful way.
|
||||
double xc, yc; /* quadratic control point */
|
||||
double x1, y1, x2, y2, x3, y3;
|
||||
|
||||
xc = 2 * current_x - current_ctrl_x;
|
||||
yc = 2 * current_y - current_ctrl_y;
|
||||
/* generate a quadratic bezier with control point = xc, yc */
|
||||
x1 = (current_x + 2 * xc) * (1.0 / 3.0);
|
||||
y1 = (current_y + 2 * yc) * (1.0 / 3.0);
|
||||
x3 = pts[0];
|
||||
y3 = pts[1];
|
||||
x2 = (x3 + 2 * xc) * (1.0 / 3.0);
|
||||
y2 = (y3 + 2 * yc) * (1.0 / 3.0);
|
||||
|
||||
USE(obj, cairo_curve_to, EINA_FALSE);
|
||||
|
||||
cairo_curve_to(pd->parent->cairo,
|
||||
x1, y1, x2, y2, x3, y3);
|
||||
|
||||
current_ctrl_x = xc;
|
||||
current_ctrl_y = yc;
|
||||
|
||||
current_x = x3;
|
||||
current_y = y3;
|
||||
|
||||
break;
|
||||
}
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_SCUBIC_TO:
|
||||
{
|
||||
// This code come from librsvg rsvg-path.c
|
||||
// Smooth cubic basically reusing the last control point
|
||||
// in a meaningful way.
|
||||
double x1, y1, x2, y2, x3, y3;
|
||||
|
||||
x1 = 2 * current_x - current_ctrl_x;
|
||||
y1 = 2 * current_y - current_ctrl_y;
|
||||
x2 = pts[2];
|
||||
y2 = pts[3];
|
||||
x3 = pts[0];
|
||||
y3 = pts[1];
|
||||
|
||||
USE(obj, cairo_curve_to, EINA_FALSE);
|
||||
|
||||
cairo_curve_to(pd->parent->cairo,
|
||||
x1, y1, x2, y2, x3, y3);
|
||||
|
||||
current_ctrl_x = x2;
|
||||
current_ctrl_y = y2;
|
||||
current_x = x3;
|
||||
current_y = y3;
|
||||
break;
|
||||
}
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_LAST:
|
||||
case EFL_GRAPHICS_PATH_COMMAND_TYPE_END:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
USE(obj, cairo_copy_path, EINA_FALSE);
|
||||
|
||||
pd->path = cairo_copy_path(pd->parent->cairo);
|
||||
}
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_ector_renderer_cairo_shape_ector_renderer_generic_base_draw(Eo *obj, Ector_Renderer_Cairo_Shape_Data *pd, Ector_Rop op, Eina_Array *clips, int x, int y)
|
||||
{
|
||||
}
|
||||
if (pd->path == NULL) return EINA_FALSE;
|
||||
|
||||
static Eina_Bool
|
||||
_ector_renderer_cairo_shape_ector_renderer_generic_base_done(Eo *obj, Ector_Renderer_Cairo_Shape_Data *pd)
|
||||
{
|
||||
// FIXME: find a way to offset the drawing and setting multiple clips
|
||||
|
||||
eo_do_super(obj, ECTOR_RENDERER_CAIRO_SHAPE_CLASS, ector_renderer_draw(op, clips, x, y));
|
||||
|
||||
USE(obj, cairo_new_path, EINA_FALSE);
|
||||
USE(obj, cairo_append_path, EINA_FALSE);
|
||||
|
||||
cairo_new_path(pd->parent->cairo);
|
||||
cairo_append_path(pd->parent->cairo, pd->path);
|
||||
|
||||
if (pd->shape->fill)
|
||||
eo_do(pd->shape->fill, ector_renderer_cairo_base_fill());
|
||||
|
||||
if (pd->shape->stroke.color.a > 0)
|
||||
{
|
||||
USE(obj, cairo_fill_preserve, EINA_FALSE);
|
||||
USE(obj, cairo_set_source_rgba, EINA_FALSE);
|
||||
USE(obj, cairo_stroke, EINA_FALSE);
|
||||
|
||||
cairo_fill_preserve(pd->parent->cairo);
|
||||
|
||||
cairo_set_source_rgba(pd->parent->cairo,
|
||||
pd->shape->stroke.color.r / 255.0,
|
||||
pd->shape->stroke.color.g / 255.0,
|
||||
pd->shape->stroke.color.b / 255.0,
|
||||
pd->shape->stroke.color.a / 255.0);
|
||||
|
||||
if (pd->shape->stroke.fill)
|
||||
eo_do(pd->shape->stroke.fill, ector_renderer_cairo_base_fill());
|
||||
// Set dash, cap and join
|
||||
cairo_stroke(pd->parent->cairo);
|
||||
}
|
||||
else
|
||||
{
|
||||
USE(obj, cairo_fill, EINA_FALSE);
|
||||
cairo_fill(pd->parent->cairo);
|
||||
}
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_ector_renderer_cairo_shape_ector_renderer_cairo_base_fill(Eo *obj, Ector_Renderer_Cairo_Shape_Data *pd)
|
||||
{
|
||||
// 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 cairo.
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_ector_renderer_cairo_shape_efl_graphics_shape_path_set(Eo *obj, Ector_Renderer_Cairo_Shape_Data *pd,
|
||||
const Efl_Graphics_Path_Command *op, const double *points)
|
||||
{
|
||||
Eina_Bool r;
|
||||
|
||||
USE(obj, cairo_path_destroy, EINA_FALSE);
|
||||
|
||||
if (pd->path) cairo_path_destroy(pd->path);
|
||||
pd->path = NULL;
|
||||
|
||||
eo_do_super(obj, ECTOR_RENDERER_CAIRO_SHAPE_CLASS, r = efl_graphics_shape_path_set(op, points));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_ector_renderer_cairo_shape_eo_base_constructor(Eo *obj, Ector_Renderer_Cairo_Shape_Data *pd)
|
||||
{
|
||||
eo_do_super(obj, ECTOR_RENDERER_CAIRO_SHAPE_CLASS, eo_constructor());
|
||||
pd->shape = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_SHAPE_CLASS, obj);
|
||||
pd->base = eo_data_xref(obj, ECTOR_RENDERER_GENERIC_BASE_CLASS, obj);
|
||||
}
|
||||
|
||||
void
|
||||
_ector_renderer_cairo_shape_eo_base_destructor(Eo *obj, Ector_Renderer_Cairo_Shape_Data *pd)
|
||||
{
|
||||
Eo *parent;
|
||||
|
||||
USE(obj, cairo_path_destroy, );
|
||||
if (pd->path) cairo_path_destroy(pd->path);
|
||||
|
||||
eo_do(obj, parent = eo_parent_get());
|
||||
eo_data_xunref(parent, pd->parent, obj);
|
||||
|
||||
eo_data_xunref(obj, pd->shape, obj);
|
||||
eo_data_xunref(obj, pd->base, obj);
|
||||
eo_do_super(obj, ECTOR_RENDERER_CAIRO_SHAPE_CLASS, eo_destructor());
|
||||
}
|
||||
|
||||
|
||||
#include "ector_renderer_cairo_shape.eo.c"
|
||||
|
|
|
@ -5,7 +5,9 @@ class Ector.Renderer.Cairo.Shape (Ector.Renderer.Cairo.Base, Ector.Renderer.Gene
|
|||
implements {
|
||||
Ector.Renderer.Generic.Base.prepare;
|
||||
Ector.Renderer.Generic.Base.draw;
|
||||
Ector.Renderer.Generic.Base.done;
|
||||
Ector.Renderer.Cairo.Base.fill;
|
||||
Efl.Graphics.Shape.path_set;
|
||||
Eo.Base.constructor;
|
||||
Eo.Base.destructor;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue