From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / webcollage-helper.c
index a661b3a41f278b0d78a84aa8ea2297a134ee6d39..46ec608a8056d5abdf20b9151f80239c7b415887 100644 (file)
@@ -1,5 +1,5 @@
 /* webcollage-helper --- scales and pastes one image into another
- * xscreensaver, Copyright (c) 2002, 2003 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 2002-2017 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
  * implied warranty.
  */
 
+/* This is the GDK + JPEGlib implementation.  See webcollage-helper-cocoa.m
+   for the Cocoa implementation.
+ */
+
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <math.h>
 #include <string.h>
 #include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#undef HAVE_STDLIB_H /* stupid jconfig.h! */
 #include <jpeglib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
@@ -59,11 +65,114 @@ load_pixbuf (const char *file)
   return pb;
 }
 
+
+static void
+bevel_image (GdkPixbuf **pbP, int bevel_pct,
+             int x, int y, int w, int h)
+{
+  GdkPixbuf *pb = *pbP;
+  int small_size = (w > h ? h : w);
+
+  int bevel_size = small_size * (bevel_pct / 100.0);
+
+  /* Use a proportionally larger bevel size for especially small images. */
+  if      (bevel_size < 20 && small_size > 40) bevel_size = 20;
+  else if (bevel_size < 10 && small_size > 20) bevel_size = 10;
+  else if (bevel_size < 5)    /* too small to bother bevelling */
+    return;
+
+  /* Ensure the pixbuf has an alpha channel. */
+  if (! gdk_pixbuf_get_has_alpha (pb))
+    {
+      GdkPixbuf *pb2 = gdk_pixbuf_add_alpha (pb, FALSE, 0, 0, 0);
+      g_object_unref (pb);
+      pb = pb2;
+    }
+
+  {
+    guchar *data = gdk_pixbuf_get_pixels (pb);
+    guchar *line;
+    int rs = gdk_pixbuf_get_rowstride (pb);
+    int ch = gdk_pixbuf_get_n_channels (pb);
+    int xx, yy;
+    double *ramp = (double *) malloc (sizeof(*ramp) * (bevel_size + 1));
+
+    if (!ramp)
+      {
+        fprintf (stderr, "%s: out of memory (%d)\n", progname, bevel_size);
+        exit (1);
+      }
+
+    for (xx = 0; xx <= bevel_size; xx++)
+      {
+
+# if 0  /* linear */
+        ramp[xx] = xx / (double) bevel_size;
+
+# else /* sinusoidal */
+        double p = (xx / (double) bevel_size);
+        double s = sin (p * M_PI / 2);
+        ramp[xx] = s;
+# endif
+      }
+
+    line = data + (rs * y);
+    for (yy = 0; yy < h; yy++)
+      {
+        guchar *p = line + (x * ch);
+        for (xx = 0; xx < w; xx++)
+          {
+            double rx, ry, r;
+
+            if (xx < bevel_size)           rx = ramp[xx];
+            else if (xx >= w - bevel_size) rx = ramp[w - xx - 1];
+            else rx = 1;
+
+            if (yy < bevel_size)           ry = ramp[yy];
+            else if (yy >= h - bevel_size) ry = ramp[h - yy - 1];
+            else ry = 1;
+
+            r = rx * ry;
+            if (r != 1)
+              p[ch-1] *= r;
+
+            p += ch;
+          }
+        line += rs;
+      }
+
+#if 0  /* show the ramp */
+    line = data + (rs * y);
+    for (yy = 0; yy < h; yy++)
+      {
+        guchar *p = line + (x * ch);
+        for (xx = 0; xx < w; xx++)
+          {
+            int cc = 0;
+            for (cc = 0; cc < ch-1; cc++)
+              p[cc] = 255;
+            p += ch;
+          }
+        line += rs;
+      }
+#endif
+
+    free (ramp);
+
+    if (verbose_p)
+      fprintf (stderr, "%s: added %d%% bevel (%d px)\n", progname,
+               bevel_pct, bevel_size);
+  }
+
+  *pbP = pb;
+}
+
+
 static void
 paste (const char *paste_file,
        const char *base_file,
        double from_scale,
-       double opacity,
+       double opacity, int bevel_pct,
        int from_x, int from_y, int to_x, int to_y,
        int w, int h)
 {
@@ -96,7 +205,7 @@ paste (const char *paste_file,
       int new_h = paste_h * from_scale;
       GdkPixbuf *new_pb = gdk_pixbuf_scale_simple (paste_pb, new_w, new_h,
                                                    GDK_INTERP_HYPER);
-      gdk_pixbuf_unref (paste_pb);
+      g_object_unref (paste_pb);
       paste_pb = new_pb;
       paste_w = gdk_pixbuf_get_width (paste_pb);
       paste_h = gdk_pixbuf_get_height (paste_pb);
@@ -183,26 +292,106 @@ paste (const char *paste_file,
       }
   }
 
-  if (opacity == 1.0)
+  if (bevel_pct > 0)
+    bevel_image (&paste_pb, bevel_pct,
+                 from_x, from_y, w, h);
+
+  if (opacity == 1.0 && bevel_pct == 0)
     gdk_pixbuf_copy_area (paste_pb,
                           from_x, from_y, w, h,
                           base_pb,
                           to_x, to_y);
   else
-    gdk_pixbuf_composite (paste_pb, base_pb,
-                          to_x, to_y, w, h,
-                          to_x - from_x, to_y - from_y,
-                          1.0, 1.0,
-                          GDK_INTERP_HYPER,
-                          opacity * 255);
+    {
+      from_x++;  /* gdk_pixbuf_composite gets confused about the bevel: */
+      from_y++;  /* it leaves a stripe on the top and left if we try to */
+      to_x++;    /* start at 0,0, so pull it right and down by 1 pixel. */
+      to_y++;    /* (problem seen in gtk2-2.4.14-2.fc3) */
+      w--;
+      h--;
+
+      if (w > 0 && h > 0)
+        gdk_pixbuf_composite (paste_pb, base_pb,
+                              to_x, to_y, w, h,
+                              to_x - from_x, to_y - from_y,
+                              1.0, 1.0,
+                              GDK_INTERP_HYPER,
+                              opacity * 255);
+    }
 
   if (verbose_p)
     fprintf (stderr, "%s: pasted %dx%d from %d,%d to %d,%d\n",
              progname, paste_w, paste_h, from_x, from_y, to_x, to_y);
 
-  gdk_pixbuf_unref (paste_pb);
+  g_object_unref (paste_pb);
   write_pixbuf (base_pb, base_file);
-  gdk_pixbuf_unref (base_pb);
+  g_object_unref (base_pb);
+}
+
+
+static guint32
+parse_color (const char *s)
+{
+  static const char hex[128] =
+    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+     0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+     0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+  unsigned char r=0, g=0, b=0;
+
+  if      (!strcasecmp (s, "black")) ;
+  else if (!strcasecmp (s, "white")) r = g = b = 0xFF;
+  else if (!strcasecmp (s, "red"))   r = 0xFF;
+  else if (!strcasecmp (s, "green")) g = 0xFF;
+  else if (!strcasecmp (s, "blue"))  b = 0xFF;
+  else
+    {
+      if (*s != '#' || strlen(s) != 7)
+        {
+          fprintf (stderr, "%s: unparsable color: \"%s\"\n", progname, s);
+          exit (1);
+        }
+      s++;
+      r = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
+      g = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
+      b = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
+    }
+
+  return (r << 16) | (g << 8) | b;
+}
+
+
+static void
+create (const char *color,
+        int w, int h,
+        const char *file)
+{
+  int i;
+  GdkPixbuf *pb;
+  guint32 pixel = parse_color (color);
+  unsigned char *bytes = malloc (w * h * 3);
+  if (!bytes) abort();
+  for (i = 0; i < w * h * 3; i += 3)
+    {
+      bytes[i]   = 0xFF & (pixel >> 16);
+      bytes[i+1] = 0xFF & (pixel >> 8);
+      bytes[i+2] = 0xFF & (pixel);
+    }
+
+  pb = gdk_pixbuf_new_from_data (bytes, GDK_COLORSPACE_RGB,
+                                 FALSE, 8, /* alpha, sample size */
+                                 w, h,
+                                 w * 3,   /* rowstride */
+                                 NULL, 0);
+  if (!pb) abort();
+  write_pixbuf (pb, file);
+  g_object_unref (pb);
+  free (bytes);
 }
 
 
