summaryrefslogtreecommitdiffstats
path: root/hacks/goop.c
diff options
context:
space:
mode:
authorSimon Rettberg2018-10-16 10:08:48 +0200
committerSimon Rettberg2018-10-16 10:08:48 +0200
commitd3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch)
treecbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /hacks/goop.c
downloadxscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip
Original 5.40
Diffstat (limited to 'hacks/goop.c')
-rw-r--r--hacks/goop.c650
1 files changed, 650 insertions, 0 deletions
diff --git a/hacks/goop.c b/hacks/goop.c
new file mode 100644
index 0000000..62b704e
--- /dev/null
+++ b/hacks/goop.c
@@ -0,0 +1,650 @@
+/* xscreensaver, Copyright (c) 1997-2008 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include "spline.h"
+#include "alpha.h"
+
+
+/* This is pretty compute-intensive, probably due to the large number of
+ polygon fills. I tried introducing a scaling factor to make the spline
+ code emit fewer line segments, but that made the edges very rough.
+ However, tuning *maxVelocity, *elasticity and *delay can result in much
+ smoother looking animation. I tuned these for a 1280x1024 Indy display,
+ but I don't know whether these values will be reasonable for a slower
+ machine...
+
+ The more planes the better -- SGIs have a 12-bit pseudocolor display
+ (4096 colormap cells) which is mostly useless, except for this program,
+ where it means you can have 11 or 12 mutually-transparent objects instead
+ of only 7 or 8. But, if you are using the 12-bit visual, you should crank
+ down the velocity and elasticity, or server slowness will cause the
+ animation to look jerky (yes, it's sad but true, SGI's X server is
+ perceptibly slower when using plane masks on a 12-bit visual than on an
+ 8-bit visual.) Using -max-velocity 0.5 -elasticity 0.9 seems to work ok
+ on my Indy R5k with visual 0x27 and the bottom-of-the-line 24-bit graphics
+ board.
+
+ It might look better if each blob had an outline, which was a *slightly*
+ darker color than the center, to give them a bit more definition -- but
+ that would mean using two planes per blob. (Or maybe allocating the
+ outline colors outside of the plane-space? Then the outlines wouldn't be
+ transparent, but maybe that wouldn't be so noticeable?)
+
+ Oh, for an alpha channel... maybe I should rewrite this in GL. Then the
+ blobs could have thickness, and curved edges with specular reflections...
+ */
+
+#define SCALE 10000 /* fixed-point math, for sub-pixel motion */
+#define DEF_COUNT 12 /* When planes and count are 0, how many blobs. */
+
+#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
+#define RANDSIGN() ((random() & 1) ? 1 : -1)
+
+struct blob {
+ long x, y; /* position of midpoint */
+ long dx, dy; /* velocity and direction */
+ double torque; /* rotational speed */
+ double th; /* angle of rotation */
+ long elasticity; /* how fast they deform */
+ long max_velocity; /* speed limit */
+ long min_r, max_r; /* radius range */
+ int npoints; /* control points */
+ long *r; /* radii */
+ spline *spline;
+};
+
+struct layer {
+ int nblobs;
+ struct blob **blobs;
+ Pixmap pixmap;
+ unsigned long pixel;
+ GC gc;
+};
+
+enum goop_mode {
+ transparent,
+ opaque,
+ xor,
+ outline
+};
+
+struct goop {
+ enum goop_mode mode;
+ int width, height;
+ int nlayers;
+ struct layer **layers;
+ unsigned long background;
+ Pixmap pixmap;
+ GC pixmap_gc;
+ GC window_gc;
+ Bool additive_p;
+ Bool cmap_p;
+ int delay;
+};
+
+
+static struct blob *
+make_blob (Display *dpy, int maxx, int maxy, int size)
+{
+ struct blob *b = (struct blob *) calloc(1, sizeof(*b));
+ int i;
+ int mid;
+
+ maxx *= SCALE;
+ maxy *= SCALE;
+ size *= SCALE;
+
+ b->max_r = size/2;
+ b->min_r = size/10;
+
+ if (b->min_r < (5*SCALE)) b->min_r = (5*SCALE);
+ mid = ((b->min_r + b->max_r) / 2);
+
+ b->torque = get_float_resource (dpy, "torque", "Torque");
+ b->elasticity = SCALE * get_float_resource (dpy, "elasticity", "Elasticity");
+ b->max_velocity = SCALE * get_float_resource (dpy, "maxVelocity", "MaxVelocity");
+
+ b->x = RAND(maxx);
+ b->y = RAND(maxy);
+
+ b->dx = RAND(b->max_velocity) * RANDSIGN();
+ b->dy = RAND(b->max_velocity) * RANDSIGN();
+ b->th = frand(M_PI+M_PI) * RANDSIGN();
+ b->npoints = (random() % 5) + 5;
+
+ b->spline = make_spline (b->npoints);
+ b->r = (long *) malloc (sizeof(*b->r) * b->npoints);
+ for (i = 0; i < b->npoints; i++)
+ b->r[i] = (long) ((random() % mid) + (mid/2)) * RANDSIGN();
+ return b;
+}
+
+static void
+free_blob(struct blob *blob)
+{
+ free_spline(blob->spline);
+ free(blob->r);
+ free(blob);
+}
+
+static void
+throb_blob (struct blob *b)
+{
+ int i;
+ double frac = ((M_PI+M_PI) / b->npoints);
+
+ for (i = 0; i < b->npoints; i++)
+ {
+ long r = b->r[i];
+ long ra = (r > 0 ? r : -r);
+ double th = (b->th > 0 ? b->th : -b->th);
+ long x, y;
+
+ /* place control points evenly around perimiter, shifted by theta */
+ x = b->x + ra * cos (i * frac + th);
+ y = b->y + ra * sin (i * frac + th);
+
+ b->spline->control_x[i] = x / SCALE;
+ b->spline->control_y[i] = y / SCALE;
+
+ /* alter the radius by a random amount, in the direction in which
+ it had been going (the sign of the radius indicates direction.) */
+ ra += (RAND(b->elasticity) * (r > 0 ? 1 : -1));
+ r = ra * (r >= 0 ? 1 : -1);
+
+ /* If we've reached the end (too long or too short) reverse direction. */
+ if ((ra > b->max_r && r >= 0) ||
+ (ra < b->min_r && r < 0))
+ r = -r;
+ /* And reverse direction in mid-course once every 50 times. */
+ else if (! (random() % 50))
+ r = -r;
+
+ b->r[i] = r;
+ }
+}
+
+static void
+move_blob (struct blob *b, int maxx, int maxy)
+{
+ maxx *= SCALE;
+ maxy *= SCALE;
+
+ b->x += b->dx;
+ b->y += b->dy;
+
+ /* If we've reached the edge of the box, reverse direction. */
+ if ((b->x > maxx && b->dx >= 0) ||
+ (b->x < 0 && b->dx < 0))
+ {
+ b->dx = -b->dx;
+ }
+ if ((b->y > maxy && b->dy >= 0) ||
+ (b->y < 0 && b->dy < 0))
+ {
+ b->dy = -b->dy;
+ }
+
+ /* Alter velocity randomly. */
+ if (! (random() % 10))
+ {
+ b->dx += (RAND(b->max_velocity/2) * RANDSIGN());
+ b->dy += (RAND(b->max_velocity/2) * RANDSIGN());
+
+ /* Throttle velocity */
+ if (b->dx > b->max_velocity || b->dx < -b->max_velocity)
+ b->dx /= 2;
+ if (b->dy > b->max_velocity || b->dy < -b->max_velocity)
+ b->dy /= 2;
+ }
+
+ {
+ double th = b->th;
+ double d = (b->torque == 0 ? 0 : frand(b->torque));
+ if (th < 0)
+ th = -(th + d);
+ else
+ th += d;
+
+ if (th > (M_PI+M_PI))
+ th -= (M_PI+M_PI);
+ else if (th < 0)
+ th += (M_PI+M_PI);
+
+ b->th = (b->th > 0 ? th : -th);
+ }
+
+ /* Alter direction of rotation randomly. */
+ if (! (random() % 100))
+ b->th *= -1;
+}
+
+static void
+draw_blob (Display *dpy, Drawable drawable, GC gc, struct blob *b,
+ Bool fill_p)
+{
+ compute_closed_spline (b->spline);
+#ifdef DEBUG
+ {
+ int i;
+ for (i = 0; i < b->npoints; i++)
+ XDrawLine (dpy, drawable, gc, b->x/SCALE, b->y/SCALE,
+ b->spline->control_x[i], b->spline->control_y[i]);
+ }
+#else
+ if (fill_p)
+ XFillPolygon (dpy, drawable, gc, b->spline->points, b->spline->n_points,
+ Nonconvex, CoordModeOrigin);
+ else
+#endif
+ XDrawLines (dpy, drawable, gc, b->spline->points, b->spline->n_points,
+ CoordModeOrigin);
+}
+
+
+static struct layer *
+make_layer (Display *dpy, Window window, int width, int height, int nblobs)
+{
+ int i;
+ struct layer *layer = (struct layer *) calloc(1, sizeof(*layer));
+ int blob_min, blob_max;
+ XGCValues gcv;
+ layer->nblobs = nblobs;
+
+ layer->blobs = (struct blob **) malloc(sizeof(*layer->blobs)*layer->nblobs);
+
+ blob_max = (width < height ? width : height) / 2;
+
+ if (width < 100 || height < 100) /* tiny window */
+ blob_max *= 10;
+
+ blob_min = (blob_max * 2) / 3;
+ for (i = 0; i < layer->nblobs; i++){
+ int j = blob_max - blob_min;
+ layer->blobs[i] = make_blob (dpy, width, height,
+ (j ? random() % j : 0) + blob_min);
+ }
+
+ layer->pixmap = XCreatePixmap (dpy, window, width, height, 1);
+ layer->gc = XCreateGC (dpy, layer->pixmap, 0, &gcv);
+
+# ifdef HAVE_JWXYZ
+ jwxyz_XSetAlphaAllowed (dpy, layer->gc, True);
+# endif /* HAVE_JWXYZ */
+
+ return layer;
+}
+
+static void
+free_layer(struct layer *layer, Display *dpy)
+{
+ int i;
+ for (i = 0; i < layer->nblobs; i++){
+ free_blob(layer->blobs[i]);
+ }
+ free(layer->blobs);
+ XFreeGC(dpy, layer->gc);
+ free(layer);
+}
+
+
+#ifndef HAVE_JWXYZ
+static void
+draw_layer_plane (Display *dpy, struct layer *layer, int width, int height)
+{
+ int i;
+ for (i = 0; i < layer->nblobs; i++)
+ {
+ throb_blob (layer->blobs[i]);
+ move_blob (layer->blobs[i], width, height);
+ draw_blob (dpy, layer->pixmap, layer->gc, layer->blobs[i], True);
+ }
+}
+#endif /* !HAVE_JWXYZ */
+
+
+static void
+draw_layer_blobs (Display *dpy, Drawable drawable, GC gc,
+ struct layer *layer, int width, int height,
+ Bool fill_p)
+{
+ int i;
+ for (i = 0; i < layer->nblobs; i++)
+ {
+ draw_blob (dpy, drawable, gc, layer->blobs[i], fill_p);
+ throb_blob (layer->blobs[i]);
+ move_blob (layer->blobs[i], width, height);
+ }
+}
+
+
+static struct goop *
+make_goop (Screen *screen, Visual *visual, Window window, Colormap cmap,
+ int width, int height, long depth)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int i;
+ struct goop *goop = (struct goop *) calloc(1, sizeof(*goop));
+ XGCValues gcv;
+ int nblobs = get_integer_resource (dpy, "count", "Count");
+
+ unsigned long *plane_masks = 0;
+# ifndef HAVE_JWXYZ
+ unsigned long base_pixel = 0;
+# endif
+ char *s;
+
+ s = get_string_resource (dpy, "mode", "Mode");
+ goop->mode = transparent;
+ if (!s || !*s || !strcasecmp (s, "transparent"))
+ ;
+ else if (!strcasecmp (s, "opaque"))
+ goop->mode = opaque;
+ else if (!strcasecmp (s, "xor"))
+ goop->mode = xor;
+ else
+ fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
+ free(s);
+
+ goop->delay = get_integer_resource (dpy, "delay", "Integer");
+
+ goop->width = width;
+ goop->height = height;
+
+ goop->nlayers = get_integer_resource (dpy, "planes", "Planes");
+ if (goop->nlayers <= 0)
+ goop->nlayers = (random() % (depth-2)) + 2;
+ if (! goop->layers)
+ goop->layers = (struct layer **)
+ malloc(sizeof(*goop->layers)*goop->nlayers);
+
+ goop->additive_p = get_boolean_resource (dpy, "additive", "Additive");
+ goop->cmap_p = has_writable_cells (screen, visual);
+
+ if (mono_p && goop->mode == transparent)
+ goop->mode = opaque;
+
+# ifndef HAVE_JWXYZ
+ /* Try to allocate some color planes before committing to nlayers.
+ */
+ if (goop->mode == transparent)
+ {
+ int nplanes = goop->nlayers;
+ allocate_alpha_colors (screen, visual, cmap,
+ &nplanes, goop->additive_p, &plane_masks,
+ &base_pixel);
+ if (nplanes > 1)
+ goop->nlayers = nplanes;
+ else
+ {
+ fprintf (stderr,
+ "%s: couldn't allocate any color planes; turning transparency off.\n",
+ progname);
+ goop->mode = opaque;
+ }
+ }
+# endif /* !HAVE_JWXYZ */
+
+ {
+ int lblobs[32];
+ int total = DEF_COUNT;
+ memset (lblobs, 0, sizeof(lblobs));
+ if (nblobs <= 0)
+ while (total)
+ for (i = 0; total && i < goop->nlayers; i++)
+ lblobs[i]++, total--;
+ for (i = 0; i < goop->nlayers; i++)
+ goop->layers[i] = make_layer (dpy, window, width, height,
+ (nblobs > 0 ? nblobs : lblobs[i]));
+ }
+
+# ifndef HAVE_JWXYZ
+ if (goop->mode == transparent && plane_masks)
+ {
+ for (i = 0; i < goop->nlayers; i++)
+ goop->layers[i]->pixel = base_pixel | plane_masks[i];
+ goop->background = base_pixel;
+ }
+# endif /* !HAVE_JWXYZ */
+
+ if (plane_masks)
+ free (plane_masks);
+
+# ifndef HAVE_JWXYZ
+ if (goop->mode != transparent)
+# endif /* !HAVE_JWXYZ */
+ {
+ XColor color;
+ color.flags = DoRed|DoGreen|DoBlue;
+
+ goop->background =
+ get_pixel_resource (dpy,cmap, "background", "Background");
+
+ for (i = 0; i < goop->nlayers; i++)
+ {
+ int H = random() % 360; /* range 0-360 */
+ double S = ((double) (random()%70) + 30)/100.0; /* range 30%-100% */
+ double V = ((double) (random()%34) + 66)/100.0; /* range 66%-100% */
+ hsv_to_rgb (H, S, V, &color.red, &color.green, &color.blue);
+ if (XAllocColor (dpy, cmap, &color))
+ goop->layers[i]->pixel = color.pixel;
+ else
+ goop->layers[i]->pixel =
+ WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
+# ifdef HAVE_JWXYZ
+ if (goop->mode == transparent)
+ {
+ /* give a non-opaque alpha to the color */
+ unsigned long pixel = goop->layers[i]->pixel;
+ unsigned long amask = BlackPixelOfScreen (screen);
+ unsigned long a = (0xBBBBBBBB & amask);
+ pixel = (pixel & (~amask)) | a;
+ goop->layers[i]->pixel = pixel;
+ }
+# endif /* HAVE_JWXYZ */
+ }
+ }
+
+ goop->pixmap = XCreatePixmap (dpy, window, width, height,
+ (goop->mode == xor ? 1L : depth));
+
+ gcv.background = goop->background;
+ gcv.foreground = get_pixel_resource (dpy, cmap, "foreground", "Foreground");
+ gcv.line_width = get_integer_resource (dpy, "thickness","Thickness");
+ goop->pixmap_gc = XCreateGC (dpy, goop->pixmap, GCLineWidth, &gcv);
+ goop->window_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
+
+# ifdef HAVE_JWXYZ
+ jwxyz_XSetAlphaAllowed (dpy, goop->pixmap_gc, True);
+# endif /* HAVE_JWXYZ */
+
+ return goop;
+}
+
+/* Well, the naming of this function is
+ confusing with goop_free()... */
+static void
+free_goop (struct goop *goop, Display *dpy)
+{
+ int i;
+ for (i = 0; i < goop->nlayers; i++){
+ struct layer * layer = goop->layers[i];
+ free_layer(layer, dpy);
+ }
+ free(goop->layers);
+ XFreeGC(dpy, goop->pixmap_gc);
+ XFreeGC(dpy, goop->window_gc);
+}
+
+static void *
+goop_init (Display *dpy, Window window)
+{
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (dpy, window, &xgwa);
+ return make_goop (xgwa.screen, xgwa.visual, window, xgwa.colormap,
+ xgwa.width, xgwa.height, xgwa.depth);
+}
+
+static unsigned long
+goop_draw (Display *dpy, Window window, void *closure)
+{
+ struct goop *goop = (struct goop *) closure;
+ int i;
+
+ switch (goop->mode)
+ {
+# ifndef HAVE_JWXYZ
+ case transparent:
+
+ for (i = 0; i < goop->nlayers; i++)
+ draw_layer_plane (dpy, goop->layers[i], goop->width, goop->height);
+
+ XSetForeground (dpy, goop->pixmap_gc, goop->background);
+ XSetFunction (dpy, goop->pixmap_gc, GXcopy);
+ XSetPlaneMask (dpy, goop->pixmap_gc, AllPlanes);
+ XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
+ goop->width, goop->height);
+
+ XSetForeground (dpy, goop->pixmap_gc, ~0L);
+
+ if (!goop->cmap_p && !goop->additive_p)
+ {
+ int j;
+ for (i = 0; i < goop->nlayers; i++)
+ for (j = 0; j < goop->layers[i]->nblobs; j++)
+ draw_blob (dpy, goop->pixmap, goop->pixmap_gc,
+ goop->layers[i]->blobs[j], True);
+ XSetFunction (dpy, goop->pixmap_gc, GXclear);
+ }
+
+ for (i = 0; i < goop->nlayers; i++)
+ {
+ XSetPlaneMask (dpy, goop->pixmap_gc, goop->layers[i]->pixel);
+ draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
+ goop->layers[i], goop->width, goop->height,
+ True);
+ }
+ XCopyArea (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
+ goop->width, goop->height, 0, 0);
+ break;
+#endif /* !HAVE_JWXYZ */
+
+ case xor:
+ XSetFunction (dpy, goop->pixmap_gc, GXcopy);
+ XSetForeground (dpy, goop->pixmap_gc, 0);
+ XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
+ goop->width, goop->height);
+ XSetFunction (dpy, goop->pixmap_gc, GXxor);
+ XSetForeground (dpy, goop->pixmap_gc, 1);
+ for (i = 0; i < goop->nlayers; i++)
+ draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
+ goop->layers[i], goop->width, goop->height,
+ (goop->mode != outline));
+ XCopyPlane (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
+ goop->width, goop->height, 0, 0, 1L);
+ break;
+
+# ifdef HAVE_JWXYZ
+ case transparent:
+# endif
+ case opaque:
+ case outline:
+ XSetForeground (dpy, goop->pixmap_gc, goop->background);
+ XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
+ goop->width, goop->height);
+ for (i = 0; i < goop->nlayers; i++)
+ {
+ XSetForeground (dpy, goop->pixmap_gc, goop->layers[i]->pixel);
+ draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
+ goop->layers[i], goop->width, goop->height,
+ (goop->mode != outline));
+ }
+ XCopyArea (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
+ goop->width, goop->height, 0, 0);
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+ return goop->delay;
+}
+
+static void
+goop_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ struct goop *goop = (struct goop *) closure;
+
+ if (w != goop->width || h != goop->height)
+ {
+ struct goop *goop2 = goop_init (dpy, window);
+ free_goop(goop, dpy);
+ memcpy (goop, goop2, sizeof(*goop));
+ free(goop2);
+ }
+}
+
+static Bool
+goop_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+goop_free (Display *dpy, Window window, void *closure)
+{
+ struct goop *goop = (struct goop *) closure;
+ free_goop(goop, dpy);
+ free(goop);
+}
+
+
+
+
+static const char *goop_defaults [] = {
+ ".background: black",
+ ".foreground: yellow",
+ "*delay: 12000",
+ "*additive: true",
+ "*mode: transparent",
+ "*count: 1",
+ "*planes: 12",
+ "*thickness: 5",
+ "*torque: 0.0075",
+ "*elasticity: 0.9",
+ "*maxVelocity: 0.5",
+#ifdef HAVE_MOBILE
+ "*ignoreRotation: True",
+#endif
+ 0
+};
+
+static XrmOptionDescRec goop_options [] = {
+ { "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-count", ".count", XrmoptionSepArg, 0 },
+ { "-planes", ".planes", XrmoptionSepArg, 0 },
+ { "-mode", ".mode", XrmoptionSepArg, 0 },
+ { "-xor", ".mode", XrmoptionNoArg, "xor" },
+ { "-transparent", ".mode", XrmoptionNoArg, "transparent" },
+ { "-opaque", ".mode", XrmoptionNoArg, "opaque" },
+ { "-additive", ".additive", XrmoptionNoArg, "True" },
+ { "-subtractive", ".additive", XrmoptionNoArg, "false" },
+ { "-thickness", ".thickness", XrmoptionSepArg, 0 },
+ { "-torque", ".torque", XrmoptionSepArg, 0 },
+ { "-elasticity", ".elasticity", XrmoptionSepArg, 0 },
+ { "-max-velocity", ".maxVelocity", XrmoptionSepArg, 0 },
+ { 0, 0, 0, 0 }
+};
+
+XSCREENSAVER_MODULE ("Goop", goop)