1
0
Fork 0

Compare commits

...

3 Commits

4 changed files with 302 additions and 29 deletions

View File

@ -15,6 +15,6 @@ pkgs.stdenv.mkDerivation {
name = "hvif-light";
hardeningDisable = [ "all" ];
buildInputs = with pkgs; [ autoconf automake gcc clang-tools gdb ];
buildInputs = with pkgs; [ autoconf automake gcc clang-tools gdb valgrind-light ];
}

View File

@ -76,6 +76,24 @@ enum
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;
#define ERROR_RESULT(E) \
(hvif_result) { .status = E }
#define SUCCESS_RESULT(I) \
@ -96,6 +114,9 @@ struct hvif_matrix
/* TODO: This is most likely wrong */
#define MATRIX_ID \
(hvif_matrix) { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0 }
/* TODO: This is most likely wrong */
#define MATRIX_TRANSLATION(P) \
(hvif_matrix) { P.x, 0.0, 0.0, 0.0, P.y, 0.0 }
typedef struct hvif_point hvif_point;
struct hvif_point
@ -156,6 +177,35 @@ typedef uint32_t hvif_color; /* 8bit each: alpha, blue, green, red */
#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
{
uint8_t 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
{
@ -175,12 +225,28 @@ struct hvif_path
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;
};
uint8_t
@ -369,6 +435,9 @@ read_coordinate(FILE* file, float* coord)
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;
@ -379,21 +448,30 @@ read_path_points(FILE* file, hvif_path* path)
hvif_point point_out;
hvif_status status;
for (unsigned i = 0; i < path->num_points; i += 3) {
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.x);
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);
@ -401,6 +479,9 @@ read_path_points(FILE* file, hvif_path* path)
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);
@ -418,9 +499,10 @@ read_path_points(FILE* file, hvif_path* path)
point_in = point_out = point;
break;
}
path->points[i] = point;
path->points[i + 1] = point_in;
path->points[i + 2] = point_out;
unsigned base = i * 3;
path->points[base] = point;
path->points[base + 1] = point_in;
path->points[base + 2] = point_out;
last = point;
}
return SUCCESS;
@ -436,33 +518,179 @@ create_static_commands(
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_PATH; }
if (fread(&path->num_points, 1, 1, file) != 1) { return ERROR_PATH; }
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)) {
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) {
#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) {
create_static_commands(
num_command_bytes, path->commands, 0xFF); /* 0b11111111: all curves */
} else { /* All lines */
#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;
char 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) {
char 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 = -1.0;
shape->max_visibility = -1.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;
}
hvif_result
hvif_from_file(FILE* file)
{
@ -472,10 +700,10 @@ hvif_from_file(FILE* file)
printf("%s: checking magic number.\n", __func__);
#endif
char magic_buffer[4];
if (fread(magic_buffer, 1, 4, file) != 4) { return ERROR_RESULT(ERROR_EOF); }
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); }
if (read_magic != magic) return ERROR_RESULT(ERROR_MAGIC);
hvif_image* image = calloc(1, sizeof(hvif_image));
if (!image) return ERROR_RESULT(ERROR_NOMEM);
@ -505,7 +733,7 @@ hvif_from_file(FILE* file)
return ERROR_RESULT(ERROR_EOF);
}
#ifdef DEBUG
printf("%s: reading %u paths.\n", __func__, image->num_styles);
printf("%s: reading %u paths.\n", __func__, image->num_paths);
#endif
image->paths = calloc(image->num_paths, sizeof(hvif_path));
if (!image->paths) {
@ -519,10 +747,26 @@ hvif_from_file(FILE* file)
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: incomplete parsing stopped.\n", __func__);
debug_print_bytes(file);
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);
}
@ -530,14 +774,27 @@ hvif_from_file(FILE* file)
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);
}
for (unsigned i = 0; i < image->num_paths; ++i) {
free(image->paths[i].commands);
free(image->paths[i].points);
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);
}

View File

@ -11,7 +11,8 @@ typedef enum hvif_status
ERROR_NOMEM,
ERROR_MAGIC,
ERROR_STYLE,
ERROR_PATH
ERROR_PATH,
ERROR_SHAPE
} hvif_status;
typedef struct hvif_image hvif_image;

View File

@ -5,19 +5,34 @@
#include <stdlib.h>
int
main(void)
main(int argc, char* argv[argc + 1])
{
puts("This is " PACKAGE_STRING ".");
puts(PACKAGE_STRING);
FILE* imagefile = fopen("test.hvif", "rb");
if (argc < 2) {
if (argc > 0) printf("Usage: %s FILE\n", argv[0]);
return EXIT_FAILURE;
}
FILE* imagefile = fopen(argv[1], "rb");
if (!imagefile) {
fputs("fopen failed.\n", stderr);
return EXIT_FAILURE;
}
hvif_result result = hvif_from_file(imagefile);
fclose(imagefile);
if (result.status != SUCCESS) {
fputs("Reading image failed.\n", stderr);
switch (result.status) {
case ERROR_NOMEM: fputs("Out of memory.\n", stderr); break;
case ERROR_EOF: fputs("File ended prematurely.\n", stderr); break;
case ERROR_STYLE: fputs("Incorrect style definition.\n", stderr); break;
case ERROR_PATH: fputs("Incorrect path definition.\n", stderr); break;
case ERROR_SHAPE: fputs("Incorrect shape definition.\n", stderr); break;
default: break;
}
return EXIT_FAILURE;
}
puts("Reading image succeeded.");