@@ -279,7 +468,7 @@ write_pixbuf (GdkPixbuf *pb, const char *file)
           perror (buf);
           exit (1);
         }
-      fprintf (stderr, " %luK\n", (st.st_size + 1023) / 1024);
+      fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
     }
 
   fclose (out);
@@ -296,7 +485,7 @@ add_jpeg_comment (struct jpeg_compress_struct *cinfo)
     "    Generated by WebCollage: Exterminate All Rational Thought. \r\n"
     "    Copyright (c) 1999-%Y by Jamie Zawinski <jwz@jwz.org> \r\n"
     "\r\n"
-    "        http://www.jwz.org/webcollage/ \r\n"
+    "        https://www.jwz.org/webcollage/ \r\n"
     "\r\n"
     "    This is what the web looked like on %d %b %Y at %I:%M:%S %p %Z. \r\n"
     "\r\n";
@@ -311,14 +500,18 @@ add_jpeg_comment (struct jpeg_compress_struct *cinfo)
 static void
 usage (void)
 {
-  fprintf (stderr, "usage: %s [-v] paste-file base-file\n"
+  fprintf (stderr,
+           "\nusage: %s [-v] paste-file base-file\n"
            "\t from-scale opacity\n"
            "\t from-x from-y to-x to-y w h\n"
            "\n"
            "\t Pastes paste-file into base-file.\n"
            "\t base-file will be overwritten (with JPEG data.)\n"
-           "\t scaling is applied first: coordinates apply to scaled image.\n",
-           progname);
+           "\t scaling is applied first: coordinates apply to scaled image.\n"
+           "\n"
+           "usage: %s [-v] color width height output-file\n"
+           "\t Creates a new image of a solid color.\n\n",
+           progname, progname);
   exit (1);
 }
 
