From 6486b516db939527fe5e830f7c4a9860cf92162a Mon Sep 17 00:00:00 2001 From: Hans-Joerg Schurr Date: Tue, 25 Feb 2020 21:10:42 +0100 Subject: [PATCH] Implement parsing of colors for styles - Change error reporting format - Add color type and helper macros - Add parser for all styles that are solid colors --- src/hvif-light.c | 186 ++++++++++++++++++++++++++++++++++++++++++++--- src/hvif-light.h | 17 +++-- src/main.c | 6 +- 3 files changed, 189 insertions(+), 20 deletions(-) diff --git a/src/hvif-light.c b/src/hvif-light.c index 1f74b25..1c5b5dc 100644 --- a/src/hvif-light.c +++ b/src/hvif-light.c @@ -3,7 +3,58 @@ #include #include -char const* const eof_error = "File ended prematurely."; +enum +{ + STYLE_TYPE_SOLID_COLOR = 1, + STYLE_TYPE_GRADIENT = 2, + STYLE_TYPE_SOLID_COLOR_NO_ALPHA = 3, + STYLE_TYPE_SOLID_GRAY = 4, + STYLE_TYPE_SOLID_GRAY_NO_ALPHA = 5, + + SHAPE_TYPE_PATH_SOURCE = 10, + + TRANSFORMER_TYPE_AFFINE = 20, + TRANSFORMER_TYPE_CONTOUR = 21, + TRANSFORMER_TYPE_PERSPECTIVE = 22, + TRANSFORMER_TYPE_STROKE = 23, +}; + +enum +{ + GRADIENT_FLAG_TRANSFORM = 1 << 1, + GRADIENT_FLAG_NO_ALPHA = 1 << 2, + GRADIENT_FLAG_16_BIT_COLORS = 1 << 3, /* not yet used */ + GRADIENT_FLAG_GRAYS = 1 << 4, +}; + +enum +{ + PATH_FLAG_CLOSED = 1 << 1, + PATH_FLAG_USES_COMMANDS = 1 << 2, + PATH_FLAG_NO_CURVES = 1 << 3, +}; + +enum +{ + PATH_COMMAND_H_LINE = 0, + PATH_COMMAND_V_LINE = 1, + PATH_COMMAND_LINE = 2, + PATH_COMMAND_CURVE = 3, +}; + +enum +{ + SHAPE_FLAG_TRANSFORM = 1 << 1, + SHAPE_FLAG_HINTING = 1 << 2, + SHAPE_FLAG_LOD_SCALE = 1 << 3, + SHAPE_FLAG_HAS_TRANSFORMERS = 1 << 4, + SHAPE_FLAG_TRANSLATION = 1 << 5, +}; + +#define ERROR_RESULT(E) \ + (hvif_result) { .status = E } +#define SUCCESS_RESULT(I) \ + (hvif_result) { .status = SUCCESS, .image = I } #define le_read(V, BUFFER) V = _Generic((V), uint32_t : le_read_uint32)(BUFFER) static inline uint32_t @@ -13,11 +64,104 @@ le_read_uint32(char buffer[static 1]) (buffer[3] << 24); } -#define ERROR_RESULT(M) \ - (hvif_result) { .success = false, .error = M } +typedef uint32_t hvif_color; /* 8bit each: alpha, blue, green, red */ +#define COLOR_GET_RED(C) (C & 0xff) +#define COLOR_GET_GREEN(C) ((C >> 8) & 0xff) +#define COLOR_GET_BLUE(C) ((C >> 16) & 0xff) +#define COLOR_GET_ALPHA(C) ((C >> 24) & 0xff) +#define _BM8(N) (0xff << N) +#define COLOR_SET_RED(C, V) (C = ((C & (~_BM8(0))) | (V & 0xff))) +#define COLOR_SET_GREEN(C, V) (C = ((C & (~_BM8(8))) | ((V & 0xff) << 8))) +#define COLOR_SET_BLUE(C, V) (C = ((C & (~_BM8(16))) | ((V & 0xff) << 16))) +#define COLOR_SET_ALPHA(C, V) (C = ((C & (~_BM8(24))) | ((V & 0xff) << 24))) +#define COLOR_SET_RGB(C, R, G, B) \ + (C = (C & (~0xffffff)) | (R & 0xff) | ((G & 0xff) << 8) | ((B & 0xff) << 16)) + +typedef struct hvif_style hvif_style; +struct hvif_style +{ + signed gradient_type; + uint8_t stops; + hvif_color* colors; + float* offsets; +}; struct hvif_image -{}; +{ + uint8_t num_styles; + hvif_style* styles; +}; + +hvif_color +read_color(char* buffer, bool gray, bool alpha) +{ + /* On disk a color is: + red, green, blue, alpha + Hence reading this as a (little-endian) uint32 result in the desired + format. If `alpha` is `false` then only three bytes are used and alpha + is `0xff`. If gray is `true` only one one byte is used for the three + colors. + */ + hvif_color c = 0; + if (alpha) { + if (gray) { + COLOR_SET_RGB(c, buffer[0], buffer[0], buffer[0]); + COLOR_SET_ALPHA(c, buffer[1]); + } else { + le_read(c, buffer); + } + } else { + COLOR_SET_ALPHA(c, 0xff); + if (gray) { + COLOR_SET_RGB(c, buffer[0], buffer[0], buffer[0]); + } else { + COLOR_SET_RGB(c, buffer[0], buffer[1], buffer[2]); + } + } + return c; +} + +hvif_status +read_color_style(hvif_style* style, char* buffer, bool gray, bool alpha) +{ + hvif_color color = read_color(buffer, gray, alpha); + style->colors = calloc(1, sizeof(hvif_color)); + style->offsets = calloc(1, sizeof(float)); + if (!style->colors || !style->colors) return ERROR_NOMEM; + + style->colors[0] = color; + style->offsets[0] = 1.0; /* TODO: Is this ok for solid colors */ + return SUCCESS; +} + +hvif_status +read_style(FILE* file, hvif_style* style) +{ + uint8_t type; + if (fread(&type, 1, 1, file) != 1) { return ERROR_STYLE; } + char buffer[4]; + switch (type) { + case STYLE_TYPE_SOLID_COLOR: + if (fread(buffer, 1, 4, file) != 4) { return ERROR_EOF; } + read_color_style(style, buffer, false, true); + break; + case STYLE_TYPE_GRADIENT: break; + case STYLE_TYPE_SOLID_COLOR_NO_ALPHA: + if (fread(buffer, 1, 3, file) != 3) { return ERROR_EOF; } + read_color_style(style, buffer, false, false); + break; + case STYLE_TYPE_SOLID_GRAY: + if (fread(buffer, 1, 2, file) != 2) { return ERROR_EOF; } + read_color_style(style, buffer, true, true); + break; + case STYLE_TYPE_SOLID_GRAY_NO_ALPHA: + if (fread(buffer, 1, 1, file) != 1) { return ERROR_EOF; } + read_color_style(style, buffer, true, false); + break; + default: return ERROR_STYLE; + } + return SUCCESS; +} hvif_result hvif_from_file(FILE* file) @@ -25,20 +169,42 @@ hvif_from_file(FILE* file) uint32_t const magic = 0x6669636e; /* 'finc' (on disk little endian 'cnif') */ char magic_buffer[4]; - if (fread(magic_buffer, 1, 4, file) != 4) { return ERROR_RESULT(eof_error); } + if (fread(magic_buffer, 1, 4, file) != 4) { return ERROR_RESULT(ERROR_EOF); } uint32_t read_magic; le_read(read_magic, magic_buffer); - if (read_magic != magic) { - printf("%04x %04x\n", magic, read_magic); - return ERROR_RESULT("Wrong magic number."); + 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) { + hvif_free(image); + return ERROR_RESULT(ERROR_EOF); + } + image->styles = calloc(num_styles, sizeof(hvif_style)); + if (!image->styles) { + hvif_free(image); + return ERROR_RESULT(ERROR_NOMEM); + } + for (unsigned i = 0; i < image->num_styles; ++i) { + hvif_status s = read_style(file, &image->styles[i]); + if (s != SUCCESS) { + hvif_free(image); + return ERROR_RESULT(s); + } } - hvif_image* image = malloc(sizeof(hvif_image)); - return (hvif_result){ .success = true, .image = image }; + return SUCCESS_RESULT(image); } void hvif_free(hvif_image* image) { + for (unsigned i = 0; i < image->num_styles; ++i) { + free(image->styles[i].colors); + free(image->styles[i].offsets); + } + free(image->styles); free(image); } diff --git a/src/hvif-light.h b/src/hvif-light.h index d919561..dc97537 100644 --- a/src/hvif-light.h +++ b/src/hvif-light.h @@ -4,17 +4,22 @@ #include #include +typedef enum hvif_status +{ + SUCCESS = 0, + ERROR_EOF, + ERROR_NOMEM, + ERROR_MAGIC, + ERROR_STYLE +} hvif_status; + typedef struct hvif_image hvif_image; typedef struct hvif_result hvif_result; struct hvif_result { - bool success; - union - { - char const* const error; - hvif_image* image; - }; + hvif_status status; + hvif_image* image; }; hvif_result hvif_from_file(FILE* file); diff --git a/src/main.c b/src/main.c index 69c2cf2..cccc66e 100644 --- a/src/main.c +++ b/src/main.c @@ -9,17 +9,15 @@ main(void) { puts("This is " PACKAGE_STRING "."); - FILE* imagefile = fopen("test.hvif", "r"); + FILE* imagefile = fopen("test.hvif", "rb"); if (!imagefile) { fputs("fopen failed.\n", stderr); return EXIT_FAILURE; } hvif_result result = hvif_from_file(imagefile); - if (!result.success) { + if (result.status != SUCCESS) { fputs("Reading image failed.\n", stderr); - fputs(result.error, stderr); - fputc('\n', stderr); return EXIT_FAILURE; } puts("Reading image succeeded.");