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