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