1 /* webcollage-helper-cocoa --- scales and pastes one image into another
2 * xscreensaver, Copyright (c) 2002-2017 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: NSUTF8StringEncoding]];
110 image = load_ppm_image (file);
113 fprintf (stderr, "%s: unable to load %s\n", progname, file);
118 // [NSImage size] defaults to the image size in points instead of pixels,
119 // so if an image file specified "pixels per inch" we can end up with
120 // absurdly sized images. Set it back to 1:1 pixel:point.
122 NSImageRep *rep = [image.representations objectAtIndex:0];
123 image.size = NSMakeSize (rep.pixelsWide, rep.pixelsHigh);
130 bevel_image (NSImage *img, int bevel_pct,
131 int x, int y, int w, int h, double scale)
133 int small_size = (w > h ? h : w);
135 int bevel_size = small_size * (bevel_pct / 100.0);
139 /* Use a proportionally larger bevel size for especially small images. */
140 if (bevel_size < 20 && small_size > 40) bevel_size = 20;
141 else if (bevel_size < 10 && small_size > 20) bevel_size = 10;
142 else if (bevel_size < 5) /* too small to bother bevelling */
146 NSBitmapImageRep *rep =
147 [[NSBitmapImageRep alloc]
148 initWithBitmapDataPlanes: NULL
155 colorSpaceName: NSDeviceRGBColorSpace
156 bitmapFormat: NSAlphaFirstBitmapFormat
161 double *ramp = (double *) malloc (sizeof(*ramp) * (bevel_size + 1));
165 fprintf (stderr, "%s: out of memory (%d)\n", progname, bevel_size);
169 for (xx = 0; xx <= bevel_size; xx++)
172 ramp[xx] = xx / (double) bevel_size;
174 # else /* sinusoidal */
175 double p = (xx / (double) bevel_size);
176 double s = sin (p * M_PI / 2);
181 memset ([rep bitmapData], 0xFFFFFFFF,
182 [rep bytesPerRow] * h);
184 for (yy = 0; yy < h; yy++)
186 for (xx = 0; xx < w; xx++)
190 if (xx < bevel_size) rx = ramp[xx];
191 else if (xx >= w - bevel_size) rx = ramp[w - xx - 1];
194 if (yy < bevel_size) ry = ramp[yy];
195 else if (yy >= h - bevel_size) ry = ramp[h - yy - 1];
203 p[1] = p[2] = p[3] = 0xFF;
204 [rep setPixel:p atX:xx y:yy];
211 NSImage *bevel_img = [[NSImage alloc]
212 initWithData: [rep TIFFRepresentation]];
215 y = [img size].height - (y + h);
216 [bevel_img drawAtPoint: NSMakePoint (x, y)
217 fromRect: NSMakeRect (0, 0, w, h)
218 operation: NSCompositeDestinationIn /* Destination image
219 wherever both images are
229 fprintf (stderr, "%s: added %d%% bevel (%d px)\n", progname,
230 bevel_pct, bevel_size);
235 paste (const char *paste_file,
236 const char *base_file,
238 double opacity, int bevel_pct,
239 int from_x, int from_y, int to_x, int to_y,
242 NSImage *paste_img = load_image (paste_file);
243 NSImage *base_img = load_image (base_file);
245 int paste_w = [paste_img size].width;
246 int paste_h = [paste_img size].height;
248 int base_w = [base_img size].width;
249 int base_h = [base_img size].height;
253 fprintf (stderr, "%s: loaded %s: %dx%d\n",
254 progname, base_file, base_w, base_h);
255 fprintf (stderr, "%s: loaded %s: %dx%d\n",
256 progname, paste_file, paste_w, paste_h);
259 if (bevel_pct > 0 && paste_w > 5 && paste_h > 5)
260 bevel_image (paste_img, bevel_pct,
261 from_x, from_y, w, h,
264 int scaled_w = w * from_scale;
265 int scaled_h = h * from_scale;
267 from_y = paste_h - (from_y + h); // Cocoa flipped coordinate system
268 to_y = base_h - (to_y + scaled_h);
270 [base_img lockFocus];
271 [paste_img drawInRect: NSMakeRect (to_x, to_y, scaled_w, scaled_h)
272 fromRect: NSMakeRect (from_x, from_y, w, h)
273 operation: NSCompositeSourceOver
275 [base_img unlockFocus];
278 fprintf (stderr, "%s: pasted %dx%d (%dx%d) from %d,%d to %d,%d\n",
279 progname, w, h, scaled_w, scaled_h, from_x, from_y, to_x, to_y);
282 write_image (base_img, base_file);
288 parse_color (const char *s)
290 static const char hex[128] =
291 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
292 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
293 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
294 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
295 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
296 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
297 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
298 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
300 unsigned char r=0, g=0, b=0;
302 if (!strcasecmp (s, "black")) ;
303 else if (!strcasecmp (s, "white")) r = g = b = 0xFF;
304 else if (!strcasecmp (s, "red")) r = 0xFF;
305 else if (!strcasecmp (s, "green")) g = 0xFF;
306 else if (!strcasecmp (s, "blue")) b = 0xFF;
309 if (*s != '#' || strlen(s) != 7)
311 fprintf (stderr, "%s: unparsable color: \"%s\"\n", progname, s);
315 r = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
316 g = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
317 b = (hex[(int) s[0]] << 4) | hex[(int) s[1]], s += 2;
320 return [NSColor colorWithRed: r / 255.0
328 create (const char *color,
332 NSColor *c = parse_color (color);
333 NSImage *img = [[NSImage alloc] initWithSize:NSMakeSize(w, h)];
335 [c drawSwatchInRect:NSMakeRect(0, 0, w, h)];
337 write_image (img, file);
343 write_image (NSImage *img, const char *file)
345 float jpeg_quality = .85;
347 // Load the NSImage's contents into an NSBitmapImageRep:
348 NSBitmapImageRep *bit_rep = [NSBitmapImageRep
349 imageRepWithData:[img TIFFRepresentation]];
351 // Write the bitmapImageRep to a JPEG file.
354 fprintf (stderr, "%s: error converting image?\n", progname);
359 fprintf (stderr, "%s: writing %s (q=%d%%) ", progname, file,
360 (int) (jpeg_quality * 100));
362 NSDictionary *props = [NSDictionary
363 dictionaryWithObject:
364 [NSNumber numberWithFloat:jpeg_quality]
365 forKey:NSImageCompressionFactor];
366 NSData *jpeg_data = [bit_rep representationUsingType:NSJPEGFileType
369 [jpeg_data writeToFile:
370 [NSString stringWithCString:file
371 encoding:NSISOLatin1StringEncoding]
377 if (stat (file, &st))
380 sprintf (buf, "%.100s: %.100s", progname, file);
384 fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
393 "\nusage: %s [-v] paste-file base-file\n"
394 "\t from-scale opacity\n"
395 "\t from-x from-y to-x to-y w h\n"
397 "\t Pastes paste-file into base-file.\n"
398 "\t base-file will be overwritten (with JPEG data.)\n"
399 "\t scaling is applied first: coordinates apply to scaled image.\n"
401 "usage: %s [-v] color width height output-file\n"
402 "\t Creates a new image of a solid color.\n\n",
409 main (int argc, char **argv)
412 char *paste_file, *base_file, *s, dummy;
413 double from_scale, opacity;
414 int from_x, from_y, to_x, to_y, w, h, bevel_pct;
417 progname = argv[i++];
418 s = strrchr (progname, '/');
419 if (s) progname = s+1;
421 // Much of Cocoa needs one of these to be available.
422 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
424 //Need an NSApp instance to make [NSImage TIFFRepresentation] work
425 NSApp = [NSApplication sharedApplication];
428 if (!strcmp(argv[i], "-v"))
431 if (argc == 11 || argc == 12)
433 paste_file = argv[i++];
434 base_file = argv[i++];
436 if (*paste_file == '-') usage();
437 if (*base_file == '-') usage();
440 if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
441 if (from_scale <= 0 || from_scale > 100) usage();
444 if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
445 if (opacity <= 0 || opacity > 1) usage();
447 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
448 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
449 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
450 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
451 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
452 s = argv[i]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
454 bevel_pct = 10; /* #### */
459 if (w == 0 || h == 0 ||
460 w > 10240 || h > 10240) {
461 fprintf (stderr, "%s: absurd size: %d x %d\n", progname, w, h);
465 paste (paste_file, base_file,
466 from_scale, opacity, bevel_pct,
467 from_x, from_y, to_x, to_y,
470 else if (argc == 4 || argc == 5)
472 char *color = argv[i++];
473 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
474 s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
475 paste_file = argv[i++];
476 if (*paste_file == '-') usage();
478 create (color, w, h, paste_file);