From c5064a8dca43abcc1bc1a6e6fbdb096d33c5fbe0 Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Mon, 1 Aug 2016 14:38:36 -0300 Subject: [PATCH] sch2fig/: implement "diff" driver (WIP) --- sch2fig/Makefile | 4 +- sch2fig/TODO | 5 + sch2fig/cairo.c | 37 +++++++ sch2fig/cairo.h | 14 ++- sch2fig/diff.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++ sch2fig/diff.h | 22 ++++ sch2fig/gfx.c | 5 +- sch2fig/gfx.h | 3 + sch2fig/main.c | 4 + 9 files changed, 367 insertions(+), 7 deletions(-) create mode 100644 sch2fig/diff.c create mode 100644 sch2fig/diff.h diff --git a/sch2fig/Makefile b/sch2fig/Makefile index b7c40f9..89b3001 100644 --- a/sch2fig/Makefile +++ b/sch2fig/Makefile @@ -13,9 +13,9 @@ NAME = sch2fig OBJS = main.o sch-parse.o sch-render.o lib-parse.o lib-render.o \ file.o \ - style.o fig.o record.o cairo.o gfx.o dwg.o text.o misc.o + style.o fig.o record.o cairo.o diff.o gfx.o dwg.o text.o misc.o -CFLAGS = -g -O -Wall -Wextra -Wno-unused-parameter -Wshadow \ +CFLAGS = -g -Wall -Wextra -Wno-unused-parameter -Wshadow \ -Wmissing-prototypes -Wmissing-declarations \ `pkg-config --cflags cairo` LIBS = -lm `pkg-config --libs cairo` diff --git a/sch2fig/TODO b/sch2fig/TODO index 7b0b041..b544af5 100644 --- a/sch2fig/TODO +++ b/sch2fig/TODO @@ -19,3 +19,8 @@ - on parse error, politely complain, don't terminate - implement destructors - check for memory leaks +- record.c (bb_rot): implement bounding boxes for text +- nesting gfx in diff is a huge kludge, caused by global vars in gfx.c +- move path name guessing into file.c +- return indication of whether diff found any differences +- in diff, pass only options understood by cairo_png diff --git a/sch2fig/cairo.c b/sch2fig/cairo.c index ede963f..3ca97c5 100644 --- a/sch2fig/cairo.c +++ b/sch2fig/cairo.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -395,6 +396,42 @@ static void cr_pdf_end(void *ctx) } +uint32_t *cairo_img_end(void *ctx, int *w, int *h, int *stride) +{ + struct cairo_ctx *cc = ctx; + uint32_t *data; + + end_common(cc, w, h); + + *stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, *w); + data = alloc_size(*stride * *h); + + cc->s = cairo_image_surface_create_for_data((unsigned char *) data, + CAIRO_FORMAT_RGB24, *w, *h, *stride); + cc->cr = cairo_create(cc->s); + + set_color(cc->cr, COLOR_WHITE); + cairo_paint(cc->cr); + + cairo_select_font_face(cc->cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_line_width(cc->cr, 2); + + record_replay(&cc->record); + record_destroy(&cc->record); + + return data; +} + + +void cairo_img_write(void *ctx, const char *name) +{ + struct cairo_ctx *cc = ctx; + + cairo_surface_write_to_png(cc->s, name); +} + + /* ----- Operations -------------------------------------------------------- */ diff --git a/sch2fig/cairo.h b/sch2fig/cairo.h index 93af9e3..b4115fb 100644 --- a/sch2fig/cairo.h +++ b/sch2fig/cairo.h @@ -11,8 +11,10 @@ */ -#ifndef CAIRO_H -#define CAIRO_H +#ifndef MY_CAIRO_H +#define MY_CAIRO_H + +#include #include "gfx.h" @@ -20,4 +22,10 @@ extern const struct gfx_ops cairo_png_ops; extern const struct gfx_ops cairo_pdf_ops; -#endif /* !CAIRO_H */ +#define cairo_img_ops cairo_png_ops /* just don't call cairo_img_ops.end */ + + +uint32_t *cairo_img_end(void *ctx, int *w, int *h, int *stride); +void cairo_img_write(void *ctx, const char *name); + +#endif /* !MY_CAIRO_H */ diff --git a/sch2fig/diff.c b/sch2fig/diff.c new file mode 100644 index 0000000..5f9c50a --- /dev/null +++ b/sch2fig/diff.c @@ -0,0 +1,280 @@ +/* + * diff.c - Schematics difference + * + * 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 +#include + +#include "util.h" +#include "main.h" +#include "cairo.h" +#include "sch.h" +#include "lib.h" +#include "diff.h" + + +#define DEFAULT_FRAME_RADIUS 30 + + +struct area { + int xa, ya, xb, yb; + struct area *next; +}; + +struct diff { + void *cr_ctx; + uint32_t *new_img; + int w, h, stride; + const char *output_name; + int frame_radius; + struct area *areas; +}; + + +/* ----- Wrappers ---------------------------------------------------------- */ + + +static void diff_line(void *ctx, int sx, int sy, int ex, int ey, + int color, unsigned layer) +{ + const struct diff *diff = ctx; + + cairo_img_ops.line(diff->cr_ctx, sx, sy, ex, ey, color, layer); +} + + +static void diff_poly(void *ctx, + int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer) +{ + const struct diff *diff = ctx; + + cairo_img_ops.poly(diff->cr_ctx, points, x, y, + color, fill_color, layer); +} + + +static void diff_circ(void *ctx, int x, int y, int r, + int color, int fill_color, unsigned layer) +{ + const struct diff *diff = ctx; + + cairo_img_ops.circ(diff->cr_ctx, x, y, r, color, fill_color, layer); +} + + +static void diff_arc(void *ctx, int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer) +{ + const struct diff *diff = ctx; + + cairo_img_ops.arc(diff->cr_ctx, x, y, r, sa, ea, + color, fill_color, layer); +} + + +static void diff_text(void *ctx, int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer) +{ + const struct diff *diff = ctx; + + cairo_img_ops.text(diff->cr_ctx, x, y, s, size, align, rot, + color, layer); +} + + +static unsigned diff_text_width(void *ctx, const char *s, unsigned size) +{ + const struct diff *diff = ctx; + + return cairo_img_ops.text_width(diff->cr_ctx, s, size); +} + + +/* ----- Initialization and termination ------------------------------------ */ + + +static void *diff_init(int argc, char *const *argv) +{ + struct diff *diff; + char c; + int arg; + struct sch_ctx new_sch; + struct lib new_lib; + + diff = alloc_type(struct diff); + diff->areas = NULL; + + sch_init(&new_sch, 0); + lib_init(&new_lib); + + diff->output_name = NULL; + diff->frame_radius = DEFAULT_FRAME_RADIUS; + while ((c = getopt(argc, argv, "o:s:")) != EOF) + switch (c) { + case 'o': + diff->output_name = optarg; + break; + case 's': + /* for cairo_png */ + break; + default: + usage(*argv); + } + + if (argc - optind < 1) + usage(*argv); + + for (arg = optind; arg != argc - 1; arg++) + lib_parse(&new_lib, argv[arg]); + sch_parse(&new_sch, argv[argc - 1], &new_lib); + + optind = 0; + gfx_init(&cairo_img_ops, argc, argv); + diff->cr_ctx = gfx_ctx; + sch_render(new_sch.sheets); + diff->new_img = cairo_img_end(gfx_ctx, + &diff->w, &diff->h, &diff->stride); + + optind = 0; + diff->cr_ctx = cairo_img_ops.init(argc, argv); + + return diff; +} + + +/* steal from schhist/ppmdiff.c */ + +#define ONLY_OLD 0xff0000 +#define ONLY_NEW 0x00d000 +#define BOTH 0x707070 + +#define AREA_FILL 0xffffc8 + + +static void mark_area(struct diff *diff, int x, int y) +{ + struct area *area; + + for (area = diff->areas; area; area = area->next) + if (x >= area->xa && x <= area->xb && + y >= area->ya && y <= area->yb) { + if (area->xa > x - diff->frame_radius) + area->xa = x - diff->frame_radius; + if (area->xb < x + diff->frame_radius) + area->xb = x + diff->frame_radius; + if (area->ya > y - diff->frame_radius) + area->ya = y - diff->frame_radius; + if (area->yb < y + diff->frame_radius) + area->yb = y + diff->frame_radius; + return; + } + + area = alloc_type(struct area); + + area->xa = x - diff->frame_radius; + area->xb = x + diff->frame_radius; + area->ya = y - diff->frame_radius; + area->yb = y + diff->frame_radius; + + area->next = diff->areas; + diff->areas = area; +} + + +#define MASK 0xffffff + + +static void differences(struct diff *diff, uint32_t *a, const uint32_t *b) +{ + int x, y; + unsigned skip = diff->w * 4 - diff->stride; + + for (y = 0; y != diff->h; y++) { + for (x = 0; x != diff->w; x++) { + if (!((*a ^ *b) & MASK)) { + *a = ((*a >> 3) & 0x1f1f1f) | 0xe0e0e0; +// *a = ((*a >> 2) & 0x3f3f3f) | 0xc0c0c0; + } else { + mark_area(diff, x, y); +//fprintf(stderr, "0x%06x 0x%06x", *a, *b); + *a = (*a & MASK) == MASK ? ONLY_NEW : + (*b & MASK) == MASK ? ONLY_OLD : BOTH; +//fprintf(stderr, "-> 0x%06x\n", *a); + } + a++; + b++; + } + a += skip; + b += skip; + } +} + + +static void show_areas(struct diff *diff, uint32_t *a) +{ + const struct area *area; + uint32_t *p; + int x, y; + + for (area = diff->areas; area; area = area->next) + for (y = area->ya; y != area->yb; y++) { + if (y < 0 || y >= diff->h) + continue; + p = a + y * (diff->stride >> 2); + for (x = area->xa; x != area->xb; x++) { + if (x >= 0 && x < diff->w && + (p[x] & MASK) == MASK) + p[x] = AREA_FILL; + } + } +} + + +static void diff_end(void *ctx) +{ + struct diff *diff = ctx; + uint32_t *old_img; + int w, h, stride; + + old_img = cairo_img_end(diff->cr_ctx, &w, &h, &stride); + if (diff->w != w || diff->h != h) { + fprintf(stderr, "%d x %d vs. %d x %d image\n", + w, h, diff->w, diff->h); + exit(1); + } + + differences(diff, old_img, diff->new_img); + show_areas(diff, old_img); + + if (diff->output_name) + cairo_img_write(diff->cr_ctx, diff->output_name); +} + + +/* ----- Operations -------------------------------------------------------- */ + + +const struct gfx_ops diff_ops = { + .name = "diff", + .line = diff_line, + .poly = diff_poly, + .circ = diff_circ, + .arc = diff_arc, + .text = diff_text, + .text_width = diff_text_width, + .init = diff_init, + .end = diff_end, +}; diff --git a/sch2fig/diff.h b/sch2fig/diff.h new file mode 100644 index 0000000..22626b1 --- /dev/null +++ b/sch2fig/diff.h @@ -0,0 +1,22 @@ +/* + * diff.h - Schematics difference + * + * 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. + */ + + +#ifndef DIFF_H +#define DIFF_H + +#include "gfx.h" + + +extern const struct gfx_ops diff_ops; + +#endif /* !DIFF_H */ diff --git a/sch2fig/gfx.c b/sch2fig/gfx.c index 7234fe2..ba88134 100644 --- a/sch2fig/gfx.c +++ b/sch2fig/gfx.c @@ -18,8 +18,9 @@ #include "gfx.h" +void *gfx_ctx; + static const struct gfx_ops *gfx_ops; -static void *gfx_ctx; void gfx_line(int sx, int sy, int ex, int ey, int color, unsigned layer) @@ -95,8 +96,8 @@ unsigned gfx_text_width(const char *s, unsigned size) void gfx_init(const struct gfx_ops *ops, int argc, char *const *argv) { - gfx_ops = ops; gfx_ctx = ops->init(argc, argv); + gfx_ops = ops; } diff --git a/sch2fig/gfx.h b/sch2fig/gfx.h index 4500c9e..d707c24 100644 --- a/sch2fig/gfx.h +++ b/sch2fig/gfx.h @@ -43,6 +43,9 @@ struct gfx_ops { }; +extern void *gfx_ctx; + + /* wrappers */ void gfx_line(int sx, int sy, int ex, int ey, int color, unsigned layer); diff --git a/sch2fig/main.c b/sch2fig/main.c index 215292f..3debd92 100644 --- a/sch2fig/main.c +++ b/sch2fig/main.c @@ -20,6 +20,7 @@ #include "util.h" #include "fig.h" #include "cairo.h" +#include "diff.h" #include "gfx.h" #include "lib.h" #include "sch.h" @@ -30,6 +31,7 @@ static struct gfx_ops const *ops_list[] = { &fig_ops, &cairo_png_ops, &cairo_pdf_ops, + &diff_ops, }; @@ -43,6 +45,8 @@ void usage(const char *name) " png [-o output.png] [-s scale]\n" " Cairo PDF driver spec:\n" " pdf [-o output.pdf] [-s scale]\n" +" Diff driver spec:\n" +" diff [-o output.pdf] [-s scale] [file.lib ...] file.sch\n" , name); exit(1); }