9e7b57b330a5bdd34793ab1c2f9cd4af9093f702
[xscreensaver] / jwxyz / jwxyz.m
1 /* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* JWXYZ Is Not Xlib.
13
14    But it's a bunch of function definitions that bear some resemblance to
15    Xlib and that do Cocoa-ish things that bear some resemblance to the
16    things that Xlib might have done.
17
18    This is the original version of jwxyz for MacOS and iOS.
19    The version used by Android is in jwxyz-gl.c and jwxyz-common.c.
20    Both versions depend on jwxyz-cocoa.m.
21  */
22
23 #ifdef JWXYZ_QUARTZ // entire file
24
25 #import <stdlib.h>
26 #import <stdint.h>
27 #import <wchar.h>
28
29 #ifdef USE_IPHONE
30 # import <UIKit/UIKit.h>
31 # import <UIKit/UIScreen.h>
32 # import <QuartzCore/QuartzCore.h>
33 # define NSView  UIView
34 # define NSRect  CGRect
35 # define NSPoint CGPoint
36 # define NSSize  CGSize
37 # define NSColor UIColor
38 # define NSImage UIImage
39 # define NSEvent UIEvent
40 # define NSFont  UIFont
41 # define NSGlyph CGGlyph
42 # define NSWindow UIWindow
43 # define NSMakeSize   CGSizeMake
44 # define NSBezierPath UIBezierPath
45 # define colorWithDeviceRed colorWithRed
46 #else
47 # import <Cocoa/Cocoa.h>
48 #endif
49
50 #import <CoreText/CTFont.h>
51 #import <CoreText/CTLine.h>
52
53 #import "jwxyzI.h"
54 #import "jwxyz-cocoa.h"
55 #import "jwxyz-timers.h"
56 #import "yarandom.h"
57 #import "utf8wc.h"
58 #import "xft.h"
59
60
61 struct jwxyz_Display {
62   const struct jwxyz_vtbl *vtbl; // Must come first.
63
64   Window main_window;
65   CGBitmapInfo bitmap_info;
66   Visual visual;
67   struct jwxyz_sources_data *timers_data;
68
69 # ifndef USE_IPHONE
70   CGDirectDisplayID cgdpy;  /* ...of the one and only Window, main_window.
71                                This can change if the window is dragged to
72                                a different screen. */
73 # endif
74
75   CGColorSpaceRef colorspace;  /* Color space of this screen.  We tag all of
76                                   our images with this to avoid translation
77                                   when rendering. */
78
79   unsigned long window_background;
80 };
81
82 struct jwxyz_GC {
83   XGCValues gcv;
84   unsigned int depth;
85   CGImageRef clip_mask;  // CGImage copy of the Pixmap in gcv.clip_mask
86 };
87
88
89 // 8/16/24/32bpp -> 32bpp image conversion.
90 // Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24
91 // bits and an optional byte order swap.
92
93 // This type encodes such a conversion.
94 typedef unsigned convert_mode_t;
95
96 // It's rotate, then swap.
97 // A rotation here shifts bytes forward in memory. On x86/ARM, that's a left
98 // rotate, and on PowerPC, a rightward rotation.
99 static const convert_mode_t CONVERT_MODE_ROTATE_MASK = 0x3;
100 static const convert_mode_t CONVERT_MODE_SWAP = 0x4;
101
102
103 #if defined __LITTLE_ENDIAN__
104 # define PAD(r, g, b, a) ((r) | ((g) << 8) | ((b) << 16) | ((a) << 24))
105 #elif defined __BIG_ENDIAN__
106 # define PAD(r, g, b, a) (((r) << 24) | ((g) << 16) | ((b) << 8) | (a))
107 #else
108 # error "Can't determine system endianness."
109 #endif
110
111
112 // Converts an array of pixels ('src') from one format to another, placing the
113 // result in 'dest', according to the pixel conversion mode 'mode'.
114 static void
115 convert_row (uint32_t *dest, const void *src, size_t count,
116              convert_mode_t mode, size_t src_bpp)
117 {
118   Assert (src_bpp == 8 || src_bpp == 24 || src_bpp == 16 || src_bpp == 32,
119           "weird bpp");
120
121   // This works OK iff src == dest or src and dest do not overlap.
122
123   if (!mode && src_bpp == 32) {
124     if (src != dest)
125       memcpy (dest, src, count * 4);
126     return;
127   }
128
129   // This is correct, but not fast.
130   convert_mode_t rot = (mode & CONVERT_MODE_ROTATE_MASK) * 8;
131   convert_mode_t flip = mode & CONVERT_MODE_SWAP;
132
133   src_bpp /= 8;
134
135   uint32_t *dest_end = dest + count;
136   while (dest != dest_end) {
137     uint32_t x;
138
139     const uint8_t *src8 = (const uint8_t *)src;
140     switch (src_bpp) {
141       case 4:
142         x = *(const uint32_t *)src;
143         break;
144       case 3:
145         x = PAD(src8[0], src8[1], src8[2], 0xff);
146         break;
147       case 2:
148         x = PAD(src8[0], src8[0], src8[0], src8[1]);
149         break;
150       case 1:
151         x = PAD(src8[0], src8[0], src8[0], 0xff);
152         break;
153     }
154
155     src = (const uint8_t *)src + src_bpp;
156
157     /* The naive (i.e. ubiquitous) portable implementation of bitwise rotation,
158        for 32-bit integers, is:
159
160        (x << rot) | (x >> (32 - rot))
161
162        This works nearly everywhere. Compilers on x86 wil generally recognize
163        the idiom and convert it to a ROL instruction. But there's a problem
164        here: according to the C specification, bit shifts greater than or equal
165        to the length of the integer are undefined. And if rot = 0:
166        1. (x << 0) | (x >> (32 - 0))
167        2. (x << 0) | (x >> 32)
168        3. (x << 0) | (Undefined!)
169
170        Still, when the compiler converts this to a ROL on x86, everything works
171        as intended. But, there are two additional problems when Clang does
172        compile-time constant expression evaluation with the (x >> 32)
173        expression:
174        1. Instead of evaluating it to something reasonable (either 0, like a
175           human would intuitively expect, or x, like x86 would with SHR), Clang
176           seems to pull a value out of nowhere, like -1, or some other random
177           number.
178        2. Clang's warning for this, -Wshift-count-overflow, only works when the
179           shift count is a literal constant, as opposed to an arbitrary
180           expression that is optimized down to a constant.
181        Put together, this means that the assertions in
182        jwxyz_quartz_make_display with convert_px break with the above naive
183        rotation, but only for a release build.
184
185        http://blog.regehr.org/archives/1063
186        http://llvm.org/bugs/show_bug.cgi?id=17332
187        As described in those links, there is a solution here: Masking the
188        undefined shift with '& 31' as below makes the experesion well-defined
189        again. And LLVM is set to pick up on this safe version of the idiom and
190        use a rotation instruction on architectures (like x86) that support it,
191        just like it does with the unsafe version.
192
193        Too bad LLVM doesn't want to pick up on that particular optimization
194        here. Oh well. At least this code usually isn't critical w.r.t.
195        performance.
196      */
197
198 # if defined __LITTLE_ENDIAN__
199     x = (x << rot) | (x >> ((32 - rot) & 31));
200 # elif defined __BIG_ENDIAN__
201     x = (x >> rot) | (x << ((32 - rot) & 31));
202 # endif
203
204     if (flip)
205       x = __builtin_bswap32(x); // LLVM/GCC built-in function.
206
207     *dest = x;
208     ++dest;
209   }
210 }
211
212
213 // Converts a single pixel.
214 static uint32_t
215 convert_px (uint32_t px, convert_mode_t mode)
216 {
217   convert_row (&px, &px, 1, mode, 32);
218   return px;
219 }
220
221
222 // This returns the inverse conversion mode, such that:
223 // pixel
224 //   == convert_px(convert_px(pixel, mode), convert_mode_invert(mode))
225 //   == convert_px(convert_px(pixel, convert_mode_invert(mode)), mode)
226 static convert_mode_t
227 convert_mode_invert (convert_mode_t mode)
228 {
229   // swap(0); rot(n) == rot(n); swap(0)
230   // swap(1); rot(n) == rot(-n); swap(1)
231   return mode & CONVERT_MODE_SWAP ? mode : CONVERT_MODE_ROTATE_MASK & -mode;
232 }
233
234
235 // This combines two conversions into one, such that:
236 // convert_px(convert_px(pixel, mode0), mode1)
237 //   == convert_px(pixel, convert_mode_merge(mode0, mode1))
238 static convert_mode_t
239 convert_mode_merge (convert_mode_t m0, convert_mode_t m1)
240 {
241   // rot(r0); swap(s0); rot(r1); swap(s1)
242   // rot(r0); rot(s0 ? -r1 : r1); swap(s0); swap(s1)
243   // rot(r0 + (s0 ? -r1 : r1)); swap(s0 + s1)
244   return
245     ((m0 + (m0 & CONVERT_MODE_SWAP ? -m1 : m1)) & CONVERT_MODE_ROTATE_MASK) |
246     ((m0 ^ m1) & CONVERT_MODE_SWAP);
247 }
248
249
250 // This returns a conversion mode that converts an arbitrary 32-bit format
251 // specified by bitmap_info to RGBA.
252 static convert_mode_t
253 convert_mode_to_rgba (CGBitmapInfo bitmap_info)
254 {
255   // Former default: kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
256   // i.e. BGRA
257   // red   = 0x00FF0000;
258   // green = 0x0000FF00;
259   // blue  = 0x000000FF;
260
261   // RGBA: kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big
262
263   CGImageAlphaInfo alpha_info =
264     (CGImageAlphaInfo)(bitmap_info & kCGBitmapAlphaInfoMask);
265
266   Assert (! (bitmap_info & kCGBitmapFloatComponents),
267           "kCGBitmapFloatComponents unsupported");
268   Assert (alpha_info != kCGImageAlphaOnly, "kCGImageAlphaOnly not supported");
269
270   convert_mode_t rot = alpha_info == kCGImageAlphaFirst ||
271                        alpha_info == kCGImageAlphaPremultipliedFirst ||
272                        alpha_info == kCGImageAlphaNoneSkipFirst ?
273                        3 : 0;
274
275   CGBitmapInfo byte_order = bitmap_info & kCGBitmapByteOrderMask;
276
277   Assert (byte_order == kCGBitmapByteOrder32Little ||
278           byte_order == kCGBitmapByteOrder32Big,
279           "byte order not supported");
280
281   convert_mode_t swap = byte_order == kCGBitmapByteOrder32Little ?
282                         CONVERT_MODE_SWAP : 0;
283   if (swap)
284     rot = CONVERT_MODE_ROTATE_MASK & -rot;
285   return swap | rot;
286 }
287
288
289 union color_bytes
290 {
291   uint32_t pixel;
292   uint8_t bytes[4];
293 };
294
295
296 static void
297 query_color_float (Display *dpy, unsigned long pixel, CGFloat *rgba)
298 {
299   JWXYZ_QUERY_COLOR (dpy, pixel, (CGFloat)1, rgba);
300 }
301
302
303 extern const struct jwxyz_vtbl quartz_vtbl;
304
305 Display *
306 jwxyz_quartz_make_display (Window w)
307 {
308   CGContextRef cgc = w->cgc;
309
310   Display *d = (Display *) calloc (1, sizeof(*d));
311   d->vtbl = &quartz_vtbl;
312
313   d->bitmap_info = CGBitmapContextGetBitmapInfo (cgc);
314
315 # if 0
316   // Tests for the image conversion modes.
317   {
318     const uint32_t key = 0x04030201;
319 #  ifdef __LITTLE_ENDIAN__
320     assert (convert_px (key, 0) == key);
321     assert (convert_px (key, 1) == 0x03020104);
322     assert (convert_px (key, 3) == 0x01040302);
323     assert (convert_px (key, 4) == 0x01020304);
324     assert (convert_px (key, 5) == 0x04010203);
325 #  endif
326     for (unsigned i = 0; i != 8; ++i) {
327       assert (convert_px(convert_px(key, i), convert_mode_invert(i)) == key);
328       assert (convert_mode_invert(convert_mode_invert(i)) == i);
329     }
330
331     for (unsigned i = 0; i != 8; ++i) {
332       for (unsigned j = 0; j != 8; ++j)
333         assert (convert_px(convert_px(key, i), j) ==
334                 convert_px(key, convert_mode_merge(i, j)));
335     }
336   }
337 # endif
338
339   Visual *v = &d->visual;
340   v->class      = TrueColor;
341
342   union color_bytes color;
343   convert_mode_t mode =
344     convert_mode_invert (convert_mode_to_rgba (d->bitmap_info));
345   for (unsigned i = 0; i != 4; ++i) {
346     color.pixel = 0;
347     color.bytes[i] = 0xff;
348     v->rgba_masks[i] = convert_px (color.pixel, mode);
349   }
350
351   CGBitmapInfo byte_order = d->bitmap_info & kCGBitmapByteOrderMask;
352   Assert ( ! (d->bitmap_info & kCGBitmapFloatComponents) &&
353           (byte_order == kCGBitmapByteOrder32Little ||
354            byte_order == kCGBitmapByteOrder32Big),
355           "invalid bits per channel");
356   
357   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
358
359   d->window_background = BlackPixel(d,0);
360
361   d->main_window = w;
362
363   Assert (cgc, "no CGContext");
364   return d;
365 }
366
367 void
368 jwxyz_quartz_free_display (Display *dpy)
369 {
370   jwxyz_sources_free (dpy->timers_data);
371   
372   free (dpy);
373 }
374
375
376 /* Call this after any modification to the bits on a Pixmap or Window.
377    Most Pixmaps are used frequently as sources and infrequently as
378    destinations, so it pays to cache the data as a CGImage as needed.
379  */
380 void
381 invalidate_drawable_cache (Drawable d)
382 {
383   if (d && d->cgi) {
384     CGImageRelease (d->cgi);
385     d->cgi = 0;
386   }
387 }
388
389
390 /* Call this when the View changes size or position.
391  */
392 void
393 jwxyz_window_resized (Display *dpy)
394 {
395   Window w = dpy->main_window;
396
397 # ifndef USE_IPHONE
398   // Figure out which screen the window is currently on.
399   {
400     int wx, wy;
401     XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
402     CGPoint p;
403     p.x = wx;
404     p.y = wy;
405     CGDisplayCount n;
406     dpy->cgdpy = 0;
407     CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
408     // Auuugh!
409     if (! dpy->cgdpy) {
410       p.x = p.y = 0;
411       CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
412     }
413     Assert (dpy->cgdpy, "unable to find CGDisplay");
414   }
415 # endif // USE_IPHONE
416
417 /*
418   {
419     // Figure out this screen's colorspace, and use that for every CGImage.
420     //
421     CMProfileRef profile = 0;
422     CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
423     Assert (profile, "unable to find colorspace profile");
424     dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
425     Assert (dpy->colorspace, "unable to find colorspace");
426   }
427  */
428
429   // WTF?  It's faster if we *do not* use the screen's colorspace!
430   //
431   dpy->colorspace = CGColorSpaceCreateDeviceRGB();
432
433   invalidate_drawable_cache (w);
434 }
435
436
437 void
438 jwxyz_flush_context (Display *dpy)
439 {
440   // CGContextSynchronize is another possibility.
441   CGContextFlush(dpy->main_window->cgc);
442 }
443
444 static jwxyz_sources_data *
445 display_sources_data (Display *dpy)
446 {
447   return dpy->timers_data;
448 }
449
450
451 static Window
452 root (Display *dpy)
453 {
454   return dpy->main_window;
455 }
456
457 static Visual *
458 visual (Display *dpy)
459 {
460   return &dpy->visual;
461 }
462
463
464 void
465 set_color (Display *dpy, CGContextRef cgc, unsigned long argb,
466            unsigned int depth, Bool alpha_allowed_p, Bool fill_p)
467 {
468   jwxyz_validate_pixel (dpy, argb, depth, alpha_allowed_p);
469   if (depth == 1) {
470     if (fill_p)
471       CGContextSetGrayFillColor   (cgc, (argb ? 1.0 : 0.0), 1.0);
472     else
473       CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
474   } else {
475     CGFloat rgba[4];
476     query_color_float (dpy, argb, rgba);
477     if (fill_p)
478       CGContextSetRGBFillColor   (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
479     else
480       CGContextSetRGBStrokeColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
481   }
482 }
483
484 static void
485 set_line_mode (CGContextRef cgc, XGCValues *gcv)
486 {
487   CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
488   CGContextSetLineJoin  (cgc,
489                          gcv->join_style == JoinMiter ? kCGLineJoinMiter :
490                          gcv->join_style == JoinRound ? kCGLineJoinRound :
491                          kCGLineJoinBevel);
492   CGContextSetLineCap   (cgc, 
493                          gcv->cap_style == CapNotLast ? kCGLineCapButt  :
494                          gcv->cap_style == CapButt    ? kCGLineCapButt  :
495                          gcv->cap_style == CapRound   ? kCGLineCapRound :
496                          kCGLineCapSquare);
497 }
498
499 static void
500 set_clip_mask (Drawable d, GC gc)
501 {
502   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
503
504   Pixmap p = gc->gcv.clip_mask;
505   if (!p) return;
506   Assert (p->type == PIXMAP, "not a pixmap");
507
508   XRectangle wr = d->frame;
509   CGRect to;
510   to.origin.x    = wr.x + gc->gcv.clip_x_origin;
511   to.origin.y    = wr.y + wr.height - gc->gcv.clip_y_origin
512                     - p->frame.height;
513   to.size.width  = p->frame.width;
514   to.size.height = p->frame.height;
515
516   CGContextClipToMask (d->cgc, to, gc->clip_mask);
517 }
518
519
520 /* Pushes a GC context; sets BlendMode and ClipMask.
521  */
522 void
523 push_gc (Drawable d, GC gc)
524 {
525   CGContextRef cgc = d->cgc;
526   CGContextSaveGState (cgc);
527
528   switch (gc->gcv.function) {
529     case GXset:
530     case GXclear:
531     case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/   break;
532     case GXxor:   CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
533     case GXor:    CGContextSetBlendMode (cgc, kCGBlendModeLighten);    break;
534     case GXand:   CGContextSetBlendMode (cgc, kCGBlendModeDarken);     break;
535     default: Assert(0, "unknown gcv function"); break;
536   }
537
538   if (gc->gcv.clip_mask)
539     set_clip_mask (d, gc);
540 }
541
542
543 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
544  */
545 void
546 push_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color,
547                Bool antialias_p, Bool fill_p)
548 {
549   push_gc (d, gc);
550
551   int depth = gc->depth;
552   switch (gc->gcv.function) {
553     case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
554     case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
555   }
556
557   CGContextRef cgc = d->cgc;
558   set_color (dpy, cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
559   CGContextSetShouldAntialias (cgc, antialias_p);
560 }
561
562
563 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
564  */
565 static void
566 push_fg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
567 {
568   push_color_gc (dpy, d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
569 }
570
571 static Bool
572 bitmap_context_p (Drawable d)
573 {
574   return True;
575 }
576
577
578
579 /* You've got to be fucking kidding me!
580
581    It is *way* faster to draw points by creating and drawing a 1x1 CGImage
582    with repeated calls to CGContextDrawImage than it is to make a single
583    call to CGContextFillRects() with a list of 1x1 rectangles!
584
585    I still wouldn't call it *fast*, however...
586  */
587 #define XDRAWPOINTS_IMAGES
588
589 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
590    the bitmap data directly is faster.  This only works on Pixmaps, though,
591    not Windows.  (Fortunately, on iOS, the Window is really a Pixmap.)
592  */
593 #define XDRAWPOINTS_CGDATA
594
595 static int
596 DrawPoints (Display *dpy, Drawable d, GC gc,
597             XPoint *points, int count, int mode)
598 {
599   int i;
600   XRectangle wr = d->frame;
601
602 # ifdef XDRAWPOINTS_CGDATA
603
604   if (bitmap_context_p (d))
605   {
606     CGContextRef cgc = d->cgc;
607     void *data = CGBitmapContextGetData (cgc);
608     size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
609     size_t w = CGBitmapContextGetWidth (cgc);
610     size_t h = CGBitmapContextGetHeight (cgc);
611
612     Assert (data, "no bitmap data in Drawable");
613
614     unsigned long argb = gc->gcv.foreground;
615     jwxyz_validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
616     if (gc->depth == 1)
617       argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
618
619     CGFloat x0 = wr.x;
620     CGFloat y0 = wr.y; // Y axis is refreshingly not flipped.
621
622     // It's uglier, but faster, to hoist the conditional out of the loop.
623     if (mode == CoordModePrevious) {
624       CGFloat x = x0, y = y0;
625       for (i = 0; i < count; i++, points++) {
626         x += points->x;
627         y += points->y;
628
629         if (x >= 0 && x < w && y >= 0 && y < h) {
630           unsigned int *p = (unsigned int *)
631             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
632           *p = (unsigned int) argb;
633         }
634       }
635     } else {
636       for (i = 0; i < count; i++, points++) {
637         CGFloat x = x0 + points->x;
638         CGFloat y = y0 + points->y;
639
640         if (x >= 0 && x < w && y >= 0 && y < h) {
641           unsigned int *p = (unsigned int *)
642             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
643           *p = (unsigned int) argb;
644         }
645       }
646     }
647
648   } else        /* d->type == WINDOW */
649
650 # endif /* XDRAWPOINTS_CGDATA */
651   {
652     push_fg_gc (dpy, d, gc, YES);
653
654 # ifdef XDRAWPOINTS_IMAGES
655
656     unsigned long argb = gc->gcv.foreground;
657     jwxyz_validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
658     if (gc->depth == 1)
659       argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
660
661     CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
662                                                            NULL);
663     CGImageRef cgi = CGImageCreate (1, 1,
664                                     8, 32, 4,
665                                     dpy->colorspace, 
666                                     /* Host-ordered, since we're using the
667                                        address of an int as the color data. */
668                                     dpy->bitmap_info,
669                                     prov, 
670                                     NULL,  /* decode[] */
671                                     NO, /* interpolate */
672                                     kCGRenderingIntentDefault);
673     CGDataProviderRelease (prov);
674
675     CGContextRef cgc = d->cgc;
676     CGRect rect;
677     rect.size.width = rect.size.height = 1;
678     for (i = 0; i < count; i++) {
679       if (i > 0 && mode == CoordModePrevious) {
680         rect.origin.x += points->x;
681         rect.origin.x -= points->y;
682       } else {
683         rect.origin.x = wr.x + points->x;
684         rect.origin.y = wr.y + wr.height - points->y - 1;
685       }
686
687       //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
688       CGContextDrawImage (cgc, rect, cgi);
689       points++;
690     }
691
692     CGImageRelease (cgi);
693
694 # else /* ! XDRAWPOINTS_IMAGES */
695
696     CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
697     CGRect *r = rects;
698   
699     for (i = 0; i < count; i++) {
700       r->size.width = r->size.height = 1;
701       if (i > 0 && mode == CoordModePrevious) {
702         r->origin.x = r[-1].origin.x + points->x;
703         r->origin.y = r[-1].origin.x - points->y;
704       } else {
705         r->origin.x = wr.origin.x + points->x;
706         r->origin.y = wr.origin.y + wr.size.height - points->y;
707       }
708       points++;
709       r++;
710     }
711
712     CGContextFillRects (d->cgc, rects, count);
713     free (rects);
714
715 # endif /* ! XDRAWPOINTS_IMAGES */
716
717     pop_gc (d, gc);
718   }
719
720   invalidate_drawable_cache (d);
721
722   return 0;
723 }
724
725
726 CGPoint
727 map_point (Drawable d, int x, int y)
728 {
729   const XRectangle *wr = &d->frame;
730   CGPoint p;
731   p.x = wr->x + x;
732   p.y = wr->y + wr->height - y;
733   return p;
734 }
735
736
737 static void
738 adjust_point_for_line (GC gc, CGPoint *p)
739 {
740   // Here's the authoritative discussion on how X draws lines:
741   // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width
742   if (gc->gcv.line_width <= 1) {
743     /* Thin lines are "drawn using an unspecified, device-dependent
744        algorithm", but seriously though, Bresenham's algorithm. Bresenham's
745        algorithm runs to and from pixel centers.
746
747        There's a few screenhacks (Maze, at the very least) that set line_width
748        to 1 when it probably should be set to 0, so it's line_width <= 1
749        instead of < 1.
750      */
751     p->x += 0.5;
752     p->y -= 0.5;
753   } else {
754     /* Thick lines OTOH run from the upper-left corners of pixels. This means
755        that a horizontal thick line of width 1 straddles two scan lines.
756        Aliasing requires one of these scan lines be chosen; the following
757        nudges the point so that the right choice is made. */
758     p->y -= 1e-3;
759   }
760 }
761
762
763 static CGPoint
764 point_for_line (Drawable d, GC gc, int x, int y)
765 {
766   CGPoint result = map_point (d, x, y);
767   adjust_point_for_line (gc, &result);
768   return result;
769 }
770
771
772 static int
773 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
774             int mode)
775 {
776   int i;
777   CGPoint p;
778   push_fg_gc (dpy, d, gc, NO);
779
780   CGContextRef cgc = d->cgc;
781
782   set_line_mode (cgc, &gc->gcv);
783   
784   // if the first and last points coincide, use closepath to get
785   // the proper line-joining.
786   BOOL closed_p = (points[0].x == points[count-1].x &&
787                    points[0].y == points[count-1].y);
788   if (closed_p) count--;
789   
790   p = point_for_line(d, gc, points->x, points->y);
791   points++;
792   CGContextBeginPath (cgc);
793   CGContextMoveToPoint (cgc, p.x, p.y);
794   for (i = 1; i < count; i++) {
795     if (mode == CoordModePrevious) {
796       p.x += points->x;
797       p.y -= points->y;
798     } else {
799       p = point_for_line(d, gc, points->x, points->y);
800     }
801     CGContextAddLineToPoint (cgc, p.x, p.y);
802     points++;
803   }
804   if (closed_p) CGContextClosePath (cgc);
805   CGContextStrokePath (cgc);
806   pop_gc (d, gc);
807   invalidate_drawable_cache (d);
808   return 0;
809 }
810
811
812 static int
813 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
814 {
815   int i;
816
817   CGContextRef cgc = d->cgc;
818
819   push_fg_gc (dpy, d, gc, NO);
820   set_line_mode (cgc, &gc->gcv);
821   CGContextBeginPath (cgc);
822   for (i = 0; i < count; i++) {
823     // when drawing a zero-length line, obey line-width and cap-style.
824     if (segments->x1 == segments->x2 && segments->y1 == segments->y2) {
825       int w = gc->gcv.line_width;
826       int x1 = segments->x1 - w/2;
827       int y1 = segments->y1 - w/2;
828       if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
829         XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
830       else {
831         if (!w)
832           w = 1; // Actually show zero-length lines.
833         XFillRectangle (dpy, d, gc, x1, y1, w, w);
834       }
835     } else {
836       CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
837       CGContextMoveToPoint (cgc, p.x, p.y);
838       p = point_for_line (d, gc, segments->x2, segments->y2);
839       CGContextAddLineToPoint (cgc, p.x, p.y);
840     }
841
842     segments++;
843   }
844   CGContextStrokePath (cgc);
845   pop_gc (d, gc);
846   invalidate_drawable_cache (d);
847   return 0;
848 }
849
850
851 static int
852 ClearWindow (Display *dpy, Window win)
853 {
854   Assert (win && win->type == WINDOW, "not a window");
855   XRectangle wr = win->frame;
856   return XClearArea (dpy, win, 0, 0, wr.width, wr.height, 0);
857 }
858
859 static unsigned long *
860 window_background (Display *dpy)
861 {
862   return &dpy->window_background;
863 }
864
865 static void
866 fill_rects (Display *dpy, Drawable d, GC gc,
867             const XRectangle *rectangles, unsigned long nrectangles,
868             unsigned long pixel)
869 {
870   Assert (!gc || gc->depth == jwxyz_drawable_depth (d), "depth mismatch");
871
872   CGContextRef cgc = d->cgc;
873
874   Bool fast_fill_p =
875     bitmap_context_p (d) &&
876     (!gc || (gc->gcv.function == GXcopy &&
877              !gc->gcv.alpha_allowed_p &&
878              !gc->gcv.clip_mask));
879
880   if (!fast_fill_p) {
881     if (gc)
882       push_color_gc (dpy, d, gc, pixel, gc->gcv.antialias_p, YES);
883     else
884       set_color (dpy, d->cgc, pixel, jwxyz_drawable_depth (d), NO, YES);
885   }
886
887   for (unsigned i = 0; i != nrectangles; ++i) {
888
889     int x = rectangles[i].x;
890     int y = rectangles[i].y;
891     unsigned long width = rectangles[i].width;
892     unsigned long height = rectangles[i].height;
893
894     if (fast_fill_p) {
895       long   // negative_int > unsigned_int == 1
896         dw = CGBitmapContextGetWidth (cgc),
897         dh = CGBitmapContextGetHeight (cgc);
898
899       if (x >= dw || y >= dh)
900         continue;
901
902       if (x < 0) {
903         width += x;
904         x = 0;
905       }
906
907       if (y < 0) {
908         height += y;
909         y = 0;
910       }
911
912       if (width <= 0 || height <= 0)
913         continue;
914
915       unsigned long max_width = dw - x;
916       if (width > max_width)
917         width = max_width;
918       unsigned long max_height = dh - y;
919       if (height > max_height)
920         height = max_height;
921
922       if (jwxyz_drawable_depth (d) == 1)
923         pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0);
924
925       size_t dst_bytes_per_row = CGBitmapContextGetBytesPerRow (d->cgc);
926       void *dst = SEEK_XY (CGBitmapContextGetData (d->cgc),
927                            dst_bytes_per_row, x, y);
928
929       Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
930       while (height) {
931         // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
932         wmemset (dst, (wchar_t) pixel, width);
933         --height;
934         dst = (char *) dst + dst_bytes_per_row;
935       }
936
937     } else {
938       CGRect r;
939       r.origin = map_point (d, x, y);
940       r.origin.y -= height;
941       r.size.width = width;
942       r.size.height = height;
943       CGContextFillRect (cgc, r);
944     }
945   }
946
947   if (!fast_fill_p && gc)
948     pop_gc (d, gc);
949   invalidate_drawable_cache (d);
950 }
951
952
953 static int
954 FillPolygon (Display *dpy, Drawable d, GC gc,
955              XPoint *points, int npoints, int shape, int mode)
956 {
957   XRectangle wr = d->frame;
958   int i;
959   push_fg_gc (dpy, d, gc, YES);
960   CGContextRef cgc = d->cgc;
961   CGContextBeginPath (cgc);
962   float x = 0, y = 0;
963   for (i = 0; i < npoints; i++) {
964     if (i > 0 && mode == CoordModePrevious) {
965       x += points[i].x;
966       y -= points[i].y;
967     } else {
968       x = wr.x + points[i].x;
969       y = wr.y + wr.height - points[i].y;
970     }
971         
972     if (i == 0)
973       CGContextMoveToPoint (cgc, x, y);
974     else
975       CGContextAddLineToPoint (cgc, x, y);
976   }
977   CGContextClosePath (cgc);
978   if (gc->gcv.fill_rule == EvenOddRule)
979     CGContextEOFillPath (cgc);
980   else
981     CGContextFillPath (cgc);
982   pop_gc (d, gc);
983   invalidate_drawable_cache (d);
984   return 0;
985 }
986
987 #define radians(DEG) ((DEG) * M_PI / 180.0)
988 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
989
990 static int
991 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
992                 unsigned int width, unsigned int height,
993                 int angle1, int angle2, Bool fill_p)
994 {
995   XRectangle wr = d->frame;
996   CGRect bound;
997   bound.origin.x = wr.x + x;
998   bound.origin.y = wr.y + wr.height - y - (int)height;
999   bound.size.width = width;
1000   bound.size.height = height;
1001   
1002   CGPoint ctr;
1003   ctr.x = bound.origin.x + bound.size.width /2;
1004   ctr.y = bound.origin.y + bound.size.height/2;
1005   
1006   float r1 = radians (angle1/64.0);
1007   float r2 = radians (angle2/64.0) + r1;
1008   BOOL clockwise = angle2 < 0;
1009   BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1010   
1011   push_fg_gc (dpy, d, gc, fill_p);
1012
1013   CGContextRef cgc = d->cgc;
1014   CGContextBeginPath (cgc);
1015   
1016   CGContextSaveGState(cgc);
1017   CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1018   CGContextScaleCTM (cgc, width/2.0, height/2.0);
1019   if (fill_p)
1020     CGContextMoveToPoint (cgc, 0, 0);
1021
1022   CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1023   CGContextRestoreGState (cgc);  // restore before stroke, for line width
1024
1025   if (closed_p)
1026     CGContextClosePath (cgc); // for proper line joining
1027   
1028   if (fill_p) {
1029     CGContextFillPath (cgc);
1030   } else {
1031     set_line_mode (cgc, &gc->gcv);
1032     CGContextStrokePath (cgc);
1033   }
1034
1035   pop_gc (d, gc);
1036   invalidate_drawable_cache (d);
1037   return 0;
1038 }
1039
1040
1041 static XGCValues *
1042 gc_gcv (GC gc)
1043 {
1044   return &gc->gcv;
1045 }
1046
1047
1048 static unsigned int
1049 gc_depth (GC gc)
1050 {
1051   return gc->depth;
1052 }
1053
1054
1055 static GC
1056 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1057 {
1058   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1059   gc->depth = jwxyz_drawable_depth (d);
1060
1061   jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1062   XChangeGC (dpy, gc, mask, xgcv);
1063   return gc;
1064 }
1065
1066
1067 static int
1068 FreeGC (Display *dpy, GC gc)
1069 {
1070   if (gc->gcv.font)
1071     XUnloadFont (dpy, gc->gcv.font);
1072
1073   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1074
1075   if (gc->gcv.clip_mask) {
1076     XFreePixmap (dpy, gc->gcv.clip_mask);
1077     CGImageRelease (gc->clip_mask);
1078   }
1079   free (gc);
1080   return 0;
1081 }
1082
1083
1084 static void
1085 flipbits (unsigned const char *in, unsigned char *out, int length)
1086 {
1087   static const unsigned char table[256] = {
1088     0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
1089     0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
1090     0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 
1091     0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
1092     0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 
1093     0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
1094     0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 
1095     0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
1096     0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 
1097     0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
1098     0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 
1099     0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
1100     0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 
1101     0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
1102     0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
1103     0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
1104     0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 
1105     0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
1106     0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 
1107     0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
1108     0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 
1109     0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
1110     0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 
1111     0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
1112     0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 
1113     0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
1114     0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 
1115     0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
1116     0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 
1117     0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
1118     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
1119     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1120   };
1121   while (length-- > 0)
1122     *out++ = table[*in++];
1123 }
1124
1125
1126 static int
1127 PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1128           int src_x, int src_y, int dest_x, int dest_y,
1129           unsigned int w, unsigned int h)
1130 {
1131   XRectangle wr = d->frame;
1132
1133   Assert (gc, "no GC");
1134   Assert ((w < 65535), "improbably large width");
1135   Assert ((h < 65535), "improbably large height");
1136   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1137   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1138   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1139   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1140
1141   // Clip width and height to the bounds of the Drawable
1142   //
1143   if (dest_x + (int)w > wr.width) {
1144     if (dest_x > wr.width)
1145       return 0;
1146     w = wr.width - dest_x;
1147   }
1148   if (dest_y + (int)h > wr.height) {
1149     if (dest_y > wr.height)
1150       return 0;
1151     h = wr.height - dest_y;
1152   }
1153   if (w <= 0 || h <= 0)
1154     return 0;
1155
1156   // Clip width and height to the bounds of the XImage
1157   //
1158   if (src_x + w > ximage->width) {
1159     if (src_x > ximage->width)
1160       return 0;
1161     w = ximage->width - src_x;
1162   }
1163   if (src_y + h > ximage->height) {
1164     if (src_y > ximage->height)
1165       return 0;
1166     h = ximage->height - src_y;
1167   }
1168   if (w <= 0 || h <= 0)
1169     return 0;
1170
1171   CGContextRef cgc = d->cgc;
1172
1173   if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1174     return 0;
1175
1176   int bpl = ximage->bytes_per_line;
1177   int bpp = ximage->bits_per_pixel;
1178   int bsize = bpl * h;
1179   char *data = ximage->data;
1180
1181   CGRect r;
1182   r.origin.x = wr.x + dest_x;
1183   r.origin.y = wr.y + wr.height - dest_y - (int)h;
1184   r.size.width = w;
1185   r.size.height = h;
1186
1187   if (bpp == 32) {
1188
1189     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1190        to create a CGImage from a sub-rectagle of the XImage.
1191      */
1192     data += (src_y * bpl) + (src_x * 4);
1193     CGDataProviderRef prov = 
1194       CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1195
1196     CGImageRef cgi = CGImageCreate (w, h,
1197                                     bpp/4, bpp, bpl,
1198                                     dpy->colorspace, 
1199                                     dpy->bitmap_info,
1200                                     prov, 
1201                                     NULL,  /* decode[] */
1202                                     NO, /* interpolate */
1203                                     kCGRenderingIntentDefault);
1204     CGDataProviderRelease (prov);
1205     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1206     CGContextDrawImage (cgc, r, cgi);
1207     CGImageRelease (cgi);
1208
1209   } else {   // (bpp == 1)
1210
1211     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1212
1213        #### However, the bit order within a byte in a 1bpp XImage is
1214             the wrong way around from what Quartz expects, so first we
1215             have to copy the data to reverse it.  Shit!  Maybe it
1216             would be worthwhile to go through the hacks and #ifdef
1217             each one that diddles 1bpp XImage->data directly...
1218      */
1219     Assert ((src_x % 8) == 0,
1220             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1221
1222     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
1223     unsigned char *flipped = (unsigned char *) malloc (bsize);
1224
1225     flipbits ((unsigned char *) data, flipped, bsize);
1226
1227     CGDataProviderRef prov = 
1228       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1229     CGImageRef mask = CGImageMaskCreate (w, h, 
1230                                          1, bpp, bpl,
1231                                          prov,
1232                                          NULL,  /* decode[] */
1233                                          NO); /* interpolate */
1234     push_fg_gc (dpy, d, gc, YES);
1235
1236     CGContextFillRect (cgc, r);                         // foreground color
1237     CGContextClipToMask (cgc, r, mask);
1238     set_color (dpy, cgc, gc->gcv.background, gc->depth, NO, YES);
1239     CGContextFillRect (cgc, r);                         // background color
1240     pop_gc (d, gc);
1241
1242     free (flipped);
1243     CGDataProviderRelease (prov);
1244     CGImageRelease (mask);
1245   }
1246
1247   invalidate_drawable_cache (d);
1248
1249   return 0;
1250 }
1251
1252
1253 static XImage *
1254 GetSubImage (Display *dpy, Drawable d, int x, int y,
1255              unsigned int width, unsigned int height,
1256              unsigned long plane_mask, int format,
1257              XImage *image, int dest_x, int dest_y)
1258 {
1259   const unsigned char *data = 0;
1260   size_t depth, ibpp, ibpl;
1261   convert_mode_t mode;
1262
1263   Assert ((width  < 65535), "improbably large width");
1264   Assert ((height < 65535), "improbably large height");
1265   Assert ((x < 65535 && x > -65535), "improbably large x");
1266   Assert ((y < 65535 && y > -65535), "improbably large y");
1267
1268   CGContextRef cgc = d->cgc;
1269
1270   {
1271     depth = jwxyz_drawable_depth (d);
1272     mode = convert_mode_to_rgba (dpy->bitmap_info);
1273     ibpp = CGBitmapContextGetBitsPerPixel (cgc);
1274     ibpl = CGBitmapContextGetBytesPerRow (cgc);
1275     data = CGBitmapContextGetData (cgc);
1276     Assert (data, "CGBitmapContextGetData failed");
1277   }
1278   
1279   // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
1280   data += (y * ibpl) + (x * (ibpp/8));
1281   
1282   format = (depth == 1 ? XYPixmap : ZPixmap);
1283   
1284   int obpl = image->bytes_per_line;
1285   
1286   /* both PPC and Intel use word-ordered ARGB frame buffers, which
1287      means that on Intel it is BGRA when viewed by bytes (And BGR
1288      when using 24bpp packing).
1289
1290      BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1291      The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1292      indicator of this latest kink.
1293    */
1294   int xx, yy;
1295   if (depth == 1) {
1296     const unsigned char *iline = data;
1297     for (yy = 0; yy < height; yy++) {
1298
1299       const unsigned char *iline2 = iline;
1300       for (xx = 0; xx < width; xx++) {
1301
1302         iline2++;                     // ignore R  or  A  or  A  or  B
1303         iline2++;                     // ignore G  or  B  or  R  or  G
1304         unsigned char r = *iline2++;  // use    B  or  G  or  G  or  R
1305         if (ibpp == 32) iline2++;     // ignore A  or  R  or  B  or  A
1306
1307         XPutPixel (image, xx + dest_x, yy + dest_y, (r ? 1 : 0));
1308       }
1309       iline += ibpl;
1310     }
1311   } else {
1312     const unsigned char *iline = data;
1313     unsigned char *oline = (unsigned char *) image->data + dest_y * obpl +
1314                              dest_x * 4;
1315
1316     mode = convert_mode_merge (mode,
1317              convert_mode_invert (
1318                convert_mode_to_rgba (dpy->bitmap_info)));
1319
1320     for (yy = 0; yy < height; yy++) {
1321
1322       convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
1323
1324       oline += obpl;
1325       iline += ibpl;
1326     }
1327   }
1328
1329   return image;
1330 }
1331
1332
1333
1334 /* Returns a transformation matrix to do rotation as per the provided
1335    EXIF "Orientation" value.
1336  */
1337 static CGAffineTransform
1338 exif_rotate (int rot, CGSize rect)
1339 {
1340   CGAffineTransform trans = CGAffineTransformIdentity;
1341   switch (rot) {
1342   case 2:               // flip horizontal
1343     trans = CGAffineTransformMakeTranslation (rect.width, 0);
1344     trans = CGAffineTransformScale (trans, -1, 1);
1345     break;
1346
1347   case 3:               // rotate 180
1348     trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1349     trans = CGAffineTransformRotate (trans, M_PI);
1350     break;
1351
1352   case 4:               // flip vertical
1353     trans = CGAffineTransformMakeTranslation (0, rect.height);
1354     trans = CGAffineTransformScale (trans, 1, -1);
1355     break;
1356
1357   case 5:               // transpose (UL-to-LR axis)
1358     trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1359     trans = CGAffineTransformScale (trans, -1, 1);
1360     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1361     break;
1362
1363   case 6:               // rotate 90
1364     trans = CGAffineTransformMakeTranslation (0, rect.width);
1365     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1366     break;
1367
1368   case 7:               // transverse (UR-to-LL axis)
1369     trans = CGAffineTransformMakeScale (-1, 1);
1370     trans = CGAffineTransformRotate (trans, M_PI / 2);
1371     break;
1372
1373   case 8:               // rotate 270
1374     trans = CGAffineTransformMakeTranslation (rect.height, 0);
1375     trans = CGAffineTransformRotate (trans, M_PI / 2);
1376     break;
1377
1378   default: 
1379     break;
1380   }
1381
1382   return trans;
1383 }
1384
1385
1386 void
1387 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
1388                                 Bool nsimg_p, void *img_arg,
1389                                XRectangle *geom_ret, int exif_rotation)
1390 {
1391   CGImageRef cgi;
1392 # ifndef USE_IPHONE
1393   CGImageSourceRef cgsrc;
1394 # endif // USE_IPHONE
1395   NSSize imgr;
1396
1397   CGContextRef cgc = d->cgc;
1398
1399   if (nsimg_p) {
1400
1401     NSImage *nsimg = (NSImage *) img_arg;
1402     imgr = [nsimg size];
1403
1404 # ifndef USE_IPHONE
1405     // convert the NSImage to a CGImage via the toll-free-bridging 
1406     // of NSData and CFData...
1407     //
1408     NSData *nsdata = [NSBitmapImageRep
1409                        TIFFRepresentationOfImageRepsInArray:
1410                          [nsimg representations]];
1411     CFDataRef cfdata = (CFDataRef) nsdata;
1412     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1413     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1414 # else  // USE_IPHONE
1415     cgi = nsimg.CGImage;
1416 # endif // USE_IPHONE
1417
1418   } else {
1419     cgi = (CGImageRef) img_arg;
1420     imgr.width  = CGImageGetWidth (cgi);
1421     imgr.height = CGImageGetHeight (cgi);
1422   }
1423
1424   Bool rot_p = (exif_rotation >= 5);
1425
1426   if (rot_p)
1427     imgr = NSMakeSize (imgr.height, imgr.width);
1428
1429   XRectangle winr = d->frame;
1430   float rw = winr.width  / imgr.width;
1431   float rh = winr.height / imgr.height;
1432   float r = (rw < rh ? rw : rh);
1433
1434   /* If the window is a goofy aspect ratio, take a middle slice of
1435      the image instead. */
1436   if (winr.width > winr.height * 5 ||
1437       winr.width > winr.width * 5) {
1438     r *= (winr.width > winr.height
1439          ? winr.width  / (double) winr.height
1440          : winr.height / (double) winr.width);
1441     // NSLog (@"weird aspect: scaling by %.1f\n", r);
1442   }
1443
1444   CGRect dst, dst2;
1445   dst.size.width  = imgr.width  * r;
1446   dst.size.height = imgr.height * r;
1447   dst.origin.x = (winr.width  - dst.size.width)  / 2;
1448   dst.origin.y = (winr.height - dst.size.height) / 2;
1449
1450   dst2.origin.x = dst2.origin.y = 0;
1451   if (rot_p) {
1452     dst2.size.width = dst.size.height; 
1453     dst2.size.height = dst.size.width;
1454   } else {
1455     dst2.size = dst.size;
1456   }
1457
1458   // Clear the part not covered by the image to background or black.
1459   //
1460   if (d->type == WINDOW)
1461     XClearWindow (dpy, d);
1462   else {
1463     jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.width, winr.height,
1464                      jwxyz_drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
1465   }
1466
1467   CGAffineTransform trans = 
1468     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1469
1470   CGContextSaveGState (cgc);
1471   CGContextConcatCTM (cgc, 
1472                       CGAffineTransformMakeTranslation (dst.origin.x,
1473                                                         dst.origin.y));
1474   CGContextConcatCTM (cgc, trans);
1475   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1476   CGContextDrawImage (cgc, dst2, cgi);
1477   CGContextRestoreGState (cgc);
1478
1479 # ifndef USE_IPHONE
1480   if (nsimg_p) {
1481     CFRelease (cgsrc);
1482     CGImageRelease (cgi);
1483   }
1484 # endif // USE_IPHONE
1485
1486   if (geom_ret) {
1487     geom_ret->x = dst.origin.x;
1488     geom_ret->y = dst.origin.y;
1489     geom_ret->width  = dst.size.width;
1490     geom_ret->height = dst.size.height;
1491   }
1492
1493   invalidate_drawable_cache (d);
1494 }
1495
1496
1497 XImage *
1498 jwxyz_png_to_ximage (Display *dpy, Visual *visual,
1499                      const unsigned char *png_data, unsigned long data_size)
1500 {
1501   NSImage *img = [[NSImage alloc] initWithData:
1502                                     [NSData dataWithBytes:png_data
1503                                             length:data_size]];
1504 #ifndef USE_IPHONE
1505   NSBitmapImageRep *bm = [NSBitmapImageRep
1506                            imageRepWithData:
1507                              [NSBitmapImageRep
1508                                TIFFRepresentationOfImageRepsInArray:
1509                                  [img representations]]];
1510   int width   = [img size].width;
1511   int height  = [img size].height;
1512   size_t ibpp = [bm bitsPerPixel];
1513   size_t ibpl = [bm bytesPerRow];
1514   const unsigned char *data = [bm bitmapData];
1515   convert_mode_t mode = (([bm bitmapFormat] & NSAlphaFirstBitmapFormat)
1516                          ? CONVERT_MODE_ROTATE_MASK
1517                          : 0);
1518 #else  // USE_IPHONE
1519   CGImageRef cgi = [img CGImage];
1520   int width = CGImageGetWidth (cgi);
1521   int height = CGImageGetHeight (cgi);
1522   size_t ibpp = 32;
1523   size_t ibpl = ibpp/4 * width;
1524   unsigned char *data = (unsigned char *) calloc (ibpl, height);
1525   const CGBitmapInfo bitmap_info =
1526     kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
1527   CGContextRef cgc =
1528     CGBitmapContextCreate (data, width, height,
1529                            8, /* bits per component */
1530                            ibpl, dpy->colorspace,
1531                            bitmap_info);
1532   CGContextDrawImage (cgc, CGRectMake (0, 0, width, height), cgi);
1533
1534   convert_mode_t mode = convert_mode_to_rgba (bitmap_info);
1535
1536 #endif // USE_IPHONE
1537
1538   XImage *image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
1539                                 width, height, 8, 0);
1540   image->data = (char *) malloc (image->height * image->bytes_per_line);
1541
1542   // data points at (x,y) with ibpl rowstride.
1543
1544   int obpl = image->bytes_per_line;
1545   const unsigned char *iline = data;
1546   unsigned char *oline = (unsigned char *) image->data;
1547   int yy;
1548   for (yy = 0; yy < height; yy++) {
1549     convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
1550     oline += obpl;
1551     iline += ibpl;
1552   }
1553
1554   [img release];
1555
1556 #ifndef USE_IPHONE
1557   // [bm release];
1558 # else
1559   CGContextRelease (cgc);
1560   free (data);
1561 # endif
1562
1563   return image;
1564 }
1565
1566
1567 Pixmap
1568 XCreatePixmap (Display *dpy, Drawable d,
1569                unsigned int width, unsigned int height, unsigned int depth)
1570 {
1571   if (!dpy) abort();
1572   char *data = (char *) malloc (width * height * 4);
1573   if (! data) return 0;
1574
1575   Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1576   p->type = PIXMAP;
1577   p->frame.width       = width;
1578   p->frame.height      = height;
1579   p->pixmap.depth      = depth;
1580   p->pixmap.cgc_buffer = data;
1581   
1582   /* Quartz doesn't have a 1bpp image type.
1583      Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
1584      don't support that!  So we always use 32bpp, regardless of depth. */
1585
1586   p->cgc = CGBitmapContextCreate (data, width, height,
1587                                   8, /* bits per component */
1588                                   width * 4, /* bpl */
1589                                   dpy->colorspace,
1590                                   dpy->bitmap_info);
1591   Assert (p->cgc, "could not create CGBitmapContext");
1592   return p;
1593 }
1594
1595
1596 int
1597 XFreePixmap (Display *d, Pixmap p)
1598 {
1599   Assert (p && p->type == PIXMAP, "not a pixmap");
1600   invalidate_drawable_cache (p);
1601   CGContextRelease (p->cgc);
1602   if (p->pixmap.cgc_buffer)
1603     free (p->pixmap.cgc_buffer);
1604   free (p);
1605   return 0;
1606 }
1607
1608
1609 static Pixmap
1610 copy_pixmap (Display *dpy, Pixmap p)
1611 {
1612   if (!p) return 0;
1613   Assert (p->type == PIXMAP, "not a pixmap");
1614
1615   Pixmap p2 = 0;
1616
1617   Window root;
1618   int x, y;
1619   unsigned int width, height, border_width, depth;
1620   if (XGetGeometry (dpy, p, &root,
1621                     &x, &y, &width, &height, &border_width, &depth)) {
1622     XGCValues gcv;
1623     gcv.function = GXcopy;
1624     GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
1625     if (gc) {
1626       p2 = XCreatePixmap (dpy, p, width, height, depth);
1627       if (p2)
1628         XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
1629       XFreeGC (dpy, gc);
1630     }
1631   }
1632
1633   Assert (p2, "could not copy pixmap");
1634
1635   return p2;
1636 }
1637
1638
1639 // Returns the verbose Unicode name of this character, like "agrave" or
1640 // "daggerdouble".  Used by fontglide debugMetrics.
1641 //
1642 char *
1643 jwxyz_unicode_character_name (Display *dpy, Font fid, unsigned long uc)
1644 {
1645   char *ret = 0;
1646   NSFont *nsfont = (NSFont *) jwxyz_native_font (fid);
1647   CTFontRef ctfont =
1648     CTFontCreateWithName ((CFStringRef) [nsfont fontName],
1649                           [nsfont pointSize],
1650                           NULL);
1651   Assert (ctfont, "no CTFontRef for UIFont");
1652
1653   CGGlyph cgglyph;
1654   if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
1655     CGFontRef cgfont = CTFontCopyGraphicsFont (ctfont, 0);
1656     NSString *name = (NSString *) CGFontCopyGlyphNameForGlyph(cgfont, cgglyph);
1657     ret = (name ? strdup ([name UTF8String]) : 0);
1658     CGFontRelease (cgfont);
1659     [name release];
1660   }
1661
1662   CFRelease (ctfont);
1663   return ret;
1664 }
1665
1666
1667 static int
1668 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
1669              const char *str, size_t len, int utf8_p)
1670 {
1671   NSString *nsstr = nsstring_from (str, len, utf8_p);
1672
1673   if (! nsstr) return 1;
1674
1675   XRectangle wr = d->frame;
1676   CGContextRef cgc = d->cgc;
1677
1678   unsigned long argb = gc->gcv.foreground;
1679   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
1680   CGFloat rgba[4];
1681   query_color_float (dpy, argb, rgba);
1682   NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
1683                                       green:rgba[1]
1684                                        blue:rgba[2]
1685                                       alpha:rgba[3]];
1686
1687   if (!gc->gcv.font) {
1688     Assert (0, "no font");
1689     return 1;
1690   }
1691
1692   /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
1693       NSFontAttributeName, and NSAttributedString are only present on iOS 6
1694       and later.  We could resurrect the Quartz code from v5.29 and do a
1695       runtime conditional on that, but that would be a pain in the ass.
1696       Probably time to just make iOS 6 a requirement.
1697    */
1698
1699   NSDictionary *attr =
1700     [NSDictionary dictionaryWithObjectsAndKeys:
1701                     (NSFont *) jwxyz_native_font (gc->gcv.font), NSFontAttributeName,
1702                     fg, NSForegroundColorAttributeName,
1703                   nil];
1704
1705   // Don't understand why we have to do both set_color and
1706   // NSForegroundColorAttributeName, but we do.
1707   //
1708   set_color (dpy, cgc, argb, 32, NO, YES);
1709
1710   NSAttributedString *astr = [[NSAttributedString alloc]
1711                                initWithString:nsstr
1712                                    attributes:attr];
1713   CTLineRef dl = CTLineCreateWithAttributedString (
1714                    (__bridge CFAttributedStringRef) astr);
1715
1716   // Not sure why this is necessary, but xoff is positive when the first
1717   // character on the line has a negative lbearing.  Without this, the
1718   // string is rendered with the first ink at 0 instead of at lbearing.
1719   // I have not seen xoff be negative, so I'm not sure if that can happen.
1720   //
1721   // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
1722   // a letter.
1723   //
1724   CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
1725   Assert (xoff >= 0, "unexpected CTLineOffset");
1726   x -= xoff;
1727
1728   CGContextSetTextPosition (cgc,
1729                             wr.x + x,
1730                             wr.y + wr.height - y);
1731   CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
1732
1733   CTLineDraw (dl, cgc);
1734   CFRelease (dl);
1735   [astr release];
1736
1737   invalidate_drawable_cache (d);
1738   return 0;
1739 }
1740
1741
1742 static int
1743 SetClipMask (Display *dpy, GC gc, Pixmap m)
1744 {
1745   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1746
1747   if (gc->gcv.clip_mask) {
1748     XFreePixmap (dpy, gc->gcv.clip_mask);
1749     CGImageRelease (gc->clip_mask);
1750   }
1751
1752   gc->gcv.clip_mask = copy_pixmap (dpy, m);
1753   if (gc->gcv.clip_mask)
1754     gc->clip_mask =
1755       CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
1756   else
1757     gc->clip_mask = 0;
1758
1759   return 0;
1760 }
1761
1762 static int
1763 SetClipOrigin (Display *dpy, GC gc, int x, int y)
1764 {
1765   gc->gcv.clip_x_origin = x;
1766   gc->gcv.clip_y_origin = y;
1767   return 0;
1768 }
1769
1770
1771 const struct jwxyz_vtbl quartz_vtbl = {
1772   root,
1773   visual,
1774   display_sources_data,
1775
1776   window_background,
1777   draw_arc,
1778   fill_rects,
1779   gc_gcv,
1780   gc_depth,
1781   draw_string,
1782
1783   jwxyz_quartz_copy_area,
1784
1785   DrawPoints,
1786   DrawSegments,
1787   CreateGC,
1788   FreeGC,
1789   ClearWindow,
1790   SetClipMask,
1791   SetClipOrigin,
1792   FillPolygon,
1793   DrawLines,
1794   PutImage,
1795   GetSubImage
1796 };
1797
1798 #endif // JWXYZ_QUARTZ -- entire file