mirror of
https://github.com/buserror/mii_emu.git
synced 2024-11-25 05:32:20 +00:00
3fd0540a83
Changes are just too long to list... Signed-off-by: Michel Pollet <buserror@gmail.com>
2513 lines
66 KiB
C
2513 lines
66 KiB
C
/*
|
|
* cg.c
|
|
*
|
|
* Copyright(c) 2007-2023 Jianjun Jiang <8192542@qq.com>
|
|
* Mobile phone: +86-18665388956
|
|
* QQ: 8192542
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "cg.h"
|
|
|
|
#define cg_array_init(array) \
|
|
do { \
|
|
array.data = NULL; \
|
|
array.size = 0; \
|
|
array.capacity = 0; \
|
|
} while(0)
|
|
|
|
#define cg_array_ensure(array, count) \
|
|
do { \
|
|
if(array.size + count > array.capacity) { \
|
|
int capacity = array.size + count; \
|
|
int newcapacity = (array.capacity == 0) ? 8 : array.capacity; \
|
|
while(newcapacity < capacity) { newcapacity <<= 1; } \
|
|
array.data = realloc(array.data, (size_t)newcapacity * sizeof(array.data[0])); \
|
|
array.capacity = newcapacity; \
|
|
} \
|
|
} while(0)
|
|
|
|
static inline void cg_color_init_rgba(struct cg_color_t * color, double r, double g, double b, double a)
|
|
{
|
|
color->r = r;
|
|
color->g = g;
|
|
color->b = b;
|
|
color->a = a;
|
|
}
|
|
|
|
static inline void cg_rect_init(struct cg_rect_t * rect, double x, double y, double w, double h)
|
|
{
|
|
rect->x = x;
|
|
rect->y = y;
|
|
rect->w = w;
|
|
rect->h = h;
|
|
}
|
|
|
|
void cg_matrix_init(struct cg_matrix_t * m, double a, double b, double c, double d, double tx, double ty)
|
|
{
|
|
m->a = a; m->b = b;
|
|
m->c = c; m->d = d;
|
|
m->tx = tx; m->ty = ty;
|
|
}
|
|
|
|
void cg_matrix_init_identity(struct cg_matrix_t * m)
|
|
{
|
|
m->a = 1; m->b = 0;
|
|
m->c = 0; m->d = 1;
|
|
m->tx = 0; m->ty = 0;
|
|
}
|
|
|
|
void cg_matrix_init_translate(struct cg_matrix_t * m, double tx, double ty)
|
|
{
|
|
m->a = 1; m->b = 0;
|
|
m->c = 0; m->d = 1;
|
|
m->tx = tx; m->ty = ty;
|
|
}
|
|
|
|
void cg_matrix_init_scale(struct cg_matrix_t * m, double sx, double sy)
|
|
{
|
|
m->a = sx; m->b = 0;
|
|
m->c = 0; m->d = sy;
|
|
m->tx = 0; m->ty = 0;
|
|
}
|
|
|
|
void cg_matrix_init_rotate(struct cg_matrix_t * m, double r)
|
|
{
|
|
double s = sin(r);
|
|
double c = cos(r);
|
|
|
|
m->a = c; m->b = s;
|
|
m->c = -s; m->d = c;
|
|
m->tx = 0; m->ty = 0;
|
|
}
|
|
|
|
void cg_matrix_translate(struct cg_matrix_t * m, double tx, double ty)
|
|
{
|
|
m->tx += m->a * tx + m->c * ty;
|
|
m->ty += m->b * tx + m->d * ty;
|
|
}
|
|
|
|
void cg_matrix_scale(struct cg_matrix_t * m, double sx, double sy)
|
|
{
|
|
m->a *= sx;
|
|
m->b *= sx;
|
|
m->c *= sy;
|
|
m->d *= sy;
|
|
}
|
|
|
|
void cg_matrix_rotate(struct cg_matrix_t * m, double r)
|
|
{
|
|
double s = sin(r);
|
|
double c = cos(r);
|
|
double ca = c * m->a;
|
|
double cb = c * m->b;
|
|
double cc = c * m->c;
|
|
double cd = c * m->d;
|
|
double sa = s * m->a;
|
|
double sb = s * m->b;
|
|
double sc = s * m->c;
|
|
double sd = s * m->d;
|
|
|
|
m->a = ca + sc;
|
|
m->b = cb + sd;
|
|
m->c = cc - sa;
|
|
m->d = cd - sb;
|
|
}
|
|
|
|
void cg_matrix_multiply(struct cg_matrix_t * m, struct cg_matrix_t * m1, struct cg_matrix_t * m2)
|
|
{
|
|
struct cg_matrix_t t;
|
|
|
|
t.a = m1->a * m2->a;
|
|
t.b = 0.0;
|
|
t.c = 0.0;
|
|
t.d = m1->d * m2->d;
|
|
t.tx = m1->tx * m2->a + m2->tx;
|
|
t.ty = m1->ty * m2->d + m2->ty;
|
|
if(m1->b != 0.0 || m1->c != 0.0 || m2->b != 0.0 || m2->c != 0.0)
|
|
{
|
|
t.a += m1->b * m2->c;
|
|
t.b += m1->a * m2->b + m1->b * m2->d;
|
|
t.c += m1->c * m2->a + m1->d * m2->c;
|
|
t.d += m1->c * m2->b;
|
|
t.tx += m1->ty * m2->c;
|
|
t.ty += m1->tx * m2->b;
|
|
}
|
|
memcpy(m, &t, sizeof(struct cg_matrix_t));
|
|
}
|
|
|
|
void cg_matrix_invert(struct cg_matrix_t * m)
|
|
{
|
|
double a, b, c, d, tx, ty;
|
|
double det;
|
|
|
|
if((m->c == 0.0) && (m->b == 0.0))
|
|
{
|
|
m->tx = -m->tx;
|
|
m->ty = -m->ty;
|
|
if(m->a != 1.0)
|
|
{
|
|
if(m->a == 0.0)
|
|
return;
|
|
m->a = 1.0 / m->a;
|
|
m->tx *= m->a;
|
|
}
|
|
if(m->d != 1.0)
|
|
{
|
|
if(m->d == 0.0)
|
|
return;
|
|
m->d = 1.0 / m->d;
|
|
m->ty *= m->d;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
det = m->a * m->d - m->b * m->c;
|
|
if(det != 0.0)
|
|
{
|
|
a = m->a;
|
|
b = m->b;
|
|
c = m->c;
|
|
d = m->d;
|
|
tx = m->tx;
|
|
ty = m->ty;
|
|
m->a = d / det;
|
|
m->b = -b / det;
|
|
m->c = -c / det;
|
|
m->d = a / det;
|
|
m->tx = (c * ty - d * tx) / det;
|
|
m->ty = (b * tx - a * ty) / det;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cg_matrix_map_point(struct cg_matrix_t * m, struct cg_point_t * p1, struct cg_point_t * p2)
|
|
{
|
|
p2->x = p1->x * m->a + p1->y * m->c + m->tx;
|
|
p2->y = p1->x * m->b + p1->y * m->d + m->ty;
|
|
}
|
|
|
|
struct cg_surface_t * cg_surface_create(int width, int height)
|
|
{
|
|
struct cg_surface_t * surface = malloc(sizeof(struct cg_surface_t));
|
|
surface->ref = 1;
|
|
surface->width = width;
|
|
surface->height = height;
|
|
surface->stride = width << 2;
|
|
surface->owndata = 1;
|
|
surface->pixels = calloc(1, (size_t)(height * surface->stride));
|
|
return surface;
|
|
}
|
|
|
|
struct cg_surface_t * cg_surface_create_for_data(int width, int height, void * pixels)
|
|
{
|
|
struct cg_surface_t * surface = malloc(sizeof(struct cg_surface_t));
|
|
surface->ref = 1;
|
|
surface->width = width;
|
|
surface->height = height;
|
|
surface->stride = width << 2;
|
|
surface->owndata = 0;
|
|
surface->pixels = pixels;
|
|
return surface;
|
|
}
|
|
|
|
void cg_surface_destroy(struct cg_surface_t * surface)
|
|
{
|
|
if(surface)
|
|
{
|
|
if(--surface->ref == 0)
|
|
{
|
|
if(surface->owndata)
|
|
free(surface->pixels);
|
|
free(surface);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct cg_surface_t * cg_surface_reference(struct cg_surface_t * surface)
|
|
{
|
|
if(surface)
|
|
{
|
|
++surface->ref;
|
|
return surface;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct cg_path_t * cg_path_create(void)
|
|
{
|
|
struct cg_path_t * path = malloc(sizeof(struct cg_path_t));
|
|
path->contours = 0;
|
|
path->start.x = 0.0;
|
|
path->start.y = 0.0;
|
|
cg_array_init(path->elements);
|
|
cg_array_init(path->points);
|
|
return path;
|
|
}
|
|
|
|
static void cg_path_destroy(struct cg_path_t * path)
|
|
{
|
|
if(path)
|
|
{
|
|
if(path->elements.data)
|
|
free(path->elements.data);
|
|
if(path->points.data)
|
|
free(path->points.data);
|
|
free(path);
|
|
}
|
|
}
|
|
|
|
static inline void cg_path_get_current_point(struct cg_path_t * path, double * x, double * y)
|
|
{
|
|
if(path->points.size == 0)
|
|
{
|
|
*x = 0.0;
|
|
*y = 0.0;
|
|
}
|
|
else
|
|
{
|
|
*x = path->points.data[path->points.size - 1].x;
|
|
*y = path->points.data[path->points.size - 1].y;
|
|
}
|
|
}
|
|
|
|
static void cg_path_move_to(struct cg_path_t * path, double x, double y)
|
|
{
|
|
cg_array_ensure(path->elements, 1);
|
|
cg_array_ensure(path->points, 1);
|
|
|
|
path->elements.data[path->elements.size] = CG_PATH_ELEMENT_MOVE_TO;
|
|
path->elements.size += 1;
|
|
path->contours += 1;
|
|
path->points.data[path->points.size].x = x;
|
|
path->points.data[path->points.size].y = y;
|
|
path->points.size += 1;
|
|
path->start.x = x;
|
|
path->start.y = y;
|
|
}
|
|
|
|
static void cg_path_line_to(struct cg_path_t * path, double x, double y)
|
|
{
|
|
cg_array_ensure(path->elements, 1);
|
|
cg_array_ensure(path->points, 1);
|
|
|
|
path->elements.data[path->elements.size] = CG_PATH_ELEMENT_LINE_TO;
|
|
path->elements.size += 1;
|
|
path->points.data[path->points.size].x = x;
|
|
path->points.data[path->points.size].y = y;
|
|
path->points.size += 1;
|
|
}
|
|
|
|
static void cg_path_curve_to(struct cg_path_t * path, double x1, double y1, double x2, double y2, double x3, double y3)
|
|
{
|
|
cg_array_ensure(path->elements, 1);
|
|
cg_array_ensure(path->points, 3);
|
|
|
|
path->elements.data[path->elements.size] = CG_PATH_ELEMENT_CURVE_TO;
|
|
path->elements.size += 1;
|
|
struct cg_point_t * points = path->points.data + path->points.size;
|
|
points[0].x = x1;
|
|
points[0].y = y1;
|
|
points[1].x = x2;
|
|
points[1].y = y2;
|
|
points[2].x = x3;
|
|
points[2].y = y3;
|
|
path->points.size += 3;
|
|
}
|
|
|
|
static void cg_path_quad_to(struct cg_path_t * path, double x1, double y1, double x2, double y2)
|
|
{
|
|
double x, y;
|
|
cg_path_get_current_point(path, &x, &y);
|
|
|
|
double cx = 2.0 / 3.0 * x1 + 1.0 / 3.0 * x;
|
|
double cy = 2.0 / 3.0 * y1 + 1.0 / 3.0 * y;
|
|
double cx1 = 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2;
|
|
double cy1 = 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2;
|
|
cg_path_curve_to(path, cx, cy, cx1, cy1, x2, y2);
|
|
}
|
|
|
|
static void cg_path_close(struct cg_path_t * path)
|
|
{
|
|
if(path->elements.size == 0)
|
|
return;
|
|
if(path->elements.data[path->elements.size - 1] == CG_PATH_ELEMENT_CLOSE)
|
|
return;
|
|
cg_array_ensure(path->elements, 1);
|
|
cg_array_ensure(path->points, 1);
|
|
path->elements.data[path->elements.size] = CG_PATH_ELEMENT_CLOSE;
|
|
path->elements.size += 1;
|
|
path->points.data[path->points.size].x = path->start.x;
|
|
path->points.data[path->points.size].y = path->start.y;
|
|
path->points.size += 1;
|
|
}
|
|
|
|
static void cg_path_rel_move_to(struct cg_path_t * path, double dx, double dy)
|
|
{
|
|
double x, y;
|
|
cg_path_get_current_point(path, &x, &y);
|
|
cg_path_move_to(path, dx + x, dy + y);
|
|
}
|
|
|
|
static void cg_path_rel_line_to(struct cg_path_t * path, double dx, double dy)
|
|
{
|
|
double x, y;
|
|
cg_path_get_current_point(path, &x, &y);
|
|
cg_path_line_to(path, dx + x, dy + y);
|
|
}
|
|
|
|
static void cg_path_rel_curve_to(struct cg_path_t * path, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3)
|
|
{
|
|
double x, y;
|
|
cg_path_get_current_point(path, &x, &y);
|
|
cg_path_curve_to(path, dx1 + x, dy1 + y, dx2 + x, dy2 + y, dx3 + x, dy3 + y);
|
|
}
|
|
|
|
static void cg_path_rel_quad_to(struct cg_path_t * path, double dx1, double dy1, double dx2, double dy2)
|
|
{
|
|
double x, y;
|
|
cg_path_get_current_point(path, &x, &y);
|
|
cg_path_quad_to(path, dx1 + x, dy1 + y, dx2 + x, dy2 + y);
|
|
}
|
|
|
|
static inline void cg_path_add_rectangle(struct cg_path_t * path, double x, double y, double w, double h)
|
|
{
|
|
cg_path_move_to(path, x, y);
|
|
cg_path_line_to(path, x + w, y);
|
|
cg_path_line_to(path, x + w, y + h);
|
|
cg_path_line_to(path, x, y + h);
|
|
cg_path_line_to(path, x, y);
|
|
cg_path_close(path);
|
|
}
|
|
|
|
static inline void cg_path_add_round_rectangle(struct cg_path_t * path, double x, double y, double w, double h, double rx, double ry)
|
|
{
|
|
rx = CG_MIN(rx, w * 0.5);
|
|
ry = CG_MIN(ry, h * 0.5);
|
|
|
|
double right = x + w;
|
|
double bottom = y + h;
|
|
double cpx = rx * 0.55228474983079339840;
|
|
double cpy = ry * 0.55228474983079339840;
|
|
|
|
cg_path_move_to(path, x, y + ry);
|
|
cg_path_curve_to(path, x, y + ry - cpy, x + rx - cpx, y, x + rx, y);
|
|
cg_path_line_to(path, right - rx, y);
|
|
cg_path_curve_to(path, right - rx + cpx, y, right, y + ry - cpy, right, y + ry);
|
|
cg_path_line_to(path, right, bottom - ry);
|
|
cg_path_curve_to(path, right, bottom - ry + cpy, right - rx + cpx, bottom, right - rx, bottom);
|
|
cg_path_line_to(path, x + rx, bottom);
|
|
cg_path_curve_to(path, x + rx - cpx, bottom, x, bottom - ry + cpy, x, bottom - ry);
|
|
cg_path_line_to(path, x, y + ry);
|
|
cg_path_close(path);
|
|
}
|
|
|
|
static void cg_path_add_ellipse(struct cg_path_t * path, double cx, double cy, double rx, double ry)
|
|
{
|
|
double left = cx - rx;
|
|
double top = cy - ry;
|
|
double right = cx + rx;
|
|
double bottom = cy + ry;
|
|
double cpx = rx * 0.55228474983079339840;
|
|
double cpy = ry * 0.55228474983079339840;
|
|
|
|
cg_path_move_to(path, cx, top);
|
|
cg_path_curve_to(path, cx + cpx, top, right, cy - cpy, right, cy);
|
|
cg_path_curve_to(path, right, cy + cpy, cx + cpx, bottom, cx, bottom);
|
|
cg_path_curve_to(path, cx - cpx, bottom, left, cy + cpy, left, cy);
|
|
cg_path_curve_to(path, left, cy - cpy, cx - cpx, top, cx, top);
|
|
cg_path_close(path);
|
|
}
|
|
|
|
static void cg_path_add_arc(struct cg_path_t * path, double cx, double cy, double r, double a0, double a1, int ccw)
|
|
{
|
|
double da = a1 - a0;
|
|
if(fabs(da) > 6.28318530717958647693)
|
|
{
|
|
da = 6.28318530717958647693;
|
|
}
|
|
else if(da != 0.0 && ccw != (da < 0.0))
|
|
{
|
|
da += 6.28318530717958647693 * (ccw ? -1 : 1);
|
|
}
|
|
int seg_n = (int)(ceil(fabs(da) / 1.57079632679489661923));
|
|
double seg_a = da / seg_n;
|
|
double d = (seg_a / 1.57079632679489661923) * 0.55228474983079339840 * r;
|
|
double a = a0;
|
|
double ax = cx + cos(a) * r;
|
|
double ay = cy + sin(a) * r;
|
|
double dx = -sin(a) * d;
|
|
double dy = cos(a) * d;
|
|
if(path->points.size == 0)
|
|
cg_path_move_to(path, ax, ay);
|
|
else
|
|
cg_path_line_to(path, ax, ay);
|
|
for(int i = 0; i < seg_n; i++)
|
|
{
|
|
double cp1x = ax + dx;
|
|
double cp1y = ay + dy;
|
|
a += seg_a;
|
|
ax = cx + cos(a) * r;
|
|
ay = cy + sin(a) * r;
|
|
dx = -sin(a) * d;
|
|
dy = cos(a) * d;
|
|
double cp2x = ax - dx;
|
|
double cp2y = ay - dy;
|
|
cg_path_curve_to(path, cp1x, cp1y, cp2x, cp2y, ax, ay);
|
|
}
|
|
}
|
|
|
|
static inline void cg_path_clear(struct cg_path_t * path)
|
|
{
|
|
path->elements.size = 0;
|
|
path->points.size = 0;
|
|
path->contours = 0;
|
|
path->start.x = 0.0;
|
|
path->start.y = 0.0;
|
|
}
|
|
|
|
struct cg_bezier_t {
|
|
double x1; double y1;
|
|
double x2; double y2;
|
|
double x3; double y3;
|
|
double x4; double y4;
|
|
};
|
|
|
|
static inline void split(struct cg_bezier_t * b, struct cg_bezier_t * first, struct cg_bezier_t * second)
|
|
{
|
|
double c = (b->x2 + b->x3) * 0.5;
|
|
first->x2 = (b->x1 + b->x2) * 0.5;
|
|
second->x3 = (b->x3 + b->x4) * 0.5;
|
|
first->x1 = b->x1;
|
|
second->x4 = b->x4;
|
|
first->x3 = (first->x2 + c) * 0.5;
|
|
second->x2 = (second->x3 + c) * 0.5;
|
|
first->x4 = second->x1 = (first->x3 + second->x2) * 0.5;
|
|
|
|
c = (b->y2 + b->y3) * 0.5;
|
|
first->y2 = (b->y1 + b->y2) * 0.5;
|
|
second->y3 = (b->y3 + b->y4) * 0.5;
|
|
first->y1 = b->y1;
|
|
second->y4 = b->y4;
|
|
first->y3 = (first->y2 + c) * 0.5;
|
|
second->y2 = (second->y3 + c) * 0.5;
|
|
first->y4 = second->y1 = (first->y3 + second->y2) * 0.5;
|
|
}
|
|
|
|
static inline void flatten(struct cg_path_t * path, struct cg_point_t * p0, struct cg_point_t * p1, struct cg_point_t * p2, struct cg_point_t * p3)
|
|
{
|
|
struct cg_bezier_t beziers[32];
|
|
struct cg_bezier_t * b = beziers;
|
|
|
|
beziers[0].x1 = p0->x;
|
|
beziers[0].y1 = p0->y;
|
|
beziers[0].x2 = p1->x;
|
|
beziers[0].y2 = p1->y;
|
|
beziers[0].x3 = p2->x;
|
|
beziers[0].y3 = p2->y;
|
|
beziers[0].x4 = p3->x;
|
|
beziers[0].y4 = p3->y;
|
|
while(b >= beziers)
|
|
{
|
|
double y4y1 = b->y4 - b->y1;
|
|
double x4x1 = b->x4 - b->x1;
|
|
double l = fabs(x4x1) + fabs(y4y1);
|
|
double d;
|
|
if(l > 1.0)
|
|
{
|
|
d = fabs((x4x1) * (b->y1 - b->y2) - (y4y1) * (b->x1 - b->x2)) + fabs((x4x1) * (b->y1 - b->y3) - (y4y1) * (b->x1 - b->x3));
|
|
}
|
|
else
|
|
{
|
|
d = fabs(b->x1 - b->x2) + fabs(b->y1 - b->y2) + fabs(b->x1 - b->x3) + fabs(b->y1 - b->y3);
|
|
l = 1.0;
|
|
}
|
|
if((d < l * 0.25) || (b == beziers + 31))
|
|
{
|
|
cg_path_line_to(path, b->x4, b->y4);
|
|
--b;
|
|
}
|
|
else
|
|
{
|
|
split(b, b + 1, b);
|
|
++b;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline struct cg_path_t * cg_path_clone(const struct cg_path_t * path)
|
|
{
|
|
struct cg_path_t * result = cg_path_create();
|
|
cg_array_ensure(result->elements, path->elements.size);
|
|
cg_array_ensure(result->points, path->points.size);
|
|
|
|
memcpy(result->elements.data, path->elements.data, (size_t)path->elements.size * sizeof(enum cg_path_element_t));
|
|
memcpy(result->points.data, path->points.data, (size_t)path->points.size * sizeof(struct cg_point_t));
|
|
|
|
result->elements.size = path->elements.size;
|
|
result->points.size = path->points.size;
|
|
result->contours = path->contours;
|
|
result->start = path->start;
|
|
return result;
|
|
}
|
|
|
|
static inline struct cg_path_t * cg_path_clone_flat(struct cg_path_t * path)
|
|
{
|
|
struct cg_point_t * points = path->points.data;
|
|
struct cg_path_t * result = cg_path_create();
|
|
struct cg_point_t p0;
|
|
|
|
cg_array_ensure(result->elements, path->elements.size);
|
|
cg_array_ensure(result->points, path->points.size);
|
|
for(int i = 0; i < path->elements.size; i++)
|
|
{
|
|
switch(path->elements.data[i])
|
|
{
|
|
case CG_PATH_ELEMENT_MOVE_TO:
|
|
cg_path_move_to(result, points[0].x, points[0].y);
|
|
points += 1;
|
|
break;
|
|
case CG_PATH_ELEMENT_LINE_TO:
|
|
cg_path_line_to(result, points[0].x, points[0].y);
|
|
points += 1;
|
|
break;
|
|
case CG_PATH_ELEMENT_CURVE_TO:
|
|
cg_path_get_current_point(result, &p0.x, &p0.y);
|
|
flatten(result, &p0, points, points + 1, points + 2);
|
|
points += 3;
|
|
break;
|
|
case CG_PATH_ELEMENT_CLOSE:
|
|
cg_path_line_to(result, points[0].x, points[0].y);
|
|
points += 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static struct cg_dash_t * cg_dash_create(double * dashes, int ndash, double offset)
|
|
{
|
|
if(dashes && (ndash > 0))
|
|
{
|
|
struct cg_dash_t * dash = malloc(sizeof(struct cg_dash_t));
|
|
dash->offset = offset;
|
|
dash->data = malloc((size_t)ndash * sizeof(double));
|
|
dash->size = ndash;
|
|
memcpy(dash->data, dashes, (size_t)ndash * sizeof(double));
|
|
return dash;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct cg_dash_t * cg_dash_clone(struct cg_dash_t * dash)
|
|
{
|
|
if(dash)
|
|
return cg_dash_create(dash->data, dash->size, dash->offset);
|
|
return NULL;
|
|
}
|
|
|
|
static void cg_dash_destroy(struct cg_dash_t * dash)
|
|
{
|
|
if(dash)
|
|
{
|
|
free(dash->data);
|
|
free(dash);
|
|
}
|
|
}
|
|
|
|
static inline struct cg_path_t * cg_dash_path(struct cg_dash_t * dash, struct cg_path_t * path)
|
|
{
|
|
if((dash->data == NULL) || (dash->size <= 0))
|
|
return cg_path_clone(path);
|
|
|
|
int toggle = 1;
|
|
int offset = 0;
|
|
double phase = dash->offset;
|
|
while(phase >= dash->data[offset])
|
|
{
|
|
toggle = !toggle;
|
|
phase -= dash->data[offset];
|
|
offset += 1;
|
|
if(offset == dash->size)
|
|
offset = 0;
|
|
}
|
|
|
|
struct cg_path_t * flat = cg_path_clone_flat(path);
|
|
struct cg_path_t * result = cg_path_create();
|
|
cg_array_ensure(result->elements, flat->elements.size);
|
|
cg_array_ensure(result->points, flat->points.size);
|
|
|
|
enum cg_path_element_t * elements = flat->elements.data;
|
|
enum cg_path_element_t * end = elements + flat->elements.size;
|
|
struct cg_point_t * points = flat->points.data;
|
|
while(elements < end)
|
|
{
|
|
int itoggle = toggle;
|
|
int ioffset = offset;
|
|
double iphase = phase;
|
|
double x0 = points->x;
|
|
double y0 = points->y;
|
|
if(itoggle)
|
|
cg_path_move_to(result, x0, y0);
|
|
++elements;
|
|
++points;
|
|
while((elements < end) && (*elements == CG_PATH_ELEMENT_LINE_TO))
|
|
{
|
|
double dx = points->x - x0;
|
|
double dy = points->y - y0;
|
|
double dist0 = sqrt(dx * dx + dy * dy);
|
|
double dist1 = 0;
|
|
while(dist0 - dist1 > dash->data[ioffset] - iphase)
|
|
{
|
|
dist1 += dash->data[ioffset] - iphase;
|
|
double a = dist1 / dist0;
|
|
double x = x0 + a * dx;
|
|
double y = y0 + a * dy;
|
|
if(itoggle)
|
|
cg_path_line_to(result, x, y);
|
|
else
|
|
cg_path_move_to(result, x, y);
|
|
itoggle = !itoggle;
|
|
iphase = 0;
|
|
ioffset += 1;
|
|
if(ioffset == dash->size)
|
|
ioffset = 0;
|
|
}
|
|
iphase += dist0 - dist1;
|
|
x0 = points->x;
|
|
y0 = points->y;
|
|
if(itoggle)
|
|
cg_path_line_to(result, x0, y0);
|
|
++elements;
|
|
++points;
|
|
}
|
|
}
|
|
cg_path_destroy(flat);
|
|
return result;
|
|
}
|
|
|
|
#define ALIGN_SIZE(size) (((size) + 7ul) & ~7ul)
|
|
static void ft_outline_init(XCG_FT_Outline * outline, struct cg_ctx_t * ctx, int points, int contours)
|
|
{
|
|
size_t size_a = ALIGN_SIZE((points + contours) * sizeof(XCG_FT_Vector));
|
|
size_t size_b = ALIGN_SIZE((points + contours) * sizeof(char));
|
|
size_t size_c = ALIGN_SIZE(contours * sizeof(int));
|
|
size_t size_d = ALIGN_SIZE(contours * sizeof(char));
|
|
size_t size_n = size_a + size_b + size_c + size_d;
|
|
if(size_n > ctx->outline_size)
|
|
{
|
|
ctx->outline_data = realloc(ctx->outline_data, size_n);
|
|
ctx->outline_size = size_n;
|
|
}
|
|
|
|
XCG_FT_Byte * data = ctx->outline_data;
|
|
outline->points = (XCG_FT_Vector *)(data);
|
|
outline->tags = outline->contours_flag = NULL;
|
|
outline->contours = NULL;
|
|
if(data)
|
|
{
|
|
outline->tags = (char *)(data + size_a);
|
|
outline->contours = (int *)(data + size_a + size_b);
|
|
outline->contours_flag = (char*)(data + size_a + size_b + size_c);
|
|
}
|
|
outline->n_points = 0;
|
|
outline->n_contours = 0;
|
|
outline->flags = 0x0;
|
|
}
|
|
|
|
#define FT_COORD(x) (XCG_FT_Pos)((x) * 64)
|
|
static void ft_outline_move_to(XCG_FT_Outline * ft, double x, double y)
|
|
{
|
|
ft->points[ft->n_points].x = FT_COORD(x);
|
|
ft->points[ft->n_points].y = FT_COORD(y);
|
|
ft->tags[ft->n_points] = XCG_FT_CURVE_TAG_ON;
|
|
if(ft->n_points)
|
|
{
|
|
ft->contours[ft->n_contours] = ft->n_points - 1;
|
|
ft->n_contours++;
|
|
}
|
|
ft->contours_flag[ft->n_contours] = 1;
|
|
ft->n_points++;
|
|
}
|
|
|
|
static void ft_outline_line_to(XCG_FT_Outline * ft, double x, double y)
|
|
{
|
|
ft->points[ft->n_points].x = FT_COORD(x);
|
|
ft->points[ft->n_points].y = FT_COORD(y);
|
|
ft->tags[ft->n_points] = XCG_FT_CURVE_TAG_ON;
|
|
ft->n_points++;
|
|
}
|
|
|
|
static void ft_outline_curve_to(XCG_FT_Outline * ft, double x1, double y1, double x2, double y2, double x3, double y3)
|
|
{
|
|
ft->points[ft->n_points].x = FT_COORD(x1);
|
|
ft->points[ft->n_points].y = FT_COORD(y1);
|
|
ft->tags[ft->n_points] = XCG_FT_CURVE_TAG_CUBIC;
|
|
ft->n_points++;
|
|
|
|
ft->points[ft->n_points].x = FT_COORD(x2);
|
|
ft->points[ft->n_points].y = FT_COORD(y2);
|
|
ft->tags[ft->n_points] = XCG_FT_CURVE_TAG_CUBIC;
|
|
ft->n_points++;
|
|
|
|
ft->points[ft->n_points].x = FT_COORD(x3);
|
|
ft->points[ft->n_points].y = FT_COORD(y3);
|
|
ft->tags[ft->n_points] = XCG_FT_CURVE_TAG_ON;
|
|
ft->n_points++;
|
|
}
|
|
|
|
static void ft_outline_close(XCG_FT_Outline * ft)
|
|
{
|
|
ft->contours_flag[ft->n_contours] = 0;
|
|
int index = ft->n_contours ? ft->contours[ft->n_contours - 1] + 1 : 0;
|
|
if(index == ft->n_points)
|
|
return;
|
|
|
|
ft->points[ft->n_points].x = ft->points[index].x;
|
|
ft->points[ft->n_points].y = ft->points[index].y;
|
|
ft->tags[ft->n_points] = XCG_FT_CURVE_TAG_ON;
|
|
ft->n_points++;
|
|
}
|
|
|
|
static void ft_outline_end(XCG_FT_Outline * ft)
|
|
{
|
|
if(ft->n_points)
|
|
{
|
|
ft->contours[ft->n_contours] = ft->n_points - 1;
|
|
ft->n_contours++;
|
|
}
|
|
}
|
|
|
|
static void ft_outline_convert(XCG_FT_Outline * outline, struct cg_ctx_t * ctx, struct cg_path_t * path, struct cg_matrix_t * matrix)
|
|
{
|
|
ft_outline_init(outline, ctx, path->points.size, path->contours);
|
|
enum cg_path_element_t * elements = path->elements.data;
|
|
struct cg_point_t * points = path->points.data;
|
|
struct cg_point_t p[3];
|
|
for(int i = 0; i < path->elements.size; i++)
|
|
{
|
|
switch(elements[i])
|
|
{
|
|
case CG_PATH_ELEMENT_MOVE_TO:
|
|
cg_matrix_map_point(matrix, &points[0], &p[0]);
|
|
ft_outline_move_to(outline, p[0].x, p[0].y);
|
|
points += 1;
|
|
break;
|
|
case CG_PATH_ELEMENT_LINE_TO:
|
|
cg_matrix_map_point(matrix, &points[0], &p[0]);
|
|
ft_outline_line_to(outline, p[0].x, p[0].y);
|
|
points += 1;
|
|
break;
|
|
case CG_PATH_ELEMENT_CURVE_TO:
|
|
cg_matrix_map_point(matrix, &points[0], &p[0]);
|
|
cg_matrix_map_point(matrix, &points[1], &p[1]);
|
|
cg_matrix_map_point(matrix, &points[2], &p[2]);
|
|
ft_outline_curve_to(outline, p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y);
|
|
points += 3;
|
|
break;
|
|
case CG_PATH_ELEMENT_CLOSE:
|
|
ft_outline_close(outline);
|
|
points += 1;
|
|
break;
|
|
}
|
|
}
|
|
ft_outline_end(outline);
|
|
}
|
|
|
|
static void ft_outline_convert_dash(XCG_FT_Outline * outline, struct cg_ctx_t * ctx, struct cg_path_t * path, struct cg_matrix_t * matrix, struct cg_dash_t * dash)
|
|
{
|
|
struct cg_path_t * dashed = cg_dash_path(dash, path);
|
|
ft_outline_convert(outline, ctx, dashed, matrix);
|
|
cg_path_destroy(dashed);
|
|
}
|
|
|
|
static void generation_callback(int count, const XCG_FT_Span * spans, void * user)
|
|
{
|
|
struct cg_rle_t * rle = user;
|
|
cg_array_ensure(rle->spans, count);
|
|
struct cg_span_t * data = rle->spans.data + rle->spans.size;
|
|
memcpy(data, spans, (size_t)count * sizeof(struct cg_span_t));
|
|
rle->spans.size += count;
|
|
}
|
|
|
|
static struct cg_rle_t * cg_rle_create(void)
|
|
{
|
|
struct cg_rle_t * rle = malloc(sizeof(struct cg_rle_t));
|
|
cg_array_init(rle->spans);
|
|
rle->x = 0;
|
|
rle->y = 0;
|
|
rle->w = 0;
|
|
rle->h = 0;
|
|
return rle;
|
|
}
|
|
|
|
static void cg_rle_destroy(struct cg_rle_t * rle)
|
|
{
|
|
if(rle)
|
|
{
|
|
free(rle->spans.data);
|
|
free(rle);
|
|
}
|
|
}
|
|
|
|
static void cg_rle_rasterize(struct cg_ctx_t * ctx, struct cg_rle_t * rle, struct cg_path_t * path, struct cg_matrix_t * m, struct cg_rect_t * clip, struct cg_stroke_data_t * stroke, enum cg_fill_rule_t winding)
|
|
{
|
|
XCG_FT_Raster_Params params;
|
|
params.flags = XCG_FT_RASTER_FLAG_DIRECT | XCG_FT_RASTER_FLAG_AA;
|
|
params.gray_spans = generation_callback;
|
|
params.user = rle;
|
|
if(clip)
|
|
{
|
|
params.flags |= XCG_FT_RASTER_FLAG_CLIP;
|
|
params.clip_box.xMin = (XCG_FT_Pos)(clip->x);
|
|
params.clip_box.yMin = (XCG_FT_Pos)(clip->y);
|
|
params.clip_box.xMax = (XCG_FT_Pos)(clip->x + clip->w);
|
|
params.clip_box.yMax = (XCG_FT_Pos)(clip->y + clip->h);
|
|
}
|
|
if(stroke)
|
|
{
|
|
XCG_FT_Outline outline;
|
|
if(stroke->dash == NULL)
|
|
ft_outline_convert(&outline, ctx, path, m);
|
|
else
|
|
ft_outline_convert_dash(&outline, ctx, path, m, stroke->dash);
|
|
XCG_FT_Stroker_LineCap ftCap;
|
|
XCG_FT_Stroker_LineJoin ftJoin;
|
|
XCG_FT_Fixed ftWidth;
|
|
XCG_FT_Fixed ftMiterLimit;
|
|
|
|
struct cg_point_t p1 = { 0, 0 };
|
|
struct cg_point_t p2 = { 1.41421356237309504880, 1.41421356237309504880 };
|
|
struct cg_point_t p3;
|
|
|
|
cg_matrix_map_point(m, &p1, &p1);
|
|
cg_matrix_map_point(m, &p2, &p2);
|
|
|
|
p3.x = p2.x - p1.x;
|
|
p3.y = p2.y - p1.y;
|
|
|
|
double scale = sqrt(p3.x * p3.x + p3.y * p3.y) / 2.0;
|
|
|
|
ftWidth = (XCG_FT_Fixed)(stroke->width * scale * 0.5 * (1 << 6));
|
|
ftMiterLimit = (XCG_FT_Fixed)(stroke->miterlimit * (1 << 16));
|
|
|
|
switch(stroke->cap)
|
|
{
|
|
case CG_LINE_CAP_SQUARE:
|
|
ftCap = XCG_FT_STROKER_LINECAP_SQUARE;
|
|
break;
|
|
case CG_LINE_CAP_ROUND:
|
|
ftCap = XCG_FT_STROKER_LINECAP_ROUND;
|
|
break;
|
|
default:
|
|
ftCap = XCG_FT_STROKER_LINECAP_BUTT;
|
|
break;
|
|
}
|
|
switch(stroke->join)
|
|
{
|
|
case CG_LINE_JOIN_BEVEL:
|
|
ftJoin = XCG_FT_STROKER_LINEJOIN_BEVEL;
|
|
break;
|
|
case CG_LINE_JOIN_ROUND:
|
|
ftJoin = XCG_FT_STROKER_LINEJOIN_ROUND;
|
|
break;
|
|
default:
|
|
ftJoin = XCG_FT_STROKER_LINEJOIN_MITER_FIXED;
|
|
break;
|
|
}
|
|
XCG_FT_Stroker stroker;
|
|
XCG_FT_Stroker_New(&stroker);
|
|
XCG_FT_Stroker_Set(stroker, ftWidth, ftCap, ftJoin, ftMiterLimit);
|
|
XCG_FT_Stroker_ParseOutline(stroker, &outline);
|
|
|
|
XCG_FT_UInt points;
|
|
XCG_FT_UInt contours;
|
|
XCG_FT_Stroker_GetCounts(stroker, &points, &contours);
|
|
|
|
ft_outline_init(&outline, ctx, points, contours);
|
|
XCG_FT_Stroker_Export(stroker, &outline);
|
|
XCG_FT_Stroker_Done(stroker);
|
|
|
|
outline.flags = XCG_FT_OUTLINE_NONE;
|
|
params.source = &outline;
|
|
XCG_FT_Raster_Render(¶ms);
|
|
}
|
|
else
|
|
{
|
|
XCG_FT_Outline outline;
|
|
ft_outline_convert(&outline, ctx, path, m);
|
|
switch(winding)
|
|
{
|
|
case CG_FILL_RULE_EVEN_ODD:
|
|
outline.flags = XCG_FT_OUTLINE_EVEN_ODD_FILL;
|
|
break;
|
|
default:
|
|
outline.flags = XCG_FT_OUTLINE_NONE;
|
|
break;
|
|
}
|
|
|
|
params.source = &outline;
|
|
XCG_FT_Raster_Render(¶ms);
|
|
}
|
|
|
|
if(rle->spans.size == 0)
|
|
{
|
|
rle->x = 0;
|
|
rle->y = 0;
|
|
rle->w = 0;
|
|
rle->h = 0;
|
|
return;
|
|
}
|
|
|
|
struct cg_span_t *spans = rle->spans.data;
|
|
int x1 = INT_MAX;
|
|
int y1 = spans[0].y;
|
|
int x2 = 0;
|
|
int y2 = spans[rle->spans.size - 1].y;
|
|
for(int i = 0; i < rle->spans.size; i++)
|
|
{
|
|
if(spans[i].x < x1)
|
|
x1 = spans[i].x;
|
|
if(spans[i].x + spans[i].len > x2)
|
|
x2 = spans[i].x + spans[i].len;
|
|
}
|
|
|
|
rle->x = x1;
|
|
rle->y = y1;
|
|
rle->w = x2 - x1;
|
|
rle->h = y2 - y1 + 1;
|
|
}
|
|
|
|
static struct cg_rle_t * cg_rle_intersection(struct cg_rle_t * a, struct cg_rle_t * b)
|
|
{
|
|
struct cg_rle_t * result = malloc(sizeof(struct cg_rle_t));
|
|
cg_array_init(result->spans);
|
|
cg_array_ensure(result->spans, CG_MAX(a->spans.size, b->spans.size));
|
|
|
|
struct cg_span_t * a_spans = a->spans.data;
|
|
struct cg_span_t * a_end = a_spans + a->spans.size;
|
|
struct cg_span_t * b_spans = b->spans.data;
|
|
struct cg_span_t * b_end = b_spans + b->spans.size;
|
|
while((a_spans < a_end) && (b_spans < b_end))
|
|
{
|
|
if(b_spans->y > a_spans->y)
|
|
{
|
|
++a_spans;
|
|
continue;
|
|
}
|
|
if(a_spans->y != b_spans->y)
|
|
{
|
|
++b_spans;
|
|
continue;
|
|
}
|
|
int ax1 = a_spans->x;
|
|
int ax2 = ax1 + a_spans->len;
|
|
int bx1 = b_spans->x;
|
|
int bx2 = bx1 + b_spans->len;
|
|
if(bx1 < ax1 && bx2 < ax1)
|
|
{
|
|
++b_spans;
|
|
continue;
|
|
}
|
|
else if(ax1 < bx1 && ax2 < bx1)
|
|
{
|
|
++a_spans;
|
|
continue;
|
|
}
|
|
int x = CG_MAX(ax1, bx1);
|
|
int len = CG_MIN(ax2, bx2) - x;
|
|
if(len)
|
|
{
|
|
struct cg_span_t * span = result->spans.data + result->spans.size;
|
|
span->x = x;
|
|
span->len = len;
|
|
span->y = a_spans->y;
|
|
span->coverage = CG_DIV255(a_spans->coverage * b_spans->coverage);
|
|
result->spans.size += 1;
|
|
}
|
|
if(ax2 < bx2)
|
|
{
|
|
++a_spans;
|
|
}
|
|
else
|
|
{
|
|
++b_spans;
|
|
}
|
|
}
|
|
if(result->spans.size == 0)
|
|
{
|
|
result->x = 0;
|
|
result->y = 0;
|
|
result->w = 0;
|
|
result->h = 0;
|
|
return result;
|
|
}
|
|
struct cg_span_t * spans = result->spans.data;
|
|
int x1 = INT_MAX;
|
|
int y1 = spans[0].y;
|
|
int x2 = 0;
|
|
int y2 = spans[result->spans.size - 1].y;
|
|
for(int i = 0; i < result->spans.size; i++)
|
|
{
|
|
if(spans[i].x < x1)
|
|
x1 = spans[i].x;
|
|
if(spans[i].x + spans[i].len > x2)
|
|
x2 = spans[i].x + spans[i].len;
|
|
}
|
|
result->x = x1;
|
|
result->y = y1;
|
|
result->w = x2 - x1;
|
|
result->h = y2 - y1 + 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void cg_rle_clip_path(struct cg_rle_t * rle, struct cg_rle_t * clip)
|
|
{
|
|
if(rle && clip)
|
|
{
|
|
struct cg_rle_t * result = cg_rle_intersection(rle, clip);
|
|
cg_array_ensure(rle->spans, result->spans.size);
|
|
memcpy(rle->spans.data, result->spans.data, (size_t)result->spans.size * sizeof(struct cg_span_t));
|
|
rle->spans.size = result->spans.size;
|
|
rle->x = result->x;
|
|
rle->y = result->y;
|
|
rle->w = result->w;
|
|
rle->h = result->h;
|
|
cg_rle_destroy(result);
|
|
}
|
|
}
|
|
|
|
static struct cg_rle_t * cg_rle_clone(struct cg_rle_t * rle)
|
|
{
|
|
if(rle)
|
|
{
|
|
struct cg_rle_t * result = malloc(sizeof(struct cg_rle_t));
|
|
cg_array_init(result->spans);
|
|
cg_array_ensure(result->spans, rle->spans.size);
|
|
memcpy(result->spans.data, rle->spans.data, (size_t)rle->spans.size * sizeof(struct cg_span_t));
|
|
result->spans.size = rle->spans.size;
|
|
result->x = rle->x;
|
|
result->y = rle->y;
|
|
result->w = rle->w;
|
|
result->h = rle->h;
|
|
return result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static inline void cg_rle_clear(struct cg_rle_t * rle)
|
|
{
|
|
rle->spans.size = 0;
|
|
rle->x = 0;
|
|
rle->y = 0;
|
|
rle->w = 0;
|
|
rle->h = 0;
|
|
}
|
|
|
|
static void cg_gradient_init_linear(struct cg_gradient_t * gradient, double x1, double y1, double x2, double y2)
|
|
{
|
|
gradient->type = CG_GRADIENT_TYPE_LINEAR;
|
|
gradient->spread = CG_SPREAD_METHOD_PAD;
|
|
gradient->opacity = 1.0;
|
|
gradient->stops.size = 0;
|
|
cg_matrix_init_identity(&gradient->matrix);
|
|
cg_gradient_set_values_linear(gradient, x1, y1, x2, y2);
|
|
}
|
|
|
|
static void cg_gradient_init_radial(struct cg_gradient_t * gradient, double cx, double cy, double cr, double fx, double fy, double fr)
|
|
{
|
|
gradient->type = CG_GRADIENT_TYPE_RADIAL;
|
|
gradient->spread = CG_SPREAD_METHOD_PAD;
|
|
gradient->opacity = 1.0;
|
|
gradient->stops.size = 0;
|
|
cg_matrix_init_identity(&gradient->matrix);
|
|
cg_gradient_set_values_radial(gradient, cx, cy, cr, fx, fy, fr);
|
|
}
|
|
|
|
void cg_gradient_set_values_linear(struct cg_gradient_t * gradient, double x1, double y1, double x2, double y2)
|
|
{
|
|
gradient->values[0] = x1;
|
|
gradient->values[1] = y1;
|
|
gradient->values[2] = x2;
|
|
gradient->values[3] = y2;
|
|
}
|
|
|
|
void cg_gradient_set_values_radial(struct cg_gradient_t * gradient, double cx, double cy, double cr, double fx, double fy, double fr)
|
|
{
|
|
gradient->values[0] = cx;
|
|
gradient->values[1] = cy;
|
|
gradient->values[2] = cr;
|
|
gradient->values[3] = fx;
|
|
gradient->values[4] = fy;
|
|
gradient->values[5] = fr;
|
|
}
|
|
|
|
void cg_gradient_set_spread(struct cg_gradient_t * gradient, enum cg_spread_method_t spread)
|
|
{
|
|
gradient->spread = spread;
|
|
}
|
|
|
|
void cg_gradient_set_matrix(struct cg_gradient_t * gradient, struct cg_matrix_t * m)
|
|
{
|
|
memcpy(&gradient->matrix, m, sizeof(struct cg_matrix_t));
|
|
}
|
|
|
|
void cg_gradient_set_opacity(struct cg_gradient_t * gradient, double opacity)
|
|
{
|
|
gradient->opacity = CG_CLAMP(opacity, 0.0, 1.0);
|
|
}
|
|
|
|
void cg_gradient_add_stop_rgb(struct cg_gradient_t * gradient, double offset, double r, double g, double b)
|
|
{
|
|
cg_gradient_add_stop_rgba(gradient, offset, r, g, b, 1.0);
|
|
}
|
|
|
|
void cg_gradient_add_stop_rgba(struct cg_gradient_t * gradient, double offset, double r, double g, double b, double a)
|
|
{
|
|
if(offset < 0.0)
|
|
offset = 0.0;
|
|
if(offset > 1.0)
|
|
offset = 1.0;
|
|
cg_array_ensure(gradient->stops, 1);
|
|
struct cg_gradient_stop_t * stops = gradient->stops.data;
|
|
int nstops = gradient->stops.size;
|
|
int i;
|
|
for(i = 0; i < nstops; i++)
|
|
{
|
|
if(offset < stops[i].offset)
|
|
{
|
|
memmove(&stops[i + 1], &stops[i], (size_t)(nstops - i) * sizeof(struct cg_gradient_stop_t));
|
|
break;
|
|
}
|
|
}
|
|
struct cg_gradient_stop_t * stop = &stops[i];
|
|
stop->offset = offset;
|
|
cg_color_init_rgba(&stop->color, r, g, b, a);
|
|
gradient->stops.size += 1;
|
|
}
|
|
|
|
void cg_gradient_add_stop_color(struct cg_gradient_t * gradient, double offset, struct cg_color_t * color)
|
|
{
|
|
cg_gradient_add_stop_rgba(gradient, offset, color->r, color->g, color->b, color->a);
|
|
}
|
|
|
|
void cg_gradient_add_stop(struct cg_gradient_t * gradient, struct cg_gradient_stop_t * stop)
|
|
{
|
|
cg_gradient_add_stop_rgba(gradient, stop->offset, stop->color.r, stop->color.g, stop->color.b, stop->color.a);
|
|
}
|
|
|
|
void cg_gradient_clear_stops(struct cg_gradient_t * gradient)
|
|
{
|
|
gradient->stops.size = 0;
|
|
}
|
|
|
|
static void cg_gradient_copy(struct cg_gradient_t * gradient, struct cg_gradient_t * source)
|
|
{
|
|
gradient->type = source->type;
|
|
gradient->spread = source->spread;
|
|
gradient->matrix = source->matrix;
|
|
gradient->opacity = source->opacity;
|
|
cg_array_ensure(gradient->stops, source->stops.size);
|
|
memcpy(gradient->values, source->values, sizeof(source->values));
|
|
memcpy(gradient->stops.data, source->stops.data, source->stops.size * sizeof(struct cg_gradient_stop_t));
|
|
}
|
|
|
|
static void cg_gradient_destroy(struct cg_gradient_t * gradient)
|
|
{
|
|
if(gradient->stops.data)
|
|
free(gradient->stops.data);
|
|
}
|
|
|
|
static void cg_texture_init(struct cg_texture_t * texture, struct cg_surface_t * surface, enum cg_texture_type_t type)
|
|
{
|
|
surface = cg_surface_reference(surface);
|
|
cg_surface_destroy(texture->surface);
|
|
texture->type = type;
|
|
texture->surface = surface;
|
|
texture->opacity = 1.0;
|
|
cg_matrix_init_identity(&texture->matrix);
|
|
}
|
|
|
|
void cg_texture_set_type(struct cg_texture_t * texture, enum cg_texture_type_t type)
|
|
{
|
|
texture->type = type;
|
|
}
|
|
|
|
void cg_texture_set_matrix(struct cg_texture_t * texture, struct cg_matrix_t * m)
|
|
{
|
|
memcpy(&texture->matrix, m, sizeof(struct cg_matrix_t));
|
|
}
|
|
|
|
void cg_texture_set_surface(struct cg_texture_t * texture, struct cg_surface_t * surface)
|
|
{
|
|
surface = cg_surface_reference(surface);
|
|
cg_surface_destroy(texture->surface);
|
|
texture->surface = surface;
|
|
}
|
|
|
|
void cg_texture_set_opacity(struct cg_texture_t * texture, double opacity)
|
|
{
|
|
texture->opacity = CG_CLAMP(opacity, 0.0, 1.0);
|
|
}
|
|
|
|
static void cg_texture_copy(struct cg_texture_t * texture, struct cg_texture_t * source)
|
|
{
|
|
struct cg_surface_t * surface = cg_surface_reference(source->surface);
|
|
cg_surface_destroy(texture->surface);
|
|
texture->type = source->type;
|
|
texture->surface = surface;
|
|
texture->opacity = source->opacity;
|
|
texture->matrix = source->matrix;
|
|
}
|
|
|
|
static void cg_texture_destroy(struct cg_texture_t * texture)
|
|
{
|
|
cg_surface_destroy(texture->surface);
|
|
}
|
|
|
|
static void cg_paint_init(struct cg_paint_t * paint)
|
|
{
|
|
paint->type = CG_PAINT_TYPE_COLOR;
|
|
paint->texture.surface = NULL;
|
|
cg_array_init(paint->gradient.stops);
|
|
cg_color_init_rgba(&paint->color, 0, 0, 0, 1.0);
|
|
}
|
|
|
|
static void cg_paint_destroy(struct cg_paint_t * paint)
|
|
{
|
|
cg_texture_destroy(&paint->texture);
|
|
cg_gradient_destroy(&paint->gradient);
|
|
}
|
|
|
|
static void cg_paint_copy(struct cg_paint_t * paint, struct cg_paint_t * source)
|
|
{
|
|
paint->type = source->type;
|
|
switch(source->type)
|
|
{
|
|
case CG_PAINT_TYPE_COLOR:
|
|
paint->color = source->color;
|
|
break;
|
|
case CG_PAINT_TYPE_GRADIENT:
|
|
cg_gradient_copy(&paint->gradient, &source->gradient);
|
|
break;
|
|
case CG_PAINT_TYPE_TEXTURE:
|
|
cg_texture_copy(&paint->texture, &source->texture);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct cg_gradient_data_t {
|
|
enum cg_spread_method_t spread;
|
|
struct cg_matrix_t matrix;
|
|
uint32_t colortable[1024];
|
|
union {
|
|
struct {
|
|
double x1, y1;
|
|
double x2, y2;
|
|
} linear;
|
|
struct {
|
|
double cx, cy, cr;
|
|
double fx, fy, fr;
|
|
} radial;
|
|
};
|
|
};
|
|
|
|
struct cg_texture_data_t {
|
|
struct cg_matrix_t matrix;
|
|
int width;
|
|
int height;
|
|
int stride;
|
|
int alpha;
|
|
void * pixels;
|
|
};
|
|
|
|
struct cg_linear_gradient_values_t {
|
|
double dx;
|
|
double dy;
|
|
double l;
|
|
double off;
|
|
};
|
|
|
|
struct cg_radial_gradient_values_t {
|
|
double dx;
|
|
double dy;
|
|
double dr;
|
|
double sqrfr;
|
|
double a;
|
|
double inv2a;
|
|
int extended;
|
|
};
|
|
|
|
static inline uint32_t premultiply_color(struct cg_color_t * color, double opacity)
|
|
{
|
|
uint32_t alpha = (uint8_t)(color->a * opacity * 255);
|
|
uint32_t pr = (uint8_t)(color->r * alpha);
|
|
uint32_t pg = (uint8_t)(color->g * alpha);
|
|
uint32_t pb = (uint8_t)(color->b * alpha);
|
|
return (alpha << 24) | (pr << 16) | (pg << 8) | (pb);
|
|
}
|
|
|
|
static inline uint32_t combine_opacity(struct cg_color_t * color, double opacity)
|
|
{
|
|
uint32_t a = (uint8_t)(color->a * opacity * 255);
|
|
uint32_t r = (uint8_t)(color->r * 255);
|
|
uint32_t g = (uint8_t)(color->g * 255);
|
|
uint32_t b = (uint8_t)(color->b * 255);
|
|
return (a << 24) | (r << 16) | (g << 8) | (b);
|
|
}
|
|
|
|
static inline uint32_t premultiply_pixel(uint32_t color)
|
|
{
|
|
uint32_t a = CG_ALPHA(color);
|
|
uint32_t r = (color >> 16) & 0xff;
|
|
uint32_t g = (color >> 8) & 0xff;
|
|
uint32_t b = (color >> 0) & 0xff;
|
|
uint32_t pr = (r * a) / 255;
|
|
uint32_t pg = (g * a) / 255;
|
|
uint32_t pb = (b * a) / 255;
|
|
return (a << 24) | (pr << 16) | (pg << 8) | (pb);
|
|
}
|
|
|
|
static inline uint32_t interpolate_pixel(uint32_t x, uint32_t a, uint32_t y, uint32_t b)
|
|
{
|
|
uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
|
|
t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
|
|
t &= 0xff00ff;
|
|
x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
|
|
x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
|
|
x &= 0xff00ff00;
|
|
x |= t;
|
|
return x;
|
|
}
|
|
|
|
static void __cg_memfill32(uint32_t * dst, uint32_t val, int len)
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = val;
|
|
}
|
|
extern __typeof(__cg_memfill32) cg_memfill32 __attribute__((weak, alias("__cg_memfill32")));
|
|
|
|
static inline int gradient_clamp(struct cg_gradient_data_t * gradient, int ipos)
|
|
{
|
|
switch(gradient->spread)
|
|
{
|
|
case CG_SPREAD_METHOD_PAD:
|
|
if(ipos < 0)
|
|
ipos = 0;
|
|
else if(ipos >= 1024)
|
|
ipos = 1024 - 1;
|
|
break;
|
|
case CG_SPREAD_METHOD_REFLECT:
|
|
ipos = ipos % 2048;
|
|
ipos = ipos < 0 ? 2048 + ipos : ipos;
|
|
ipos = ipos >= 1024 ? 2048 - 1 - ipos : ipos;
|
|
break;
|
|
case CG_SPREAD_METHOD_REPEAT:
|
|
ipos = ipos % 1024;
|
|
ipos = ipos < 0 ? 1024 + ipos : ipos;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ipos;
|
|
}
|
|
|
|
#define FIXPT_BITS (8)
|
|
#define FIXPT_SIZE (1 << FIXPT_BITS)
|
|
|
|
static inline uint32_t gradient_pixel_fixed(struct cg_gradient_data_t * gradient, int fixed_pos)
|
|
{
|
|
int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
|
|
return gradient->colortable[gradient_clamp(gradient, ipos)];
|
|
}
|
|
|
|
static inline uint32_t gradient_pixel(struct cg_gradient_data_t * gradient, double pos)
|
|
{
|
|
int ipos = (int)(pos * (1024 - 1) + 0.5);
|
|
return gradient->colortable[gradient_clamp(gradient, ipos)];
|
|
}
|
|
|
|
static inline void fetch_linear_gradient(uint32_t * buffer, struct cg_linear_gradient_values_t * v, struct cg_gradient_data_t * gradient, int y, int x, int length)
|
|
{
|
|
double t, inc;
|
|
double rx = 0, ry = 0;
|
|
|
|
if(v->l == 0.0)
|
|
{
|
|
t = inc = 0;
|
|
}
|
|
else
|
|
{
|
|
rx = gradient->matrix.c * (y + 0.5) + gradient->matrix.a * (x + 0.5) + gradient->matrix.tx;
|
|
ry = gradient->matrix.d * (y + 0.5) + gradient->matrix.b * (x + 0.5) + gradient->matrix.ty;
|
|
t = v->dx * rx + v->dy * ry + v->off;
|
|
inc = v->dx * gradient->matrix.a + v->dy * gradient->matrix.b;
|
|
t *= (1024 - 1);
|
|
inc *= (1024 - 1);
|
|
}
|
|
uint32_t * end = buffer + length;
|
|
if((inc > -1e-5) && (inc < 1e-5))
|
|
{
|
|
cg_memfill32(buffer, gradient_pixel_fixed(gradient, (int)(t * FIXPT_SIZE)), length);
|
|
}
|
|
else
|
|
{
|
|
if(t + inc * length < (double)(INT_MAX >> (FIXPT_BITS + 1)) && t + inc * length > (double)(INT_MIN >> (FIXPT_BITS + 1)))
|
|
{
|
|
int t_fixed = (int)(t * FIXPT_SIZE);
|
|
int inc_fixed = (int)(inc * FIXPT_SIZE);
|
|
while(buffer < end)
|
|
{
|
|
*buffer = gradient_pixel_fixed(gradient, t_fixed);
|
|
t_fixed += inc_fixed;
|
|
++buffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(buffer < end)
|
|
{
|
|
*buffer = gradient_pixel(gradient, t / 1024);
|
|
t += inc;
|
|
++buffer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void fetch_radial_gradient(uint32_t * buffer, struct cg_radial_gradient_values_t * v, struct cg_gradient_data_t * gradient, int y, int x, int length)
|
|
{
|
|
if(v->a == 0.0)
|
|
{
|
|
cg_memfill32(buffer, 0, length);
|
|
return;
|
|
}
|
|
|
|
double rx = gradient->matrix.c * (y + 0.5) + gradient->matrix.tx + gradient->matrix.a * (x + 0.5);
|
|
double ry = gradient->matrix.d * (y + 0.5) + gradient->matrix.ty + gradient->matrix.b * (x + 0.5);
|
|
rx -= gradient->radial.fx;
|
|
ry -= gradient->radial.fy;
|
|
|
|
double inv_a = 1.0 / (2.0 * v->a);
|
|
double delta_rx = gradient->matrix.a;
|
|
double delta_ry = gradient->matrix.b;
|
|
|
|
double b = 2 * (v->dr * gradient->radial.fr + rx * v->dx + ry * v->dy);
|
|
double delta_b = 2 * (delta_rx * v->dx + delta_ry * v->dy);
|
|
double b_delta_b = 2 * b * delta_b;
|
|
double delta_b_delta_b = 2 * delta_b * delta_b;
|
|
|
|
double bb = b * b;
|
|
double delta_bb = delta_b * delta_b;
|
|
|
|
b *= inv_a;
|
|
delta_b *= inv_a;
|
|
|
|
double rxrxryry = rx * rx + ry * ry;
|
|
double delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
|
|
double rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry);
|
|
double delta_rx_plus_ry = 2 * delta_rxrxryry;
|
|
|
|
inv_a *= inv_a;
|
|
|
|
double det = (bb - 4 * v->a * (v->sqrfr - rxrxryry)) * inv_a;
|
|
double delta_det = (b_delta_b + delta_bb + 4 * v->a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
|
|
double delta_delta_det = (delta_b_delta_b + 4 * v->a * delta_rx_plus_ry) * inv_a;
|
|
|
|
uint32_t * end = buffer + length;
|
|
if(v->extended)
|
|
{
|
|
while(buffer < end)
|
|
{
|
|
uint32_t result = 0;
|
|
det = fabs(det) < DBL_EPSILON ? 0.0 : det;
|
|
if(det >= 0)
|
|
{
|
|
double w = sqrt(det) - b;
|
|
if(gradient->radial.fr + v->dr * w >= 0)
|
|
result = gradient_pixel(gradient, w);
|
|
}
|
|
*buffer = result;
|
|
det += delta_det;
|
|
delta_det += delta_delta_det;
|
|
b += delta_b;
|
|
++buffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(buffer < end)
|
|
{
|
|
det = fabs(det) < DBL_EPSILON ? 0.0 : det;
|
|
uint32_t result = 0;
|
|
if(det >= 0)
|
|
result = gradient_pixel(gradient, sqrt(det) - b);
|
|
*buffer++ = result;
|
|
det += delta_det;
|
|
delta_det += delta_delta_det;
|
|
b += delta_b;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void __cg_comp_solid_source(uint32_t * dst, int len, uint32_t color, uint32_t alpha)
|
|
{
|
|
if(alpha == 255)
|
|
{
|
|
cg_memfill32(dst, color, len);
|
|
}
|
|
else
|
|
{
|
|
uint32_t ialpha = 255 - alpha;
|
|
color = CG_BYTE_MUL(color, alpha);
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = color + CG_BYTE_MUL(dst[i], ialpha);
|
|
}
|
|
}
|
|
extern __typeof(__cg_comp_solid_source) cg_comp_solid_source __attribute__((weak, alias("__cg_comp_solid_source")));
|
|
|
|
static void __cg_comp_solid_source_over(uint32_t * dst, int len, uint32_t color, uint32_t alpha)
|
|
{
|
|
if((alpha & CG_ALPHA(color)) == 255)
|
|
{
|
|
cg_memfill32(dst, color, len);
|
|
}
|
|
else
|
|
{
|
|
if(alpha != 255)
|
|
color = CG_BYTE_MUL(color, alpha);
|
|
uint32_t ialpha = 255 - CG_ALPHA(color);
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = color + CG_BYTE_MUL(dst[i], ialpha);
|
|
}
|
|
}
|
|
extern __typeof(__cg_comp_solid_source_over) cg_comp_solid_source_over __attribute__((weak, alias("__cg_comp_solid_source_over")));
|
|
|
|
static void __cg_comp_solid_destination_in(uint32_t * dst, int len, uint32_t color, uint32_t alpha)
|
|
{
|
|
uint32_t a = CG_ALPHA(color);
|
|
if(alpha != 255)
|
|
a = CG_BYTE_MUL(a, alpha) + 255 - alpha;
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = CG_BYTE_MUL(dst[i], a);
|
|
}
|
|
extern __typeof(__cg_comp_solid_destination_in) cg_comp_solid_destination_in __attribute__((weak, alias("__cg_comp_solid_destination_in")));
|
|
|
|
static void __cg_comp_solid_destination_out(uint32_t * dst, int len, uint32_t color, uint32_t alpha)
|
|
{
|
|
uint32_t a = CG_ALPHA(~color);
|
|
if(alpha != 255)
|
|
a = CG_BYTE_MUL(a, alpha) + 255 - alpha;
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = CG_BYTE_MUL(dst[i], a);
|
|
}
|
|
extern __typeof(__cg_comp_solid_destination_out) cg_comp_solid_destination_out __attribute__((weak, alias("__cg_comp_solid_destination_out")));
|
|
|
|
static void __cg_comp_source(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
|
|
{
|
|
if(alpha == 255)
|
|
{
|
|
memcpy(dst, src, (size_t)(len) * sizeof(uint32_t));
|
|
}
|
|
else
|
|
{
|
|
uint32_t ialpha = 255 - alpha;
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = interpolate_pixel(src[i], alpha, dst[i], ialpha);
|
|
}
|
|
}
|
|
extern __typeof(__cg_comp_source) cg_comp_source __attribute__((weak, alias("__cg_comp_source")));
|
|
|
|
static void __cg_comp_source_over(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
|
|
{
|
|
uint32_t s, sia;
|
|
if(alpha == 255)
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
s = src[i];
|
|
if(s >= 0xff000000)
|
|
dst[i] = s;
|
|
else if(s != 0)
|
|
{
|
|
sia = CG_ALPHA(~s);
|
|
dst[i] = s + CG_BYTE_MUL(dst[i], sia);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
s = CG_BYTE_MUL(src[i], alpha);
|
|
sia = CG_ALPHA(~s);
|
|
dst[i] = s + CG_BYTE_MUL(dst[i], sia);
|
|
}
|
|
}
|
|
}
|
|
extern __typeof(__cg_comp_source_over) cg_comp_source_over __attribute__((weak, alias("__cg_comp_source_over")));
|
|
|
|
static void __cg_comp_destination_in(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
|
|
{
|
|
if(alpha == 255)
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = CG_BYTE_MUL(dst[i], CG_ALPHA(src[i]));
|
|
}
|
|
else
|
|
{
|
|
uint32_t cia = 255 - alpha;
|
|
uint32_t a;
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
a = CG_BYTE_MUL(CG_ALPHA(src[i]), alpha) + cia;
|
|
dst[i] = CG_BYTE_MUL(dst[i], a);
|
|
}
|
|
}
|
|
}
|
|
extern __typeof(__cg_comp_destination_in) cg_comp_destination_in __attribute__((weak, alias("__cg_comp_destination_in")));
|
|
|
|
static void __cg_comp_destination_out(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
|
|
{
|
|
if(alpha == 255)
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
dst[i] = CG_BYTE_MUL(dst[i], CG_ALPHA(~src[i]));
|
|
}
|
|
else
|
|
{
|
|
uint32_t cia = 255 - alpha;
|
|
uint32_t sia;
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
sia = CG_BYTE_MUL(CG_ALPHA(~src[i]), alpha) + cia;
|
|
dst[i] = CG_BYTE_MUL(dst[i], sia);
|
|
}
|
|
}
|
|
}
|
|
extern __typeof(__cg_comp_destination_out) cg_comp_destination_out __attribute__((weak, alias("__cg_comp_destination_out")));
|
|
|
|
typedef void (*cg_comp_solid_function_t)(uint32_t * dst, int len, uint32_t color, uint32_t alpha);
|
|
static const cg_comp_solid_function_t cg_comp_solid_map[] = {
|
|
cg_comp_solid_source,
|
|
cg_comp_solid_source_over,
|
|
cg_comp_solid_destination_in,
|
|
cg_comp_solid_destination_out,
|
|
};
|
|
|
|
typedef void (*cg_comp_function_t)(uint32_t * dst, int len, uint32_t * src, uint32_t alpha);
|
|
static const cg_comp_function_t cg_comp_map[] = {
|
|
cg_comp_source,
|
|
cg_comp_source_over,
|
|
cg_comp_destination_in,
|
|
cg_comp_destination_out,
|
|
};
|
|
|
|
static inline void blend_solid(struct cg_surface_t * surface, enum cg_operator_t op, struct cg_rle_t * rle, uint32_t solid)
|
|
{
|
|
cg_comp_solid_function_t func = cg_comp_solid_map[op];
|
|
int count = rle->spans.size;
|
|
struct cg_span_t * spans = rle->spans.data;
|
|
while(count--)
|
|
{
|
|
uint32_t * target = (uint32_t *)(surface->pixels + spans->y * surface->stride) + spans->x;
|
|
func(target, spans->len, solid, spans->coverage);
|
|
++spans;
|
|
}
|
|
}
|
|
|
|
static inline void blend_linear_gradient(struct cg_surface_t * surface, enum cg_operator_t op, struct cg_rle_t * rle, struct cg_gradient_data_t * gradient)
|
|
{
|
|
cg_comp_function_t func = cg_comp_map[op];
|
|
unsigned int buffer[1024];
|
|
|
|
struct cg_linear_gradient_values_t v;
|
|
v.dx = gradient->linear.x2 - gradient->linear.x1;
|
|
v.dy = gradient->linear.y2 - gradient->linear.y1;
|
|
v.l = v.dx * v.dx + v.dy * v.dy;
|
|
v.off = 0.0;
|
|
if(v.l != 0.0)
|
|
{
|
|
v.dx /= v.l;
|
|
v.dy /= v.l;
|
|
v.off = -v.dx * gradient->linear.x1 - v.dy * gradient->linear.y1;
|
|
}
|
|
|
|
int count = rle->spans.size;
|
|
struct cg_span_t * spans = rle->spans.data;
|
|
while(count--)
|
|
{
|
|
int length = spans->len;
|
|
int x = spans->x;
|
|
while(length)
|
|
{
|
|
int l = CG_MIN(length, 1024);
|
|
fetch_linear_gradient(buffer, &v, gradient, spans->y, x, l);
|
|
uint32_t * target = (uint32_t *)(surface->pixels + spans->y * surface->stride) + x;
|
|
func(target, l, buffer, spans->coverage);
|
|
x += l;
|
|
length -= l;
|
|
}
|
|
++spans;
|
|
}
|
|
}
|
|
|
|
static inline void blend_radial_gradient(struct cg_surface_t * surface, enum cg_operator_t op, struct cg_rle_t * rle, struct cg_gradient_data_t * gradient)
|
|
{
|
|
cg_comp_function_t func = cg_comp_map[op];
|
|
unsigned int buffer[1024];
|
|
|
|
struct cg_radial_gradient_values_t v;
|
|
v.dx = gradient->radial.cx - gradient->radial.fx;
|
|
v.dy = gradient->radial.cy - gradient->radial.fy;
|
|
v.dr = gradient->radial.cr - gradient->radial.fr;
|
|
v.sqrfr = gradient->radial.fr * gradient->radial.fr;
|
|
v.a = v.dr * v.dr - v.dx * v.dx - v.dy * v.dy;
|
|
v.inv2a = 1.0 / (2.0 * v.a);
|
|
v.extended = gradient->radial.fr != 0.0 || v.a <= 0.0;
|
|
|
|
int count = rle->spans.size;
|
|
struct cg_span_t * spans = rle->spans.data;
|
|
while(count--)
|
|
{
|
|
int length = spans->len;
|
|
int x = spans->x;
|
|
while(length)
|
|
{
|
|
int l = CG_MIN(length, 1024);
|
|
fetch_radial_gradient(buffer, &v, gradient, spans->y, x, l);
|
|
uint32_t * target = (uint32_t *)(surface->pixels + spans->y * surface->stride) + x;
|
|
func(target, l, buffer, spans->coverage);
|
|
x += l;
|
|
length -= l;
|
|
}
|
|
++spans;
|
|
}
|
|
}
|
|
|
|
#define FIXED_SCALE (1 << 16)
|
|
static inline void blend_untransformed_argb(struct cg_surface_t * surface, enum cg_operator_t op, struct cg_rle_t * rle, struct cg_texture_data_t * texture)
|
|
{
|
|
cg_comp_function_t func = cg_comp_map[op];
|
|
|
|
int image_width = texture->width;
|
|
int image_height = texture->height;
|
|
int xoff = (int)(texture->matrix.tx);
|
|
int yoff = (int)(texture->matrix.ty);
|
|
int count = rle->spans.size;
|
|
struct cg_span_t * spans = rle->spans.data;
|
|
while(count--)
|
|
{
|
|
int x = spans->x;
|
|
int length = spans->len;
|
|
int sx = xoff + x;
|
|
int sy = yoff + spans->y;
|
|
if(sy >= 0 && sy < image_height && sx < image_width)
|
|
{
|
|
if(sx < 0)
|
|
{
|
|
x -= sx;
|
|
length += sx;
|
|
sx = 0;
|
|
}
|
|
if(sx + length > image_width)
|
|
length = image_width - sx;
|
|
if(length > 0)
|
|
{
|
|
int coverage = (spans->coverage * texture->alpha) >> 8;
|
|
uint32_t * src = (uint32_t *)(texture->pixels + sy * texture->stride) + sx;
|
|
uint32_t * dst = (uint32_t *)(surface->pixels + spans->y * surface->stride) + x;
|
|
func(dst, length, src, coverage);
|
|
}
|
|
}
|
|
++spans;
|
|
}
|
|
}
|
|
|
|
static inline void blend_transformed_argb(struct cg_surface_t * surface, enum cg_operator_t op, struct cg_rle_t * rle, struct cg_texture_data_t * texture)
|
|
{
|
|
cg_comp_function_t func = cg_comp_map[op];
|
|
uint32_t buffer[1024];
|
|
|
|
int image_width = texture->width;
|
|
int image_height = texture->height;
|
|
int fdx = (int)(texture->matrix.a * FIXED_SCALE);
|
|
int fdy = (int)(texture->matrix.b * FIXED_SCALE);
|
|
int count = rle->spans.size;
|
|
struct cg_span_t * spans = rle->spans.data;
|
|
while(count--)
|
|
{
|
|
uint32_t * target = (uint32_t *)(surface->pixels + spans->y * surface->stride) + spans->x;
|
|
double cx = spans->x + 0.5;
|
|
double cy = spans->y + 0.5;
|
|
int x = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.tx) * FIXED_SCALE);
|
|
int y = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.ty) * FIXED_SCALE);
|
|
int length = spans->len;
|
|
int coverage = (spans->coverage * texture->alpha) >> 8;
|
|
while(length)
|
|
{
|
|
int l = CG_MIN(length, 1024);
|
|
uint32_t * end = buffer + l;
|
|
uint32_t * b = buffer;
|
|
int start = 0;
|
|
int clen = 0;
|
|
while(b < end)
|
|
{
|
|
int px = x >> 16;
|
|
int py = y >> 16;
|
|
if(((unsigned int)px < (unsigned int)image_width) && ((unsigned int)py < (unsigned int)image_height))
|
|
{
|
|
*b = ((uint32_t *)(texture->pixels + py * texture->stride))[px];
|
|
clen++;
|
|
}
|
|
x += fdx;
|
|
y += fdy;
|
|
++b;
|
|
if(clen == 0)
|
|
start++;
|
|
}
|
|
func(target + start, clen, buffer + start, coverage);
|
|
target += l;
|
|
length -= l;
|
|
}
|
|
++spans;
|
|
}
|
|
}
|
|
|
|
static inline void blend_untransformed_tiled_argb(struct cg_surface_t * surface, enum cg_operator_t op, struct cg_rle_t * rle, struct cg_texture_data_t * texture)
|
|
{
|
|
cg_comp_function_t func = cg_comp_map[op];
|
|
|
|
int image_width = texture->width;
|
|
int image_height = texture->height;
|
|
int xoff = (int)(texture->matrix.tx) % image_width;
|
|
int yoff = (int)(texture->matrix.ty) % image_height;
|
|
if(xoff < 0)
|
|
xoff += image_width;
|
|
if(yoff < 0)
|
|
yoff += image_height;
|
|
int count = rle->spans.size;
|
|
struct cg_span_t * spans = rle->spans.data;
|
|
while(count--)
|
|
{
|
|
int x = spans->x;
|
|
int length = spans->len;
|
|
int sx = (xoff + spans->x) % image_width;
|
|
int sy = (spans->y + yoff) % image_height;
|
|
if(sx < 0)
|
|
sx += image_width;
|
|
if(sy < 0)
|
|
sy += image_height;
|
|
int coverage = (spans->coverage * texture->alpha) >> 8;
|
|
while(length)
|
|
{
|
|
int l = CG_MIN(image_width - sx, length);
|
|
if(1024 < l)
|
|
l = 1024;
|
|
uint32_t * src = (uint32_t *)(texture->pixels + sy * texture->stride) + sx;
|
|
uint32_t * dst = (uint32_t *)(surface->pixels + spans->y * surface->stride) + x;
|
|
func(dst, l, src, coverage);
|
|
x += l;
|
|
length -= l;
|
|
sx = 0;
|
|
}
|
|
++spans;
|
|
}
|
|
}
|
|
|
|
static inline void blend_transformed_tiled_argb(struct cg_surface_t * surface, enum cg_operator_t op, struct cg_rle_t * rle, struct cg_texture_data_t * texture)
|
|
{
|
|
cg_comp_function_t func = cg_comp_map[op];
|
|
uint32_t buffer[1024];
|
|
|
|
int image_width = texture->width;
|
|
int image_height = texture->height;
|
|
int scanline_offset = texture->stride / 4;
|
|
int fdx = (int)(texture->matrix.a * FIXED_SCALE);
|
|
int fdy = (int)(texture->matrix.b * FIXED_SCALE);
|
|
int count = rle->spans.size;
|
|
struct cg_span_t * spans = rle->spans.data;
|
|
while(count--)
|
|
{
|
|
uint32_t * target = (uint32_t *)(surface->pixels + spans->y * surface->stride) + spans->x;
|
|
uint32_t * image_bits = (uint32_t *)texture->pixels;
|
|
double cx = spans->x + 0.5;
|
|
double cy = spans->y + 0.5;
|
|
int x = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.tx) * FIXED_SCALE);
|
|
int y = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.ty) * FIXED_SCALE);
|
|
int coverage = (spans->coverage * texture->alpha) >> 8;
|
|
int length = spans->len;
|
|
while(length)
|
|
{
|
|
int l = CG_MIN(length, 1024);
|
|
uint32_t * end = buffer + l;
|
|
uint32_t * b = buffer;
|
|
int px16 = x % (image_width << 16);
|
|
int py16 = y % (image_height << 16);
|
|
int px_delta = fdx % (image_width << 16);
|
|
int py_delta = fdy % (image_height << 16);
|
|
while(b < end)
|
|
{
|
|
if(px16 < 0)
|
|
px16 += image_width << 16;
|
|
if(py16 < 0)
|
|
py16 += image_height << 16;
|
|
int px = px16 >> 16;
|
|
int py = py16 >> 16;
|
|
int y_offset = py * scanline_offset;
|
|
|
|
*b = image_bits[y_offset + px];
|
|
x += fdx;
|
|
y += fdy;
|
|
px16 += px_delta;
|
|
if(px16 >= image_width << 16)
|
|
px16 -= image_width << 16;
|
|
py16 += py_delta;
|
|
if(py16 >= image_height << 16)
|
|
py16 -= image_height << 16;
|
|
++b;
|
|
}
|
|
func(target, l, buffer, coverage);
|
|
target += l;
|
|
length -= l;
|
|
}
|
|
++spans;
|
|
}
|
|
}
|
|
|
|
static inline void cg_blend_color(struct cg_ctx_t * ctx, struct cg_rle_t * rle, struct cg_color_t * color)
|
|
{
|
|
if(color)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
uint32_t solid = premultiply_color(color, state->opacity);
|
|
if((CG_ALPHA(solid) == 255) && (state->op == CG_OPERATOR_SRC_OVER))
|
|
blend_solid(ctx->surface, CG_OPERATOR_SRC, rle, solid);
|
|
else
|
|
blend_solid(ctx->surface, state->op, rle, solid);
|
|
}
|
|
}
|
|
|
|
static inline void cg_blend_gradient(struct cg_ctx_t * ctx, struct cg_rle_t * rle, struct cg_gradient_t * gradient)
|
|
{
|
|
if(gradient && (gradient->stops.size > 0))
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
struct cg_gradient_data_t data;
|
|
int i, pos = 0, nstop = gradient->stops.size;
|
|
struct cg_gradient_stop_t *curr, *next, *start, *last;
|
|
uint32_t curr_color, next_color, last_color;
|
|
uint32_t dist, idist;
|
|
double delta, t, incr, fpos;
|
|
double opacity = state->opacity * gradient->opacity;
|
|
|
|
start = gradient->stops.data;
|
|
curr = start;
|
|
curr_color = combine_opacity(&curr->color, opacity);
|
|
|
|
data.colortable[pos] = premultiply_pixel(curr_color);
|
|
++pos;
|
|
incr = 1.0 / 1024;
|
|
fpos = 1.5 * incr;
|
|
|
|
while(fpos <= curr->offset)
|
|
{
|
|
data.colortable[pos] = data.colortable[pos - 1];
|
|
++pos;
|
|
fpos += incr;
|
|
}
|
|
for(i = 0; i < nstop - 1; i++)
|
|
{
|
|
curr = (start + i);
|
|
next = (start + i + 1);
|
|
delta = 1.0 / (next->offset - curr->offset);
|
|
next_color = combine_opacity(&next->color, opacity);
|
|
while(fpos < next->offset && pos < 1024)
|
|
{
|
|
t = (fpos - curr->offset) * delta;
|
|
dist = (uint32_t)(255 * t);
|
|
idist = 255 - dist;
|
|
data.colortable[pos] = premultiply_pixel(interpolate_pixel(curr_color, idist, next_color, dist));
|
|
++pos;
|
|
fpos += incr;
|
|
}
|
|
curr_color = next_color;
|
|
}
|
|
|
|
last = start + nstop - 1;
|
|
last_color = premultiply_color(&last->color, opacity);
|
|
for(; pos < 1024; ++pos)
|
|
data.colortable[pos] = last_color;
|
|
|
|
data.spread = gradient->spread;
|
|
data.matrix = gradient->matrix;
|
|
cg_matrix_multiply(&data.matrix, &data.matrix, &state->matrix);
|
|
cg_matrix_invert(&data.matrix);
|
|
|
|
if(gradient->type == CG_GRADIENT_TYPE_LINEAR)
|
|
{
|
|
data.linear.x1 = gradient->values[0];
|
|
data.linear.y1 = gradient->values[1];
|
|
data.linear.x2 = gradient->values[2];
|
|
data.linear.y2 = gradient->values[3];
|
|
blend_linear_gradient(ctx->surface, state->op, rle, &data);
|
|
}
|
|
else
|
|
{
|
|
data.radial.cx = gradient->values[0];
|
|
data.radial.cy = gradient->values[1];
|
|
data.radial.cr = gradient->values[2];
|
|
data.radial.fx = gradient->values[3];
|
|
data.radial.fy = gradient->values[4];
|
|
data.radial.fr = gradient->values[5];
|
|
blend_radial_gradient(ctx->surface, state->op, rle, &data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void cg_blend_texture(struct cg_ctx_t * ctx, struct cg_rle_t * rle, struct cg_texture_t * texture)
|
|
{
|
|
if(texture)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
struct cg_texture_data_t data;
|
|
data.width = texture->surface->width;
|
|
data.height = texture->surface->height;
|
|
data.stride = texture->surface->stride;
|
|
data.alpha = (int)(state->opacity * texture->opacity * 256.0);
|
|
data.pixels = texture->surface->pixels;
|
|
data.matrix = texture->matrix;
|
|
cg_matrix_multiply(&data.matrix, &data.matrix, &state->matrix);
|
|
cg_matrix_invert(&data.matrix);
|
|
struct cg_matrix_t * m = &data.matrix;
|
|
if((m->a == 1.0) && (m->b == 0.0) && (m->c == 0.0) && (m->d == 1.0))
|
|
{
|
|
if(texture->type == CG_TEXTURE_TYPE_PLAIN)
|
|
blend_untransformed_argb(ctx->surface, state->op, rle, &data);
|
|
else
|
|
blend_untransformed_tiled_argb(ctx->surface, state->op, rle, &data);
|
|
}
|
|
else
|
|
{
|
|
if(texture->type == CG_TEXTURE_TYPE_PLAIN)
|
|
blend_transformed_argb(ctx->surface, state->op, rle, &data);
|
|
else
|
|
blend_transformed_tiled_argb(ctx->surface, state->op, rle, &data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cg_blend(struct cg_ctx_t * ctx, struct cg_rle_t * rle)
|
|
{
|
|
if(rle && (rle->spans.size > 0))
|
|
{
|
|
struct cg_paint_t * source = &ctx->state->paint;
|
|
switch(source->type)
|
|
{
|
|
case CG_PAINT_TYPE_COLOR:
|
|
cg_blend_color(ctx, rle, &source->color);
|
|
break;
|
|
case CG_PAINT_TYPE_GRADIENT:
|
|
cg_blend_gradient(ctx, rle, &source->gradient);
|
|
break;
|
|
case CG_PAINT_TYPE_TEXTURE:
|
|
cg_blend_texture(ctx, rle, &source->texture);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct cg_state_t * cg_state_create(void)
|
|
{
|
|
struct cg_state_t * state = malloc(sizeof(struct cg_state_t));
|
|
state->clippath = NULL;
|
|
cg_paint_init(&state->paint);
|
|
cg_matrix_init_identity(&state->matrix);
|
|
state->winding = CG_FILL_RULE_NON_ZERO;
|
|
state->stroke.width = 1.0;
|
|
state->stroke.miterlimit = 10.0;
|
|
state->stroke.cap = CG_LINE_CAP_BUTT;
|
|
state->stroke.join = CG_LINE_JOIN_MITER;
|
|
state->stroke.dash = NULL;
|
|
state->op = CG_OPERATOR_SRC_OVER;
|
|
state->opacity = 1.0;
|
|
state->next = NULL;
|
|
return state;
|
|
}
|
|
|
|
static struct cg_state_t * cg_state_clone(struct cg_state_t * state)
|
|
{
|
|
struct cg_state_t * newstate = cg_state_create();
|
|
newstate->clippath = cg_rle_clone(state->clippath);
|
|
cg_paint_copy(&newstate->paint, &state->paint);
|
|
newstate->matrix = state->matrix;
|
|
newstate->winding = state->winding;
|
|
newstate->stroke.width = state->stroke.width;
|
|
newstate->stroke.miterlimit = state->stroke.miterlimit;
|
|
newstate->stroke.cap = state->stroke.cap;
|
|
newstate->stroke.join = state->stroke.join;
|
|
newstate->stroke.dash = cg_dash_clone(state->stroke.dash);
|
|
newstate->op = state->op;
|
|
newstate->opacity = state->opacity;
|
|
newstate->next = NULL;
|
|
return newstate;
|
|
}
|
|
|
|
static void cg_state_destroy(struct cg_state_t * state)
|
|
{
|
|
cg_rle_destroy(state->clippath);
|
|
cg_paint_destroy(&state->paint);
|
|
cg_dash_destroy(state->stroke.dash);
|
|
free(state);
|
|
}
|
|
|
|
struct cg_ctx_t * cg_create(struct cg_surface_t * surface)
|
|
{
|
|
struct cg_ctx_t * ctx = malloc(sizeof(struct cg_ctx_t));
|
|
ctx->surface = cg_surface_reference(surface);
|
|
ctx->state = cg_state_create();
|
|
ctx->path = cg_path_create();
|
|
ctx->rle = cg_rle_create();
|
|
ctx->clippath = NULL;
|
|
ctx->clip.x = 0.0;
|
|
ctx->clip.y = 0.0;
|
|
ctx->clip.w = surface->width;
|
|
ctx->clip.h = surface->height;
|
|
ctx->outline_data = NULL;
|
|
ctx->outline_size = 0;
|
|
return ctx;
|
|
}
|
|
|
|
void cg_destroy(struct cg_ctx_t * ctx)
|
|
{
|
|
if(ctx)
|
|
{
|
|
while(ctx->state)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
ctx->state = state->next;
|
|
cg_state_destroy(state);
|
|
}
|
|
cg_surface_destroy(ctx->surface);
|
|
cg_path_destroy(ctx->path);
|
|
cg_rle_destroy(ctx->rle);
|
|
cg_rle_destroy(ctx->clippath);
|
|
if(ctx->outline_data)
|
|
free(ctx->outline_data);
|
|
free(ctx);
|
|
}
|
|
}
|
|
|
|
void cg_save(struct cg_ctx_t * ctx)
|
|
{
|
|
struct cg_state_t * state = cg_state_clone(ctx->state);
|
|
state->next = ctx->state;
|
|
ctx->state = state;
|
|
}
|
|
|
|
void cg_restore(struct cg_ctx_t * ctx)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
ctx->state = state->next;
|
|
cg_state_destroy(state);
|
|
}
|
|
|
|
struct cg_color_t * cg_set_source_rgb(struct cg_ctx_t * ctx, double r, double g, double b)
|
|
{
|
|
return cg_set_source_rgba(ctx, r, g, b, 1.0);
|
|
}
|
|
|
|
struct cg_color_t * cg_set_source_rgba(struct cg_ctx_t * ctx, double r, double g, double b, double a)
|
|
{
|
|
struct cg_paint_t * paint = &ctx->state->paint;
|
|
paint->type = CG_PAINT_TYPE_COLOR;
|
|
cg_color_init_rgba(&paint->color, r, g, b, a);
|
|
return &paint->color;
|
|
}
|
|
|
|
struct cg_color_t * cg_set_source_color(struct cg_ctx_t * ctx, struct cg_color_t * color)
|
|
{
|
|
return cg_set_source_rgba(ctx, color->r, color->g, color->b, color->a);
|
|
}
|
|
|
|
struct cg_gradient_t * cg_set_source_linear_gradient(struct cg_ctx_t * ctx, double x1, double y1, double x2, double y2)
|
|
{
|
|
struct cg_paint_t * paint = &ctx->state->paint;
|
|
paint->type = CG_PAINT_TYPE_GRADIENT;
|
|
cg_gradient_init_linear(&paint->gradient, x1, y1, x2, y2);
|
|
return &paint->gradient;
|
|
}
|
|
|
|
struct cg_gradient_t * cg_set_source_radial_gradient(struct cg_ctx_t * ctx, double cx, double cy, double cr, double fx, double fy, double fr)
|
|
{
|
|
struct cg_paint_t * paint = &ctx->state->paint;
|
|
paint->type = CG_PAINT_TYPE_GRADIENT;
|
|
cg_gradient_init_radial(&paint->gradient, cx, cy, cr, fx, fy, fr);
|
|
return &paint->gradient;
|
|
}
|
|
|
|
static inline struct cg_texture_t * cg_set_texture(struct cg_ctx_t *ctx, struct cg_surface_t * surface, enum cg_texture_type_t type)
|
|
{
|
|
struct cg_paint_t * paint = &ctx->state->paint;
|
|
paint->type = CG_PAINT_TYPE_TEXTURE;
|
|
cg_texture_init(&paint->texture, surface, type);
|
|
return &paint->texture;
|
|
}
|
|
|
|
struct cg_texture_t * cg_set_source_surface(struct cg_ctx_t * ctx, struct cg_surface_t * surface, double x, double y)
|
|
{
|
|
struct cg_texture_t * texture = cg_set_texture(ctx, surface, CG_TEXTURE_TYPE_PLAIN);
|
|
cg_matrix_init_translate(&texture->matrix, x, y);
|
|
return texture;
|
|
}
|
|
|
|
void cg_set_operator(struct cg_ctx_t * ctx, enum cg_operator_t op)
|
|
{
|
|
ctx->state->op = op;
|
|
}
|
|
|
|
void cg_set_opacity(struct cg_ctx_t * ctx, double opacity)
|
|
{
|
|
ctx->state->opacity = CG_CLAMP(opacity, 0.0, 1.0);
|
|
}
|
|
|
|
void cg_set_fill_rule(struct cg_ctx_t * ctx, enum cg_fill_rule_t winding)
|
|
{
|
|
ctx->state->winding = winding;
|
|
}
|
|
|
|
void cg_set_line_width(struct cg_ctx_t * ctx, double width)
|
|
{
|
|
ctx->state->stroke.width = width;
|
|
}
|
|
|
|
void cg_set_line_cap(struct cg_ctx_t * ctx, enum cg_line_cap_t cap)
|
|
{
|
|
ctx->state->stroke.cap = cap;
|
|
}
|
|
|
|
void cg_set_line_join(struct cg_ctx_t * ctx, enum cg_line_join_t join)
|
|
{
|
|
ctx->state->stroke.join = join;
|
|
}
|
|
|
|
void cg_set_miter_limit(struct cg_ctx_t * ctx, double limit)
|
|
{
|
|
ctx->state->stroke.miterlimit = limit;
|
|
}
|
|
|
|
void cg_set_dash(struct cg_ctx_t * ctx, double * dashes, int ndash, double offset)
|
|
{
|
|
cg_dash_destroy(ctx->state->stroke.dash);
|
|
ctx->state->stroke.dash = cg_dash_create(dashes, ndash, offset);
|
|
}
|
|
|
|
void cg_translate(struct cg_ctx_t * ctx, double tx, double ty)
|
|
{
|
|
cg_matrix_translate(&ctx->state->matrix, tx, ty);
|
|
}
|
|
|
|
void cg_scale(struct cg_ctx_t * ctx, double sx, double sy)
|
|
{
|
|
cg_matrix_scale(&ctx->state->matrix, sx, sy);
|
|
}
|
|
|
|
void cg_rotate(struct cg_ctx_t * ctx, double r)
|
|
{
|
|
cg_matrix_rotate(&ctx->state->matrix, r);
|
|
}
|
|
|
|
void cg_transform(struct cg_ctx_t * ctx, struct cg_matrix_t * m)
|
|
{
|
|
cg_matrix_multiply(&ctx->state->matrix, m, &ctx->state->matrix);
|
|
}
|
|
|
|
void cg_set_matrix(struct cg_ctx_t * ctx, struct cg_matrix_t * m)
|
|
{
|
|
memcpy(&ctx->state->matrix, m, sizeof(struct cg_matrix_t));
|
|
}
|
|
|
|
void cg_identity_matrix(struct cg_ctx_t * ctx)
|
|
{
|
|
cg_matrix_init_identity(&ctx->state->matrix);
|
|
}
|
|
|
|
void cg_move_to(struct cg_ctx_t * ctx, double x, double y)
|
|
{
|
|
cg_path_move_to(ctx->path, x, y);
|
|
}
|
|
|
|
void cg_line_to(struct cg_ctx_t * ctx, double x, double y)
|
|
{
|
|
cg_path_line_to(ctx->path, x, y);
|
|
}
|
|
|
|
void cg_curve_to(struct cg_ctx_t * ctx, double x1, double y1, double x2, double y2, double x3, double y3)
|
|
{
|
|
cg_path_curve_to(ctx->path, x1, y1, x2, y2, x3, y3);
|
|
}
|
|
|
|
void cg_quad_to(struct cg_ctx_t * ctx, double x1, double y1, double x2, double y2)
|
|
{
|
|
cg_path_quad_to(ctx->path, x1, y1, x2, y2);
|
|
}
|
|
|
|
void cg_rel_move_to(struct cg_ctx_t * ctx, double dx, double dy)
|
|
{
|
|
cg_path_rel_move_to(ctx->path, dx, dy);
|
|
}
|
|
|
|
void cg_rel_line_to(struct cg_ctx_t * ctx, double dx, double dy)
|
|
{
|
|
cg_path_rel_line_to(ctx->path, dx, dy);
|
|
}
|
|
|
|
void cg_rel_curve_to(struct cg_ctx_t * ctx, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3)
|
|
{
|
|
cg_path_rel_curve_to(ctx->path, dx1, dy1, dx2, dy2, dx3, dy3);
|
|
}
|
|
|
|
void cg_rel_quad_to(struct cg_ctx_t * ctx, double dx1, double dy1, double dx2, double dy2)
|
|
{
|
|
cg_path_rel_quad_to(ctx->path, dx1, dy1, dx2, dy2);
|
|
}
|
|
|
|
void cg_rectangle(struct cg_ctx_t * ctx, double x, double y, double w, double h)
|
|
{
|
|
cg_path_add_rectangle(ctx->path, x, y, w, h);
|
|
}
|
|
|
|
void cg_round_rectangle(struct cg_ctx_t * ctx, double x, double y, double w, double h, double rx, double ry)
|
|
{
|
|
cg_path_add_round_rectangle(ctx->path, x, y, w, h, rx, ry);
|
|
}
|
|
|
|
void cg_ellipse(struct cg_ctx_t * ctx, double cx, double cy, double rx, double ry)
|
|
{
|
|
cg_path_add_ellipse(ctx->path, cx, cy, rx, ry);
|
|
}
|
|
|
|
void cg_circle(struct cg_ctx_t * ctx, double cx, double cy, double r)
|
|
{
|
|
cg_path_add_ellipse(ctx->path, cx, cy, r, r);
|
|
}
|
|
|
|
void cg_arc(struct cg_ctx_t * ctx, double cx, double cy, double r, double a0, double a1)
|
|
{
|
|
cg_path_add_arc(ctx->path, cx, cy, r, a0, a1, 0);
|
|
}
|
|
|
|
void cg_arc_negative(struct cg_ctx_t * ctx, double cx, double cy, double r, double a0, double a1)
|
|
{
|
|
cg_path_add_arc(ctx->path, cx, cy, r, a0, a1, 1);
|
|
}
|
|
|
|
void cg_new_path(struct cg_ctx_t * ctx)
|
|
{
|
|
cg_path_clear(ctx->path);
|
|
}
|
|
|
|
void cg_close_path(struct cg_ctx_t * ctx)
|
|
{
|
|
cg_path_close(ctx->path);
|
|
}
|
|
|
|
void cg_reset_clip(struct cg_ctx_t * ctx)
|
|
{
|
|
cg_rle_destroy(ctx->state->clippath);
|
|
ctx->state->clippath = NULL;
|
|
}
|
|
|
|
void cg_clip(struct cg_ctx_t * ctx)
|
|
{
|
|
cg_clip_preserve(ctx);
|
|
cg_new_path(ctx);
|
|
}
|
|
|
|
void cg_clip_preserve(struct cg_ctx_t * ctx)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
if(state->clippath)
|
|
{
|
|
cg_rle_clear(ctx->rle);
|
|
cg_rle_rasterize(ctx, ctx->rle, ctx->path, &state->matrix, &ctx->clip, NULL, state->winding);
|
|
cg_rle_clip_path(state->clippath, ctx->rle);
|
|
}
|
|
else
|
|
{
|
|
state->clippath = cg_rle_create();
|
|
cg_rle_rasterize(ctx, state->clippath, ctx->path, &state->matrix, &ctx->clip, NULL, state->winding);
|
|
}
|
|
}
|
|
|
|
void cg_fill(struct cg_ctx_t * ctx)
|
|
{
|
|
cg_fill_preserve(ctx);
|
|
cg_new_path(ctx);
|
|
}
|
|
|
|
void cg_fill_preserve(struct cg_ctx_t * ctx)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
cg_rle_clear(ctx->rle);
|
|
cg_rle_rasterize(ctx, ctx->rle, ctx->path, &state->matrix, &ctx->clip, NULL, state->winding);
|
|
cg_rle_clip_path(ctx->rle, state->clippath);
|
|
cg_blend(ctx, ctx->rle);
|
|
}
|
|
|
|
void cg_stroke(struct cg_ctx_t * ctx)
|
|
{
|
|
cg_stroke_preserve(ctx);
|
|
cg_new_path(ctx);
|
|
}
|
|
|
|
void cg_stroke_preserve(struct cg_ctx_t * ctx)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
cg_rle_clear(ctx->rle);
|
|
cg_rle_rasterize(ctx, ctx->rle, ctx->path, &state->matrix, &ctx->clip, &state->stroke, CG_FILL_RULE_NON_ZERO);
|
|
cg_rle_clip_path(ctx->rle, state->clippath);
|
|
cg_blend(ctx, ctx->rle);
|
|
}
|
|
|
|
void cg_paint(struct cg_ctx_t * ctx)
|
|
{
|
|
struct cg_state_t * state = ctx->state;
|
|
if((state->clippath == NULL) && (ctx->clippath == NULL))
|
|
{
|
|
struct cg_path_t * path = cg_path_create();
|
|
cg_path_add_rectangle(path, ctx->clip.x, ctx->clip.y, ctx->clip.w, ctx->clip.h);
|
|
struct cg_matrix_t m;
|
|
cg_matrix_init_identity(&m);
|
|
ctx->clippath = cg_rle_create();
|
|
cg_rle_rasterize(ctx, ctx->clippath, path, &m, &ctx->clip, NULL, CG_FILL_RULE_NON_ZERO);
|
|
cg_path_destroy(path);
|
|
}
|
|
struct cg_rle_t * rle = state->clippath ? state->clippath : ctx->clippath;
|
|
cg_blend(ctx, rle);
|
|
}
|