From 76afd0b90373019fa537671eeb96dbd09551abc4 Mon Sep 17 00:00:00 2001 From: Hans-Joerg Schurr Date: Mon, 27 Apr 2020 19:54:27 +0200 Subject: [PATCH] Implement basic cairo rendering loop --- src/hvif-cairo.c | 220 ++++++++++++++++++++++++++++++----------------- src/hvif-light.c | 3 +- 2 files changed, 142 insertions(+), 81 deletions(-) diff --git a/src/hvif-cairo.c b/src/hvif-cairo.c index 17d1ccb..bd8d4b1 100644 --- a/src/hvif-cairo.c +++ b/src/hvif-cairo.c @@ -15,16 +15,10 @@ #endif /* TODO: - * 1. Draw the path in each shape as outline - * 2. Then do the same for the styles - * 3. Put everything together - */ - -/* TODO: Gradients - * - Check the various forms - * - How is the gradient oriented in the picture - * - Apply transformation matrix * - Color space + * - Contour + * - LOD + * - Check miter limit */ void @@ -37,22 +31,10 @@ cairo_matrix_init_from_matrix( } void -create_path(cairo_t* cr, hvif_path* path) +print_matrix(const hvif_matrix* matrix) { - assert(path->num_points > 0); - - for (unsigned i = 0; i < path->num_points; ++i) { - hvif_point p = path->points[3 * i]; - hvif_point cp1 = path->points[3 * i + 1]; - hvif_point cp2 = path->points[3 * i + 2]; - uint8_t command = path_command_at(path, i); - if (command == PATH_COMMAND_CURVE) - cairo_curve_to(cr, cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y); - else - cairo_line_to(cr, p.x, p.y); - } - - if (path->closed) cairo_close_path(cr); + printf("x_new = %f * x + %f * y + %f\n", matrix->xx, matrix->xy, matrix->x0); + printf("y_new = %f * x + %f * y + %f\n", matrix->yx, matrix->yy, matrix->y0); } #define R(c) (COLOR_GET_RED(c) / 255.0) @@ -124,7 +106,6 @@ create_conic_patch( cos2, sin2); cairo_mesh_pattern_line_to(pat, 0, 0); cairo_mesh_pattern_set_corner_color_rgba(pat, 0, R(c1), G(c1), B(c1), A(c1)); - //cairo_mesh_pattern_set_corner_color_rgb(pat, 0, 0, 0, 0); cairo_mesh_pattern_set_corner_color_rgba(pat, 1, R(c1), G(c1), B(c1), A(c1)); cairo_mesh_pattern_set_corner_color_rgba(pat, 2, R(c2), G(c2), B(c2), A(c2)); cairo_mesh_pattern_set_corner_color_rgba(pat, 3, R(c2), G(c2), B(c2), A(c2)); @@ -132,10 +113,9 @@ create_conic_patch( } void -create_style(cairo_t* cr, hvif_style* style) +create_pattern_style(cairo_pattern_t** pat, hvif_style* style) { assert(style->num_stops > 0); - cairo_pattern_t* pat; double width = 64.0; double height = 64.0; @@ -146,69 +126,66 @@ create_style(cairo_t* cr, hvif_style* style) switch (style->gradient_type) { case GRADIENT_LINEAR: - printf("GRADIENT_LINEAR\n"); - pat = cairo_pattern_create_linear(-64, 32, 64, 32); + *pat = cairo_pattern_create_linear(-64, 32, 64, 32); for (unsigned i = 0; i < style->num_stops; ++i) { hvif_color c = style->colors[i]; cairo_pattern_add_color_stop_rgba( - pat, style->offsets[i], R(c), G(c), B(c), A(c)); + *pat, style->offsets[i], R(c), G(c), B(c), A(c)); } break; case GRADIENT_CIRCULAR: - printf("GRADIENT_CIRCULAR\n"); - pat = cairo_pattern_create_radial(0, 0, 0, 0, 0, 64); + *pat = cairo_pattern_create_radial(0, 0, 0, 0, 0, 64); for (unsigned i = 0; i < style->num_stops; ++i) { hvif_color c = style->colors[i]; cairo_pattern_add_color_stop_rgba( - pat, style->offsets[i], R(c), G(c), B(c), A(c)); + *pat, style->offsets[i], R(c), G(c), B(c), A(c)); } break; case GRADIENT_DIAMOND: - printf("GRADIENT_DIAMOND\n"); - pat = cairo_pattern_create_mesh(); + *pat = cairo_pattern_create_mesh(); if (style->offsets[0] != 0.0) { create_diamond_patch( - pat, 0, 64.0 * style->offsets[0], style->colors[0], style->colors[0]); + *pat, 0, 64.0 * style->offsets[0], style->colors[0], + style->colors[0]); } for (unsigned i = 0; i + 1 < style->num_stops; ++i) { double s1 = style->offsets[i] * 64.0; double s2 = style->offsets[i + 1] * 64.0; hvif_color c1 = style->colors[i]; hvif_color c2 = style->colors[i + 1]; - create_diamond_patch(pat, s1, s2, c1, c2); + create_diamond_patch(*pat, s1, s2, c1, c2); } unsigned i = style->num_stops - 1; create_diamond_patch( - pat, 64.0 * style->offsets[i], extremum, style->colors[i], + *pat, 64.0 * style->offsets[i], extremum, style->colors[i], style->colors[i]); break; case GRADIENT_CONIC: - printf("GRADIENT_CONIC\n"); - pat = cairo_pattern_create_mesh(); + *pat = cairo_pattern_create_mesh(); if (style->offsets[0] != 0.0) { create_conic_patch( - pat, extremum, 0, M_PI * style->offsets[0], style->colors[0], + *pat, extremum, 0, M_PI * style->offsets[0], style->colors[0], style->colors[0]); create_conic_patch( - pat, extremum, 0, -M_PI * style->offsets[0], style->colors[0], + *pat, extremum, 0, -M_PI * style->offsets[0], style->colors[0], style->colors[0]); } for (unsigned i = 0; i + 1 < style->num_stops; ++i) { create_conic_patch( - pat, extremum, M_PI * style->offsets[i], M_PI * style->offsets[i + 1], - style->colors[i], style->colors[i + 1]); + *pat, extremum, M_PI * style->offsets[i], + M_PI * style->offsets[i + 1], style->colors[i], style->colors[i + 1]); create_conic_patch( - pat, extremum, -M_PI * style->offsets[i], + *pat, extremum, -M_PI * style->offsets[i], -M_PI * style->offsets[i + 1], style->colors[i], style->colors[i + 1]); } i = style->num_stops - 1; if (style->offsets[i] != 1.0) { create_conic_patch( - pat, extremum, M_PI * style->offsets[i], 0, style->colors[i], + *pat, extremum, M_PI * style->offsets[i], 0, style->colors[i], style->colors[i]); create_conic_patch( - pat, extremum, -M_PI * style->offsets[i], 0, style->colors[i], + *pat, extremum, -M_PI * style->offsets[i], 0, style->colors[i], style->colors[i]); } break; @@ -220,55 +197,138 @@ create_style(cairo_t* cr, hvif_style* style) if (cairo_matrix_invert(&transformation) != CAIRO_STATUS_SUCCESS) { printf("could not invert style transformation\n"); exit(1); - } - cairo_pattern_set_matrix(pat, &transformation); + } else + cairo_pattern_set_matrix(*pat, &transformation); +} - cairo_rectangle(cr, 0, 0, 64, 64); - cairo_set_source(cr, pat); - cairo_fill(cr); - cairo_pattern_destroy(pat); +void +create_path(cairo_t* cr, cairo_path_t** path, hvif_path* hvif_path) +{ + assert(hvif_path->num_points > 0); + + cairo_new_path(cr); + cairo_move_to(cr, hvif_path->points[0].x, hvif_path->points[0].y); + for (unsigned i = 1; i < hvif_path->num_points; ++i) { + hvif_point cp1 = hvif_path->points[3 * i - 1]; + hvif_point cp2 = hvif_path->points[3 * i + 1]; + hvif_point p = hvif_path->points[3 * i]; + cairo_curve_to(cr, cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y); + } + + if (hvif_path->closed) { + unsigned max = hvif_path->num_points - 1; + hvif_point cp1 = hvif_path->points[3 * max + 2]; + hvif_point cp2 = hvif_path->points[1]; + hvif_point p = hvif_path->points[0]; + cairo_curve_to(cr, cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y); + } + cairo_close_path(cr); + *path = cairo_copy_path_flat(cr); +} + +void +render_shape( + cairo_t* cr, hvif_shape* shape, cairo_pattern_t** pattern, + cairo_path_t** path) +{ + cairo_matrix_t old_transform; + cairo_get_matrix(cr, &old_transform); + cairo_matrix_t shape_transform; + cairo_matrix_init_from_matrix(&shape_transform, &shape->transformation); + cairo_transform(cr, &shape_transform); + + int stroke_idx = -1; + for (unsigned i = 0; i < shape->num_transformers; ++i) { + hvif_transformer* t = &shape->transformers[i]; + switch (t->transformer_type) { + case TRANSFORMER_TYPE_STROKE: + if (stroke_idx != -1) + printf("unsupported: multiple stroke transform\n"); + stroke_idx = i; + break; + case TRANSFORMER_TYPE_AFFINE: + /* TODO: this is a guess what this does, but it's not exposed in + Icon-o-Matic. */ + printf("warning: affine transform\n"); + cairo_matrix_init_from_matrix( + &shape_transform, &t->transformer.affine.matrix); + break; + default: printf("unsupported: transform\n"); + } + } + + cairo_new_path(cr); + for (unsigned i = 0; i < shape->num_paths; ++i) { + cairo_append_path(cr, path[shape->path_idxs[i]]); + cairo_new_sub_path(cr); + } + cairo_set_source(cr, pattern[shape->style_idx]); + + if (stroke_idx >= 0) { + hvif_transformer* t = &shape->transformers[stroke_idx]; + cairo_set_line_width(cr, t->transformer.stroke.width); + switch (t->transformer.stroke.line_join) { + case (MITER_JOIN): cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); break; + case (ROUND_CAP): cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); break; + case (BEVEL_JOIN): cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL); break; + default: printf("unsupported: line join\n"); + } + cairo_set_miter_limit(cr, t->transformer.stroke.miter_limit); + switch (t->transformer.stroke.line_cap) { + case (BUTT_CAP): cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); break; + case (SQUARE_CAP): cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE); break; + case (ROUND_CAP): cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); break; + default: printf("illegal line cap"); exit(1); + } + cairo_stroke(cr); + } else + cairo_fill(cr); + + cairo_set_matrix(cr, &old_transform); + + if (shape->hinting) printf("unsupported: hinting\n"); + /* TODO: implement LOD */ } bool hvif_render_image(const char* filename, hvif_image* image) { + unsigned image_size = 512; + cairo_surface_t* surface = - /* cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 254, 254); */ - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 64, 64); + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, image_size, image_size); cairo_t* cr = cairo_create(surface); - /* cairo_scale(cr, 4.0, 4.0); */ - cairo_scale(cr, 1.0, 1.0); + cairo_scale(cr, image_size / 64.0, image_size / 64.0); - cairo_set_line_width(cr, 0.2); + cairo_pattern_t** patterns = + malloc(sizeof(cairo_pattern_t*) * image->num_styles); + if (!patterns) return false; + cairo_path_t** paths = malloc(sizeof(cairo_path_t*) * image->num_paths); + if (!paths) return false; - double r = 0; - double delta = 1.0 / image->num_shapes; - - printf("styles %u\n", image->num_styles); - create_style(cr, &image->styles[0]); - - /* - for (unsigned i = 0; i < image->num_shapes; ++i) { - cairo_set_source_rgb(cr, r, 0, 0); - r = r + delta; - hvif_shape s = image->shapes[i]; - if (s.hinting) - printf("shape %u has hinting\n", i); - else - printf("shape %u has no hinting\n", i); - for (unsigned j = 0; j < s.num_paths; ++j) { - hvif_path* p = &image->paths[s.path_idxs[j]]; - create_path(cr, p); - cairo_stroke(cr); - } + for (unsigned i = 0; i < image->num_styles; ++i) { + create_pattern_style(&patterns[i], &image->styles[i]); + /* TODO: error handling */ + } + for (unsigned i = 0; i < image->num_paths; ++i) { + create_path(cr, &paths[i], &image->paths[i]); + /* TODO: error handling */ } - */ - cairo_destroy(cr); + for (unsigned i = 0; i < image->num_shapes; ++i) { + render_shape(cr, &image->shapes[i], patterns, paths); + } + for (unsigned i = 0; i < image->num_paths; ++i) { + cairo_path_destroy(paths[i]); + } + for (unsigned i = 0; i < image->num_styles; ++i) { + cairo_pattern_destroy(patterns[i]); + } bool result = true; if (cairo_surface_write_to_png(surface, filename) != CAIRO_STATUS_SUCCESS) result = false; cairo_surface_destroy(surface); + cairo_destroy(cr); return result; } diff --git a/src/hvif-light.c b/src/hvif-light.c index d126890..5b752b5 100644 --- a/src/hvif-light.c +++ b/src/hvif-light.c @@ -156,7 +156,7 @@ struct hvif_transformer /* perspective is empty */ struct { - uint8_t width; + double width; hvif_line_join line_join; hvif_line_cap line_cap; double miter_limit; @@ -304,6 +304,7 @@ read_color_style(hvif_style* style, uint8_t* buffer, bool gray, bool alpha) style->colors[0] = color; style->offsets[0] = 1.0; /* TODO: Is this ok for solid colors */ + style->transformation = MATRIX_ID; return SUCCESS; }