http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / webcollage-helper.c
1 /* webcollage-helper --- scales and pastes one image into another
2  * xscreensaver, Copyright (c) 2002, 2003 Jamie Zawinski <jwz@jwz.org>
3  *
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 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #if defined(HAVE_GDK_PIXBUF) && defined(HAVE_JPEGLIB)  /* whole file */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25
26 #include <jpeglib.h>
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28
29
30 char *progname;
31 static int verbose_p = 0;
32
33 static void add_jpeg_comment (struct jpeg_compress_struct *cinfo);
34 static void write_pixbuf (GdkPixbuf *pb, const char *file);
35
36 static GdkPixbuf *
37 load_pixbuf (const char *file)
38 {
39   GdkPixbuf *pb;
40 #ifdef HAVE_GTK2
41   GError *err = NULL;
42
43   pb = gdk_pixbuf_new_from_file (file, &err);
44 #else  /* !HAVE_GTK2 */
45   pb = gdk_pixbuf_new_from_file (file);
46 #endif /* HAVE_GTK2 */
47
48   if (!pb)
49     {
50 #ifdef HAVE_GTK2
51       fprintf (stderr, "%s: %s\n", progname, err->message);
52       g_error_free (err);
53 #else  /* !HAVE_GTK2 */
54       fprintf (stderr, "%s: unable to load %s\n", progname, file);
55 #endif /* !HAVE_GTK2 */
56       exit (1);
57     }
58
59   return pb;
60 }
61
62 static void
63 paste (const char *paste_file,
64        const char *base_file,
65        double from_scale,
66        double opacity,
67        int from_x, int from_y, int to_x, int to_y,
68        int w, int h)
69 {
70   GdkPixbuf *paste_pb;
71   GdkPixbuf *base_pb;
72
73   int paste_w, paste_h;
74   int base_w, base_h;
75
76   paste_pb = load_pixbuf (paste_file);
77   base_pb  = load_pixbuf (base_file);
78
79   paste_w = gdk_pixbuf_get_width (paste_pb);
80   paste_h = gdk_pixbuf_get_height (paste_pb);
81
82   base_w = gdk_pixbuf_get_width (base_pb);
83   base_h = gdk_pixbuf_get_height (base_pb);
84
85   if (verbose_p)
86     {
87       fprintf (stderr, "%s: loaded %s: %dx%d\n",
88                progname, base_file, base_w, base_h);
89       fprintf (stderr, "%s: loaded %s: %dx%d\n",
90                progname, paste_file, paste_w, paste_h);
91     }
92
93   if (from_scale != 1.0)
94     {
95       int new_w = paste_w * from_scale;
96       int new_h = paste_h * from_scale;
97       GdkPixbuf *new_pb = gdk_pixbuf_scale_simple (paste_pb, new_w, new_h,
98                                                    GDK_INTERP_HYPER);
99       gdk_pixbuf_unref (paste_pb);
100       paste_pb = new_pb;
101       paste_w = gdk_pixbuf_get_width (paste_pb);
102       paste_h = gdk_pixbuf_get_height (paste_pb);
103
104       if (verbose_p)
105         fprintf (stderr, "%s: %s: scaled to %dx%d (%.2f)\n",
106                  progname, paste_file, paste_w, paste_h, from_scale);
107     }
108
109   if (w == 0) w = paste_w - from_x;
110   if (h == 0) h = paste_h - from_y;
111
112   {
113     int ofx = from_x;
114     int ofy = from_y;
115     int otx = to_x;
116     int oty = to_y;
117     int ow = w;
118     int oh = h;
119
120     int clipped = 0;
121
122     if (from_x < 0)             /* from left out of bounds */
123       {
124         w += from_x;
125         from_x = 0;
126         clipped = 1;
127       }
128
129     if (from_y < 0)             /* from top out of bounds */
130       {
131         h += from_y;
132         from_y = 0;
133         clipped = 1;
134       }
135
136     if (to_x < 0)               /* to left out of bounds */
137       {
138         w += to_x;
139         from_x -= to_x;
140         to_x = 0;
141         clipped = 1;
142       }
143
144     if (to_y < 0)               /* to top out of bounds */
145       {
146         h += to_y;
147         from_y -= to_y;
148         to_y = 0;
149         clipped = 1;
150       }
151
152     if (from_x + w > paste_w)   /* from right out of bounds */
153       {
154         w = paste_w - from_x;
155         clipped = 1;
156       }
157
158     if (from_y + h > paste_h)   /* from bottom out of bounds */
159       {
160         h = paste_h - from_y;
161         clipped = 1;
162       }
163
164     if (to_x + w > base_w)      /* to right out of bounds */
165       {
166         w = base_w - to_x;
167         clipped = 1;
168       }
169
170     if (to_y + h > base_h)      /* to bottom out of bounds */
171       {
172         h = base_h - to_y;
173         clipped = 1;
174       }
175
176
177     if (clipped && verbose_p)
178       {
179         fprintf (stderr, "clipped from: %dx%d %d,%d %d,%d\n",
180                  ow, oh, ofx, ofy, otx, oty);
181         fprintf (stderr, "clipped   to: %dx%d %d,%d %d,%d\n",
182                  w, h, from_x, from_y, to_x, to_y);
183       }
184   }
185
186   if (opacity == 1.0)
187     gdk_pixbuf_copy_area (paste_pb,
188                           from_x, from_y, w, h,
189                           base_pb,
190                           to_x, to_y);
191   else
192     gdk_pixbuf_composite (paste_pb, base_pb,
193                           to_x, to_y, w, h,
194                           to_x - from_x, to_y - from_y,
195                           1.0, 1.0,
196                           GDK_INTERP_HYPER,
197                           opacity * 255);
198
199   if (verbose_p)
200     fprintf (stderr, "%s: pasted %dx%d from %d,%d to %d,%d\n",
201              progname, paste_w, paste_h, from_x, from_y, to_x, to_y);
202
203   gdk_pixbuf_unref (paste_pb);
204   write_pixbuf (base_pb, base_file);
205   gdk_pixbuf_unref (base_pb);
206 }
207
208
209 static void
210 write_pixbuf (GdkPixbuf *pb, const char *file)
211 {
212   int jpeg_quality = 85;
213
214   int w = gdk_pixbuf_get_width (pb);
215   int h = gdk_pixbuf_get_height (pb);
216   guchar *data = gdk_pixbuf_get_pixels (pb);
217   int ww = gdk_pixbuf_get_rowstride (pb);
218   int channels = gdk_pixbuf_get_n_channels (pb);
219
220   struct jpeg_compress_struct cinfo;
221   struct jpeg_error_mgr jerr;
222   FILE *out;
223
224   if (channels != 3)
225     {
226       fprintf (stderr, "%s: %d channels?\n", progname, channels);
227       exit (1);
228     }
229
230   cinfo.err = jpeg_std_error (&jerr);
231   jpeg_create_compress (&cinfo);
232
233   out = fopen (file, "wb");
234   if (!out)
235     {
236       char buf[255];
237       sprintf (buf, "%.100s: %.100s", progname, file);
238       perror (buf);
239       exit (1);
240     }
241   else if (verbose_p)
242     fprintf (stderr, "%s: writing %s...", progname, file);
243
244   jpeg_stdio_dest (&cinfo, out);
245
246   cinfo.image_width = w;
247   cinfo.image_height = h;
248   cinfo.input_components = channels;
249   cinfo.in_color_space = JCS_RGB;
250
251   jpeg_set_defaults (&cinfo);
252   jpeg_simple_progression (&cinfo);
253   jpeg_set_quality (&cinfo, jpeg_quality, TRUE);
254
255   jpeg_start_compress (&cinfo, TRUE);
256   add_jpeg_comment (&cinfo);
257
258   {
259     guchar *d = data;
260     guchar *end = d + (ww * h);
261     while (d < end)
262       {
263         jpeg_write_scanlines (&cinfo, &d, 1);
264         d += ww;
265       }
266   }
267
268   jpeg_finish_compress (&cinfo);
269   jpeg_destroy_compress (&cinfo);
270
271   if (verbose_p)
272     {
273       struct stat st;
274       fflush (out);
275       if (fstat (fileno (out), &st))
276         {
277           char buf[255];
278           sprintf (buf, "%.100s: %.100s", progname, file);
279           perror (buf);
280           exit (1);
281         }
282       fprintf (stderr, " %luK\n", (st.st_size + 1023) / 1024);
283     }
284
285   fclose (out);
286 }
287
288
289 static void
290 add_jpeg_comment (struct jpeg_compress_struct *cinfo)
291 {
292   time_t now = time ((time_t *) 0);
293   struct tm *tm = localtime (&now);
294   const char fmt[] =
295     "\r\n"
296     "    Generated by WebCollage: Exterminate All Rational Thought. \r\n"
297     "    Copyright (c) 1999-%Y by Jamie Zawinski <jwz@jwz.org> \r\n"
298     "\r\n"
299     "        http://www.jwz.org/webcollage/ \r\n"
300     "\r\n"
301     "    This is what the web looked like on %d %b %Y at %I:%M:%S %p %Z. \r\n"
302     "\r\n";
303   char comment[sizeof(fmt) + 100];
304   strftime (comment, sizeof(comment)-1, fmt, tm);
305   jpeg_write_marker (cinfo, JPEG_COM,
306                      (unsigned char *) comment,
307                      strlen (comment));
308 }
309
310
311 static void
312 usage (void)
313 {
314   fprintf (stderr, "usage: %s [-v] paste-file base-file\n"
315            "\t from-scale opacity\n"
316            "\t from-x from-y to-x to-y w h\n"
317            "\n"
318            "\t Pastes paste-file into base-file.\n"
319            "\t base-file will be overwritten (with JPEG data.)\n"
320            "\t scaling is applied first: coordinates apply to scaled image.\n",
321            progname);
322   exit (1);
323 }
324
325
326 int
327 main (int argc, char **argv)
328 {
329   int i;
330   char *paste_file, *base_file, *s, dummy;
331   double from_scale, opacity;
332   int from_x, from_y, to_x, to_y, w, h;
333
334   i = 0;
335   progname = argv[i++];
336   s = strrchr (progname, '/');
337   if (s) progname = s+1;
338
339   if (argc != 11 && argc != 12) usage();
340
341   if (!strcmp(argv[i], "-v"))
342     verbose_p++, i++;
343
344   paste_file = argv[i++];
345   base_file = argv[i++];
346
347   if (*paste_file == '-') usage();
348   if (*base_file == '-') usage();
349
350   s = argv[i++];
351   if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
352   if (from_scale <= 0 || from_scale > 100) usage();
353
354   s = argv[i++];
355   if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
356   if (opacity <= 0 || opacity > 1) usage();
357
358   s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
359   s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
360   s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
361   s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
362   s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
363   s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
364
365   if (w < 0) usage();
366   if (h < 0) usage();
367
368 #ifdef HAVE_GTK2
369   g_type_init ();
370 #endif /* HAVE_GTK2 */
371
372   paste (paste_file, base_file,
373          from_scale, opacity,
374          from_x, from_y, to_x, to_y,
375          w, h);
376   exit (0);
377 }
378
379 #endif /* HAVE_GDK_PIXBUF && HAVE_JPEGLIB -- whole file */