1 /* glitchpeg, Copyright (c) 2018 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Insert errors into an image file, then display the corrupted result.
13 * This only works on X11 and MacOS because iOS and Android don't have
14 * access to the source files of images, only the decoded image data.
17 #include "screenhack.h"
18 #include "ximage-loader.h"
21 # include <X11/Intrinsic.h> /* for XtInputId, etc */
27 #define countof(x) (sizeof((x))/sizeof((*x)))
33 XWindowAttributes xgwa;
38 unsigned char *image_data; unsigned long image_size;
48 union { int i; char c[sizeof(int)]; } u;
54 /* Given a bitmask, returns the position and width of the field.
55 Duplicated from ximage-loader.c.
58 decode_mask (unsigned long mask, unsigned long *pos_ret,
59 unsigned long *size_ret)
62 for (i = 0; i < 32; i++)
67 for (; i < 32; i++, j++)
68 if (! (mask & (1L << i)))
76 /* Renders a scaled, cropped version of the RGBA XImage onto the window.
79 draw_image (Display *dpy, Window window, Visual *v, GC gc,
80 int w, int h, int depth, XImage *in)
83 int x, y, w2, h2, xoff, yoff;
86 unsigned long crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
87 unsigned long srpos=0, sgpos=0, sbpos=0;
88 unsigned long srmsk=0, sgmsk=0, sbmsk=0;
89 unsigned long srsiz=0, sgsiz=0, sbsiz=0;
92 // BlackPixel has alpha: 0xFF000000.
93 unsigned long black = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
95 unsigned long black = 0;
98 xs = in->width / (double) w;
99 ys = in->height / (double) h;
100 s = (xs > ys ? ys : xs);
106 /* Create a new image in the depth and bit-order of the server. */
107 out = XCreateImage (dpy, v, depth, ZPixmap, 0, 0, w, h, 8, 0);
108 out->bitmap_bit_order = in->bitmap_bit_order;
109 out->byte_order = in->byte_order;
111 out->bitmap_bit_order = BitmapBitOrder (dpy);
112 out->byte_order = ImageByteOrder (dpy);
114 out->data = (char *) malloc (out->height * out->bytes_per_line);
115 if (!out->data) abort();
117 /* Find the server's color masks.
118 We could cache this and just do it once, but it's a small number
119 of instructions compared to the per-pixel operations happening next.
121 srmsk = out->red_mask;
122 sgmsk = out->green_mask;
123 sbmsk = out->blue_mask;
125 if (!(srmsk && sgmsk && sbmsk)) abort(); /* No server color masks? */
127 decode_mask (srmsk, &srpos, &srsiz);
128 decode_mask (sgmsk, &sgpos, &sgsiz);
129 decode_mask (sbmsk, &sbpos, &sbsiz);
131 /* 'in' is RGBA in client endianness. Convert to what the server wants. */
133 crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
135 crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
137 /* Iterate the destination rectangle and pull in the corresponding
138 scaled and cropped source pixel, or black. Nearest-neighbor is fine.
140 for (y = 0; y < out->height; y++)
142 int iy = (out->height - y - yoff - 1) * s;
143 for (x = 0; x < out->width; x++)
145 int ix = (x - xoff) * s;
146 unsigned long p = (ix >= 0 && ix < in->width &&
147 iy >= 0 && iy < in->height
148 ? XGetPixel (in, ix, iy)
150 /* unsigned char a = (p >> capos) & 0xFF; */
151 unsigned char b = (p >> cbpos) & 0xFF;
152 unsigned char g = (p >> cgpos) & 0xFF;
153 unsigned char r = (p >> crpos) & 0xFF;
154 XPutPixel (out, x, y, ((r << srpos) |
161 XPutImage (dpy, window, gc, out, 0, 0, 0, 0, out->width, out->height);
166 # define BACKSLASH(c) \
167 (! ((c >= 'a' && c <= 'z') || \
168 (c >= 'A' && c <= 'Z') || \
169 (c >= '0' && c <= '9') || \
170 c == '.' || c == '_' || c == '-' || c == '+' || c == '/'))
172 /* Gets the name of an image file to load by running xscreensaver-getimage-file
173 at the end of a pipe. This can be very slow!
174 Duplicated from utils/grabclient.c
177 open_image_name_pipe (void)
181 /* /bin/sh on OS X 10.10 wipes out the PATH. */
182 const char *path = getenv("PATH");
183 char *cmd = s = malloc (strlen(path) * 2 + 100);
184 strcpy (s, "/bin/sh -c 'export PATH=");
188 if (BACKSLASH(c)) *s++ = '\\';
194 strcpy (s, "xscreensaver-getimage-file --name --absolute");
202 FILE *pipe = popen (cmd, "r");
208 /* Duplicated from utils/grabclient.c */
210 xscreensaver_getimage_file_cb (XtPointer closure, int *source, XtInputId *id)
212 /* This is not called from a signal handler, so doing stuff here is fine.
214 struct state *st = (struct state *) closure;
224 fgets (buf, sizeof(buf)-1, st->pipe);
227 XtRemoveInput (st->pipe_id);
230 /* strip trailing newline */
232 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
235 fp = fopen (file, "r");
238 fprintf (stderr, "%s: unable to read %s\n", progname, file);
242 if (fstat (fileno (fp), &stat))
244 fprintf (stderr, "%s: %s: stat failed\n", progname, file);
248 if (st->image_data) free (st->image_data);
249 st->image_size = stat.st_size;
250 st->image_data = malloc (st->image_size);
254 n = fread (s, 1, st->image_data + st->image_size - s, fp);
260 /* fprintf (stderr, "loaded %s (%lu)\n", file, st->image_size); */
262 st->start_time = time((time_t *) 0);
267 glitchpeg_init (Display *dpy, Window window)
269 struct state *st = (struct state *) calloc (1, sizeof(*st));
274 st->gc = XCreateGC (dpy, window, 0, &gcv);
276 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
278 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
279 if (st->delay < 1) st->delay = 1;
281 st->duration = get_integer_resource (st->dpy, "duration", "Integer");
282 if (st->duration < 0) st->duration = 0;
284 st->count = get_integer_resource (st->dpy, "count", "Integer");
285 if (st->count < 1) st->count = 1;
287 XClearWindow (st->dpy, st->window);
294 glitchpeg_draw (Display *dpy, Window window, void *closure)
296 struct state *st = (struct state *) closure;
298 if ((!st->image_data ||
299 time((time_t *) 0) >= st->start_time + st->duration) &&
303 st->pipe = open_image_name_pipe();
305 XtAppAddInput (XtDisplayToApplicationContext (dpy),
307 (XtPointer) (XtInputReadMask | XtInputExceptMask),
308 xscreensaver_getimage_file_cb, (XtPointer) st);
311 if (st->image_data && !st->button_down_p)
315 unsigned char *glitched = malloc (st->image_size);
316 int nn = random() % st->count;
319 memcpy (glitched, st->image_data, st->image_size);
321 for (n = 0; n < nn; n++)
324 int end = st->image_size - 255;
325 int size = end - start;
326 Bool byte_p = True; /* random() % 100; */
327 if (size <= 100) break;
330 int i = start + (random() % size);
331 if (!(random() % 10))
332 /* Take one random byte and randomize it. */
333 glitched[i] = random() % 0xFF;
335 /* Take one random byte and add 5% to it. */
337 (1 + (random() % 0x0C)) * ((random() & 1) ? 1 : -1);
341 /* Take two randomly-sized chunks of the file and swap them.
342 This tends to just destroy the image. Doesn't look good. */
343 int s2 = 2 + size * 0.05;
344 char *swap = malloc (s2);
345 int start1 = start + (random() % (size - s2));
346 int start2 = start + (random() % (size - s2));
347 memcpy (glitched + start1, swap, s2);
348 memmove (glitched + start2, glitched + start1, s2);
349 memcpy (swap, glitched + start2, s2);
354 image = image_data_to_ximage (dpy, st->xgwa.visual,
355 glitched, st->image_size);
358 if (image) /* Might be null if decode fails */
360 draw_image (dpy, window, st->xgwa.visual, st->gc,
361 st->xgwa.width, st->xgwa.height, st->xgwa.depth,
363 XDestroyImage (image);
372 glitchpeg_reshape (Display *dpy, Window window, void *closure,
373 unsigned int w, unsigned int h)
375 struct state *st = (struct state *) closure;
376 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
381 glitchpeg_event (Display *dpy, Window window, void *closure, XEvent *event)
383 struct state *st = (struct state *) closure;
384 if (event->xany.type == ButtonPress)
386 st->button_down_p = True;
389 else if (event->xany.type == ButtonRelease)
391 st->button_down_p = False;
394 else if (screenhack_event_helper (dpy, window, event))
396 st->start_time = 0; /* reload */
405 glitchpeg_free (Display *dpy, Window window, void *closure)
407 struct state *st = (struct state *) closure;
408 XFreeGC (dpy, st->gc);
409 if (st->pipe_id) XtRemoveInput (st->pipe_id);
410 if (st->pipe) fclose (st->pipe);
411 if (st->image_data) free (st->image_data);
416 static const char *glitchpeg_defaults [] = {
417 ".background: black",
418 ".foreground: white",
424 "*grabDesktopImages: False", /* HAVE_JWXYZ */
425 "*chooseRandomImages: True", /* HAVE_JWXYZ */
427 "*ignoreRotation: True",
428 "*rotateImages: True",
433 static XrmOptionDescRec glitchpeg_options [] = {
434 { "-delay", ".delay", XrmoptionSepArg, 0 },
435 { "-duration", ".duration", XrmoptionSepArg, 0 },
436 { "-count", ".count", XrmoptionSepArg, 0 },
440 XSCREENSAVER_MODULE ("GlitchPEG", glitchpeg)