#include "plutovg.h" #include "plutovg-utils.h" void plutovg_matrix_init(plutovg_matrix_t* matrix, float a, float b, float c, float d, float e, float f) { matrix->a = a; matrix->b = b; matrix->c = c; matrix->d = d; matrix->e = e; matrix->f = f; } void plutovg_matrix_init_identity(plutovg_matrix_t* matrix) { matrix->a = 1; matrix->b = 0; matrix->c = 0; matrix->d = 1; matrix->e = 0; matrix->f = 0; } void plutovg_matrix_init_translate(plutovg_matrix_t* matrix, float tx, float ty) { plutovg_matrix_init(matrix, 1, 0, 0, 1, tx, ty); } void plutovg_matrix_init_scale(plutovg_matrix_t* matrix, float sx, float sy) { plutovg_matrix_init(matrix, sx, 0, 0, sy, 0, 0); } void plutovg_matrix_init_rotate(plutovg_matrix_t* matrix, float angle) { float c = cosf(angle); float s = sinf(angle); plutovg_matrix_init(matrix, c, s, -s, c, 0, 0); } void plutovg_matrix_init_shear(plutovg_matrix_t* matrix, float shx, float shy) { plutovg_matrix_init(matrix, 1, tanf(shy), tanf(shx), 1, 0, 0); } void plutovg_matrix_translate(plutovg_matrix_t* matrix, float tx, float ty) { plutovg_matrix_t m; plutovg_matrix_init_translate(&m, tx, ty); plutovg_matrix_multiply(matrix, &m, matrix); } void plutovg_matrix_scale(plutovg_matrix_t* matrix, float sx, float sy) { plutovg_matrix_t m; plutovg_matrix_init_scale(&m, sx, sy); plutovg_matrix_multiply(matrix, &m, matrix); } void plutovg_matrix_rotate(plutovg_matrix_t* matrix, float angle) { plutovg_matrix_t m; plutovg_matrix_init_rotate(&m, angle); plutovg_matrix_multiply(matrix, &m, matrix); } void plutovg_matrix_shear(plutovg_matrix_t* matrix, float shx, float shy) { plutovg_matrix_t m; plutovg_matrix_init_shear(&m, shx, shy); plutovg_matrix_multiply(matrix, &m, matrix); } void plutovg_matrix_multiply(plutovg_matrix_t* matrix, const plutovg_matrix_t* left, const plutovg_matrix_t* right) { float a = left->a * right->a + left->b * right->c; float b = left->a * right->b + left->b * right->d; float c = left->c * right->a + left->d * right->c; float d = left->c * right->b + left->d * right->d; float e = left->e * right->a + left->f * right->c + right->e; float f = left->e * right->b + left->f * right->d + right->f; plutovg_matrix_init(matrix, a, b, c, d, e, f); } bool plutovg_matrix_invert(const plutovg_matrix_t* matrix, plutovg_matrix_t* inverse) { float det = (matrix->a * matrix->d - matrix->b * matrix->c); if(det == 0.f) return false; if(inverse) { float inv_det = 1.f / det; float a = matrix->a * inv_det; float b = matrix->b * inv_det; float c = matrix->c * inv_det; float d = matrix->d * inv_det; float e = (matrix->c * matrix->f - matrix->d * matrix->e) * inv_det; float f = (matrix->b * matrix->e - matrix->a * matrix->f) * inv_det; plutovg_matrix_init(inverse, d, -b, -c, a, e, f); } return true; } void plutovg_matrix_map(const plutovg_matrix_t* matrix, float x, float y, float* xx, float* yy) { *xx = x * matrix->a + y * matrix->c + matrix->e; *yy = x * matrix->b + y * matrix->d + matrix->f; } void plutovg_matrix_map_point(const plutovg_matrix_t* matrix, const plutovg_point_t* src, plutovg_point_t* dst) { plutovg_matrix_map(matrix, src->x, src->y, &dst->x, &dst->y); } void plutovg_matrix_map_points(const plutovg_matrix_t* matrix, const plutovg_point_t* src, plutovg_point_t* dst, int count) { for(int i = 0; i < count; ++i) { plutovg_matrix_map_point(matrix, &src[i], &dst[i]); } } void plutovg_matrix_map_rect(const plutovg_matrix_t* matrix, const plutovg_rect_t* src, plutovg_rect_t* dst) { plutovg_point_t p[4]; p[0].x = src->x; p[0].y = src->y; p[1].x = src->x + src->w; p[1].y = src->y; p[2].x = src->x + src->w; p[2].y = src->y + src->h; p[3].x = src->x; p[3].y = src->y + src->h; plutovg_matrix_map_points(matrix, p, p, 4); float l = p[0].x; float t = p[0].y; float r = p[0].x; float b = p[0].y; for(int i = 1; i < 4; i++) { if(p[i].x < l) l = p[i].x; if(p[i].x > r) r = p[i].x; if(p[i].y < t) t = p[i].y; if(p[i].y > b) b = p[i].y; } dst->x = l; dst->y = t; dst->w = r - l; dst->h = b - t; } static int parse_matrix_parameters(const char** begin, const char* end, float values[6], int required, int optional) { if(!plutovg_skip_ws_and_delim(begin, end, '(')) return 0; int count = 0; int max_count = required + optional; bool has_trailing_comma = false; for(; count < max_count; ++count) { if(!plutovg_parse_number(begin, end, values + count)) break; plutovg_skip_ws_or_comma(begin, end, &has_trailing_comma); } if(!has_trailing_comma && (count == required || count == max_count) && plutovg_skip_delim(begin, end, ')')) { return count; } return 0; } bool plutovg_matrix_parse(plutovg_matrix_t* matrix, const char* data, int length) { float values[6]; plutovg_matrix_init_identity(matrix); if(length == -1) length = strlen(data); const char* it = data; const char* end = it + length; bool has_trailing_comma = false; plutovg_skip_ws(&it, end); while(it < end) { if(plutovg_skip_string(&it, end, "matrix")) { int count = parse_matrix_parameters(&it, end, values, 6, 0); if(count == 0) return false; plutovg_matrix_t m = { values[0], values[1], values[2], values[3], values[4], values[5] }; plutovg_matrix_multiply(matrix, &m, matrix); } else if(plutovg_skip_string(&it, end, "translate")) { int count = parse_matrix_parameters(&it, end, values, 1, 1); if(count == 0) return false; if(count == 1) { plutovg_matrix_translate(matrix, values[0], 0); } else { plutovg_matrix_translate(matrix, values[0], values[1]); } } else if(plutovg_skip_string(&it, end, "scale")) { int count = parse_matrix_parameters(&it, end, values, 1, 1); if(count == 0) return false; if(count == 1) { plutovg_matrix_scale(matrix, values[0], values[0]); } else { plutovg_matrix_scale(matrix, values[0], values[1]); } } else if(plutovg_skip_string(&it, end, "rotate")) { int count = parse_matrix_parameters(&it, end, values, 1, 2); if(count == 0) return false; if(count == 3) plutovg_matrix_translate(matrix, values[1], values[2]); plutovg_matrix_rotate(matrix, PLUTOVG_DEG2RAD(values[0])); if(count == 3) { plutovg_matrix_translate(matrix, -values[1], -values[2]); } } else if(plutovg_skip_string(&it, end, "skewX")) { int count = parse_matrix_parameters(&it, end, values, 1, 0); if(count == 0) return false; plutovg_matrix_shear(matrix, PLUTOVG_DEG2RAD(values[0]), 0); } else if(plutovg_skip_string(&it, end, "skewY")) { int count = parse_matrix_parameters(&it, end, values, 1, 0); if(count == 0) return false; plutovg_matrix_shear(matrix, 0, PLUTOVG_DEG2RAD(values[0])); } else { return false; } plutovg_skip_ws_or_comma(&it, end, &has_trailing_comma); } return !has_trailing_comma; }