http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[xscreensaver] / hacks / webcollage-helper.c
diff --git a/hacks/webcollage-helper.c b/hacks/webcollage-helper.c
new file mode 100644 (file)
index 0000000..0b95dc5
--- /dev/null
@@ -0,0 +1,360 @@
+/* webcollage-helper --- scales and pastes one image into another
+ * xscreensaver, Copyright (c) 2002 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(HAVE_GDK_PIXBUF) && defined(HAVE_JPEGLIB)  /* whole file */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <jpeglib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+
+char *progname;
+static int verbose_p = 0;
+
+static void add_jpeg_comment (struct jpeg_compress_struct *cinfo);
+static void write_pixbuf (GdkPixbuf *pb, const char *file);
+
+
+static void
+paste (const char *paste_file,
+       const char *base_file,
+       double from_scale,
+       double opacity,
+       int from_x, int from_y, int to_x, int to_y,
+       int w, int h)
+{
+  GdkPixbuf *paste_pb;
+  GdkPixbuf *base_pb;
+
+  int paste_w, paste_h;
+  int base_w, base_h;
+
+  paste_pb = gdk_pixbuf_new_from_file (paste_file);
+
+  if (!paste_pb)
+    {
+      fprintf (stderr, "%s: unable to load %s\n", progname, paste_file);
+      exit (1);
+    }
+
+  base_pb = gdk_pixbuf_new_from_file (base_file);
+  if (!base_pb)
+    {
+      fprintf (stderr, "%s: unable to load %s\n", progname, base_file);
+      exit (1);
+    }
+
+  paste_w = gdk_pixbuf_get_width (paste_pb);
+  paste_h = gdk_pixbuf_get_height (paste_pb);
+
+  base_w = gdk_pixbuf_get_width (base_pb);
+  base_h = gdk_pixbuf_get_height (base_pb);
+
+  if (verbose_p)
+    {
+      fprintf (stderr, "%s: loaded %s: %dx%d\n",
+               progname, base_file, base_w, base_h);
+      fprintf (stderr, "%s: loaded %s: %dx%d\n",
+               progname, paste_file, paste_w, paste_h);
+    }
+
+  if (from_scale != 1.0)
+    {
+      int new_w = paste_w * from_scale;
+      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);
+      paste_pb = new_pb;
+      paste_w = gdk_pixbuf_get_width (paste_pb);
+      paste_h = gdk_pixbuf_get_height (paste_pb);
+
+      if (verbose_p)
+        fprintf (stderr, "%s: %s: scaled to %dx%d (%.2f)\n",
+                 progname, paste_file, paste_w, paste_h, from_scale);
+    }
+
+  if (w == 0) w = paste_w - from_x;
+  if (h == 0) h = paste_h - from_y;
+
+  {
+    int ofx = from_x;
+    int ofy = from_y;
+    int otx = to_x;
+    int oty = to_y;
+    int ow = w;
+    int oh = h;
+
+    int clipped = 0;
+
+    if (from_x < 0)            /* from left out of bounds */
+      {
+        w += from_x;
+        from_x = 0;
+        clipped = 1;
+      }
+
+    if (from_y < 0)            /* from top out of bounds */
+      {
+        h += from_y;
+        from_y = 0;
+        clipped = 1;
+      }
+
+    if (to_x < 0)              /* to left out of bounds */
+      {
+        w += to_x;
+        from_x -= to_x;
+        to_x = 0;
+        clipped = 1;
+      }
+
+    if (to_y < 0)              /* to top out of bounds */
+      {
+        h += to_y;
+        from_y -= to_y;
+        to_y = 0;
+        clipped = 1;
+      }
+
+    if (from_x + w > paste_w)  /* from right out of bounds */
+      {
+        w = paste_w - from_x;
+        clipped = 1;
+      }
+
+    if (from_y + h > paste_h)  /* from bottom out of bounds */
+      {
+        h = paste_h - from_y;
+        clipped = 1;
+      }
+
+    if (to_x + w > base_w)     /* to right out of bounds */
+      {
+        w = base_w - to_x;
+        clipped = 1;
+      }
+
+    if (to_y + h > base_h)     /* to bottom out of bounds */
+      {
+        h = base_h - to_y;
+        clipped = 1;
+      }
+
+
+    if (clipped && verbose_p)
+      {
+        fprintf (stderr, "clipped from: %dx%d %d,%d %d,%d\n",
+                 ow, oh, ofx, ofy, otx, oty);
+        fprintf (stderr, "clipped   to: %dx%d %d,%d %d,%d\n",
+                 w, h, from_x, from_y, to_x, to_y);
+      }
+  }
+
+  if (opacity == 1.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);
+
+  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);
+  write_pixbuf (base_pb, base_file);
+  gdk_pixbuf_unref (base_pb);
+}
+
+
+static void
+write_pixbuf (GdkPixbuf *pb, const char *file)
+{
+  int jpeg_quality = 85;
+
+  int w = gdk_pixbuf_get_width (pb);
+  int h = gdk_pixbuf_get_height (pb);
+  guchar *data = gdk_pixbuf_get_pixels (pb);
+  int ww = gdk_pixbuf_get_rowstride (pb);
+  int channels = gdk_pixbuf_get_n_channels (pb);
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  FILE *out;
+
+  if (channels != 3)
+    {
+      fprintf (stderr, "%s: %d channels?\n", progname);
+      exit (1);
+    }
+
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+
+  out = fopen (file, "wb");
+  if (!out)
+    {
+      char buf[255];
+      sprintf (buf, "%.100s: %.100s", progname, file);
+      perror (buf);
+      exit (1);
+    }
+  else if (verbose_p)
+    fprintf (stderr, "%s: writing %s...", progname, file);
+
+  jpeg_stdio_dest (&cinfo, out);
+
+  cinfo.image_width = w;
+  cinfo.image_height = h;
+  cinfo.input_components = channels;
+  cinfo.in_color_space = JCS_RGB;
+
+  jpeg_set_defaults (&cinfo);
+  cinfo.progressive_mode = TRUE;
+  jpeg_set_quality (&cinfo, jpeg_quality, TRUE);
+
+  jpeg_start_compress (&cinfo, TRUE);
+  add_jpeg_comment (&cinfo);
+
+  {
+    guchar *d = data;
+    guchar *end = d + (ww * h);
+    while (d < end)
+      {
+        jpeg_write_scanlines (&cinfo, &d, 1);
+        d += ww;
+      }
+  }
+
+  jpeg_finish_compress (&cinfo);
+  jpeg_destroy_compress (&cinfo);
+
+  if (verbose_p)
+    {
+      struct stat st;
+      fflush (out);
+      if (fstat (fileno (out), &st))
+        {
+          char buf[255];
+          sprintf (buf, "%.100s: %.100s", progname, file);
+          perror (buf);
+          exit (1);
+        }
+      fprintf (stderr, " %dK\n", (st.st_size + 1023) / 1024);
+    }
+
+  fclose (out);
+}
+
+
+static void
+add_jpeg_comment (struct jpeg_compress_struct *cinfo)
+{
+  time_t now = time ((time_t *) 0);
+  struct tm *tm = localtime (&now);
+  const char fmt[] =
+    "\r\n"
+    "    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"
+    "\r\n"
+    "    This is what the web looked like on %d %b %Y at %I:%M:%S %p %Z. \r\n"
+    "\r\n";
+  char comment[sizeof(fmt) + 100];
+  strftime (comment, sizeof(comment)-1, fmt, tm);
+  jpeg_write_marker (cinfo, JPEG_COM, comment, strlen (comment));
+}
+
+
+static void
+usage (void)
+{
+  fprintf (stderr, "usage: %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);
+  exit (1);
+}
+
+
+int
+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;
+
+  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();
+
+  paste (paste_file, base_file,
+         from_scale, opacity,
+         from_x, from_y, to_x, to_y,
+         w, h);
+  exit (0);
+}
+
+#endif /* HAVE_GDK_PIXBUF && HAVE_JPEGLIB -- whole file */