/* webcollage-helper --- scales and pastes one image into another
- * xscreensaver, Copyright (c) 2002 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>
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)
{
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);
}
}
- 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);
}
perror (buf);
exit (1);
}
- fprintf (stderr, " %ldK\n", (st.st_size + 1023) / 1024);
+ fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
}
fclose (out);
" 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";
char comment[sizeof(fmt) + 100];
strftime (comment, sizeof(comment)-1, fmt, tm);
- jpeg_write_marker (cinfo, JPEG_COM, comment, strlen (comment));
+ jpeg_write_marker (cinfo, JPEG_COM,
+ (unsigned char *) comment,
+ strlen (comment));
}
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);
}
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);
}