|
|
|
@ -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); |
|
|
|
|
} else |
|
|
|
|
cairo_pattern_set_matrix(*pat, &transformation); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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_pattern_set_matrix(pat, &transformation); |
|
|
|
|
|
|
|
|
|
cairo_rectangle(cr, 0, 0, 64, 64); |
|
|
|
|
cairo_set_source(cr, pat); |
|
|
|
|
cairo_fill(cr); |
|
|
|
|
cairo_pattern_destroy(pat); |
|
|
|
|
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_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 */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
render_shape(cr, &image->shapes[i], patterns, paths); |
|
|
|
|
} |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
cairo_destroy(cr); |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|