/* * layer.h - Separate 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 "layer.h" struct layer_obj { enum lo_type { lo_line, lo_rect, lo_poly, lo_circ, lo_arc, lo_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 layer_obj *next; }; static void bb(struct layer *layer, int x, int y) { if (layer->xmin > x) layer->xmin = x; if (layer->ymin > y) layer->ymin = y; if (layer->xmax < x) layer->xmax = x; if (layer->ymax < y) layer->ymax = y; } static void bb_rot(struct layer *layer, int x, int y, int rot) { double a = rot / 180.0 * M_PI; bb(layer, cos(a) * x + sin(a) * y, cos(a) * y - sin(a) * y); } static struct layer_obj *new_obj(struct layer *lc, enum lo_type type, int color, int fill_color, unsigned layer) { struct layer_objs **objs; struct layer_objs *new_objs; struct layer_obj *new_obj; for (objs = &lc->objs; *objs; objs = &(*objs)->next) { if ((*objs)->layer == layer) goto this_layer; if ((*objs)->layer < layer) break; } new_objs = alloc_type(struct layer_objs); new_objs->layer = layer; new_objs->obj = NULL; new_objs->next_obj = &new_objs->obj; new_objs->next = *objs; *objs = new_objs; this_layer: new_obj = alloc_type(struct layer_obj); new_obj->type = type; new_obj->color = color; new_obj->fill_color = fill_color; new_obj->next = NULL; *(*objs)->next_obj = new_obj; (*objs)->next_obj = &new_obj->next; return new_obj; } void layer_line(void *ctx, int sx, int sy, int ex, int ey, int color, unsigned layer) { struct layer *lc = ctx; struct layer_obj *obj = new_obj(ctx, lo_line, color, COLOR_NONE, layer); bb(lc, sx, sy); bb(lc, ex, ey); obj->x = sx; obj->y = sy; obj->u.line.ex = ex; obj->u.line.ey = ey; } void layer_rect(void *ctx, int sx, int sy, int ex, int ey, int color, int fill_color, unsigned layer) { struct layer *lc = ctx; struct layer_obj *obj = new_obj(ctx, lo_rect, color, fill_color, layer); bb(lc, sx, sy); bb(lc, ex, ey); obj->x = sx; obj->y = sy; obj->u.rect.ex = ex; obj->u.rect.ey = ey; } void layer_poly(void *ctx, int points, int x[points], int y[points], int color, int fill_color, unsigned layer) { struct layer *lc = ctx; struct layer_obj *obj = new_obj(ctx, lo_poly, color, fill_color, layer); int i; unsigned size; for (i = 0; i != points; i++) bb(lc, 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 layer_circ(void *ctx, int x, int y, int r, int color, int fill_color, unsigned layer) { struct layer *lc = ctx; struct layer_obj *obj = new_obj(ctx, lo_circ, color, fill_color, layer); bb(lc, x - r, y - r); bb(lc, x + r, y + r); obj->x = x; obj->y = y; obj->u.circ.r = r; } void layer_arc(void *ctx, int x, int y, int r, int sa, int ea, int color, int fill_color, unsigned layer) { struct layer *lc = ctx; struct layer_obj *obj = new_obj(ctx, lo_arc, color, fill_color, layer); bb(lc, x - r, y - r); bb(lc, 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 layer_text(void *ctx, int x, int y, const char *s, unsigned size, enum text_align align, int rot, unsigned color, unsigned layer) { struct layer *lc = ctx; struct layer_obj *obj = new_obj(ctx, lo_text, color, COLOR_NONE, layer); unsigned width = lc->ops->text_width(lc->user, s, size); bb_rot(lc, x, y - size, rot); bb_rot(lc, 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 layer_init(struct layer *layer, const struct gfx_ops *ops, void *user) { layer->ops = ops; layer->user = user; layer->xmin = layer->ymin = INT_MAX; layer->xmax = layer->ymax = INT_MIN; layer->objs = NULL; } void layer_replay(const struct layer *layer) { const struct gfx_ops *ops = layer->ops; void *ctx = layer->user; const struct layer_objs *objs; const struct layer_obj *obj; for (objs = layer->objs; objs; objs = objs->next) for (obj = objs->obj; obj; obj = obj->next) switch (obj->type) { case lo_line: ops->line(ctx, obj->x, obj->y, obj->u.line.ex, obj->u.line.ey, obj->color, objs->layer); break; case lo_rect: ops->rect(ctx, obj->x, obj->y, obj->u.rect.ex, obj->u.rect.ey, obj->color, obj->fill_color, objs->layer); break; case lo_poly: ops->poly(ctx, obj->u.poly.n, obj->u.poly.vx, obj->u.poly.vy, obj->color, obj->fill_color, objs->layer); break; case lo_circ: ops->circ(ctx, obj->x, obj->y, obj->u.circ.r, obj->color, obj->fill_color, objs->layer); break; case lo_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, objs->layer); break; case lo_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, objs->layer); break; default: abort(); } } void layer_bbox(const struct layer *layer, int *x, int *y, int *w, int *h) { if (x) *x = layer->xmin; if (y) *y = layer->ymin; if (w) *w = layer->xmax - layer->xmin + 1; if (h) *h = layer->ymax - layer->ymin + 1; } static void layer_obj_destroy(struct layer_obj *obj) { switch (obj->type) { case lo_poly: free(obj->u.poly.vx); free(obj->u.poly.vy); break; case lo_text: free(obj->u.text.s); break; default: break; } free(obj); } void layer_destroy(struct layer *layer) { struct layer_objs *next_objs; struct layer_obj *next_obj; while (layer->objs) { next_objs = layer->objs->next; while (layer->objs->obj) { next_obj = layer->objs->obj->next; layer_obj_destroy(layer->objs->obj); layer->objs->obj = next_obj; } free(layer->objs); layer->objs = next_objs; } }