/* Animated frequency filter * * Dylan Simon */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX(X, Y) ({ typeof(X) _x = X; typeof(Y) _y = Y; _y > _x ? _y : _x; }) #define MIN(X, Y) ({ typeof(X) _x = X; typeof(Y) _y = Y; _y < _x ? _y : _x; }) enum color { COLOR_R = 1, COLOR_G = 2, COLOR_B = 4, COLOR_W = COLOR_R | COLOR_G | COLOR_B }; struct ffid { unsigned iw, ih; unsigned fw, fh; bool rgb; unsigned ni; double complex **fi; double complex *fv; double *iv; fftw_plan ft, it; XImage *xi; }; static inline size_t fsize(struct ffid *d) { return d->fw*d->fh*sizeof(*d->fv); } static inline size_t isize(struct ffid *d) { return d->iw*d->ih*sizeof(*d->iv); } static void init_ffi(struct ffid *d, unsigned w, unsigned h, unsigned n, bool rgb) { d->iw = w; d->ih = h; d->fw = d->iw/2+1; d->fh = d->ih; d->iv = malloc(isize(d)); d->fv = malloc(fsize(d)); d->rgb = rgb; d->ni = n; if (d->rgb) n *= 3; d->fi = malloc(n*sizeof(*d->fi)); unsigned i; for (i = 0; i < n; i ++) d->fi[i] = malloc(fsize(d)); d->ft = fftw_plan_dft_r2c_2d(d->ih, d->iw, d->iv, d->fv, FFTW_ESTIMATE); d->it = fftw_plan_dft_c2r_2d(d->ih, d->iw, d->fv, d->iv, FFTW_MEASURE); } static void fini_ffi(struct ffid *d) { XDestroyImage(d->xi); fftw_destroy_plan(d->it); fftw_destroy_plan(d->ft); unsigned i; for (i = 0; i < d->ni; i ++) free(d->fi[i]); free(d->fi); d->ni = 0; free(d->fv); free(d->iv); d->ih = 0; d->iw = 0; } static gdImagePtr open_image(const char *file) { FILE *f; gdImagePtr img = NULL; char *e = strrchr(file, '.'); if (!e || !*++e) return img; if (!(f = fopen(file, "r"))) { if (errno != EEXIST) fprintf(stderr, "%s: %m\n", file); return img; } if (!strcasecmp(e, "png")) img = gdImageCreateFromPng(f); else if (!strcasecmp(e, "jpg") || !strcasecmp(e, "jpeg")) img = gdImageCreateFromJpeg(f); else if (!strcasecmp(e, "gif")) img = gdImageCreateFromGif(f); if (!img) fprintf(stderr, "%s: error loading image\n", file); fclose(f); return img; } static gdImagePtr draw_text(unsigned w, unsigned h, char *text, char *font, double size) { if (!w) w = 256; if (!h) h = 256; if (!text || !*text) { return gdImageCreateTrueColor(w, h); } int b[8]; double s = size ?: w/strlen(text); int x, y; while (1) { char *e = gdImageStringFT(NULL, b, 0, font, s, 0, 0, 0, text); if (e) { fprintf(stderr, "error drawing text: %s\n", e); return NULL; } x = b[2] - b[6]; y = b[3] - b[7]; if (size != 0) break; if (x > w || y > h) s *= 0.99*MIN((double)w/x, (double)h/y); else if (x < 0.97*w && y < 0.97*h) s *= 0.99*MIN((double)w/x, (double)h/y); else break; } gdImagePtr img = gdImageCreateTrueColor(w, h); gdImageStringFT(img, b, gdTrueColor(255,255,255), font, s, 0, (w-x)/2-b[6], (h-y)/2-b[7], text); return img; } static void read_image(struct ffid *d, const gdImagePtr img, enum color color) { unsigned x, y; double n = 1./(d->ih*d->iw)/__builtin_popcountl(color); for (y = 0; y < d->ih; y ++) for (x = 0; x < d->iw; x ++) { int c = gdImageGetPixel(img, x, y); double v = 0; if (color & COLOR_R) v += gdImageRed(img, c) - 128; if (color & COLOR_G) v += gdImageGreen(img, c) - 128; if (color & COLOR_B) v += gdImageBlue(img, c) - 128; d->iv[d->iw*y+x] = n*v; } } static void load_image(struct ffid *d, unsigned i, const gdImagePtr img) { if (d->rgb) { unsigned c; for (c = 0; c < 3; c ++) { read_image(d, img, 1 << c); fftw_execute(d->ft); memcpy(d->fi[3*i+c], d->fv, fsize(d)); } } else { read_image(d, img, COLOR_W); fftw_execute(d->ft); memcpy(d->fi[i], d->fv, fsize(d)); } } static void draw_image(struct ffid *d, enum color color) { unsigned x, y; double m = 2./d->ni; for (y = 0; y < d->ih; y ++) for (x = 0; x < d->iw; x ++) { double v = d->iv[y*d->iw+x]; int c = v*m; if (c > 255) c = 255; else if (c < -255) c = -255; if (color & COLOR_B) d->xi->data[d->xi->bytes_per_line*y+d->xi->bits_per_pixel/8*x+0] = c < 0 ? -c : 0; if (color & COLOR_G) d->xi->data[d->xi->bytes_per_line*y+d->xi->bits_per_pixel/8*x+1] = c > 0 ? c : 0; if (color & COLOR_R) d->xi->data[d->xi->bytes_per_line*y+d->xi->bits_per_pixel/8*x+2] = c > 0 ? c : 0; } } static void add_filter(struct ffid *d, unsigned n, double lo, double hi) { if (lo > 1 || lo < 0) lo -= floor(lo); if (hi > 1 || hi < 0) hi -= floor(hi); if (lo > hi) { add_filter(d, n, 0, hi); add_filter(d, n, lo, 1); return; } if (lo >= hi || hi <= 0 || lo >= 1) return; double s = MIN(d->iw, d->ih); lo = pow(s/2, lo); hi = pow(s/2, hi); void keep(unsigned x, unsigned y, double a) { d->fv[d->fw*y+x] += a*d->fi[n][d->fw*y+x]; if (y) d->fv[d->fw*(d->fh-y)+x] += a*d->fi[n][d->fw*(d->fh-y)+x]; } unsigned y = MIN(ceil(hi)-1, d->fh/2); for (; (signed)y >= 0; y --) { double x0 = y >= lo ? 0 : MIN(sqrt(lo*lo - y*y), d->fw); double x1 = MIN(sqrt(hi*hi - y*y), d->fw); double a = modf(x0, &x0); double b = modf(x1, &x1); unsigned x = x0; if (x0 == x1) { keep(x, y, b-a); } else { if (a > 0) { keep(x,y,1-a); x ++; } for (; x < x1; x++) { keep(x,y,1); } if (b > 0) { keep(x,y,b); } } } } static void filter_images(struct ffid *d, float t, unsigned c) { memset(d->fv, 0, sizeof(*d->fv)*d->fw*d->fh); double n = d->ni; unsigned i; for (i = 0; i < d->ni; i ++) add_filter(d, (d->rgb?3:1)*i+c, i/n-t, (i+1)/n-t); d->fv[0] = 0; } static void make_image(struct ffid *d, float t) { if (d->rgb) { unsigned c; for (c = 0; c < 3; c ++) { filter_images(d, t, c); fftw_execute(d->it); draw_image(d, 1 << c); } } else { filter_images(d, t, 0); fftw_execute(d->it); draw_image(d, COLOR_W); } } static Display *Disp; static Widget App; static Window Win; static char *Defaults[] = { "ffi.width: 0", "ffi.height: 0", "ffi.font: /usr/lib/X11/fonts/TTF/Vera.ttf", "ffi.speed: 0", "ffi.size: 0", "ffi.rgb: 0", //BOOL 0 }; static XrmOptionDescRec Options[] = { {"-width", ".width", XrmoptionSepArg, 0}, {"-height", ".height", XrmoptionSepArg, 0}, {"-font", ".font", XrmoptionSepArg, 0}, {"-size", ".size", XrmoptionSepArg, 0}, {"-speed", ".speed", XrmoptionSepArg, 0}, {"-rgb", ".rgb", XrmoptionSepArg, 0}, }; static char *get_resource(const char *name) { char *val = NULL; XrmValue value; char *type; if (XrmGetResource(XtDatabase(Disp), name, "FFi.Integer", &type, &value)) val = strndup(value.addr, value.size); return val; } #define RESOURCE(VAR, NAME, NONE, EXPR) ({ \ char *VAR = get_resource(NAME); \ typeof(EXPR) _res = VAR ? EXPR : NONE; \ if (VAR) free(VAR); \ _res; \ }) int main(int argc, char **argv) { XtAppContext ctx; App = XtOpenApplication(&ctx, "ffi", Options, sizeof(Options)/sizeof(*Options), &argc, argv, Defaults, topLevelShellWidgetClass, NULL, 0); Disp = XtDisplay(App); int w = RESOURCE(s, "ffi.width", 0, atoi(s)); int h = RESOURCE(s, "ffi.height", 0, atoi(s)); char *font = get_resource("ffi.font"); double speed = RESOURCE(s, "ffi.speed", 0, strtod(s, NULL)); double size = RESOURCE(s, "ffi.size", 0, strtod(s, NULL)); int rgb = RESOURCE(s, "ffi.rgb", 0, atoi(s)); int n = argc - 1; if (n < 2) { fprintf(stderr, "Usage: %s IMAGE ...\n", argv[0]); XtDestroyWidget(App); exit(1); } if (speed == 0) speed = 4*n; gdImagePtr imgs[n]; int i; unsigned mw = 0, mh = 0; for (i = 0; i < n; i ++) { char *arg = argv[i+1]; if (!(imgs[i] = open_image(arg))) imgs[i] = draw_text(w ?: mw, h ?: mh, arg, font, size); if (!imgs[i]) exit(1); mw = MAX(mw, gdImageSX(imgs[i])); mh = MAX(mh, gdImageSY(imgs[i])); } struct ffid ffi; init_ffi(&ffi, w ?: mw, h ?: mh, n, rgb); for (i = 0; i < n; i ++) { gdImagePtr img = imgs[i]; if (gdImageSX(img) != ffi.iw || gdImageSY(img) != ffi.ih) { gdImagePtr new = gdImageCreateTrueColor(ffi.iw, ffi.ih); gdImageCopyResampled(new, img, 0, 0, 0, 0, ffi.iw, ffi.ih, gdImageSX(img), gdImageSY(img)); gdImageDestroy(img); img = new; } load_image(&ffi, i, img); gdImageDestroy(img); } XtVaSetValues(App, XtNwidth, ffi.iw, XtNheight, ffi.ih, NULL); XtRealizeWidget(App); Win = XtWindow(App); XWindowAttributes attr; XGetWindowAttributes(Disp, Win, &attr); //Wpm = XCreatePixmap(Disp, Win, attr.width, attr.height, attr.depth); ffi.xi = XCreateImage(Disp, attr.visual, attr.depth, ZPixmap, 0, NULL, ffi.iw, ffi.ih, 8, 0); ffi.xi->data = malloc(ffi.xi->bytes_per_line*ffi.xi->height); GC gc = XCreateGC(Disp, Win, 0, NULL); struct timeval t0; gettimeofday(&t0, NULL); unsigned fc = 0; double lt = 0; do { struct timeval t; gettimeofday(&t, NULL); double dt = t.tv_sec - t0.tv_sec + (double)(t.tv_usec - t0.tv_usec)/1000000; if (dt > lt + 5) { printf("%f FPS\n", fc / dt); lt = dt; } dt /= speed; make_image(&ffi, dt); XPutImage(Disp, Win, gc, ffi.xi, 0, 0, 0, 0, ffi.iw, ffi.ih); XSync(Disp, False); } while (++ fc); sleep(1); fini_ffi(&ffi); XtDestroyWidget(App); XtDestroyApplicationContext(ctx); return 0; }