From fd0148ec213dccd8e612a3c8d4276b5a1f1f0782 Mon Sep 17 00:00:00 2001 From: Hans-Joerg Schurr Date: Sun, 22 Mar 2020 17:12:39 +0100 Subject: [PATCH] Add parser for path - Adds parser for path - Fix bug: read wrong number of bytes for matrix - Fix bug: checked wrong number of bytes read by fread in many places - Rename _read macros to _decode --- src/hvif-light.c | 220 +++++++++++++++++++++++++++++++++++++++++------ src/hvif-light.h | 3 +- 2 files changed, 198 insertions(+), 25 deletions(-) diff --git a/src/hvif-light.c b/src/hvif-light.c index 2e45f95..09448ce 100644 --- a/src/hvif-light.c +++ b/src/hvif-light.c @@ -80,22 +80,32 @@ struct hvif_matrix }; /* TODO: This is most likely wrong */ -#define ID_TRANSFORMATION \ +#define MATRIX_ID \ (hvif_matrix) { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0 } -#define hvif_read(V, BUFFER) \ +typedef struct hvif_point hvif_point; +struct hvif_point +{ + float x; + float y; +}; + +#define POINT_ORIGIN \ + (hvif_point) { 0.0, 0.0 } + +#define hvif_decode(V, BUFFER) \ V = _Generic((V), uint32_t \ - : hvif_read_uint32, float \ - : hvif_read_float, hvif_matrix \ - : hvif_read_hvif_matrix)(BUFFER) + : hvif_decode_uint32, float \ + : hvif_decode_float, hvif_matrix \ + : hvif_decode_hvif_matrix)(BUFFER) static inline uint32_t -hvif_read_uint32(char buffer[static 1]) +hvif_decode_uint32(char buffer[static 1]) { return (buffer[0] << 0) | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); } static inline float -hvif_read_float(char buffer[static 1]) +hvif_decode_float(char buffer[static 1]) { int shortValue = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; int sign = (shortValue & 0x800000) >> 23; @@ -110,12 +120,13 @@ hvif_read_float(char buffer[static 1]) } } static inline hvif_matrix -hvif_read_hvif_matrix(char buffer[static 1]) +hvif_decode_hvif_matrix(char buffer[static 1]) { - return ( - hvif_matrix){ hvif_read_float(buffer), hvif_read_float(&buffer[3]), - hvif_read_float(&buffer[6]), hvif_read_float(&buffer[9]), - hvif_read_float(&buffer[12]), hvif_read_float(&buffer[15]) }; + return (hvif_matrix){ + hvif_decode_float(buffer), hvif_decode_float(&buffer[3]), + hvif_decode_float(&buffer[6]), hvif_decode_float(&buffer[9]), + hvif_decode_float(&buffer[12]), hvif_decode_float(&buffer[15]) + }; } typedef uint32_t hvif_color; /* 8bit each: alpha, blue, green, red */ @@ -135,18 +146,37 @@ typedef struct hvif_style hvif_style; struct hvif_style { signed gradient_type; - uint8_t stops; + uint8_t num_stops; hvif_color* colors; float* offsets; hvif_matrix transformation; }; +typedef struct hvif_path hvif_path; +struct hvif_path +{ + uint8_t num_points; + bool closed; + uint8_t* commands; + hvif_point* points; +}; + struct hvif_image { uint8_t num_styles; hvif_style* styles; + uint8_t num_paths; + hvif_path* paths; }; +uint8_t +path_command_at(hvif_path* path, uint8_t index) +{ + uint8_t byte = path->commands[index / 4]; + uint8_t byte_idx = index % 4; + return (byte >> (2 * byte_idx)) & 0x03; +} + hvif_color decode_color(char* buffer, bool gray, bool alpha) { @@ -163,7 +193,7 @@ decode_color(char* buffer, bool gray, bool alpha) COLOR_SET_RGB(c, buffer[0], buffer[0], buffer[0]); COLOR_SET_ALPHA(c, buffer[1]); } else { - hvif_read(c, buffer); + hvif_decode(c, buffer); } } else { COLOR_SET_ALPHA(c, 0xff); @@ -180,6 +210,7 @@ hvif_status read_color_style(hvif_style* style, char* buffer, bool gray, bool alpha) { hvif_color color = decode_color(buffer, gray, alpha); + style->num_stops = 1; style->colors = calloc(1, sizeof(hvif_color)); style->offsets = calloc(1, sizeof(float)); if (!style->colors || !style->colors) return ERROR_NOMEM; @@ -210,16 +241,19 @@ read_gradient_style(hvif_style* style, FILE* file, char* style_buffer) if (gradient_flags & GRADIENT_FLAG_TRANSFORM) { /* The transformation matrix is 6 hvif-floats: 18 bytes */ char buffer[18]; - if (fread(buffer, 1, 2, file) != 2) { return ERROR_EOF; } - hvif_read(transformation, buffer); + if (fread(buffer, 1, 18, file) != 18) { return ERROR_EOF; } + hvif_decode(transformation, buffer); } else { - transformation = ID_TRANSFORMATION; + transformation = MATRIX_ID; } style->transformation = transformation; + style->num_stops = gradient_stops; style->offsets = calloc(gradient_stops, sizeof(float)); style->colors = calloc(gradient_stops, sizeof(hvif_color)); + if (!style->offsets || !style->colors) return ERROR_NOMEM; + char buffer[4]; for (unsigned i = 0; i < gradient_stops; ++i) { if (fread(buffer, 1, 1, file) != 1) { return ERROR_EOF; } @@ -230,7 +264,7 @@ read_gradient_style(hvif_style* style, FILE* file, char* style_buffer) if (fread(buffer, 1, 2, file) != 2) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } else { - if (fread(buffer, 1, 4, file) != 1) { return ERROR_EOF; } + if (fread(buffer, 1, 4, file) != 4) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } } else { @@ -238,7 +272,7 @@ read_gradient_style(hvif_style* style, FILE* file, char* style_buffer) if (fread(buffer, 1, 1, file) != 1) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } else { - if (fread(buffer, 1, 3, file) != 1) { return ERROR_EOF; } + if (fread(buffer, 1, 3, file) != 3) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } } @@ -253,7 +287,7 @@ hvif_status read_style(FILE* file, hvif_style* style) { uint8_t type; - if (fread(&type, 1, 1, file) != 1) { return ERROR_STYLE; } + if (fread(&type, 1, 1, file) != 1) { return ERROR_EOF; } char buffer[4]; switch (type) { case STYLE_TYPE_SOLID_COLOR: @@ -281,6 +315,124 @@ read_style(FILE* file, hvif_style* style) return SUCCESS; } +hvif_status +read_coordinate(FILE* file, float* coord) +{ + uint8_t high_value; + if (fread(&high_value, 1, 1, file) != 1) { return ERROR_EOF; } + + /* If the highest bit is set, the coordinate uses two bytes */ + if (high_value & 128) { + high_value &= 127; + + uint8_t low_value; + if (fread(&low_value, 1, 1, file) != 1) { return ERROR_EOF; } + + uint16_t coord_value = (high_value << 8) | low_value; + *coord = ((float)coord_value) / 102.0 - 128.0; + } else { + *coord = ((float)high_value) - 32.0; + } + return SUCCESS; +} + +hvif_status +read_path_points(FILE* file, hvif_path* path) +{ + /* Every path point is three points: the proper point, in point, out point */ + path->points = calloc(3 * path->num_points, sizeof(hvif_point)); + if (!path->points) return ERROR_NOMEM; + + hvif_point last = POINT_ORIGIN; + hvif_point point; + hvif_point point_in; + hvif_point point_out; + hvif_status status; + + for (unsigned i = 0; i < path->num_points; i += 3) { + switch (path_command_at(path, i)) { + case PATH_COMMAND_H_LINE: + point = last; + status = read_coordinate(file, &point.x); + if (status != SUCCESS) return status; + point_in = point_out = point; + break; + case PATH_COMMAND_V_LINE: + point = last; + status = read_coordinate(file, &point.x); + if (status != SUCCESS) return status; + point_in = point_out = point; + break; + case PATH_COMMAND_LINE: + status = read_coordinate(file, &point.x); + if (status != SUCCESS) return status; + status = read_coordinate(file, &point.y); + if (status != SUCCESS) return status; + point_in = point_out = point; + break; + case PATH_COMMAND_CURVE: + status = read_coordinate(file, &point.x); + if (status != SUCCESS) return status; + status = read_coordinate(file, &point.y); + if (status != SUCCESS) return status; + point_in = point_out = point; + status = read_coordinate(file, &point_in.x); + if (status != SUCCESS) return status; + status = read_coordinate(file, &point_in.y); + if (status != SUCCESS) return status; + point_in = point_out = point; + status = read_coordinate(file, &point_out.x); + if (status != SUCCESS) return status; + status = read_coordinate(file, &point_out.y); + if (status != SUCCESS) return status; + point_in = point_out = point; + break; + } + path->points[i] = point; + path->points[i + 1] = point_in; + path->points[i + 2] = point_out; + last = point; + } + return SUCCESS; +} + +static inline void +create_static_commands( + uint8_t num_command_bytes, uint8_t commands[static 1], const uint8_t cmd) +{ + for (unsigned i = 0; i < num_command_bytes; ++i) commands[i] = cmd; +} + +hvif_status +read_path(FILE* file, hvif_path* path) +{ + uint8_t flags; + if (fread(&flags, 1, 1, file) != 1) { return ERROR_PATH; } + if (fread(&path->num_points, 1, 1, file) != 1) { return ERROR_PATH; } + + path->closed = flags & PATH_FLAG_CLOSED; + uint8_t num_command_bytes = (path->num_points + 3) / 4; + path->commands = calloc(num_command_bytes, 1); + if (!path->commands) return ERROR_NOMEM; + + if ((flags & PATH_FLAG_USES_COMMANDS) && (flags & PATH_FLAG_NO_CURVES)) { + return ERROR_PATH; + } + if (flags & PATH_FLAG_USES_COMMANDS) { + if (fread(&flags, 1, num_command_bytes, file) != num_command_bytes) { + return ERROR_PATH; + } + } else if (flags & PATH_FLAG_NO_CURVES) { + create_static_commands( + num_command_bytes, path->commands, 0xFF); /* 0b11111111: all curves */ + } else { /* All lines */ + create_static_commands( + num_command_bytes, path->commands, 0xAA); /* 0b10101010: all lines */ + } + + return read_path_points(file, path); +} + hvif_result hvif_from_file(FILE* file) { @@ -289,18 +441,17 @@ hvif_from_file(FILE* file) char magic_buffer[4]; if (fread(magic_buffer, 1, 4, file) != 4) { return ERROR_RESULT(ERROR_EOF); } uint32_t read_magic; - hvif_read(read_magic, magic_buffer); + hvif_decode(read_magic, magic_buffer); if (read_magic != magic) { return ERROR_RESULT(ERROR_MAGIC); } hvif_image* image = calloc(1, sizeof(hvif_image)); if (!image) return ERROR_RESULT(ERROR_NOMEM); - uint8_t num_styles; - if (fread(&num_styles, 1, 1, file) != 1) { + if (fread(&image->num_styles, 1, 1, file) != 1) { hvif_free(image); return ERROR_RESULT(ERROR_EOF); } - image->styles = calloc(num_styles, sizeof(hvif_style)); + image->styles = calloc(image->num_styles, sizeof(hvif_style)); if (!image->styles) { hvif_free(image); return ERROR_RESULT(ERROR_NOMEM); @@ -313,6 +464,23 @@ hvif_from_file(FILE* file) } } + if (fread(&image->num_paths, 1, 1, file) != 1) { + hvif_free(image); + return ERROR_RESULT(ERROR_EOF); + } + image->paths = calloc(image->num_paths, sizeof(hvif_path)); + if (!image->paths) { + hvif_free(image); + return ERROR_RESULT(ERROR_NOMEM); + } + for (unsigned i = 0; i < image->num_paths; ++i) { + hvif_status s = read_path(file, &image->paths[i]); + if (s != SUCCESS) { + hvif_free(image); + return ERROR_RESULT(s); + } + } + return SUCCESS_RESULT(image); } @@ -323,6 +491,10 @@ hvif_free(hvif_image* image) free(image->styles[i].colors); free(image->styles[i].offsets); } + for (unsigned i = 0; i < image->num_paths; ++i) { + free(image->paths[i].commands); + free(image->paths[i].points); + } free(image->styles); free(image); } diff --git a/src/hvif-light.h b/src/hvif-light.h index dc97537..1bb635f 100644 --- a/src/hvif-light.h +++ b/src/hvif-light.h @@ -10,7 +10,8 @@ typedef enum hvif_status ERROR_EOF, ERROR_NOMEM, ERROR_MAGIC, - ERROR_STYLE + ERROR_STYLE, + ERROR_PATH } hvif_status; typedef struct hvif_image hvif_image;