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