/* * record.c - Record graphics operations by layers and replay * * Written 2016 by Werner Almesberger * Copyright 2016 by Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include "util.h" #include "style.h" #include "gfx.h" #include "text.h" #include "record.h" struct record_obj { enum ro_type { ro_line, ro_rect, ro_poly, ro_circ, ro_arc, ro_text, } type; int x, y; int color, fill_color; union { struct { int ex, ey; } line; struct { int ex, ey; } rect; struct { unsigned n; int *vx, *vy; } poly; struct { int r; } circ; struct { int r; int sa, ea; } arc; struct { char *s; unsigned size; enum text_align align; int rot; } text; } u; struct record_obj *next; }; static void bb(struct record *rec, int x, int y) { if (rec->xmin > x) rec->xmin = x; if (rec->ymin > y) rec->ymin = y; if (rec->xmax < x) rec->xmax = x; if (rec->ymax < y) rec->ymax = y; } static void bb_rot(struct record *rec, int x, int y, int rot) { double a = -rot / 180.0 * M_PI; // @@@ figure this out later return; bb(rec, cos(a) * x + sin(a) * y, cos(a) * y - sin(a) * x); } static struct record_obj *new_obj(struct record *rec, enum ro_type type, int color, int fill_color, unsigned layer) { struct record_layer **curr_layer; struct record_layer *new_layer; struct record_obj *new_obj; for (curr_layer = &rec->layers; *curr_layer; curr_layer= &(*curr_layer)->next) { if ((*curr_layer)->layer == layer) goto this_layer; if ((*curr_layer)->layer < layer) break; } new_layer = alloc_type(struct record_layer); new_layer->layer = layer; new_layer->objs = NULL; new_layer->next_obj = &new_layer->objs; new_layer->next = *curr_layer; *curr_layer = new_layer; this_layer: new_obj = alloc_type(struct record_obj); new_obj->type = type; new_obj->color = color; new_obj->fill_color = fill_color; new_obj->next = NULL; *(*curr_layer)->next_obj = new_obj; (*curr_layer)->next_obj = &new_obj->next; return new_obj; } void record_line(void *ctx, int sx, int sy, int ex, int ey, int color, unsigned layer) { struct record *rec = ctx; struct record_obj *obj = new_obj(rec, ro_line, color, COLOR_NONE, layer); bb(rec, sx, sy); bb(rec, ex, ey); obj->x = sx; obj->y = sy; obj->u.line.ex = ex; obj->u.line.ey = ey; } void record_rect(void *ctx, int sx, int sy, int ex, int ey, int color, int fill_color, unsigned layer) { struct record *rec = ctx; struct record_obj *obj = new_obj(rec, ro_rect, color, fill_color, layer); bb(rec, sx, sy); bb(rec, ex, ey); obj->x = sx; obj->y = sy; obj->u.rect.ex = ex; obj->u.rect.ey = ey; } void record_poly(void *ctx, int points, const int x[points], const int y[points], int color, int fill_color, unsigned layer) { struct record *rec = ctx; struct record_obj *obj = new_obj(ctx, ro_poly, color, fill_color, layer); int i; unsigned size; for (i = 0; i != points; i++) bb(rec, x[i], y[i]); obj->u.poly.n = points; size = sizeof(int) * points; obj->u.poly.vx = alloc_size(size); obj->u.poly.vy = alloc_size(size); memcpy(obj->u.poly.vx, x, size); memcpy(obj->u.poly.vy, y, size); } void record_circ(void *ctx, int x, int y, int r, int color, int fill_color, unsigned layer) { struct record *rec = ctx; struct record_obj *obj = new_obj(ctx, ro_circ, color, fill_color, layer); bb(rec, x - r, y - r); bb(rec, x + r, y + r); obj->x = x; obj->y = y; obj->u.circ.r = r; } void record_arc(void *ctx, int x, int y, int r, int sa, int ea, int color, int fill_color, unsigned layer) { struct record *rec = ctx; struct record_obj *obj = new_obj(ctx, ro_arc, color, fill_color, layer); bb(rec, x - r, y - r); bb(rec, x + r, y + r); obj->x = x; obj->y = y; obj->u.arc.r = r; obj->u.arc.sa = sa; obj->u.arc.ea = ea; } void record_text(void *ctx, int x, int y, const char *s, unsigned size, enum text_align align, int rot, unsigned color, unsigned layer) { struct record *rec = ctx; struct record_obj *obj = new_obj(ctx, ro_text, color, COLOR_NONE, layer); unsigned width = rec->ops->text_width(rec->user, s, size); bb_rot(rec, x, y - size, rot); bb_rot(rec, x + width, y, rot); obj->x = x; obj->y = y; obj->u.text.s = stralloc(s); obj->u.text.size = size; obj->u.text.align = align; obj->u.text.rot = rot; } void record_init(struct record *rec, const struct gfx_ops *ops, void *user) { rec->ops = ops; rec->user = user; rec->xmin = rec->ymin = INT_MAX; rec->xmax = rec->ymax = INT_MIN; rec->layers = NULL; } void record_wipe(struct record *rec) { rec->layers = NULL; } void record_replay(const struct record *rec) { const struct gfx_ops *ops = rec->ops; void *ctx = rec->user; const struct record_layer *layer; const struct record_obj *obj; for (layer = rec->layers; layer; layer = layer->next) for (obj = layer->objs; obj; obj = obj->next) switch (obj->type) { case ro_line: ops->line(ctx, obj->x, obj->y, obj->u.line.ex, obj->u.line.ey, obj->color, layer->layer); break; case ro_rect: ops->rect(ctx, obj->x, obj->y, obj->u.rect.ex, obj->u.rect.ey, obj->color, obj->fill_color, layer->layer); break; case ro_poly: ops->poly(ctx, obj->u.poly.n, obj->u.poly.vx, obj->u.poly.vy, obj->color, obj->fill_color, layer->layer); break; case ro_circ: ops->circ(ctx, obj->x, obj->y, obj->u.circ.r, obj->color, obj->fill_color, layer->layer); break; case ro_arc: ops->arc(ctx, obj->x, obj->y, obj->u.arc.r, obj->u.arc.sa, obj->u.arc.ea, obj->color, obj->fill_color, layer->layer); break; case ro_text: ops->text(ctx, obj->x, obj->y, obj->u.text.s, obj->u.text.size, obj->u.text.align, obj->u.text.rot, obj->color, layer->layer); break; default: abort(); } } void record_bbox(const struct record *rec, int *x, int *y, int *w, int *h) { if (x) *x = rec->xmin; if (y) *y = rec->ymin; if (w) *w = rec->xmax - rec->xmin + 1; if (h) *h = rec->ymax - rec->ymin + 1; } static void record_obj_destroy(struct record_obj *obj) { switch (obj->type) { case ro_poly: free(obj->u.poly.vx); free(obj->u.poly.vy); break; case ro_text: free(obj->u.text.s); break; default: break; } free(obj); } void record_destroy(struct record *rec) { struct record_layer *next_layer; struct record_obj *next_obj; while (rec->layers) { next_layer = rec->layers->next; while (rec->layers->objs) { next_obj = rec->layers->objs->next; record_obj_destroy(rec->layers->objs); rec->layers->objs = next_obj; } free(rec->layers); rec->layers = next_layer; } }