/* * 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); }