1 /* webcollage-helper-cocoa --- scales and pastes one image into another
2 * xscreensaver, Copyright (c) 2002-2009 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 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
25 typedef int NSInteger;
26 typedef unsigned int NSUInteger;
31 static int verbose_p = 0;
33 static void write_image (NSImage *img, const char *file);
36 /* NSImage can't load PPMs by default...
39 load_ppm_image (const char *file)
41 FILE *in = fopen (file, "r");
46 char *s = fgets (buf, sizeof(buf)-1, in); /* P6 */
47 if (!s || !!strcmp (s, "P6\n"))
50 s = fgets (buf, sizeof(buf)-1, in); /* W H */
54 int w = 0, h = 0, d = 0;
55 if (2 != sscanf (buf, " %d %d \n", &w, &h))
60 s = fgets (buf, sizeof(buf)-1, in); /* 255 */
64 if (1 != sscanf (buf, " %d \n", &d))
69 int size = (w * (h+1) * 3);
70 unsigned char *bits = malloc (size);
73 int n = read (fileno (in), (void *) bits, size); /* body */
78 NSBitmapImageRep *rep =
79 [[NSBitmapImageRep alloc]
80 initWithBitmapDataPlanes: &bits
87 colorSpaceName: NSDeviceRGBColorSpace
88 bitmapFormat: NSAlphaFirstBitmapFormat
92 NSImage *image = [[NSImage alloc] initWithSize: NSMakeSize (w, h)];
93 [image addRepresentation: rep];
96 // #### 'bits' is leaked... the NSImageRep doesn't free it when freed.
103 load_image (const char *file)
105 NSImage *image = [[NSImage alloc]
106 initWithContentsOfFile:
107 [NSString stringWithCString: file
108 encoding: kCFStringEncodingUTF8]];
110 image = load_ppm_image (file);
113 fprintf (stderr, "%s: unable to load %s\n", progname, file);
122 bevel_image (NSImage *img, int bevel_pct,
123 int x, int y, int w, int h, double scale)
125 int small_size = (w > h ? h : w);
127 int bevel_size = small_size * (bevel_pct / 100.0);
131 /* Use a proportionally larger bevel size for especially small images. */
132 if (bevel_size < 20 && small_size > 40) bevel_size = 20;
133 else if (bevel_size < 10 && small_size > 20) bevel_size = 10;
134 else if (bevel_size < 5) /* too small to bother bevelling */
138 NSBitmapImageRep *rep =
139 [[NSBitmapImageRep alloc]
140 initWithBitmapDataPlanes: NULL
147 colorSpaceName: NSDeviceRGBColorSpace
148 bitmapFormat: NSAlphaFirstBitmapFormat
153 double *ramp = (double *) malloc (sizeof(*ramp) * (bevel_size + 1));
157 fprintf (stderr, "%s: out of memory (%d)\n", progname, bevel_size);
161 for (xx = 0; xx <= bevel_size; xx++)
164 ramp[xx] = xx / (double) bevel_size;
166 # else /* sinusoidal */
167 double p = (xx / (double) bevel_size);
168 double s = sin (p * M_PI / 2);
173 memset ([rep bitmapData], 0xFFFFFFFF,
174 [rep bytesPerRow] * h);
176 for (yy = 0; yy < h; yy++)
178 for (xx = 0; xx < w; xx++)
182 if (xx < bevel_size) rx = ramp[xx];
183 else if (xx >= w - bevel_size) rx = ramp[w - xx - 1];
186 if (yy < bevel_size) ry = ramp[yy];
187 else if (yy >= h - bevel_size) ry = ramp[h - yy - 1];
195 p[1] = p[2] = p[3] = 0xFF;
196 [rep setPixel:p atX:xx y:yy];
203 NSImage *bevel_img = [[NSImage alloc]
204 initWithData: [rep TIFFRepresentation]];
207 y = [img size].height - (y + h);
208 [bevel_img drawAtPoint: NSMakePoint (x, y)
209 fromRect: NSMakeRect (0, 0, w, h)
210 operation: NSCompositeDestinationIn /* Destination image
211 wherever both images are
220 fprintf (stderr, "%s: added %d%% bevel (%d px)\n", progname,
221 bevel_pct, bevel_size);
226 paste (const char *paste_file,
227 const char *base_file,
229 double opacity, int bevel_pct,
230 int from_x, int from_y, int to_x, int to_y,
233 NSImage *paste_img = load_image (paste_file);
234 NSImage *base_img = load_image (base_file);
236 int paste_w = [paste_img size].width;
237 int paste_h = [paste_img size].height;
239 int base_w = [base_img size].width;
240 int base_h = [base_img size].height;
244 fprintf (stderr, "%s: loaded %s: %dx%d\n",
245 progname, base_file, base_w, base_h);
246 fprintf (stderr, "%s: loaded %s: %dx%d\n",
247 progname, paste_file, paste_w, paste_h);
250 if (bevel_pct > 0 && paste_w > 5 && paste_h > 5)
251 bevel_image (paste_img, bevel_pct,
252 from_x, from_y, w, h,
255 int scaled_w = w * from_scale;
256 int scaled_h = h * from_scale;
258 from_y = paste_h - (from_y + h); // Cocoa flipped coordinate system
259 to_y = base_h - (to_y + scaled_h);
261 [base_img lockFocus];
262 [paste_img drawInRect: NSMakeRect (to_x, to_y, scaled_w, scaled_h)
263 fromRect: NSMakeRect (from_x, from_y, w, h)
264 operation: NSCompositeSourceOver
266 [base_img unlockFocus];
269 fprintf (stderr, "%s: pasted %dx%d (%dx%d) from %d,%d to %d,%d\n",
270 progname, w, h, scaled_w, scaled_h, from_x, from_y, to_x, to_y);
273 write_image (base_img, base_file);
279 write_image (NSImage *img, const char *file)
281 float jpeg_quality = .85;
283 // Load the NSImage's contents into an NSBitmapImageRep:
284 NSBitmapImageRep *bit_rep = [NSBitmapImageRep
285 imageRepWithData:[img TIFFRepresentation]];
287 // Write the bitmapImageRep to a JPEG file.
290 fprintf (stderr, "%s: error converting image?\n", progname);
295 fprintf (stderr, "%s: writing %s (q=%d%%) ", progname, file,
296 (int) (jpeg_quality * 100));
298 NSDictionary *props = [NSDictionary
299 dictionaryWithObject:
300 [NSNumber numberWithFloat:jpeg_quality]
301 forKey:NSImageCompressionFactor];
302 NSData *jpeg_data = [bit_rep representationUsingType:NSJPEGFileType
305 [jpeg_data writeToFile:
306 [NSString stringWithCString:file
307 encoding:NSISOLatin1StringEncoding]
313 if (stat (file, &st))
316 sprintf (buf, "%.100s: %.100s", progname, file);
320 fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
328 fprintf (stderr, "usage: %s [-v] paste-file base-file\n"
329 "\t from-scale opacity\n"
330 "\t from-x from-y to-x to-y w h\n"
332 "\t Pastes paste-file into base-file.\n"
333 "\t base-file will be overwritten (with JPEG data.)\n"
334 "\t scaling is applied first: coordinates apply to scaled image.\n",
341 main (int argc, char **argv)
344 char *paste_file, *base_file, *s, dummy;
345 double from_scale, opacity;
346 int from_x, from_y, to_x, to_y, w, h, bevel_pct;
349 progname = argv[i++];
350 s = strrchr (progname, '/');
351 if (s) progname = s+1;
353 if (argc != 11 && argc != 12) usage();
355 if (!strcmp(argv[i], "-v"))
358 paste_file = argv[i++];
359 base_file = argv[i++];
361 if (*paste_file == '-') usage();
362 if (*base_file == '-') usage();
365 if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
366 if (from_scale <= 0 || from_scale > 100) usage();
369 if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
370 if (opacity <= 0 || opacity > 1) usage();
372 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
373 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
374 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
375 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
376 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
377 s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
379 bevel_pct = 10; /* #### */
385 // Much of Cocoa needs one of these to be available.
386 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
388 //Need an NSApp instance to make [NSImage TIFFRepresentation] work
389 NSApp = [NSApplication sharedApplication];
392 paste (paste_file, base_file,
393 from_scale, opacity, bevel_pct,
394 from_x, from_y, to_x, to_y,