1
0
Fork 0
hvif-light/src/hvif-light.c

811 lines
21 KiB
C

#ifndef INTERNAL_DATASTRUCTURES
#include "hvif-light.h"
#include <stdint.h>
#include <stdlib.h>
#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,
TRANSFORMER_TYPE_AFFINE = 20,
TRANSFORMER_TYPE_CONTOUR = 21,
TRANSFORMER_TYPE_PERSPECTIVE = 22,
TRANSFORMER_TYPE_STROKE = 23,
};
enum gradients_type
{
GRADIENT_LINEAR = 0,
GRADIENT_CIRCULAR,
GRADIENT_DIAMOND,
GRADIENT_CONIC,
GRADIENT_XY,
GRADIENT_SQRT_XY,
HVIF_GRADIENT_TYPE_MAX
};
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,
};
typedef enum hvif_line_join
{
MITER_JOIN = 0,
MITER_JOIN_REVERT = 1,
ROUND_JOIN = 2,
BEVEL_JOIN = 3,
MITER_JOIN_ROUND = 4,
HVIF_LINE_JOIN_TYPE_MAX
} hvif_line_join;
typedef enum hvif_line_cap
{
BUTT_CAP,
SQUARE_CAP,
ROUND_CAP,
HVIF_LINE_CAP_TYPE_MAX
} hvif_line_cap;
/* An affine transformation. */
typedef struct hvif_matrix hvif_matrix;
struct hvif_matrix
{
double xx;
double yx;
double xy;
double yy;
double x0;
double y0;
};
#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 }
typedef struct hvif_point hvif_point;
struct hvif_point
{
float x;
float y;
};
#define POINT_ORIGIN \
(hvif_point) { 0.0, 0.0 }
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_transformer hvif_transformer;
struct hvif_transformer
{
signed 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
{
signed 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])
};
}
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 = 0;
if (alpha) {
if (gray) {
COLOR_SET_RGB(c, buffer[0], buffer[0], buffer[0]);
COLOR_SET_ALPHA(c, buffer[1]);
} else {
hvif_decode(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, 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 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 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 TRANSFORMER_TYPE_PERSPECTIVE:
transformer->transformer_type = byte;
/* This seems to not do anything right now */
return ERROR_SHAPE;
break;
case 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);
}
#endif /* INTERNAL_DATASTRUCTURES */