1 /* webcollage-helper-cocoa --- scales and pastes one image into another
2 * xscreensaver, Copyright (c) 2002-2018 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.
350 // If the local display is Retina, this doubles the size of the output JPEG.
351 NSBitmapImageRep *bit_rep = [NSBitmapImageRep
352 imageRepWithData:[img TIFFRepresentation]];
354 // Render the image into a rep using pixels instead of points.
355 NSBitmapImageRep *bit_rep = [[NSBitmapImageRep alloc]
356 initWithBitmapDataPlanes:NULL
357 pixelsWide:[img size].width
358 pixelsHigh:[img size].height
363 colorSpaceName:NSCalibratedRGBColorSpace
366 bit_rep.size = [img size];
367 [NSGraphicsContext saveGraphicsState];
368 [NSGraphicsContext setCurrentContext:
370 graphicsContextWithBitmapImageRep:bit_rep]];
371 [img drawInRect:NSMakeRect(0, 0, [img size].width, [img size].height)
372 fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
373 [NSGraphicsContext restoreGraphicsState];
376 // Write the bitmapImageRep to a JPEG file.
379 fprintf (stderr, "%s: error converting image?\n", progname);
384 fprintf (stderr, "%s: writing %s (q=%d%%) ", progname, file,
385 (int) (jpeg_quality * 100));
387 NSData *jpeg_data = [bit_rep representationUsingType:NSJPEGFileType
388 properties:@{ NSImageCompressionFactor:
389 [NSNumber numberWithFloat:
391 [jpeg_data writeToFile:
392 [NSString stringWithCString:file
393 encoding:NSISOLatin1StringEncoding]
399 if (stat (file, &st))
402 sprintf (buf, "%.100s: %.100s", progname, file);
406 fprintf (stderr, " %luK\n", ((unsigned long) st.st_size + 1023) / 1024);
415 "\nusage: %s [-v] paste-file base-file\n"
416 "\t from-scale opacity\n"
417 "\t from-x from-y to-x to-y w h\n"
419 "\t Pastes paste-file into base-file.\n"
420 "\t base-file will be overwritten (with JPEG data.)\n"
421 "\t scaling is applied first: coordinates apply to scaled image.\n"
423 "usage: %s [-v] color width height output-file\n"
424 "\t Creates a new image of a solid color.\n\n",
431 main (int argc, char **argv)
434 char *paste_file, *base_file, *s, dummy;
435 double from_scale, opacity;
436 int from_x, from_y, to_x, to_y, w, h, bevel_pct;
439 progname = argv[i++];
440 s = strrchr (progname, '/');
441 if (s) progname = s+1;
443 // Much of Cocoa needs one of these to be available.
444 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
446 //Need an NSApp instance to make [NSImage TIFFRepresentation] work
447 NSApp = [NSApplication sharedApplication];
450 if (!strcmp(argv[i], "-v"))
453 if (argc == 11 || argc == 12)
455 paste_file = argv[i++];
456 base_file = argv[i++];
458 if (*paste_file == '-') usage();
459 if (*base_file == '-') usage();
462 if (1 != sscanf (s, " %lf %c", &from_scale, &dummy)) usage();
463 if (from_scale <= 0 || from_scale > 100) usage();
466 if (1 != sscanf (s, " %lf %c", &opacity, &dummy)) usage();
467 if (opacity <= 0 || opacity > 1) usage();
469 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_x, &dummy)) usage();
470 s = argv[i++]; if (1 != sscanf (s, " %d %c", &from_y, &dummy)) usage();
471 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_x, &dummy)) usage();
472 s = argv[i++]; if (1 != sscanf (s, " %d %c", &to_y, &dummy)) usage();
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();
476 bevel_pct = 10; /* #### */
481 if (w == 0 || h == 0 ||
482 w > 10240 || h > 10240) {
483 fprintf (stderr, "%s: absurd size: %d x %d\n", progname, w, h);
487 paste (paste_file, base_file,
488 from_scale, opacity, bevel_pct,
489 from_x, from_y, to_x, to_y,
492 else if (argc == 4 || argc == 5)
494 char *color = argv[i++];
495 s = argv[i++]; if (1 != sscanf (s, " %d %c", &w, &dummy)) usage();
496 s = argv[i++]; if (1 != sscanf (s, " %d %c", &h, &dummy)) usage();
497 paste_file = argv[i++];
498 if (*paste_file == '-') usage();
500 create (color, w, h, paste_file);