1 /* webcollage-helper-cocoa --- scales and pastes one image into another
2 * xscreensaver, Copyright (c) 2002-2008 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 Cocoa implementation. See webcollage-helper.c for the
14 GDK + JPEGlib implementation.
17 #import <Cocoa/Cocoa.h>
24 static int verbose_p = 0;
26 static void write_image (NSImage *img, const char *file);
29 /* NSImage can't load PPMs by default...
32 load_ppm_image (const char *file)
34 FILE *in = fopen (file, "r");
39 char *s = fgets (buf, sizeof(buf)-1, in); /* P6 */
40 if (!s || !!strcmp (s, "P6\n"))
43 s = fgets (buf, sizeof(buf)-1, in); /* W H */
47 int w = 0, h = 0, d = 0;
48 if (2 != sscanf (buf, " %d %d \n", &w, &h))
53 s = fgets (buf, sizeof(buf)-1, in); /* 255 */
57 if (1 != sscanf (buf, " %d \n", &d))
62 int size = (w * (h+1) * 3);
63 unsigned char *bits = malloc (size);
66 int n = read (fileno (in), (void *) bits, size); /* body */
71 NSBitmapImageRep *rep =
72 [[NSBitmapImageRep alloc]
73 initWithBitmapDataPlanes: &bits
80 colorSpaceName: NSDeviceRGBColorSpace
81 bitmapFormat: NSAlphaFirstBitmapFormat
85 NSImage *image = [[NSImage alloc] initWithSize: NSMakeSize (w, h)];
86 [image addRepresentation: rep];
89 // #### 'bits' is leaked... the NSImageRep doesn't free it when freed.
96 load_image (const char *file)
98 NSImage *image = [[NSImage alloc]
99 initWithContentsOfFile:
100 [NSString stringWithCString: file
101 encoding: kCFStringEncodingUTF8]];
103 image = load_ppm_image (file);
106 fprintf (stderr, "%s: unable to load %s\n", progname, file);
115 bevel_image (NSImage *img, int bevel_pct,
116 int x, int y, int w, int h, double scale)
118 int small_size = (w > h ? h : w);
120 int bevel_size = small_size * (bevel_pct / 100.0);
124 /* Use a proportionally larger bevel size for especially small images. */
125 if (bevel_size < 20 && small_size > 40) bevel_size = 20;
126 else if (bevel_size < 10 && small_size > 20) bevel_size = 10;
127 else if (bevel_size < 5) /* too small to bother bevelling */
131 NSBitmapImageRep *rep =
132 [[NSBitmapImageRep alloc]
133 initWithBitmapDataPlanes: NULL
140 colorSpaceName: NSDeviceRGBColorSpace
141 bitmapFormat: NSAlphaFirstBitmapFormat
146 double *ramp = (double *) malloc (sizeof(*ramp) * (bevel_size + 1));
150 fprintf (stderr, "%s: out of memory (%d)\n", progname, bevel_size);
154 for (xx = 0; xx <= bevel_size; xx++)
157 ramp[xx] = xx / (double) bevel_size;
159 # else /* sinusoidal */
160 double p = (xx / (double) bevel_size);
161 double s = sin (p * M_PI / 2);
166 memset ([rep bitmapData], 0xFFFFFFFF,
167 [rep bytesPerRow] * h);
169 for (yy = 0; yy < h; yy++)
171 for (xx = 0; xx < w; xx++)
175 if (xx < bevel_size) rx = ramp[xx];
176 else if (xx >= w - bevel_size) rx = ramp[w - xx - 1];
179 if (yy < bevel_size) ry = ramp[yy];
180 else if (yy >= h - bevel_size) ry = ramp[h - yy - 1];
188 p[1] = p[2] = p[3] = 0xFF;
189 [rep setPixel:p atX:xx y:yy];
196 NSImage *bevel_img = [[NSImage alloc]
197 initWithData: [rep TIFFRepresentation]];
200 y = [img size].height - (y + h);
201 [bevel_img drawAtPoint: NSMakePoint (x, y)
202 fromRect: NSMakeRect (0, 0, w, h)
203 operation: NSCompositeDestinationIn /* Destination image
204 wherever both images are
213 fprintf (stderr, "%s: added %d%% bevel (%d px)\n", progname,
214 bevel_pct, bevel_size);
219 paste (const char *paste_file,
220 const char *base_file,
222 double opacity, int bevel_pct,
223 int from_x, int from_y, int to_x, int to_y,
226 NSImage *paste_img = load_image (paste_file);
227 NSImage *base_img = load_image (base_file);
229 int paste_w = [paste_img size].width;
230 int paste_h = [paste_img size].height;
232 int base_w = [base_img size].width;
233 int base_h = [base_img size].height;
237 fprintf (stderr, "%s: loaded %s: %dx%d\n",
238 progname, base_file, base_w, base_h);
239 fprintf (stderr, "%s: loaded %s: %dx%d\n",
240 progname, paste_file, paste_w, paste_h);
243 if (bevel_pct > 0 && paste_w > 5 && paste_h > 5)
244 bevel_image (paste_img, bevel_pct,
245 from_x, from_y, w, h,
248 int scaled_w = w * from_scale;
249 int scaled_h = h * from_scale;
251 from_y = paste_h - (from_y + h); // Cocoa flipped coordinate system
252 to_y = base_h - (to_y + scaled_h);
254 [base_img lockFocus];
255 [paste_img drawInRect: NSMakeRect (to_x, to_y, scaled_w, scaled_h)
256 fromRect: NSMakeRect (from_x, from_y, w, h)
257 operation: NSCompositeSourceOver
259 [base_img unlockFocus];
262 fprintf (stderr, "%s: pasted %dx%d (%dx%d) from %d,%d to %d,%d\n",
263 progname, w, h, scaled_w, scaled_h, from_x, from_y, to_x, to_y);
266 write_image (base_img, base_file);
272 write_image (NSImage *img, const char *file)
274 float jpeg_quality = .85;
276 // Load the NSImage's contents into an NSBitmapImageRep:
277 NSBitmapImageRep *bit_rep = [NSBitmapImageRep
278 imageRepWithData:[img TIFFRepresentation]];
280 // Write the bitmapImageRep to a JPEG file.
283 fprintf (stderr, "%s: error converting image?\n", progname);
288 fprintf (stderr, "%s: writing %s (q=%d%%) ", progname, file,
289 (int) (jpeg_quality * 100));
291 NSDictionary *props = [NSDictionary
292 dictionaryWithObject:
293 [NSNumber numberWithFloat:jpeg_quality]
294 forKey:NSImageCompressionFactor];
295 NSData *jpeg_data = [bit_rep representationUsingType:NSJPEGFileType
298 [jpeg_data writeToFile:
299 [NSString stringWithCString:file]
305 if (stat (file, &st))
308 sprintf (buf, "%.100s: %.100s", progname, file);
312 fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
320 fprintf (stderr, "usage: %s [-v] paste-file base-file\n"
321 "\t from-scale opacity\n"
322 "\t from-x from-y to-x to-y w h\n"
324 "\t Pastes paste-file into base-file.\n"
325 "\t base-file will be overwritten (with JPEG data.)\n"
326 "\t scaling is applied first: coordinates apply to scaled image.\n",
333 main (int argc, char **argv)
336 char *paste_file, *base_file, *s, dummy;
337 double from_scale, opacity;
338 int from_x, from_y, to_x, to_y, w, h, bevel_pct;
341 progname = argv[i++];
342 s = strrchr (progname, '/');
343 if (s) progname = s+1;
345 if (argc != 11 && argc != 12) usage();
347 if (!strcmp(argv[i], "-v"))
350 paste_file = argv[i++];
351 base_file = argv[i++];
353 if (*paste_file == '-') usage();
354 if (*base_file == '-') usage();
357 if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
358 if (from_scale <= 0 || from_scale > 100) usage();
361 if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
362 if (opacity <= 0 || opacity > 1) usage();
364 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
365 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
366 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
367 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
368 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
369 s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
371 bevel_pct = 10; /* #### */
377 // Much of Cocoa needs one of these to be available.
378 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
380 //Need an NSApp instance to make [NSImage TIFFRepresentation] work
381 NSApp = [NSApplication sharedApplication];
384 paste (paste_file, base_file,
385 from_scale, opacity, bevel_pct,
386 from_x, from_y, to_x, to_y,