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