#ifndef INTERNAL_DATASTRUCTURES #include "hvif-light.h" #include #include #include #ifdef DEBUG void debug_print_bytes(FILE* file) { long p = ftell(file); uint8_t buffer[5]; unsigned l = fread(buffer, 1, 5, file); printf("..."); for (unsigned i = 0; i < l; ++i) printf(" %02x", buffer[i]); printf("\n"); fseek(file, p, SEEK_SET); } #endif #endif /* INTERNAL_DATASTRUCTURES */ 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, }; 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 MATRIX_ID \ (hvif_matrix) { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } #define MATRIX_TRANSLATION(P) \ (hvif_matrix) { 1.0, 0.0, 0.0, 1.0, P.x, P.y } #define POINT_ORIGIN \ (hvif_point) { 0.0, 0.0 } typedef struct hvif_transformer hvif_transformer; struct hvif_transformer { hvif_transformer_type transformer_type; union { struct { hvif_matrix matrix; } affine; struct { double width; hvif_line_join line_join; double miter_limit; } contour; /* perspective is empty */ struct { double width; hvif_line_join line_join; hvif_line_cap line_cap; double miter_limit; } stroke; } transformer; }; /* Core components of the format */ typedef struct hvif_style hvif_style; struct hvif_style { hvif_gradients_type gradient_type; 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; }; typedef struct hvif_shape hvif_shape; struct hvif_shape { uint8_t style_idx; uint8_t num_paths; uint8_t* path_idxs; bool hinting; hvif_matrix transformation; float min_visibility; float max_visibility; uint8_t num_transformers; hvif_transformer* transformers; }; struct hvif_image { uint8_t num_styles; hvif_style* styles; uint8_t num_paths; hvif_path* paths; uint8_t num_shapes; hvif_shape* shapes; }; static inline 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; } #ifndef INTERNAL_DATASTRUCTURES #define hvif_decode(V, BUFFER) \ V = _Generic((V), uint32_t \ : hvif_decode_uint32, float \ : hvif_decode_float, hvif_matrix \ : hvif_decode_hvif_matrix)(BUFFER) static inline uint32_t hvif_decode_uint32(uint8_t buffer[static 1]) { return (buffer[0] << 0) | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); } static inline float hvif_decode_float(uint8_t buffer[static 1]) { int shortValue = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; int sign = (shortValue & 0x800000) >> 23; int exponent = ((shortValue & 0x7e0000) >> 17) - 32; int mantissa = (shortValue & 0x01ffff) << 6; if (shortValue == 0) return 0.0; else { /* TODO: float might not be IEEE 754 compatible */ union { uint32_t uint_f; float float_f; } value; value.uint_f = (sign << 31) | ((exponent + 127) << 23) | mantissa; return value.float_f; } } static inline hvif_matrix hvif_decode_hvif_matrix(uint8_t buffer[static 1]) { 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]) }; } #define COLOR_SET_RGB(C, R, G, B) \ do { \ C.red = R; \ C.green = G; \ C.blue = B; \ } while (0) hvif_color decode_color(uint8_t* 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 = (hvif_color){ 0 }; if (alpha) { if (gray) { COLOR_SET_RGB(c, buffer[0], buffer[0], buffer[0]); c.alpha = buffer[1]; } else { c.red = buffer[0]; c.green = buffer[1]; c.blue = buffer[2]; c.alpha = buffer[3]; } } else { c.alpha = 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, uint8_t* buffer, bool gray, bool alpha) { #ifdef DEBUG printf("%s: start.\n", __func__); #endif 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; style->colors[0] = color; style->offsets[0] = 1.0; /* TODO: Is this ok for solid colors? */ style->transformation = MATRIX_ID; return SUCCESS; } hvif_status read_gradient_style(hvif_style* style, FILE* file, uint8_t* style_buffer) { #ifdef DEBUG printf("%s: start.\n", __func__); #endif uint8_t gradient_type = style_buffer[0]; uint8_t gradient_flags = style_buffer[1]; uint8_t gradient_stops = style_buffer[2]; if (gradient_type >= HVIF_GRADIENT_TYPE_MAX) return ERROR_STYLE; style->gradient_type = gradient_type; bool gray = gradient_flags & GRADIENT_FLAG_GRAYS; bool alpha = !(gradient_flags & GRADIENT_FLAG_NO_ALPHA); /* structure: * if transformable [matrix] * then number of stops entries of the form: [stop][color] */ hvif_matrix transformation; if (gradient_flags & GRADIENT_FLAG_TRANSFORM) { #ifdef DEBUG printf("%s: read transfomation matrix.\n", __func__); #endif /* The transformation matrix is 6 hvif-floats: 18 bytes */ uint8_t buffer[18]; if (fread(buffer, 1, 18, file) != 18) { return ERROR_EOF; } hvif_decode(transformation, buffer); } else { 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; #ifdef DEBUG printf("%s: read %u stops.\n", __func__, gradient_stops); #endif uint8_t buffer[4]; for (unsigned i = 0; i < gradient_stops; ++i) { if (fread(buffer, 1, 1, file) != 1) { return ERROR_EOF; } float offset = buffer[0] / 255.0; hvif_color c; if (alpha) { if (gray) { if (fread(buffer, 1, 2, file) != 2) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } else { if (fread(buffer, 1, 4, file) != 4) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } } else { if (gray) { if (fread(buffer, 1, 1, file) != 1) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } else { if (fread(buffer, 1, 3, file) != 3) { return ERROR_EOF; } c = decode_color(buffer, gray, alpha); } } style->offsets[i] = offset; style->colors[i] = c; } return SUCCESS; } hvif_status read_style(FILE* file, hvif_style* style) { uint8_t type; if (fread(&type, 1, 1, file) != 1) { return ERROR_EOF; } uint8_t 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: if (fread(buffer, 1, 3, file) != 3) { return ERROR_EOF; } read_gradient_style(style, file, buffer); 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: #ifdef DEBUG printf("%s: error unkown style type %u.\n", __func__, type); #endif return ERROR_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) { #ifdef DEBUG printf("%s: read path points.\n", __func__); #endif /* 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) { switch (path_command_at(path, i)) { case PATH_COMMAND_H_LINE: #ifdef DEBUG printf("%s: read h line.\n", __func__); #endif point = last; status = read_coordinate(file, &point.x); if (status != SUCCESS) return status; point_in = point_out = point; break; case PATH_COMMAND_V_LINE: #ifdef DEBUG printf("%s: read v line.\n", __func__); #endif point = last; status = read_coordinate(file, &point.y); if (status != SUCCESS) return status; point_in = point_out = point; break; case PATH_COMMAND_LINE: #ifdef DEBUG printf("%s: read line.\n", __func__); #endif 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: #ifdef DEBUG printf("%s: read curve.\n", __func__); #endif status = read_coordinate(file, &point.x); if (status != SUCCESS) return status; status = read_coordinate(file, &point.y); if (status != SUCCESS) return status; status = read_coordinate(file, &point_in.x); if (status != SUCCESS) return status; status = read_coordinate(file, &point_in.y); if (status != SUCCESS) return status; status = read_coordinate(file, &point_out.x); if (status != SUCCESS) return status; status = read_coordinate(file, &point_out.y); if (status != SUCCESS) return status; break; } unsigned base = i * 3; path->points[base] = point; path->points[base + 1] = point_in; path->points[base + 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) { #ifdef DEBUG printf("%s: read path.\n", __func__); #endif uint8_t flags; if (fread(&flags, 1, 1, file) != 1) { return ERROR_EOF; } if (fread(&path->num_points, 1, 1, file) != 1) { return ERROR_EOF; } 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) { #ifdef DEBUG printf( "%s: path uses %u commands (%u bytes).\n", __func__, path->num_points, num_command_bytes); #endif if ( fread(path->commands, 1, num_command_bytes, file) != num_command_bytes) { return ERROR_PATH; } } else if (flags & PATH_FLAG_NO_CURVES) { #ifdef DEBUG printf("%s: path no curves.\n", __func__); #endif create_static_commands( num_command_bytes, path->commands, 0xAA); /* 0b10101010: all lines */ } else { /* All curves */ #ifdef DEBUG printf("%s: path all curves.\n", __func__); #endif create_static_commands( num_command_bytes, path->commands, 0xFF); /* 0b11111111: all curves */ } return read_path_points(file, path); } hvif_status read_transformer(FILE* file, hvif_transformer* transformer) { uint8_t byte; if (fread(&byte, 1, 1, file) != 1) return ERROR_EOF; switch (byte) { case HVIF_TRANSFORMER_TYPE_AFFINE: transformer->transformer_type = byte; uint8_t buffer[18]; hvif_matrix matrix; if (fread(buffer, 1, 18, file) != 18) return ERROR_EOF; hvif_decode(matrix, buffer); transformer->transformer.affine.matrix = matrix; break; case HVIF_TRANSFORMER_TYPE_CONTOUR: transformer->transformer_type = byte; if (fread(&byte, 1, 1, file) != 1) return ERROR_EOF; transformer->transformer.contour.width = byte - 128.0; if (fread(&byte, 1, 1, file) != 1) return ERROR_EOF; if (byte >= HVIF_LINE_JOIN_TYPE_MAX) return ERROR_SHAPE; transformer->transformer.contour.line_join = byte; if (fread(&byte, 1, 1, file) != 1) return ERROR_EOF; transformer->transformer.contour.miter_limit = byte; break; case HVIF_TRANSFORMER_TYPE_PERSPECTIVE: transformer->transformer_type = byte; /* This seems to not do anything right now */ return ERROR_SHAPE; break; case HVIF_TRANSFORMER_TYPE_STROKE: transformer->transformer_type = byte; if (fread(&byte, 1, 1, file) != 1) return ERROR_EOF; transformer->transformer.stroke.width = byte - 128.0; if (fread(&byte, 1, 1, file) != 1) return ERROR_EOF; uint8_t line_join = byte & 15; if (line_join >= HVIF_LINE_JOIN_TYPE_MAX) return ERROR_SHAPE; uint8_t line_cap = byte >> 4; ; if (line_cap >= HVIF_LINE_CAP_TYPE_MAX) return ERROR_SHAPE; transformer->transformer.stroke.line_join = line_join; transformer->transformer.stroke.line_cap = line_cap; if (fread(&byte, 1, 1, file) != 1) return ERROR_EOF; transformer->transformer.stroke.miter_limit = byte; break; default: return ERROR_SHAPE; } return SUCCESS; } hvif_status read_shape(FILE* file, hvif_image const* const image, hvif_shape* shape) { #ifdef DEBUG printf("%s: start.\n", __func__); #endif uint8_t shape_type; if (fread(&shape_type, 1, 1, file) != 1) { return ERROR_EOF; } /* the official decoder skips unkown shapes, we are a bit more strict */ if (shape_type != SHAPE_TYPE_PATH_SOURCE) return ERROR_SHAPE; if (fread(&shape->style_idx, 1, 1, file) != 1) { return ERROR_EOF; } if (shape->style_idx > image->num_styles) { return ERROR_SHAPE; } if (fread(&shape->num_paths, 1, 1, file) != 1) { return ERROR_EOF; } shape->path_idxs = calloc(shape->num_paths, 1); if (!shape->path_idxs) return ERROR_NOMEM; #ifdef DEBUG printf( "%s: style %u and %u paths.\n", __func__, shape->style_idx, shape->num_paths); #endif for (unsigned i = 0; i < shape->num_paths; ++i) { if (fread(&shape->path_idxs[i], 1, 1, file) != 1) { return ERROR_EOF; } if (shape->path_idxs[i] > image->num_paths) { return ERROR_SHAPE; } } uint8_t shape_flags; if (fread(&shape_flags, 1, 1, file) != 1) { return ERROR_EOF; } shape->hinting = shape_flags & SHAPE_FLAG_HINTING; hvif_matrix transformation; if (shape_flags & SHAPE_FLAG_TRANSFORM) { uint8_t buffer[18]; if (fread(buffer, 1, 18, file) != 18) { return ERROR_EOF; } hvif_decode(transformation, buffer); } else if (shape_flags & SHAPE_FLAG_TRANSLATION) { hvif_point point; hvif_status status; status = read_coordinate(file, &point.x); if (status != SUCCESS) return status; status = read_coordinate(file, &point.y); if (status != SUCCESS) return status; transformation = MATRIX_TRANSLATION(point); } else { transformation = MATRIX_ID; } shape->transformation = transformation; if (shape_flags & SHAPE_FLAG_LOD_SCALE) { uint8_t scale; if (fread(&scale, 1, 1, file) != 1) { return ERROR_EOF; } shape->min_visibility = scale / 63.75; if (fread(&scale, 1, 1, file) != 1) { return ERROR_EOF; } shape->max_visibility = scale / 63.75; } else { shape->min_visibility = 0.0; shape->max_visibility = 4.0; } shape->num_transformers = 0; shape->transformers = 0; if (shape_flags & SHAPE_FLAG_HAS_TRANSFORMERS) { if (fread(&shape->num_transformers, 1, 1, file) != 1) { return ERROR_EOF; } shape->transformers = calloc(shape->num_transformers, sizeof(hvif_transformer)); if (!shape->num_transformers) return ERROR_NOMEM; for (unsigned i = 0; i < shape->num_transformers; ++i) { hvif_status s = read_transformer(file, &shape->transformers[i]); if (s != SUCCESS) return s; } } return SUCCESS; } #define ERROR_RESULT(E) \ (hvif_result) { .status = E } #define SUCCESS_RESULT(I) \ (hvif_result) { .status = SUCCESS, .image = I } hvif_result hvif_from_file(FILE* file) { uint32_t const magic = 0x6669636e; /* 'finc' (on disk little endian 'cnif') */ #ifdef DEBUG printf("%s: checking magic number.\n", __func__); #endif uint8_t magic_buffer[4]; if (fread(magic_buffer, 1, 4, file) != 4) return ERROR_RESULT(ERROR_EOF); uint32_t read_magic; 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); if (fread(&image->num_styles, 1, 1, file) != 1) { hvif_free(image); return ERROR_RESULT(ERROR_EOF); } #ifdef DEBUG printf("%s: reading %u styles.\n", __func__, image->num_styles); #endif image->styles = calloc(image->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); } } if (fread(&image->num_paths, 1, 1, file) != 1) { hvif_free(image); return ERROR_RESULT(ERROR_EOF); } #ifdef DEBUG printf("%s: reading %u paths.\n", __func__, image->num_paths); #endif 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); } } if (fread(&image->num_shapes, 1, 1, file) != 1) { hvif_free(image); return ERROR_RESULT(ERROR_EOF); } #ifdef DEBUG printf("%s: reading %u shapes.\n", __func__, image->num_shapes); #endif image->shapes = calloc(image->num_shapes, sizeof(hvif_shape)); if (!image->shapes) { hvif_free(image); return ERROR_RESULT(ERROR_NOMEM); } for (unsigned i = 0; i < image->num_shapes; ++i) { hvif_status s = read_shape(file, image, &image->shapes[i]); if (s != SUCCESS) { hvif_free(image); return ERROR_RESULT(s); } } return SUCCESS_RESULT(image); } void hvif_free(hvif_image* image) { if (image->styles) { for (unsigned i = 0; i < image->num_styles; ++i) { free(image->styles[i].colors); free(image->styles[i].offsets); } } free(image->styles); if (image->paths) { for (unsigned i = 0; i < image->num_paths; ++i) { free(image->paths[i].commands); free(image->paths[i].points); } } free(image->paths); if (image->shapes) { for (unsigned i = 0; i < image->num_shapes; ++i) { free(NULL); free(image->shapes[i].path_idxs); free(image->shapes[i].transformers); } } free(image->shapes); free(image); } uint8_t hvif_num_styles(hvif_image* image) { if (image == NULL) return 0; return image->num_styles; } uint8_t hvif_num_paths(hvif_image* image) { if (image == NULL) return 0; return image->num_paths; } uint8_t hvif_num_shapes(hvif_image* image) { if (image == NULL) return 0; return image->num_shapes; } hvif_status hvif_style_gradient_type( hvif_image* image, uint8_t style, hvif_gradients_type* type) { if (type == NULL) return ERROR_NULL_OUT; if (image == NULL) return ERROR_STYLE; if (style >= image->num_styles) return ERROR_STYLE; *type = image->styles[style].gradient_type; return SUCCESS; } hvif_status hvif_style_num_stops(hvif_image* image, uint8_t style, uint8_t* num_stops) { if (num_stops == NULL) return ERROR_NULL_OUT; if (image == NULL) return ERROR_STYLE; if (style >= image->num_styles) return ERROR_STYLE; *num_stops = image->styles[style].num_stops; return SUCCESS; } hvif_status hvif_style_stop( hvif_image* image, uint8_t style, uint8_t stop, hvif_gradient_stop* gradient_stop) { if (gradient_stop == NULL) return ERROR_NULL_OUT; if (image == NULL) return ERROR_STYLE; if (style >= image->num_styles) return ERROR_STYLE; if (stop >= image->styles[style].num_stops) return ERROR_STYLE; gradient_stop->color = image->styles[style].colors[stop]; gradient_stop->offset = image->styles[style].offsets[stop]; return SUCCESS; } hvif_status hvif_style_transformation( hvif_image* image, uint8_t style, hvif_matrix* transformation) { if (transformation == NULL) return ERROR_NULL_OUT; if (image == NULL) return ERROR_STYLE; if (style >= image->num_styles) return ERROR_STYLE; *transformation = image->styles[style].transformation; return SUCCESS; } hvif_status hvif_path_num_points( hvif_image* image, uint8_t path, uint8_t* points) { if (points == NULL) return ERROR_NULL_OUT; if (image == NULL) return ERROR_PATH; if (path >= image->num_paths) return ERROR_PATH; *points = image->paths[path].num_points; return SUCCESS; } hvif_status hvif_path_closed( hvif_image* image, uint8_t path, bool* closed) { if (closed == NULL) return ERROR_NULL_OUT; if (image == NULL) return ERROR_PATH; if (path >= image->num_paths) return ERROR_PATH; *closed = image->paths[path].closed; return SUCCESS; } hvif_status hvif_path_points( hvif_image* image, uint8_t path, hvif_point** points) { if (image == NULL) return ERROR_PATH; if (path >= image->num_paths) return ERROR_PATH; size_t total_points = 3 * image->paths[path].num_points; hvif_point* p = calloc(total_points, sizeof(hvif_point)); if (!p) return ERROR_NOMEM; memcpy(p, image->paths[path].points, total_points * sizeof(hvif_point)); *points = p; return SUCCESS; } #endif /* INTERNAL_DATASTRUCTURES */