@@ -329,50 +522,70 @@ main (int argc, char **argv)
   int i;
   char *paste_file, *base_file, *s, dummy;
   double from_scale, opacity;
-  int from_x, from_y, to_x, to_y, w, h;
+  int from_x, from_y, to_x, to_y, w, h, bevel_pct;
 
   i = 0;
   progname = argv[i++];
   s = strrchr (progname, '/');
   if (s) progname = s+1;
 
-  if (argc != 11 && argc != 12) usage();
-
   if (!strcmp(argv[i], "-v"))
     verbose_p++, i++;
 
-  paste_file = argv[i++];
-  base_file = argv[i++];
-
-  if (*paste_file == '-') usage();
-  if (*base_file == '-') usage();
-
-  s = argv[i++];
-  if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
-  if (from_scale <= 0 || from_scale > 100) usage();
-
-  s = argv[i++];
-  if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
-  if (opacity <= 0 || opacity > 1) usage();
-
-  s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
-  s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
-  s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
-  s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
-  s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
-  s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
-
-  if (w < 0) usage();
-  if (h < 0) usage();
+  if (argc == 11 || argc == 12)
+    {
+      paste_file = argv[i++];
+      base_file = argv[i++];
+
+      if (*paste_file == '-') usage();
+      if (*base_file == '-') usage();
+
+      s = argv[i++];
+      if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
+      if (from_scale <= 0 || from_scale > 100) usage();
+
+      s = argv[i++];
+      if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
+      if (opacity <= 0 || opacity > 1) usage();
+
+      s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
+      s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
+      s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
+      s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
+      s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
+      s = argv[i];   if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
+
+      bevel_pct = 10; /* #### */
+
+      if (w < 0) usage();
+      if (h < 0) usage();
+
+# ifdef HAVE_GTK2
+#  if !GLIB_CHECK_VERSION(2, 36 ,0)
+      g_type_init ();
+#  endif
+# endif /* HAVE_GTK2 */
+
+      paste (paste_file, base_file,
+             from_scale, opacity, bevel_pct,
+             from_x, from_y, to_x, to_y,
+             w, h);
+    }
+  else if (argc == 4 || argc == 5)
+    {
+      char *color = argv[i++];
+      s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
+      s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
+      paste_file = argv[i++];
+      if (*paste_file == '-') usage();
 
-#ifdef HAVE_GTK2
-  g_type_init ();
-#endif /* HAVE_GTK2 */
+      create (color, w, h, paste_file);
+    }
+  else
+    {
+      usage();
+    }
 
-  paste (paste_file, base_file,
-         from_scale, opacity,
-         from_x, from_y, to_x, to_y,
-         w, h);
   exit (0);
 }