1 /* webcollage-helper --- scales and pastes one image into another
2 * xscreensaver, Copyright (c) 2002-2005 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);
88 gdk_pixbuf_unref (pb);
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 gdk_pixbuf_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 gdk_pixbuf_unref (paste_pb);
327 write_pixbuf (base_pb, base_file);
328 gdk_pixbuf_unref (base_pb);
333 write_pixbuf (GdkPixbuf *pb, const char *file)
335 int jpeg_quality = 85;
337 int w = gdk_pixbuf_get_width (pb);
338 int h = gdk_pixbuf_get_height (pb);
339 guchar *data = gdk_pixbuf_get_pixels (pb);
340 int ww = gdk_pixbuf_get_rowstride (pb);
341 int channels = gdk_pixbuf_get_n_channels (pb);
343 struct jpeg_compress_struct cinfo;
344 struct jpeg_error_mgr jerr;
349 fprintf (stderr, "%s: %d channels?\n", progname, channels);
353 cinfo.err = jpeg_std_error (&jerr);
354 jpeg_create_compress (&cinfo);
356 out = fopen (file, "wb");
360 sprintf (buf, "%.100s: %.100s", progname, file);
365 fprintf (stderr, "%s: writing %s...", progname, file);
367 jpeg_stdio_dest (&cinfo, out);
369 cinfo.image_width = w;
370 cinfo.image_height = h;
371 cinfo.input_components = channels;
372 cinfo.in_color_space = JCS_RGB;
374 jpeg_set_defaults (&cinfo);
375 jpeg_simple_progression (&cinfo);
376 jpeg_set_quality (&cinfo, jpeg_quality, TRUE);
378 jpeg_start_compress (&cinfo, TRUE);
379 add_jpeg_comment (&cinfo);
383 guchar *end = d + (ww * h);
386 jpeg_write_scanlines (&cinfo, &d, 1);
391 jpeg_finish_compress (&cinfo);
392 jpeg_destroy_compress (&cinfo);
398 if (fstat (fileno (out), &st))
401 sprintf (buf, "%.100s: %.100s", progname, file);
405 fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
413 add_jpeg_comment (struct jpeg_compress_struct *cinfo)
415 time_t now = time ((time_t *) 0);
416 struct tm *tm = localtime (&now);
419 " Generated by WebCollage: Exterminate All Rational Thought. \r\n"
420 " Copyright (c) 1999-%Y by Jamie Zawinski <jwz@jwz.org> \r\n"
422 " http://www.jwz.org/webcollage/ \r\n"
424 " This is what the web looked like on %d %b %Y at %I:%M:%S %p %Z. \r\n"
426 char comment[sizeof(fmt) + 100];
427 strftime (comment, sizeof(comment)-1, fmt, tm);
428 jpeg_write_marker (cinfo, JPEG_COM,
429 (unsigned char *) comment,
437 fprintf (stderr, "usage: %s [-v] paste-file base-file\n"
438 "\t from-scale opacity\n"
439 "\t from-x from-y to-x to-y w h\n"
441 "\t Pastes paste-file into base-file.\n"
442 "\t base-file will be overwritten (with JPEG data.)\n"
443 "\t scaling is applied first: coordinates apply to scaled image.\n",
450 main (int argc, char **argv)
453 char *paste_file, *base_file, *s, dummy;
454 double from_scale, opacity;
455 int from_x, from_y, to_x, to_y, w, h, bevel_pct;
458 progname = argv[i++];
459 s = strrchr (progname, '/');
460 if (s) progname = s+1;
462 if (argc != 11 && argc != 12) usage();
464 if (!strcmp(argv[i], "-v"))
467 paste_file = argv[i++];
468 base_file = argv[i++];
470 if (*paste_file == '-') usage();
471 if (*base_file == '-') usage();
474 if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
475 if (from_scale <= 0 || from_scale > 100) usage();
478 if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
479 if (opacity <= 0 || opacity > 1) usage();
481 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
482 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
483 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
484 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
485 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
486 s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
488 bevel_pct = 10; /* #### */
495 #endif /* HAVE_GTK2 */
497 paste (paste_file, base_file,
498 from_scale, opacity, bevel_pct,
499 from_x, from_y, to_x, to_y,
504 #endif /* HAVE_GDK_PIXBUF && HAVE_JPEGLIB -- whole file */