1 /* webcollage-helper --- scales and pastes one image into another
2 * xscreensaver, Copyright (c) 2002-2017 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* This is the GDK + JPEGlib implementation. See webcollage-helper-cocoa.m
14 for the Cocoa implementation.
21 #if defined(HAVE_GDK_PIXBUF) && defined(HAVE_JPEGLIB) /* whole file */
28 #include <sys/types.h>
31 #undef HAVE_STDLIB_H /* stupid jconfig.h! */
33 #include <gdk-pixbuf/gdk-pixbuf.h>
37 static int verbose_p = 0;
39 static void add_jpeg_comment (struct jpeg_compress_struct *cinfo);
40 static void write_pixbuf (GdkPixbuf *pb, const char *file);
43 load_pixbuf (const char *file)
49 pb = gdk_pixbuf_new_from_file (file, &err);
50 #else /* !HAVE_GTK2 */
51 pb = gdk_pixbuf_new_from_file (file);
52 #endif /* HAVE_GTK2 */
57 fprintf (stderr, "%s: %s\n", progname, err->message);
59 #else /* !HAVE_GTK2 */
60 fprintf (stderr, "%s: unable to load %s\n", progname, file);
61 #endif /* !HAVE_GTK2 */
70 bevel_image (GdkPixbuf **pbP, int bevel_pct,
71 int x, int y, int w, int h)
74 int small_size = (w > h ? h : w);
76 int bevel_size = small_size * (bevel_pct / 100.0);
78 /* Use a proportionally larger bevel size for especially small images. */
79 if (bevel_size < 20 && small_size > 40) bevel_size = 20;
80 else if (bevel_size < 10 && small_size > 20) bevel_size = 10;
81 else if (bevel_size < 5) /* too small to bother bevelling */
84 /* Ensure the pixbuf has an alpha channel. */
85 if (! gdk_pixbuf_get_has_alpha (pb))
87 GdkPixbuf *pb2 = gdk_pixbuf_add_alpha (pb, FALSE, 0, 0, 0);
93 guchar *data = gdk_pixbuf_get_pixels (pb);
95 int rs = gdk_pixbuf_get_rowstride (pb);
96 int ch = gdk_pixbuf_get_n_channels (pb);
98 double *ramp = (double *) malloc (sizeof(*ramp) * (bevel_size + 1));
102 fprintf (stderr, "%s: out of memory (%d)\n", progname, bevel_size);
106 for (xx = 0; xx <= bevel_size; xx++)
110 ramp[xx] = xx / (double) bevel_size;
112 # else /* sinusoidal */
113 double p = (xx / (double) bevel_size);
114 double s = sin (p * M_PI / 2);
119 line = data + (rs * y);
120 for (yy = 0; yy < h; yy++)
122 guchar *p = line + (x * ch);
123 for (xx = 0; xx < w; xx++)
127 if (xx < bevel_size) rx = ramp[xx];
128 else if (xx >= w - bevel_size) rx = ramp[w - xx - 1];
131 if (yy < bevel_size) ry = ramp[yy];
132 else if (yy >= h - bevel_size) ry = ramp[h - yy - 1];
144 #if 0 /* show the ramp */
145 line = data + (rs * y);
146 for (yy = 0; yy < h; yy++)
148 guchar *p = line + (x * ch);
149 for (xx = 0; xx < w; xx++)
152 for (cc = 0; cc < ch-1; cc++)
163 fprintf (stderr, "%s: added %d%% bevel (%d px)\n", progname,
164 bevel_pct, bevel_size);
172 paste (const char *paste_file,
173 const char *base_file,
175 double opacity, int bevel_pct,
176 int from_x, int from_y, int to_x, int to_y,
182 int paste_w, paste_h;
185 paste_pb = load_pixbuf (paste_file);
186 base_pb = load_pixbuf (base_file);
188 paste_w = gdk_pixbuf_get_width (paste_pb);
189 paste_h = gdk_pixbuf_get_height (paste_pb);
191 base_w = gdk_pixbuf_get_width (base_pb);
192 base_h = gdk_pixbuf_get_height (base_pb);
196 fprintf (stderr, "%s: loaded %s: %dx%d\n",
197 progname, base_file, base_w, base_h);
198 fprintf (stderr, "%s: loaded %s: %dx%d\n",
199 progname, paste_file, paste_w, paste_h);
202 if (from_scale != 1.0)
204 int new_w = paste_w * from_scale;
205 int new_h = paste_h * from_scale;
206 GdkPixbuf *new_pb = gdk_pixbuf_scale_simple (paste_pb, new_w, new_h,
208 g_object_unref (paste_pb);
210 paste_w = gdk_pixbuf_get_width (paste_pb);
211 paste_h = gdk_pixbuf_get_height (paste_pb);
214 fprintf (stderr, "%s: %s: scaled to %dx%d (%.2f)\n",
215 progname, paste_file, paste_w, paste_h, from_scale);
218 if (w == 0) w = paste_w - from_x;
219 if (h == 0) h = paste_h - from_y;
231 if (from_x < 0) /* from left out of bounds */
238 if (from_y < 0) /* from top out of bounds */
245 if (to_x < 0) /* to left out of bounds */
253 if (to_y < 0) /* to top out of bounds */
261 if (from_x + w > paste_w) /* from right out of bounds */
263 w = paste_w - from_x;
267 if (from_y + h > paste_h) /* from bottom out of bounds */
269 h = paste_h - from_y;
273 if (to_x + w > base_w) /* to right out of bounds */
279 if (to_y + h > base_h) /* to bottom out of bounds */
286 if (clipped && verbose_p)
288 fprintf (stderr, "clipped from: %dx%d %d,%d %d,%d\n",
289 ow, oh, ofx, ofy, otx, oty);
290 fprintf (stderr, "clipped to: %dx%d %d,%d %d,%d\n",
291 w, h, from_x, from_y, to_x, to_y);
296 bevel_image (&paste_pb, bevel_pct,
297 from_x, from_y, w, h);
299 if (opacity == 1.0 && bevel_pct == 0)
300 gdk_pixbuf_copy_area (paste_pb,
301 from_x, from_y, w, h,
306 from_x++; /* gdk_pixbuf_composite gets confused about the bevel: */
307 from_y++; /* it leaves a stripe on the top and left if we try to */
308 to_x++; /* start at 0,0, so pull it right and down by 1 pixel. */
309 to_y++; /* (problem seen in gtk2-2.4.14-2.fc3) */
314 gdk_pixbuf_composite (paste_pb, base_pb,
316 to_x - from_x, to_y - from_y,
323 fprintf (stderr, "%s: pasted %dx%d from %d,%d to %d,%d\n",
324 progname, paste_w, paste_h, from_x, from_y, to_x, to_y);
326 g_object_unref (paste_pb);
327 write_pixbuf (base_pb, base_file);
328 g_object_unref (base_pb);
333 parse_color (const char *s)
335 static const char hex[128] =
336 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
337 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
338 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
339 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
340 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
341 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
342 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
343 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
345 unsigned char r=0, g=0, b=0;
347 if (!strcasecmp (s, "black")) ;
348 else if (!strcasecmp (s, "white")) r = g = b = 0xFF;
349 else if (!strcasecmp (s, "red")) r = 0xFF;
350 else if (!strcasecmp (s, "green")) g = 0xFF;
351 else if (!strcasecmp (s, "blue")) b = 0xFF;
354 if (*s != '#' || strlen(s) != 7)
356 fprintf (stderr, "%s: unparsable color: \"%s\"\n", progname, s);
360 r = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
361 g = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
362 b = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
365 return (r << 16) | (g << 8) | b;
370 create (const char *color,
376 guint32 pixel = parse_color (color);
377 unsigned char *bytes = malloc (w * h * 3);
379 for (i = 0; i < w * h * 3; i += 3)
381 bytes[i] = 0xFF & (pixel >> 16);
382 bytes[i+1] = 0xFF & (pixel >> 8);
383 bytes[i+2] = 0xFF & (pixel);
386 pb = gdk_pixbuf_new_from_data (bytes, GDK_COLORSPACE_RGB,
387 FALSE, 8, /* alpha, sample size */
389 w * 3, /* rowstride */
392 write_pixbuf (pb, file);
399 write_pixbuf (GdkPixbuf *pb, const char *file)
401 int jpeg_quality = 85;
403 int w = gdk_pixbuf_get_width (pb);
404 int h = gdk_pixbuf_get_height (pb);
405 guchar *data = gdk_pixbuf_get_pixels (pb);
406 int ww = gdk_pixbuf_get_rowstride (pb);
407 int channels = gdk_pixbuf_get_n_channels (pb);
409 struct jpeg_compress_struct cinfo;
410 struct jpeg_error_mgr jerr;
415 fprintf (stderr, "%s: %d channels?\n", progname, channels);
419 cinfo.err = jpeg_std_error (&jerr);
420 jpeg_create_compress (&cinfo);
422 out = fopen (file, "wb");
426 sprintf (buf, "%.100s: %.100s", progname, file);
431 fprintf (stderr, "%s: writing %s...", progname, file);
433 jpeg_stdio_dest (&cinfo, out);
435 cinfo.image_width = w;
436 cinfo.image_height = h;
437 cinfo.input_components = channels;
438 cinfo.in_color_space = JCS_RGB;
440 jpeg_set_defaults (&cinfo);
441 jpeg_simple_progression (&cinfo);
442 jpeg_set_quality (&cinfo, jpeg_quality, TRUE);
444 jpeg_start_compress (&cinfo, TRUE);
445 add_jpeg_comment (&cinfo);
449 guchar *end = d + (ww * h);
452 jpeg_write_scanlines (&cinfo, &d, 1);
457 jpeg_finish_compress (&cinfo);
458 jpeg_destroy_compress (&cinfo);
464 if (fstat (fileno (out), &st))
467 sprintf (buf, "%.100s: %.100s", progname, file);
471 fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
479 add_jpeg_comment (struct jpeg_compress_struct *cinfo)
481 time_t now = time ((time_t *) 0);
482 struct tm *tm = localtime (&now);
485 " Generated by WebCollage: Exterminate All Rational Thought. \r\n"
486 " Copyright (c) 1999-%Y by Jamie Zawinski <jwz@jwz.org> \r\n"
488 " https://www.jwz.org/webcollage/ \r\n"
490 " This is what the web looked like on %d %b %Y at %I:%M:%S %p %Z. \r\n"
492 char comment[sizeof(fmt) + 100];
493 strftime (comment, sizeof(comment)-1, fmt, tm);
494 jpeg_write_marker (cinfo, JPEG_COM,
495 (unsigned char *) comment,
504 "\nusage: %s [-v] paste-file base-file\n"
505 "\t from-scale opacity\n"
506 "\t from-x from-y to-x to-y w h\n"
508 "\t Pastes paste-file into base-file.\n"
509 "\t base-file will be overwritten (with JPEG data.)\n"
510 "\t scaling is applied first: coordinates apply to scaled image.\n"
512 "usage: %s [-v] color width height output-file\n"
513 "\t Creates a new image of a solid color.\n\n",
520 main (int argc, char **argv)
523 char *paste_file, *base_file, *s, dummy;
524 double from_scale, opacity;
525 int from_x, from_y, to_x, to_y, w, h, bevel_pct;
528 progname = argv[i++];
529 s = strrchr (progname, '/');
530 if (s) progname = s+1;
532 if (!strcmp(argv[i], "-v"))
535 if (argc == 11 || argc == 12)
537 paste_file = argv[i++];
538 base_file = argv[i++];
540 if (*paste_file == '-') usage();
541 if (*base_file == '-') usage();
544 if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
545 if (from_scale <= 0 || from_scale > 100) usage();
548 if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
549 if (opacity <= 0 || opacity > 1) usage();
551 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
552 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
553 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
554 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
555 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
556 s = argv[i]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
558 bevel_pct = 10; /* #### */
564 # if !GLIB_CHECK_VERSION(2, 36 ,0)
567 # endif /* HAVE_GTK2 */
569 paste (paste_file, base_file,
570 from_scale, opacity, bevel_pct,
571 from_x, from_y, to_x, to_y,
574 else if (argc == 4 || argc == 5)
576 char *color = argv[i++];
577 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
578 s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
579 paste_file = argv[i++];
580 if (*paste_file == '-') usage();
582 create (color, w, h, paste_file);
592 #endif /* HAVE_GDK_PIXBUF && HAVE_JPEGLIB -- whole file */