Root/eeshow/gfx/diff.c

1/*
2 * gfx/diff.c - Schematics difference
3 *
4 * Written 2016 by Werner Almesberger
5 * Copyright 2016 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stddef.h>
15#include <stdint.h>
16#include <stdlib.h>
17#include <stdio.h>
18#include <unistd.h>
19
20#include <cairo/cairo.h>
21
22#include "misc/util.h"
23#include "misc/diag.h"
24#include "main.h"
25#include "gfx/cro.h"
26#include "file/file.h"
27#include "kicad/sch.h"
28#include "kicad/lib.h"
29#include "gfx/record.h"
30#include "gfx/diff.h"
31
32
33#define DEFAULT_FRAME_RADIUS 30
34
35#define FADE_SHIFT 3
36#define FADE_MASK ((0xff >> FADE_SHIFT) * (0x010101))
37#define FADE_OFFSET (~FADE_MASK & 0xffffff)
38
39#define MASK 0xffffff
40
41/* steal from schhist/ppmdiff.c */
42
43#define ONLY_OLD 0xff5050
44#define ONLY_NEW 0x00c000
45#define BOTH 0x707070
46
47#define AREA_FILL 0xffd0f0
48
49
50struct diff {
51    void *cr_ctx;
52    uint32_t *new_img;
53    int w, h, stride;
54    const char *output_name;
55    int frame_radius;
56    struct area *areas;
57};
58
59
60/* ----- Wrappers ---------------------------------------------------------- */
61
62
63static void diff_line(void *ctx, int sx, int sy, int ex, int ey,
64    int color, unsigned layer)
65{
66    const struct diff *diff = ctx;
67
68    cro_img_ops.line(diff->cr_ctx, sx, sy, ex, ey, color, layer);
69}
70
71
72static void diff_poly(void *ctx,
73    int points, const int x[points], const int y[points],
74    int color, int fill_color, unsigned layer)
75{
76    const struct diff *diff = ctx;
77
78    cro_img_ops.poly(diff->cr_ctx, points, x, y, color, fill_color, layer);
79}
80
81
82static void diff_circ(void *ctx, int x, int y, int r,
83    int color, int fill_color, unsigned layer)
84{
85    const struct diff *diff = ctx;
86
87    cro_img_ops.circ(diff->cr_ctx, x, y, r, color, fill_color, layer);
88}
89
90
91static void diff_arc(void *ctx, int x, int y, int r, int sa, int ea,
92    int color, int fill_color, unsigned layer)
93{
94    const struct diff *diff = ctx;
95
96    cro_img_ops.arc(diff->cr_ctx, x, y, r, sa, ea,
97        color, fill_color, layer);
98}
99
100
101static void diff_text(void *ctx, int x, int y, const char *s, unsigned size,
102    enum text_align align, int rot, unsigned color, unsigned layer)
103{
104    const struct diff *diff = ctx;
105
106    cro_img_ops.text(diff->cr_ctx, x, y, s, size, align, rot,
107        color, layer);
108}
109
110
111static unsigned diff_text_width(void *ctx, const char *s, unsigned size)
112{
113    const struct diff *diff = ctx;
114
115    return cro_img_ops.text_width(diff->cr_ctx, s, size);
116}
117
118
119/* ----- Initialization ---------------------------------------------------- */
120
121
122static void *diff_init(int argc, char *const *argv)
123{
124    struct diff *diff;
125    char c;
126    int arg;
127    struct sch_ctx new_sch;
128    struct file sch_file;
129    struct lib new_lib;
130
131    diff = alloc_type(struct diff);
132    diff->areas = NULL;
133
134    sch_init(&new_sch, 0);
135    lib_init(&new_lib);
136
137    diff->output_name = NULL;
138    diff->frame_radius = DEFAULT_FRAME_RADIUS;
139    while ((c = getopt(argc, argv, "o:s:")) != EOF)
140        switch (c) {
141        case 'o':
142            diff->output_name = optarg;
143            break;
144        case 's':
145            /* for cro_png */
146            break;
147        default:
148            usage(*argv);
149        }
150
151    if (argc - optind < 1)
152        usage(*argv);
153
154    if (!file_open(&sch_file, argv[argc - 1], NULL))
155        goto fail_open;
156    for (arg = optind; arg != argc - 1; arg++)
157        if (!lib_parse(&new_lib, argv[arg], &sch_file))
158            goto fail_parse;
159    if (!sch_parse(&new_sch, &sch_file, &new_lib, NULL))
160        goto fail_parse;
161    file_close(&sch_file);
162
163    optind = 0;
164    gfx_init(&cro_img_ops, argc, argv);
165    diff->cr_ctx = gfx_ctx;
166    sch_render(new_sch.sheets);
167    diff->new_img = cro_img_end(gfx_ctx,
168        &diff->w, &diff->h, &diff->stride);
169
170    optind = 0;
171    diff->cr_ctx = cro_img_ops.init(argc, argv);
172
173    return diff;
174
175fail_parse:
176    file_close(&sch_file);
177fail_open:
178    sch_free(&new_sch);
179    lib_free(&new_lib);
180    free(diff);
181    return NULL;
182}
183
184
185/* ----- Area highlighting ------------------------------------------------- */
186
187
188void add_area(struct area **areas, int xa, int ya, int xb, int yb,
189    uint32_t color)
190{
191    struct area *area;
192
193    area = alloc_type(struct area);
194
195    area->xa = xa;
196    area->ya = ya;
197    area->xb = xb;
198    area->yb = yb;
199    area->color = color;
200
201    area->next = *areas;
202    *areas = area;
203}
204
205
206static void mark_area(struct diff *diff, int x, int y)
207{
208    struct area *area;
209    int xa = x - diff->frame_radius;
210    int ya = y - diff->frame_radius;
211    int xb = x + diff->frame_radius;
212    int yb = y + diff->frame_radius;
213
214    for (area = diff->areas; area; area = area->next)
215        if (x >= area->xa && x <= area->xb &&
216            y >= area->ya && y <= area->yb) {
217            if (area->xa > xa)
218                area->xa = xa;
219            if (area->xb < xb)
220                area->xb = xb;
221            if (area->ya > ya)
222                area->ya = ya;
223            if (area->yb < yb)
224                area->yb = yb;
225            return;
226        }
227
228    add_area(&diff->areas, xa, ya, xb, yb, AREA_FILL);
229}
230
231
232static void complement_box(struct diff *diff, uint32_t *a,
233    int xa, int ya, int xb, int yb, uint32_t color)
234{
235    int sx, sy, ex, ey;
236    uint32_t *p;
237    int x, y;
238
239    sx = xa > 0 ? xa : 0;
240    ex = xb < diff->w ? xb : diff->w;
241    sy = ya > 0 ? ya : 0;
242    ey = yb < diff->h ? yb : diff->h;
243
244    if (sx >= ex || sy >= ey)
245        return;
246
247    for (y = sy; y != ey; y++) {
248        p = a + y * (diff->stride >> 2);
249        for (x = sx; x != ex; x++)
250            if ((p[x] & MASK) == MASK)
251                p[x] = color;
252    }
253}
254
255
256static void show_areas(struct diff *diff, uint32_t *a)
257{
258    const struct area *area;
259
260    for (area = diff->areas; area; area = area->next)
261        complement_box(diff, a, area->xa, area->ya, area->xb, area->yb,
262            area->color);
263}
264
265
266void free_areas(struct area **areas)
267{
268    struct area *next;
269
270    while (*areas) {
271        next = (*areas)->next;
272        free(*areas);
273        *areas = next;
274    }
275}
276
277
278/* ----- Termination ------------------------------------------------------- */
279
280
281static void differences(struct diff *diff, uint32_t *a, const uint32_t *b)
282{
283    unsigned skip = diff->w * 4 - diff->stride;
284    int x, y;
285
286    for (y = 0; y != diff->h; y++) {
287        for (x = 0; x != diff->w; x++) {
288            if (!((*a ^ *b) & MASK)) {
289                *a = ((*a >> FADE_SHIFT) & FADE_MASK) |
290                    FADE_OFFSET;
291            } else {
292                mark_area(diff, x, y);
293                *a = (*a & MASK) == MASK ? ONLY_NEW :
294                    (*b & MASK) == MASK ? ONLY_OLD : BOTH;
295            }
296            a++;
297            b++;
298        }
299        a += skip;
300        b += skip;
301    }
302}
303
304
305static void diff_end(void *ctx)
306{
307    struct diff *diff = ctx;
308    uint32_t *old_img;
309    int w, h, stride;
310
311    old_img = cro_img_end(diff->cr_ctx, &w, &h, &stride);
312    if (diff->w != w || diff->h != h)
313        fatal("%d x %d vs. %d x %d image\n", w, h, diff->w, diff->h);
314
315    differences(diff, old_img, diff->new_img);
316    show_areas(diff, old_img);
317    free_areas(&diff->areas);
318
319    cro_img_write(diff->cr_ctx, diff->output_name);
320}
321
322
323/* ----- Diff to canvas ---------------------------------------------------- */
324
325
326static void merge_coord(int pos_a, int pos_b, int dim_a, int dim_b,
327    int *pos_res, int *res_dim)
328{
329    if (pos_a < pos_b) {
330        *pos_res = pos_a;
331        dim_b += pos_b - pos_a;
332    } else {
333        *pos_res = pos_b;
334        dim_a += pos_a - pos_b;
335    }
336    *res_dim = dim_a > dim_b ? dim_a : dim_b;
337}
338
339
340void diff_to_canvas(cairo_t *cr, int cx, int cy, float scale,
341    struct cro_ctx *old, struct cro_ctx *new,
342    const struct area *areas)
343{
344    int old_xmin, old_ymin, old_w, old_h;
345    int new_xmin, new_ymin, new_w, new_h;
346    int xmin, ymin, w, h, stride;
347    uint32_t *img_old, *img_new;
348    double x1, y1, x2, y2;
349    int sw, sh;
350    cairo_t *old_cr;
351    cairo_surface_t *s;
352
353    cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
354    sw = x2 - x1;
355    sh = y2 - y1;
356
357    /* @@@ baeh ! */
358    record_bbox((const struct record *) old,
359        &old_xmin, &old_ymin, &old_w, &old_h);
360    record_bbox((const struct record *) new,
361        &new_xmin, &new_ymin, &new_w, &new_h);
362
363    merge_coord(old_xmin, new_xmin, old_w, new_w, &xmin, &w);
364    merge_coord(old_ymin, new_ymin, old_h, new_h, &ymin, &h);
365
366    img_old = cro_img(old,
367        sw / 2.0 - (cx + xmin) * scale,
368        sh / 2.0 - (cy + ymin) * scale,
369        sw, sh,
370        scale, &old_cr, &stride);
371    img_new = cro_img(new,
372        sw / 2.0 - (cx + xmin) * scale,
373        sh / 2.0 - (cy + ymin) * scale,
374        sw, sh,
375        scale, NULL, NULL);
376
377    struct diff diff = {
378        .w = sw,
379        .h = sh,
380        .stride = stride,
381        .frame_radius = DEFAULT_FRAME_RADIUS,
382        .areas = NULL,
383    };
384
385    s = cairo_get_target(old_cr);
386    cairo_surface_flush(s);
387    differences(&diff, img_old, img_new);
388    show_areas(&diff, img_old);
389    free_areas(&diff.areas);
390    if (areas) {
391        diff.areas = (struct area *) areas;
392        show_areas(&diff, img_old);
393    }
394    cairo_surface_mark_dirty(s);
395
396    cairo_set_source_surface(cr, s, 0, 0);
397    cairo_paint(cr);
398
399    cairo_surface_destroy(s);
400    cairo_destroy(old_cr);
401    free(img_old);
402    free(img_new);
403}
404
405
406/* ----- Operations -------------------------------------------------------- */
407
408
409const struct gfx_ops diff_ops = {
410    .name = "diff",
411    .line = diff_line,
412    .poly = diff_poly,
413    .circ = diff_circ,
414    .arc = diff_arc,
415    .text = diff_text,
416    .text_width = diff_text_width,
417    .init = diff_init,
418    .end = diff_end,
419};
420

Archive Download this file

Branches:
master



interactive