From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / jwxyz / jwxyz.m
1 /* xscreensaver, Copyright (c) 1991-2016 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
47 # define NSFontTraitMask      UIFontDescriptorSymbolicTraits
48 // The values for the flags for NSFontTraitMask and
49 // UIFontDescriptorSymbolicTraits match up, not that it really matters here.
50 # define NSBoldFontMask       UIFontDescriptorTraitBold
51 # define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
52 # define NSItalicFontMask     UIFontDescriptorTraitItalic
53 #else
54 # import <Cocoa/Cocoa.h>
55 #endif
56
57 #import <CoreText/CTFont.h>
58 #import <CoreText/CTLine.h>
59 #import <CoreText/CTRun.h>
60
61 #import "jwxyzI.h"
62 #import "jwxyz-cocoa.h"
63 #import "jwxyz-timers.h"
64 #import "yarandom.h"
65 #import "utf8wc.h"
66 #import "xft.h"
67
68 # undef MAX
69 # undef MIN
70 # define MAX(a,b) ((a)>(b)?(a):(b))
71 # define MIN(a,b) ((a)<(b)?(a):(b))
72
73
74 struct jwxyz_Display {
75   Window main_window;
76   Screen *screen;
77   int screen_count;
78   struct jwxyz_sources_data *timers_data;
79
80 # ifndef USE_IPHONE
81   CGDirectDisplayID cgdpy;  /* ...of the one and only Window, main_window.
82                                This can change if the window is dragged to
83                                a different screen. */
84 # endif
85
86   CGColorSpaceRef colorspace;  /* Color space of this screen.  We tag all of
87                                   our images with this to avoid translation
88                                   when rendering. */
89
90   unsigned long window_background;
91 };
92
93 struct jwxyz_Screen {
94   Display *dpy;
95   CGBitmapInfo bitmap_info;
96   unsigned long black, white;
97   Visual *visual;
98   int screen_number;
99 };
100
101 struct jwxyz_GC {
102   XGCValues gcv;
103   unsigned int depth;
104   CGImageRef clip_mask;  // CGImage copy of the Pixmap in gcv.clip_mask
105 };
106
107 struct jwxyz_Font {
108   Display *dpy;
109   char *ps_name;
110   NSFont *nsfont;
111   float size;   // points
112   char *xa_font;
113
114   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
115   // But we need the metrics on both of them, so they go here.
116   XFontStruct metrics;
117 };
118
119 struct jwxyz_XFontSet {
120   XFontStruct *font;
121 };
122
123
124 // 24/32bpp -> 32bpp image conversion.
125 // Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24
126 // bits and an optional byte order swap.
127
128 // This type encodes such a conversion.
129 typedef unsigned convert_mode_t;
130
131 // It's rotate, then swap.
132 // A rotation here shifts bytes forward in memory. On x86/ARM, that's a left
133 // rotate, and on PowerPC, a rightward rotation.
134 static const convert_mode_t CONVERT_MODE_ROTATE_MASK = 0x3;
135 static const convert_mode_t CONVERT_MODE_SWAP = 0x4;
136
137
138 // Converts an array of pixels ('src') from one format to another, placing the
139 // result in 'dest', according to the pixel conversion mode 'mode'.
140 static void
141 convert_row (uint32_t *dest, const void *src, size_t count,
142              convert_mode_t mode, size_t src_bpp)
143 {
144   Assert (src_bpp == 24 || src_bpp == 32, "weird bpp");
145
146   // This works OK iff src == dest or src and dest do not overlap.
147
148   if (!mode) {
149     if (src != dest)
150       memcpy (dest, src, count * 4);
151     return;
152   }
153
154   // This is correct, but not fast.
155   convert_mode_t rot = (mode & CONVERT_MODE_ROTATE_MASK) * 8;
156   convert_mode_t flip = mode & CONVERT_MODE_SWAP;
157
158   src_bpp /= 8;
159
160   uint32_t *dest_end = dest + count;
161   while (dest != dest_end) {
162     uint32_t x;
163
164     if (src_bpp == 4)
165       x = *(const uint32_t *)src;
166     else { // src_bpp == 3
167       const uint8_t *src8 = (const uint8_t *)src;
168       // __LITTLE/BIG_ENDIAN__ are defined by the compiler.
169 # if defined __LITTLE_ENDIAN__
170       x = src8[0] | (src8[1] << 8) | (src8[2] << 16) | 0xff000000;
171 # elif defined __BIG_ENDIAN__
172       x = (src8[0] << 24) | (src8[1] << 16) | (src8[2] << 8) | 0xff;
173 # else
174 #  error "Can't determine system endianness."
175 # endif
176     }
177
178     src = (const uint8_t *)src + src_bpp;
179
180     /* The naive (i.e. ubiquitous) portable implementation of bitwise rotation,
181        for 32-bit integers, is:
182
183        (x << rot) | (x >> (32 - rot))
184
185        This works nearly everywhere. Compilers on x86 wil generally recognize
186        the idiom and convert it to a ROL instruction. But there's a problem
187        here: according to the C specification, bit shifts greater than or equal
188        to the length of the integer are undefined. And if rot = 0:
189        1. (x << 0) | (x >> (32 - 0))
190        2. (x << 0) | (x >> 32)
191        3. (x << 0) | (Undefined!)
192
193        Still, when the compiler converts this to a ROL on x86, everything works
194        as intended. But, there are two additional problems when Clang does
195        compile-time constant expression evaluation with the (x >> 32)
196        expression:
197        1. Instead of evaluating it to something reasonable (either 0, like a
198           human would intuitively expect, or x, like x86 would with SHR), Clang
199           seems to pull a value out of nowhere, like -1, or some other random
200           number.
201        2. Clang's warning for this, -Wshift-count-overflow, only works when the
202           shift count is a literal constant, as opposed to an arbitrary
203           expression that is optimized down to a constant.
204        Put together, this means that the assertions in jwxyz_make_display with
205        convert_px break with the above naive rotation, but only for a release
206        build.
207
208        http://blog.regehr.org/archives/1063
209        http://llvm.org/bugs/show_bug.cgi?id=17332
210        As described in those links, there is a solution here: Masking the
211        undefined shift with '& 31' as below makes the experesion well-defined
212        again. And LLVM is set to pick up on this safe version of the idiom and
213        use a rotation instruction on architectures (like x86) that support it,
214        just like it does with the unsafe version.
215
216        Too bad LLVM doesn't want to pick up on that particular optimization
217        here. Oh well. At least this code usually isn't critical w.r.t.
218        performance.
219      */
220
221 # if defined __LITTLE_ENDIAN__
222     x = (x << rot) | (x >> ((32 - rot) & 31));
223 # elif defined __BIG_ENDIAN__
224     x = (x >> rot) | (x << ((32 - rot) & 31));
225 # endif
226
227     if (flip)
228       x = __builtin_bswap32(x); // LLVM/GCC built-in function.
229
230     *dest = x;
231     ++dest;
232   }
233 }
234
235
236 // Converts a single pixel.
237 static uint32_t
238 convert_px (uint32_t px, convert_mode_t mode)
239 {
240   convert_row (&px, &px, 1, mode, 32);
241   return px;
242 }
243
244
245 // This returns the inverse conversion mode, such that:
246 // pixel
247 //   == convert_px(convert_px(pixel, mode), convert_mode_invert(mode))
248 //   == convert_px(convert_px(pixel, convert_mode_invert(mode)), mode)
249 static convert_mode_t
250 convert_mode_invert (convert_mode_t mode)
251 {
252   // swap(0); rot(n) == rot(n); swap(0)
253   // swap(1); rot(n) == rot(-n); swap(1)
254   return mode & CONVERT_MODE_SWAP ? mode : CONVERT_MODE_ROTATE_MASK & -mode;
255 }
256
257
258 // This combines two conversions into one, such that:
259 // convert_px(convert_px(pixel, mode0), mode1)
260 //   == convert_px(pixel, convert_mode_merge(mode0, mode1))
261 static convert_mode_t
262 convert_mode_merge (convert_mode_t m0, convert_mode_t m1)
263 {
264   // rot(r0); swap(s0); rot(r1); swap(s1)
265   // rot(r0); rot(s0 ? -r1 : r1); swap(s0); swap(s1)
266   // rot(r0 + (s0 ? -r1 : r1)); swap(s0 + s1)
267   return
268     ((m0 + (m0 & CONVERT_MODE_SWAP ? -m1 : m1)) & CONVERT_MODE_ROTATE_MASK) |
269     ((m0 ^ m1) & CONVERT_MODE_SWAP);
270 }
271
272
273 // This returns a conversion mode that converts an arbitrary 32-bit format
274 // specified by bitmap_info to RGBA.
275 static convert_mode_t
276 convert_mode_to_rgba (CGBitmapInfo bitmap_info)
277 {
278   // Former default: kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
279   // i.e. BGRA
280   // red   = 0x00FF0000;
281   // green = 0x0000FF00;
282   // blue  = 0x000000FF;
283
284   // RGBA: kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big
285
286   CGImageAlphaInfo alpha_info =
287     (CGImageAlphaInfo)(bitmap_info & kCGBitmapAlphaInfoMask);
288
289   Assert (! (bitmap_info & kCGBitmapFloatComponents),
290           "kCGBitmapFloatComponents unsupported");
291   Assert (alpha_info != kCGImageAlphaOnly, "kCGImageAlphaOnly not supported");
292
293   convert_mode_t rot = alpha_info == kCGImageAlphaFirst ||
294                        alpha_info == kCGImageAlphaPremultipliedFirst ||
295                        alpha_info == kCGImageAlphaNoneSkipFirst ?
296                        3 : 0;
297
298   CGBitmapInfo byte_order = bitmap_info & kCGBitmapByteOrderMask;
299
300   Assert (byte_order == kCGBitmapByteOrder32Little ||
301           byte_order == kCGBitmapByteOrder32Big,
302           "byte order not supported");
303
304   convert_mode_t swap = byte_order == kCGBitmapByteOrder32Little ?
305                         CONVERT_MODE_SWAP : 0;
306   if (swap)
307     rot = CONVERT_MODE_ROTATE_MASK & -rot;
308   return swap | rot;
309 }
310
311
312 union color_bytes
313 {
314   uint32_t pixel;
315   uint8_t bytes[4];
316 };
317
318
319 uint32_t
320 jwxyz_alloc_color (Display *dpy,
321                    uint16_t r, uint16_t g, uint16_t b, uint16_t a)
322 {
323   union color_bytes color;
324
325   /* Instead of (int)(c / 256.0), another possibility is
326      (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
327      uint8_t integer_math(uint16_t c) {
328        unsigned c0 = c + 128;
329        return (c0 - (c0 >> 8)) >> 8;
330      }
331    */
332
333   color.bytes[0] = r >> 8;
334   color.bytes[1] = g >> 8;
335   color.bytes[2] = b >> 8;
336   color.bytes[3] = a >> 8;
337
338   return
339     convert_px (color.pixel,
340       convert_mode_invert (convert_mode_to_rgba (dpy->screen->bitmap_info)));
341 }
342
343
344 void
345 jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
346 {
347   union color_bytes color;
348   color.pixel = convert_px ((uint32_t)pixel,
349                             convert_mode_to_rgba (dpy->screen->bitmap_info));
350   for (unsigned i = 0; i != 4; ++i)
351     rgba[i] = color.bytes[i];
352 }
353
354
355 static void
356 query_color_float (Display *dpy, unsigned long pixel, float *rgba)
357 {
358   uint8_t rgba8[4];
359   jwxyz_query_color (dpy, pixel, rgba8);
360   for (unsigned i = 0; i != 4; ++i)
361     rgba[i] = rgba8[i] * (1.0f / 255.0f);
362 }
363
364
365 /* We keep a list of all of the Displays that have been created and not
366    yet freed so that they can have sensible display numbers.  If three
367    displays are created (0, 1, 2) and then #1 is closed, then the fourth
368    display will be given the now-unused display number 1. (Everything in
369    here assumes a 1:1 Display/Screen mapping.)
370
371    The size of this array is the most number of live displays at one time.
372    So if it's 20, then we'll blow up if the system has 19 monitors and also
373    has System Preferences open (the small preview window).
374
375    Note that xlockmore-style savers tend to allocate big structures, so
376    setting this to 1000 will waste a few megabytes.  Also some of them assume
377    that the number of screens never changes, so dynamically expanding this
378    array won't work.
379  */
380 # ifndef USE_IPHONE
381 static Display *jwxyz_live_displays[20] = { 0, };
382 # endif
383
384
385 Display *
386 jwxyz_make_display (Window w)
387 {
388   CGContextRef cgc = w->cgc;
389
390   Display *d = (Display *) calloc (1, sizeof(*d));
391   d->screen = (Screen *) calloc (1, sizeof(Screen));
392   d->screen->dpy = d;
393   
394   d->screen_count = 1;
395   d->screen->screen_number = 0;
396 # ifndef USE_IPHONE
397   {
398     // Find the first empty slot in live_displays and plug us in.
399     int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
400     int i;
401     for (i = 0; i < size; i++) {
402       if (! jwxyz_live_displays[i])
403         break;
404     }
405     if (i >= size) abort();
406     jwxyz_live_displays[i] = d;
407     d->screen_count = size;
408     d->screen->screen_number = i;
409   }
410 # endif // !USE_IPHONE
411
412   d->screen->bitmap_info = CGBitmapContextGetBitmapInfo (cgc);
413   d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
414   d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
415
416 # if 0
417   // Tests for the image conversion modes.
418   {
419     const uint32_t key = 0x04030201;
420 #  ifdef __LITTLE_ENDIAN__
421     assert (convert_px (key, 0) == key);
422     assert (convert_px (key, 1) == 0x03020104);
423     assert (convert_px (key, 3) == 0x01040302);
424     assert (convert_px (key, 4) == 0x01020304);
425     assert (convert_px (key, 5) == 0x04010203);
426 #  endif
427     for (unsigned i = 0; i != 8; ++i) {
428       assert (convert_px(convert_px(key, i), convert_mode_invert(i)) == key);
429       assert (convert_mode_invert(convert_mode_invert(i)) == i);
430     }
431
432     for (unsigned i = 0; i != 8; ++i) {
433       for (unsigned j = 0; j != 8; ++j)
434         assert (convert_px(convert_px(key, i), j) ==
435                 convert_px(key, convert_mode_merge(i, j)));
436     }
437   }
438 # endif
439
440   Visual *v = (Visual *) calloc (1, sizeof(Visual));
441   v->class      = TrueColor;
442   v->red_mask   = jwxyz_alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
443   v->green_mask = jwxyz_alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
444   v->blue_mask  = jwxyz_alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
445   CGBitmapInfo byte_order = d->screen->bitmap_info & kCGBitmapByteOrderMask;
446   Assert ( ! (d->screen->bitmap_info & kCGBitmapFloatComponents) &&
447           (byte_order == kCGBitmapByteOrder32Little ||
448            byte_order == kCGBitmapByteOrder32Big),
449           "invalid bits per channel");
450   v->bits_per_rgb = 8;
451   d->screen->visual = v;
452   
453   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
454
455   d->window_background = BlackPixel(d,0);
456
457   d->main_window = w;
458
459   Assert (cgc, "no CGContext");
460   return d;
461 }
462
463 void
464 jwxyz_free_display (Display *dpy)
465 {
466   jwxyz_sources_free (dpy->timers_data);
467   
468 # ifndef USE_IPHONE
469   {
470     // Find us in live_displays and clear that slot.
471     int size = ScreenCount(dpy);
472     int i;
473     for (i = 0; i < size; i++) {
474       if (dpy == jwxyz_live_displays[i]) {
475         jwxyz_live_displays[i] = 0;
476         break;
477       }
478     }
479     if (i >= size) abort();
480   }
481 # endif // !USE_IPHONE
482
483   free (dpy->screen->visual);
484   free (dpy->screen);
485   free (dpy);
486 }
487
488
489 /* Call this after any modification to the bits on a Pixmap or Window.
490    Most Pixmaps are used frequently as sources and infrequently as
491    destinations, so it pays to cache the data as a CGImage as needed.
492  */
493 void
494 invalidate_drawable_cache (Drawable d)
495 {
496   if (d && d->cgi) {
497     CGImageRelease (d->cgi);
498     d->cgi = 0;
499   }
500 }
501
502
503 /* Call this when the View changes size or position.
504  */
505 void
506 jwxyz_window_resized (Display *dpy)
507 {
508   Window w = dpy->main_window;
509
510 # ifndef USE_IPHONE
511   // Figure out which screen the window is currently on.
512   {
513     int wx, wy;
514     XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
515     CGPoint p;
516     p.x = wx;
517     p.y = wy;
518     CGDisplayCount n;
519     dpy->cgdpy = 0;
520     CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
521     // Auuugh!
522     if (! dpy->cgdpy) {
523       p.x = p.y = 0;
524       CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
525     }
526     Assert (dpy->cgdpy, "unable to find CGDisplay");
527   }
528 # endif // USE_IPHONE
529
530 /*
531   {
532     // Figure out this screen's colorspace, and use that for every CGImage.
533     //
534     CMProfileRef profile = 0;
535     CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
536     Assert (profile, "unable to find colorspace profile");
537     dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
538     Assert (dpy->colorspace, "unable to find colorspace");
539   }
540  */
541
542   // WTF?  It's faster if we *do not* use the screen's colorspace!
543   //
544   dpy->colorspace = CGColorSpaceCreateDeviceRGB();
545
546   invalidate_drawable_cache (w);
547 }
548
549
550 void
551 jwxyz_flush_context (Display *dpy)
552 {
553   // CGContextSynchronize is another possibility.
554   CGContextFlush(dpy->main_window->cgc);
555 }
556
557 jwxyz_sources_data *
558 display_sources_data (Display *dpy)
559 {
560   return dpy->timers_data;
561 }
562
563
564 Window
565 XRootWindow (Display *dpy, int screen)
566 {
567   return dpy ? dpy->main_window : 0;
568 }
569
570 Screen *
571 XDefaultScreenOfDisplay (Display *dpy)
572 {
573   return dpy ? dpy->screen : 0;
574 }
575
576 Visual *
577 XDefaultVisualOfScreen (Screen *screen)
578 {
579   return screen ? screen->visual : 0;
580 }
581
582 Display *
583 XDisplayOfScreen (Screen *s)
584 {
585   return s ? s->dpy : 0;
586 }
587
588 int
589 XDisplayNumberOfScreen (Screen *s)
590 {
591   return 0;
592 }
593
594 int
595 XScreenNumberOfScreen (Screen *s)
596 {
597   return s? s->screen_number : 0;
598 }
599
600 int
601 jwxyz_ScreenCount (Display *dpy)
602 {
603   return dpy ? dpy->screen_count : 0;
604 }
605
606 unsigned long
607 XBlackPixelOfScreen(Screen *screen)
608 {
609   return screen->black;
610 }
611
612 unsigned long
613 XWhitePixelOfScreen(Screen *screen)
614 {
615   return screen->white;
616 }
617
618 unsigned long
619 XCellsOfScreen(Screen *screen)
620 {
621   Visual *v = screen->visual;
622   return v->red_mask | v->green_mask | v->blue_mask;
623 }
624
625
626 void
627 set_color (Display *dpy, CGContextRef cgc, unsigned long argb,
628            unsigned int depth, Bool alpha_allowed_p, Bool fill_p)
629 {
630   jwxyz_validate_pixel (dpy, argb, depth, alpha_allowed_p);
631   if (depth == 1) {
632     if (fill_p)
633       CGContextSetGrayFillColor   (cgc, (argb ? 1.0 : 0.0), 1.0);
634     else
635       CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
636   } else {
637     float rgba[4];
638     query_color_float (dpy, argb, rgba);
639     if (fill_p)
640       CGContextSetRGBFillColor   (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
641     else
642       CGContextSetRGBStrokeColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
643   }
644 }
645
646 static void
647 set_line_mode (CGContextRef cgc, XGCValues *gcv)
648 {
649   CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
650   CGContextSetLineJoin  (cgc,
651                          gcv->join_style == JoinMiter ? kCGLineJoinMiter :
652                          gcv->join_style == JoinRound ? kCGLineJoinRound :
653                          kCGLineJoinBevel);
654   CGContextSetLineCap   (cgc, 
655                          gcv->cap_style == CapNotLast ? kCGLineCapButt  :
656                          gcv->cap_style == CapButt    ? kCGLineCapButt  :
657                          gcv->cap_style == CapRound   ? kCGLineCapRound :
658                          kCGLineCapSquare);
659 }
660
661 static void
662 set_clip_mask (Drawable d, GC gc)
663 {
664   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
665
666   Pixmap p = gc->gcv.clip_mask;
667   if (!p) return;
668   Assert (p->type == PIXMAP, "not a pixmap");
669
670   XRectangle wr = d->frame;
671   CGRect to;
672   to.origin.x    = wr.x + gc->gcv.clip_x_origin;
673   to.origin.y    = wr.y + wr.height - gc->gcv.clip_y_origin
674                     - p->frame.height;
675   to.size.width  = p->frame.width;
676   to.size.height = p->frame.height;
677
678   CGContextClipToMask (d->cgc, to, gc->clip_mask);
679 }
680
681
682 /* Pushes a GC context; sets BlendMode and ClipMask.
683  */
684 void
685 push_gc (Drawable d, GC gc)
686 {
687   CGContextRef cgc = d->cgc;
688   CGContextSaveGState (cgc);
689
690   switch (gc->gcv.function) {
691     case GXset:
692     case GXclear:
693     case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/   break;
694     case GXxor:   CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
695     case GXor:    CGContextSetBlendMode (cgc, kCGBlendModeLighten);    break;
696     case GXand:   CGContextSetBlendMode (cgc, kCGBlendModeDarken);     break;
697     default: Assert(0, "unknown gcv function"); break;
698   }
699
700   if (gc->gcv.clip_mask)
701     set_clip_mask (d, gc);
702 }
703
704
705 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
706  */
707 void
708 push_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color,
709                Bool antialias_p, Bool fill_p)
710 {
711   push_gc (d, gc);
712
713   int depth = gc->depth;
714   switch (gc->gcv.function) {
715     case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
716     case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
717   }
718
719   CGContextRef cgc = d->cgc;
720   set_color (dpy, cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
721   CGContextSetShouldAntialias (cgc, antialias_p);
722 }
723
724
725 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
726  */
727 static void
728 push_fg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
729 {
730   push_color_gc (dpy, d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
731 }
732
733 static Bool
734 bitmap_context_p (Drawable d)
735 {
736   return True;
737 }
738
739
740
741 /* You've got to be fucking kidding me!
742
743    It is *way* faster to draw points by creating and drawing a 1x1 CGImage
744    with repeated calls to CGContextDrawImage than it is to make a single
745    call to CGContextFillRects() with a list of 1x1 rectangles!
746
747    I still wouldn't call it *fast*, however...
748  */
749 #define XDRAWPOINTS_IMAGES
750
751 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
752    the bitmap data directly is faster.  This only works on Pixmaps, though,
753    not Windows.  (Fortunately, on iOS, the Window is really a Pixmap.)
754  */
755 #define XDRAWPOINTS_CGDATA
756
757 int
758 XDrawPoints (Display *dpy, Drawable d, GC gc, 
759              XPoint *points, int count, int mode)
760 {
761   int i;
762   XRectangle wr = d->frame;
763
764 # ifdef XDRAWPOINTS_CGDATA
765
766   if (bitmap_context_p (d))
767   {
768     CGContextRef cgc = d->cgc;
769     void *data = CGBitmapContextGetData (cgc);
770     size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
771     size_t w = CGBitmapContextGetWidth (cgc);
772     size_t h = CGBitmapContextGetHeight (cgc);
773
774     Assert (data, "no bitmap data in Drawable");
775
776     unsigned long argb = gc->gcv.foreground;
777     jwxyz_validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
778     if (gc->depth == 1)
779       argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
780
781     CGFloat x0 = wr.x;
782     CGFloat y0 = wr.y; // Y axis is refreshingly not flipped.
783
784     // It's uglier, but faster, to hoist the conditional out of the loop.
785     if (mode == CoordModePrevious) {
786       CGFloat x = x0, y = y0;
787       for (i = 0; i < count; i++, points++) {
788         x += points->x;
789         y += points->y;
790
791         if (x >= 0 && x < w && y >= 0 && y < h) {
792           unsigned int *p = (unsigned int *)
793             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
794           *p = (unsigned int) argb;
795         }
796       }
797     } else {
798       for (i = 0; i < count; i++, points++) {
799         CGFloat x = x0 + points->x;
800         CGFloat y = y0 + points->y;
801
802         if (x >= 0 && x < w && y >= 0 && y < h) {
803           unsigned int *p = (unsigned int *)
804             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
805           *p = (unsigned int) argb;
806         }
807       }
808     }
809
810   } else        /* d->type == WINDOW */
811
812 # endif /* XDRAWPOINTS_CGDATA */
813   {
814     push_fg_gc (dpy, d, gc, YES);
815
816 # ifdef XDRAWPOINTS_IMAGES
817
818     unsigned long argb = gc->gcv.foreground;
819     jwxyz_validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
820     if (gc->depth == 1)
821       argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
822
823     CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
824                                                            NULL);
825     CGImageRef cgi = CGImageCreate (1, 1,
826                                     8, 32, 4,
827                                     dpy->colorspace, 
828                                     /* Host-ordered, since we're using the
829                                        address of an int as the color data. */
830                                     dpy->screen->bitmap_info,
831                                     prov, 
832                                     NULL,  /* decode[] */
833                                     NO, /* interpolate */
834                                     kCGRenderingIntentDefault);
835     CGDataProviderRelease (prov);
836
837     CGContextRef cgc = d->cgc;
838     CGRect rect;
839     rect.size.width = rect.size.height = 1;
840     for (i = 0; i < count; i++) {
841       if (i > 0 && mode == CoordModePrevious) {
842         rect.origin.x += points->x;
843         rect.origin.x -= points->y;
844       } else {
845         rect.origin.x = wr.x + points->x;
846         rect.origin.y = wr.y + wr.height - points->y - 1;
847       }
848
849       //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
850       CGContextDrawImage (cgc, rect, cgi);
851       points++;
852     }
853
854     CGImageRelease (cgi);
855
856 # else /* ! XDRAWPOINTS_IMAGES */
857
858     CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
859     CGRect *r = rects;
860   
861     for (i = 0; i < count; i++) {
862       r->size.width = r->size.height = 1;
863       if (i > 0 && mode == CoordModePrevious) {
864         r->origin.x = r[-1].origin.x + points->x;
865         r->origin.y = r[-1].origin.x - points->y;
866       } else {
867         r->origin.x = wr.origin.x + points->x;
868         r->origin.y = wr.origin.y + wr.size.height - points->y;
869       }
870       points++;
871       r++;
872     }
873
874     CGContextFillRects (d->cgc, rects, count);
875     free (rects);
876
877 # endif /* ! XDRAWPOINTS_IMAGES */
878
879     pop_gc (d, gc);
880   }
881
882   invalidate_drawable_cache (d);
883
884   return 0;
885 }
886
887
888 CGPoint
889 map_point (Drawable d, int x, int y)
890 {
891   const XRectangle *wr = &d->frame;
892   CGPoint p;
893   p.x = wr->x + x;
894   p.y = wr->y + wr->height - y;
895   return p;
896 }
897
898
899 static void
900 adjust_point_for_line (GC gc, CGPoint *p)
901 {
902   // Here's the authoritative discussion on how X draws lines:
903   // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width
904   if (gc->gcv.line_width <= 1) {
905     /* Thin lines are "drawn using an unspecified, device-dependent
906        algorithm", but seriously though, Bresenham's algorithm. Bresenham's
907        algorithm runs to and from pixel centers.
908
909        There's a few screenhacks (Maze, at the very least) that set line_width
910        to 1 when it probably should be set to 0, so it's line_width <= 1
911        instead of < 1.
912      */
913     p->x += 0.5;
914     p->y -= 0.5;
915   } else {
916     /* Thick lines OTOH run from the upper-left corners of pixels. This means
917        that a horizontal thick line of width 1 straddles two scan lines.
918        Aliasing requires one of these scan lines be chosen; the following
919        nudges the point so that the right choice is made. */
920     p->y -= 1e-3;
921   }
922 }
923
924
925 static CGPoint
926 point_for_line (Drawable d, GC gc, int x, int y)
927 {
928   CGPoint result = map_point (d, x, y);
929   adjust_point_for_line (gc, &result);
930   return result;
931 }
932
933
934 int
935 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
936 {
937   // when drawing a zero-length line, obey line-width and cap-style.
938   if (x1 == x2 && y1 == y2) {
939     int w = gc->gcv.line_width;
940     x1 -= w/2;
941     y1 -= w/2;
942     if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
943       return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
944     else {
945       if (!w)
946         w = 1; // Actually show zero-length lines.
947       return XFillRectangle (dpy, d, gc, x1, y1, w, w);
948     }
949   }
950   
951   CGPoint p = point_for_line (d, gc, x1, y1);
952
953   push_fg_gc (dpy, d, gc, NO);
954
955   CGContextRef cgc = d->cgc;
956   set_line_mode (cgc, &gc->gcv);
957   CGContextBeginPath (cgc);
958   CGContextMoveToPoint (cgc, p.x, p.y);
959   p = point_for_line(d, gc, x2, y2);
960   CGContextAddLineToPoint (cgc, p.x, p.y);
961   CGContextStrokePath (cgc);
962   pop_gc (d, gc);
963   invalidate_drawable_cache (d);
964   return 0;
965 }
966
967 int
968 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
969             int mode)
970 {
971   int i;
972   CGPoint p;
973   push_fg_gc (dpy, d, gc, NO);
974
975   CGContextRef cgc = d->cgc;
976
977   set_line_mode (cgc, &gc->gcv);
978   
979   // if the first and last points coincide, use closepath to get
980   // the proper line-joining.
981   BOOL closed_p = (points[0].x == points[count-1].x &&
982                    points[0].y == points[count-1].y);
983   if (closed_p) count--;
984   
985   p = point_for_line(d, gc, points->x, points->y);
986   points++;
987   CGContextBeginPath (cgc);
988   CGContextMoveToPoint (cgc, p.x, p.y);
989   for (i = 1; i < count; i++) {
990     if (mode == CoordModePrevious) {
991       p.x += points->x;
992       p.y -= points->y;
993     } else {
994       p = point_for_line(d, gc, points->x, points->y);
995     }
996     CGContextAddLineToPoint (cgc, p.x, p.y);
997     points++;
998   }
999   if (closed_p) CGContextClosePath (cgc);
1000   CGContextStrokePath (cgc);
1001   pop_gc (d, gc);
1002   invalidate_drawable_cache (d);
1003   return 0;
1004 }
1005
1006
1007 int
1008 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1009 {
1010   int i;
1011
1012   CGContextRef cgc = d->cgc;
1013
1014   push_fg_gc (dpy, d, gc, NO);
1015   set_line_mode (cgc, &gc->gcv);
1016   CGContextBeginPath (cgc);
1017   for (i = 0; i < count; i++) {
1018     CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
1019     CGContextMoveToPoint (cgc, p.x, p.y);
1020     p = point_for_line (d, gc, segments->x2, segments->y2);
1021     CGContextAddLineToPoint (cgc, p.x, p.y);
1022     segments++;
1023   }
1024   CGContextStrokePath (cgc);
1025   pop_gc (d, gc);
1026   invalidate_drawable_cache (d);
1027   return 0;
1028 }
1029
1030
1031 int
1032 XClearWindow (Display *dpy, Window win)
1033 {
1034   Assert (win && win->type == WINDOW, "not a window");
1035   XRectangle wr = win->frame;
1036   return XClearArea (dpy, win, 0, 0, wr.width, wr.height, 0);
1037 }
1038
1039 unsigned long
1040 jwxyz_window_background (Display *dpy)
1041 {
1042   return dpy->window_background;
1043 }
1044
1045 int
1046 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1047 {
1048   Assert (w && w->type == WINDOW, "not a window");
1049   jwxyz_validate_pixel (dpy, pixel, 32, NO);
1050   dpy->window_background = pixel;
1051   return 0;
1052 }
1053
1054 void
1055 jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
1056                   const XRectangle *rectangles, unsigned long nrectangles,
1057                   unsigned long pixel)
1058 {
1059   Assert (!gc || gc->depth == jwxyz_drawable_depth (d), "depth mismatch");
1060
1061   CGContextRef cgc = d->cgc;
1062
1063   Bool fast_fill_p =
1064     bitmap_context_p (d) &&
1065     (!gc || (gc->gcv.function == GXcopy &&
1066              !gc->gcv.alpha_allowed_p &&
1067              !gc->gcv.clip_mask));
1068
1069   if (!fast_fill_p) {
1070     if (gc)
1071       push_color_gc (dpy, d, gc, pixel, gc->gcv.antialias_p, YES);
1072     else
1073       set_color (dpy, d->cgc, pixel, jwxyz_drawable_depth (d), NO, YES);
1074   }
1075
1076   for (unsigned i = 0; i != nrectangles; ++i) {
1077
1078     int x = rectangles[i].x;
1079     int y = rectangles[i].y;
1080     unsigned long width = rectangles[i].width;
1081     unsigned long height = rectangles[i].height;
1082
1083     if (fast_fill_p) {
1084       long   // negative_int > unsigned_int == 1
1085         dw = CGBitmapContextGetWidth (cgc),
1086         dh = CGBitmapContextGetHeight (cgc);
1087
1088       if (x >= dw || y >= dh)
1089         continue;
1090
1091       if (x < 0) {
1092         width += x;
1093         x = 0;
1094       }
1095
1096       if (y < 0) {
1097         height += y;
1098         y = 0;
1099       }
1100
1101       if (width <= 0 || height <= 0)
1102         continue;
1103
1104       unsigned long max_width = dw - x;
1105       if (width > max_width)
1106         width = max_width;
1107       unsigned long max_height = dh - y;
1108       if (height > max_height)
1109         height = max_height;
1110
1111       if (jwxyz_drawable_depth (d) == 1)
1112         pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0);
1113
1114       size_t dst_bytes_per_row = CGBitmapContextGetBytesPerRow (d->cgc);
1115       void *dst = seek_xy (CGBitmapContextGetData (d->cgc),
1116                            dst_bytes_per_row, x, y);
1117
1118       Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
1119       while (height) {
1120         // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
1121         wmemset (dst, (wchar_t) pixel, width);
1122         --height;
1123         dst = (char *) dst + dst_bytes_per_row;
1124       }
1125
1126     } else {
1127       CGRect r;
1128       r.origin = map_point (d, x, y);
1129       r.origin.y -= height;
1130       r.size.width = width;
1131       r.size.height = height;
1132       CGContextFillRect (cgc, r);
1133     }
1134   }
1135
1136   if (!fast_fill_p && gc)
1137     pop_gc (d, gc);
1138   invalidate_drawable_cache (d);
1139 }
1140
1141
1142 int
1143 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1144 {
1145   Assert (win && win->type == WINDOW, "not a window");
1146   jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
1147   return 0;
1148 }
1149
1150
1151 int
1152 XFillPolygon (Display *dpy, Drawable d, GC gc, 
1153               XPoint *points, int npoints, int shape, int mode)
1154 {
1155   XRectangle wr = d->frame;
1156   int i;
1157   push_fg_gc (dpy, d, gc, YES);
1158   CGContextRef cgc = d->cgc;
1159   CGContextBeginPath (cgc);
1160   float x = 0, y = 0;
1161   for (i = 0; i < npoints; i++) {
1162     if (i > 0 && mode == CoordModePrevious) {
1163       x += points[i].x;
1164       y -= points[i].y;
1165     } else {
1166       x = wr.x + points[i].x;
1167       y = wr.y + wr.height - points[i].y;
1168     }
1169         
1170     if (i == 0)
1171       CGContextMoveToPoint (cgc, x, y);
1172     else
1173       CGContextAddLineToPoint (cgc, x, y);
1174   }
1175   CGContextClosePath (cgc);
1176   if (gc->gcv.fill_rule == EvenOddRule)
1177     CGContextEOFillPath (cgc);
1178   else
1179     CGContextFillPath (cgc);
1180   pop_gc (d, gc);
1181   invalidate_drawable_cache (d);
1182   return 0;
1183 }
1184
1185 #define radians(DEG) ((DEG) * M_PI / 180.0)
1186 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1187
1188 int
1189 jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1190                 unsigned int width, unsigned int height,
1191                 int angle1, int angle2, Bool fill_p)
1192 {
1193   XRectangle wr = d->frame;
1194   CGRect bound;
1195   bound.origin.x = wr.x + x;
1196   bound.origin.y = wr.y + wr.height - y - (int)height;
1197   bound.size.width = width;
1198   bound.size.height = height;
1199   
1200   CGPoint ctr;
1201   ctr.x = bound.origin.x + bound.size.width /2;
1202   ctr.y = bound.origin.y + bound.size.height/2;
1203   
1204   float r1 = radians (angle1/64.0);
1205   float r2 = radians (angle2/64.0) + r1;
1206   BOOL clockwise = angle2 < 0;
1207   BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1208   
1209   push_fg_gc (dpy, d, gc, fill_p);
1210
1211   CGContextRef cgc = d->cgc;
1212   CGContextBeginPath (cgc);
1213   
1214   CGContextSaveGState(cgc);
1215   CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1216   CGContextScaleCTM (cgc, width/2.0, height/2.0);
1217   if (fill_p)
1218     CGContextMoveToPoint (cgc, 0, 0);
1219
1220   CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1221   CGContextRestoreGState (cgc);  // restore before stroke, for line width
1222
1223   if (closed_p)
1224     CGContextClosePath (cgc); // for proper line joining
1225   
1226   if (fill_p) {
1227     CGContextFillPath (cgc);
1228   } else {
1229     set_line_mode (cgc, &gc->gcv);
1230     CGContextStrokePath (cgc);
1231   }
1232
1233   pop_gc (d, gc);
1234   invalidate_drawable_cache (d);
1235   return 0;
1236 }
1237
1238
1239 XGCValues *
1240 jwxyz_gc_gcv (GC gc)
1241 {
1242   return &gc->gcv;
1243 }
1244
1245
1246 unsigned int
1247 jwxyz_gc_depth (GC gc)
1248 {
1249   return gc->depth;
1250 }
1251
1252
1253 GC
1254 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1255 {
1256   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1257   gc->depth = jwxyz_drawable_depth (d);
1258
1259   jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1260   XChangeGC (dpy, gc, mask, xgcv);
1261   return gc;
1262 }
1263
1264
1265 int
1266 XFreeGC (Display *dpy, GC gc)
1267 {
1268   if (gc->gcv.font)
1269     XUnloadFont (dpy, gc->gcv.font);
1270
1271   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1272
1273   if (gc->gcv.clip_mask) {
1274     XFreePixmap (dpy, gc->gcv.clip_mask);
1275     CGImageRelease (gc->clip_mask);
1276   }
1277   free (gc);
1278   return 0;
1279 }
1280
1281
1282 static void
1283 flipbits (unsigned const char *in, unsigned char *out, int length)
1284 {
1285   static const unsigned char table[256] = {
1286     0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
1287     0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
1288     0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 
1289     0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
1290     0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 
1291     0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
1292     0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 
1293     0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
1294     0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 
1295     0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
1296     0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 
1297     0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
1298     0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 
1299     0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
1300     0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
1301     0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
1302     0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 
1303     0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
1304     0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 
1305     0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
1306     0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 
1307     0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
1308     0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 
1309     0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
1310     0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 
1311     0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
1312     0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 
1313     0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
1314     0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 
1315     0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
1316     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
1317     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1318   };
1319   while (length-- > 0)
1320     *out++ = table[*in++];
1321 }
1322
1323
1324 int
1325 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1326            int src_x, int src_y, int dest_x, int dest_y,
1327            unsigned int w, unsigned int h)
1328 {
1329   XRectangle wr = d->frame;
1330
1331   Assert (gc, "no GC");
1332   Assert ((w < 65535), "improbably large width");
1333   Assert ((h < 65535), "improbably large height");
1334   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1335   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1336   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1337   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1338
1339   // Clip width and height to the bounds of the Drawable
1340   //
1341   if (dest_x + (int)w > wr.width) {
1342     if (dest_x > wr.width)
1343       return 0;
1344     w = wr.width - dest_x;
1345   }
1346   if (dest_y + (int)h > wr.height) {
1347     if (dest_y > wr.height)
1348       return 0;
1349     h = wr.height - dest_y;
1350   }
1351   if (w <= 0 || h <= 0)
1352     return 0;
1353
1354   // Clip width and height to the bounds of the XImage
1355   //
1356   if (src_x + w > ximage->width) {
1357     if (src_x > ximage->width)
1358       return 0;
1359     w = ximage->width - src_x;
1360   }
1361   if (src_y + h > ximage->height) {
1362     if (src_y > ximage->height)
1363       return 0;
1364     h = ximage->height - src_y;
1365   }
1366   if (w <= 0 || h <= 0)
1367     return 0;
1368
1369   CGContextRef cgc = d->cgc;
1370
1371   if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1372     return 0;
1373
1374   int bpl = ximage->bytes_per_line;
1375   int bpp = ximage->bits_per_pixel;
1376   int bsize = bpl * h;
1377   char *data = ximage->data;
1378
1379   CGRect r;
1380   r.origin.x = wr.x + dest_x;
1381   r.origin.y = wr.y + wr.height - dest_y - (int)h;
1382   r.size.width = w;
1383   r.size.height = h;
1384
1385   if (bpp == 32) {
1386
1387     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1388        to create a CGImage from a sub-rectagle of the XImage.
1389      */
1390     data += (src_y * bpl) + (src_x * 4);
1391     CGDataProviderRef prov = 
1392       CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1393
1394     CGImageRef cgi = CGImageCreate (w, h,
1395                                     bpp/4, bpp, bpl,
1396                                     dpy->colorspace, 
1397                                     dpy->screen->bitmap_info,
1398                                     prov, 
1399                                     NULL,  /* decode[] */
1400                                     NO, /* interpolate */
1401                                     kCGRenderingIntentDefault);
1402     CGDataProviderRelease (prov);
1403     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1404     CGContextDrawImage (cgc, r, cgi);
1405     CGImageRelease (cgi);
1406
1407   } else {   // (bpp == 1)
1408
1409     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1410
1411        #### However, the bit order within a byte in a 1bpp XImage is
1412             the wrong way around from what Quartz expects, so first we
1413             have to copy the data to reverse it.  Shit!  Maybe it
1414             would be worthwhile to go through the hacks and #ifdef
1415             each one that diddles 1bpp XImage->data directly...
1416      */
1417     Assert ((src_x % 8) == 0,
1418             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1419
1420     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
1421     unsigned char *flipped = (unsigned char *) malloc (bsize);
1422
1423     flipbits ((unsigned char *) data, flipped, bsize);
1424
1425     CGDataProviderRef prov = 
1426       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1427     CGImageRef mask = CGImageMaskCreate (w, h, 
1428                                          1, bpp, bpl,
1429                                          prov,
1430                                          NULL,  /* decode[] */
1431                                          NO); /* interpolate */
1432     push_fg_gc (dpy, d, gc, YES);
1433
1434     CGContextFillRect (cgc, r);                         // foreground color
1435     CGContextClipToMask (cgc, r, mask);
1436     set_color (dpy, cgc, gc->gcv.background, gc->depth, NO, YES);
1437     CGContextFillRect (cgc, r);                         // background color
1438     pop_gc (d, gc);
1439
1440     free (flipped);
1441     CGDataProviderRelease (prov);
1442     CGImageRelease (mask);
1443   }
1444
1445   invalidate_drawable_cache (d);
1446
1447   return 0;
1448 }
1449
1450
1451 XImage *
1452 XGetImage (Display *dpy, Drawable d, int x, int y,
1453            unsigned int width, unsigned int height,
1454            unsigned long plane_mask, int format)
1455 {
1456   const unsigned char *data = 0;
1457   size_t depth, ibpp, ibpl;
1458   convert_mode_t mode;
1459
1460   Assert ((width  < 65535), "improbably large width");
1461   Assert ((height < 65535), "improbably large height");
1462   Assert ((x < 65535 && x > -65535), "improbably large x");
1463   Assert ((y < 65535 && y > -65535), "improbably large y");
1464
1465   CGContextRef cgc = d->cgc;
1466
1467   {
1468     depth = jwxyz_drawable_depth (d);
1469     mode = convert_mode_to_rgba (dpy->screen->bitmap_info);
1470     ibpp = CGBitmapContextGetBitsPerPixel (cgc);
1471     ibpl = CGBitmapContextGetBytesPerRow (cgc);
1472     data = CGBitmapContextGetData (cgc);
1473     Assert (data, "CGBitmapContextGetData failed");
1474   }
1475   
1476   // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
1477   data += (y * ibpl) + (x * (ibpp/8));
1478   
1479   format = (depth == 1 ? XYPixmap : ZPixmap);
1480   XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
1481                                 format, 0, 0, width, height, 0, 0);
1482   image->data = (char *) malloc (height * image->bytes_per_line);
1483   
1484   int obpl = image->bytes_per_line;
1485   
1486   /* both PPC and Intel use word-ordered ARGB frame buffers, which
1487      means that on Intel it is BGRA when viewed by bytes (And BGR
1488      when using 24bpp packing).
1489
1490      BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1491      The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1492      indicator of this latest kink.
1493    */
1494   int xx, yy;
1495   if (depth == 1) {
1496     const unsigned char *iline = data;
1497     for (yy = 0; yy < height; yy++) {
1498
1499       const unsigned char *iline2 = iline;
1500       for (xx = 0; xx < width; xx++) {
1501
1502         iline2++;                     // ignore R  or  A  or  A  or  B
1503         iline2++;                     // ignore G  or  B  or  R  or  G
1504         unsigned char r = *iline2++;  // use    B  or  G  or  G  or  R
1505         if (ibpp == 32) iline2++;     // ignore A  or  R  or  B  or  A
1506
1507         XPutPixel (image, xx, yy, (r ? 1 : 0));
1508       }
1509       iline += ibpl;
1510     }
1511   } else {
1512     const unsigned char *iline = data;
1513     unsigned char *oline = (unsigned char *) image->data;
1514
1515     mode = convert_mode_merge (mode,
1516              convert_mode_invert (
1517                convert_mode_to_rgba (dpy->screen->bitmap_info)));
1518
1519     for (yy = 0; yy < height; yy++) {
1520
1521       convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
1522
1523       oline += obpl;
1524       iline += ibpl;
1525     }
1526   }
1527
1528   return image;
1529 }
1530
1531
1532
1533 /* Returns a transformation matrix to do rotation as per the provided
1534    EXIF "Orientation" value.
1535  */
1536 static CGAffineTransform
1537 exif_rotate (int rot, CGSize rect)
1538 {
1539   CGAffineTransform trans = CGAffineTransformIdentity;
1540   switch (rot) {
1541   case 2:               // flip horizontal
1542     trans = CGAffineTransformMakeTranslation (rect.width, 0);
1543     trans = CGAffineTransformScale (trans, -1, 1);
1544     break;
1545
1546   case 3:               // rotate 180
1547     trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1548     trans = CGAffineTransformRotate (trans, M_PI);
1549     break;
1550
1551   case 4:               // flip vertical
1552     trans = CGAffineTransformMakeTranslation (0, rect.height);
1553     trans = CGAffineTransformScale (trans, 1, -1);
1554     break;
1555
1556   case 5:               // transpose (UL-to-LR axis)
1557     trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1558     trans = CGAffineTransformScale (trans, -1, 1);
1559     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1560     break;
1561
1562   case 6:               // rotate 90
1563     trans = CGAffineTransformMakeTranslation (0, rect.width);
1564     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1565     break;
1566
1567   case 7:               // transverse (UR-to-LL axis)
1568     trans = CGAffineTransformMakeScale (-1, 1);
1569     trans = CGAffineTransformRotate (trans, M_PI / 2);
1570     break;
1571
1572   case 8:               // rotate 270
1573     trans = CGAffineTransformMakeTranslation (rect.height, 0);
1574     trans = CGAffineTransformRotate (trans, M_PI / 2);
1575     break;
1576
1577   default: 
1578     break;
1579   }
1580
1581   return trans;
1582 }
1583
1584
1585 void
1586 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
1587                                 Bool nsimg_p, void *img_arg,
1588                                XRectangle *geom_ret, int exif_rotation)
1589 {
1590   CGImageRef cgi;
1591 # ifndef USE_IPHONE
1592   CGImageSourceRef cgsrc;
1593 # endif // USE_IPHONE
1594   NSSize imgr;
1595
1596   CGContextRef cgc = d->cgc;
1597
1598   if (nsimg_p) {
1599
1600     NSImage *nsimg = (NSImage *) img_arg;
1601     imgr = [nsimg size];
1602
1603 # ifndef USE_IPHONE
1604     // convert the NSImage to a CGImage via the toll-free-bridging 
1605     // of NSData and CFData...
1606     //
1607     NSData *nsdata = [NSBitmapImageRep
1608                        TIFFRepresentationOfImageRepsInArray:
1609                          [nsimg representations]];
1610     CFDataRef cfdata = (CFDataRef) nsdata;
1611     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1612     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1613 # else  // USE_IPHONE
1614     cgi = nsimg.CGImage;
1615 # endif // USE_IPHONE
1616
1617   } else {
1618     cgi = (CGImageRef) img_arg;
1619     imgr.width  = CGImageGetWidth (cgi);
1620     imgr.height = CGImageGetHeight (cgi);
1621   }
1622
1623   Bool rot_p = (exif_rotation >= 5);
1624
1625   if (rot_p)
1626     imgr = NSMakeSize (imgr.height, imgr.width);
1627
1628   XRectangle winr = d->frame;
1629   float rw = winr.width  / imgr.width;
1630   float rh = winr.height / imgr.height;
1631   float r = (rw < rh ? rw : rh);
1632
1633   CGRect dst, dst2;
1634   dst.size.width  = imgr.width  * r;
1635   dst.size.height = imgr.height * r;
1636   dst.origin.x = (winr.width  - dst.size.width)  / 2;
1637   dst.origin.y = (winr.height - dst.size.height) / 2;
1638
1639   dst2.origin.x = dst2.origin.y = 0;
1640   if (rot_p) {
1641     dst2.size.width = dst.size.height; 
1642     dst2.size.height = dst.size.width;
1643   } else {
1644     dst2.size = dst.size;
1645   }
1646
1647   // Clear the part not covered by the image to background or black.
1648   //
1649   if (d->type == WINDOW)
1650     XClearWindow (dpy, d);
1651   else {
1652     jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.width, winr.height,
1653                      jwxyz_drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
1654   }
1655
1656   CGAffineTransform trans = 
1657     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1658
1659   CGContextSaveGState (cgc);
1660   CGContextConcatCTM (cgc, 
1661                       CGAffineTransformMakeTranslation (dst.origin.x,
1662                                                         dst.origin.y));
1663   CGContextConcatCTM (cgc, trans);
1664   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1665   CGContextDrawImage (cgc, dst2, cgi);
1666   CGContextRestoreGState (cgc);
1667
1668 # ifndef USE_IPHONE
1669   if (nsimg_p) {
1670     CFRelease (cgsrc);
1671     CGImageRelease (cgi);
1672   }
1673 # endif // USE_IPHONE
1674
1675   if (geom_ret) {
1676     geom_ret->x = dst.origin.x;
1677     geom_ret->y = dst.origin.y;
1678     geom_ret->width  = dst.size.width;
1679     geom_ret->height = dst.size.height;
1680   }
1681
1682   invalidate_drawable_cache (d);
1683 }
1684
1685
1686
1687 Pixmap
1688 XCreatePixmap (Display *dpy, Drawable d,
1689                unsigned int width, unsigned int height, unsigned int depth)
1690 {
1691   if (!dpy) abort();
1692   char *data = (char *) malloc (width * height * 4);
1693   if (! data) return 0;
1694
1695   Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1696   p->type = PIXMAP;
1697   p->frame.width       = width;
1698   p->frame.height      = height;
1699   p->pixmap.depth      = depth;
1700   p->pixmap.cgc_buffer = data;
1701   
1702   /* Quartz doesn't have a 1bpp image type.
1703      Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
1704      don't support that!  So we always use 32bpp, regardless of depth. */
1705
1706   p->cgc = CGBitmapContextCreate (data, width, height,
1707                                   8, /* bits per component */
1708                                   width * 4, /* bpl */
1709                                   dpy->colorspace,
1710                                   dpy->screen->bitmap_info);
1711   Assert (p->cgc, "could not create CGBitmapContext");
1712   return p;
1713 }
1714
1715
1716 int
1717 XFreePixmap (Display *d, Pixmap p)
1718 {
1719   Assert (p && p->type == PIXMAP, "not a pixmap");
1720   invalidate_drawable_cache (p);
1721   CGContextRelease (p->cgc);
1722   if (p->pixmap.cgc_buffer)
1723     free (p->pixmap.cgc_buffer);
1724   free (p);
1725   return 0;
1726 }
1727
1728
1729 static Pixmap
1730 copy_pixmap (Display *dpy, Pixmap p)
1731 {
1732   if (!p) return 0;
1733   Assert (p->type == PIXMAP, "not a pixmap");
1734
1735   Pixmap p2 = 0;
1736
1737   Window root;
1738   int x, y;
1739   unsigned int width, height, border_width, depth;
1740   if (XGetGeometry (dpy, p, &root,
1741                     &x, &y, &width, &height, &border_width, &depth)) {
1742     XGCValues gcv;
1743     gcv.function = GXcopy;
1744     GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
1745     if (gc) {
1746       p2 = XCreatePixmap (dpy, p, width, height, depth);
1747       if (p2)
1748         XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
1749       XFreeGC (dpy, gc);
1750     }
1751   }
1752
1753   Assert (p2, "could not copy pixmap");
1754
1755   return p2;
1756 }
1757
1758
1759 /* Font metric terminology, as used by X11:
1760
1761    "lbearing" is the distance from the logical origin to the leftmost pixel.
1762    If a character's ink extends to the left of the origin, it is negative.
1763
1764    "rbearing" is the distance from the logical origin to the rightmost pixel.
1765
1766    "descent" is the distance from the logical origin to the bottommost pixel.
1767    For characters with descenders, it is positive.  For superscripts, it
1768    is negative.
1769
1770    "ascent" is the distance from the logical origin to the topmost pixel.
1771    It is the number of pixels above the baseline.
1772
1773    "width" is the distance from the logical origin to the position where
1774    the logical origin of the next character should be placed.
1775
1776    If "rbearing" is greater than "width", then this character overlaps the
1777    following character.  If smaller, then there is trailing blank space.
1778  */
1779 static void
1780 utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs)
1781 {
1782   // Returns the metrics of the multi-character, single-line UTF8 string.
1783
1784   NSFont *nsfont = fid->nsfont;
1785   Drawable d = XRootWindow (fid->dpy, 0);
1786
1787   CGContextRef cgc = d->cgc;
1788   NSDictionary *attr =
1789     [NSDictionary dictionaryWithObjectsAndKeys:
1790                     nsfont, NSFontAttributeName,
1791                   nil];
1792   NSAttributedString *astr = [[NSAttributedString alloc]
1793                                initWithString:nsstr
1794                                    attributes:attr];
1795   CTLineRef ctline = CTLineCreateWithAttributedString (
1796                        (__bridge CFAttributedStringRef) astr);
1797   CGContextSetTextPosition (cgc, 0, 0);
1798   CGContextSetShouldAntialias (cgc, True);  // #### Guess?
1799
1800   memset (cs, 0, sizeof(*cs));
1801
1802   // "CTRun represents set of consecutive glyphs sharing the same
1803   // attributes and direction".
1804   //
1805   // We also get multiple runs any time font subsitution happens:
1806   // E.g., if the current font is Verdana-Bold, a &larr; character
1807   // in the NSString will actually be rendered in LucidaGrande-Bold.
1808   //
1809   int count = 0;
1810   for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
1811     CTRunRef run = (CTRunRef) runid;
1812     CFRange r = { 0, };
1813     CGRect bbox = CTRunGetImageBounds (run, cgc, r);
1814     CGFloat ascent, descent, leading;
1815     CGFloat advancement =
1816       CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
1817
1818 # ifndef USE_IPHONE
1819     // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
1820     bbox.origin.x    -= 2.0/3.0;
1821     bbox.size.width  += 4.0/3.0;
1822     bbox.size.height += 1.0/2.0;
1823 # endif
1824
1825     // Create the metrics for this run:
1826     XCharStruct cc;
1827     cc.ascent   = ceil  (bbox.origin.y + bbox.size.height);
1828     cc.descent  = ceil (-bbox.origin.y);
1829     cc.lbearing = floor (bbox.origin.x);
1830     cc.rbearing = ceil  (bbox.origin.x + bbox.size.width);
1831     cc.width    = floor (advancement + 0.5);
1832
1833     // Add those metrics into the cumulative metrics:
1834     if (count == 0)
1835       *cs = cc;
1836     else
1837       {
1838         cs->ascent   = MAX (cs->ascent,     cc.ascent);
1839         cs->descent  = MAX (cs->descent,    cc.descent);
1840         cs->lbearing = MIN (cs->lbearing,   cs->width + cc.lbearing);
1841         cs->rbearing = MAX (cs->rbearing,   cs->width + cc.rbearing);
1842         cs->width    = MAX (cs->width,      cs->width + cc.width);
1843       }
1844
1845     // Why no y? What about vertical text?
1846     // XCharStruct doesn't encapsulate that but XGlyphInfo does.
1847
1848     count++;
1849   }
1850
1851   [astr release];
1852   CFRelease (ctline);
1853 }
1854
1855
1856
1857 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1858 //
1859 static void
1860 query_font (Font fid)
1861 {
1862   if (!fid || !fid->nsfont) {
1863     Assert (0, "no NSFont in fid");
1864     return;
1865   }
1866   if (![fid->nsfont fontName]) {
1867     Assert(0, "broken NSFont in fid");
1868     return;
1869   }
1870
1871   int first = 32;
1872   int last = 255;
1873
1874   XFontStruct *f = &fid->metrics;
1875   XCharStruct *min = &f->min_bounds;
1876   XCharStruct *max = &f->max_bounds;
1877
1878   f->fid               = fid;
1879   f->min_char_or_byte2 = first;
1880   f->max_char_or_byte2 = last;
1881   f->default_char      = 'M';
1882   f->ascent            =  ceil ([fid->nsfont ascender]);
1883   f->descent           = -floor ([fid->nsfont descender]);
1884
1885   min->width    = 32767;  // set to smaller values in the loop
1886   min->ascent   = 32767;
1887   min->descent  = 32767;
1888   min->lbearing = 32767;
1889   min->rbearing = 32767;
1890
1891   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1892
1893   for (int i = first; i <= last; i++) {
1894     XCharStruct *cs = &f->per_char[i-first];
1895
1896     char s2[2];
1897     s2[0] = i;
1898     s2[1] = 0;
1899     NSString *nsstr = [NSString stringWithCString:s2
1900                                encoding:NSISOLatin1StringEncoding];
1901     utf8_metrics (fid, nsstr, cs);
1902
1903     max->width    = MAX (max->width,    cs->width);
1904     max->ascent   = MAX (max->ascent,   cs->ascent);
1905     max->descent  = MAX (max->descent,  cs->descent);
1906     max->lbearing = MAX (max->lbearing, cs->lbearing);
1907     max->rbearing = MAX (max->rbearing, cs->rbearing);
1908
1909     min->width    = MIN (min->width,    cs->width);
1910     min->ascent   = MIN (min->ascent,   cs->ascent);
1911     min->descent  = MIN (min->descent,  cs->descent);
1912     min->lbearing = MIN (min->lbearing, cs->lbearing);
1913     min->rbearing = MIN (min->rbearing, cs->rbearing);
1914
1915 # if 0
1916     fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
1917                     " bb=%5.1f x %5.1f @ %5.1f %5.1f  adv=%5.1f %5.1f\n",
1918             i, i, cs->width, cs->lbearing, cs->rbearing, 
1919             cs->ascent, cs->descent,
1920             bbox.size.width, bbox.size.height,
1921             bbox.origin.x, bbox.origin.y,
1922             advancement.width, advancement.height);
1923 # endif // 0
1924   }
1925 }
1926
1927
1928 // Since 'Font' includes the metrics, this just makes a copy of that.
1929 //
1930 XFontStruct *
1931 XQueryFont (Display *dpy, Font fid)
1932 {
1933   // copy XFontStruct
1934   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
1935   *f = fid->metrics;
1936
1937   // build XFontProps
1938   f->n_properties = 1;
1939   f->properties = malloc (sizeof(*f->properties) * f->n_properties);
1940   f->properties[0].name = XA_FONT;
1941   Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
1942           "atoms probably needs a real implementation");
1943   // If XInternAtom is ever implemented, use it here.
1944   f->properties[0].card32 = (char *)fid->xa_font;
1945
1946   // copy XCharStruct array
1947   int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
1948   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
1949   memcpy (f->per_char, fid->metrics.per_char,
1950           size * sizeof (XCharStruct));
1951
1952   return f;
1953 }
1954
1955
1956 static Font
1957 copy_font (Font fid)
1958 {
1959   // copy 'Font' struct
1960   Font fid2 = (Font) malloc (sizeof(*fid2));
1961   *fid2 = *fid;
1962
1963   // copy XCharStruct array
1964   int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
1965   fid2->metrics.per_char = (XCharStruct *) 
1966     malloc ((size + 2) * sizeof (XCharStruct));
1967   memcpy (fid2->metrics.per_char, fid->metrics.per_char, 
1968           size * sizeof (XCharStruct));
1969
1970   // copy the other pointers
1971   fid2->ps_name = strdup (fid->ps_name);
1972   fid2->xa_font = strdup (fid->xa_font);
1973 //  [fid2->nsfont retain];
1974   fid2->metrics.fid = fid2;
1975
1976   return fid2;
1977 }
1978
1979
1980 static NSArray *
1981 font_family_members (NSString *family_name)
1982 {
1983 # ifndef USE_IPHONE
1984   return [[NSFontManager sharedFontManager]
1985           availableMembersOfFontFamily:family_name];
1986 # else
1987   return [UIFont fontNamesForFamilyName:family_name];
1988 # endif
1989 }
1990
1991
1992 static NSString *
1993 default_font_family (NSFontTraitMask require)
1994 {
1995   return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
1996 }
1997
1998
1999 static NSFont *
2000 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
2001           NSString *family_name, float size,
2002           char **name_ret)
2003 {
2004   Assert (size > 0, "zero font size");
2005
2006   NSArray *family_members = font_family_members (family_name);
2007   if (!family_members.count)
2008     family_members = font_family_members (default_font_family (traits));
2009
2010 # ifndef USE_IPHONE
2011   for (unsigned k = 0; k != family_members.count; ++k) {
2012
2013     NSArray *member = [family_members objectAtIndex:k];
2014     NSFontTraitMask font_mask =
2015     [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
2016
2017     if ((font_mask & mask) == traits) {
2018
2019       NSString *name = [member objectAtIndex:0];
2020       NSFont *f = [NSFont fontWithName:name size:size];
2021       if (!f)
2022         break;
2023
2024       /* Don't use this font if it (probably) doesn't include ASCII characters.
2025        */
2026       NSStringEncoding enc = [f mostCompatibleStringEncoding];
2027       if (! (enc == NSUTF8StringEncoding ||
2028              enc == NSISOLatin1StringEncoding ||
2029              enc == NSNonLossyASCIIStringEncoding ||
2030              enc == NSISOLatin2StringEncoding ||
2031              enc == NSUnicodeStringEncoding ||
2032              enc == NSWindowsCP1250StringEncoding ||
2033              enc == NSWindowsCP1252StringEncoding ||
2034              enc == NSMacOSRomanStringEncoding)) {
2035         // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2036         break;
2037       }
2038       // NSLog(@"using \"%@\": %d", name, enc);
2039
2040       // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2041       *name_ret = strdup (name.UTF8String);
2042       return f;
2043     }
2044   }
2045 # else // USE_IPHONE
2046
2047   // This trick needs iOS 3.1, see "Using SDK-Based Development".
2048   Class has_font_descriptor = [UIFontDescriptor class];
2049
2050   for (NSString *fn in family_members) {
2051 # define MATCH(X) \
2052          ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2053          != NSNotFound)
2054
2055     NSFontTraitMask font_mask;
2056     if (has_font_descriptor) {
2057       // This only works on iOS 7 and later.
2058       font_mask = [[UIFontDescriptor
2059                     fontDescriptorWithFontAttributes:
2060                     @{UIFontDescriptorNameAttribute:fn}]
2061                    symbolicTraits];
2062     } else {
2063       font_mask = 0;
2064       if (MATCH(@"Bold"))
2065         font_mask |= NSBoldFontMask;
2066       if (MATCH(@"Italic") || MATCH(@"Oblique"))
2067         font_mask |= NSItalicFontMask;
2068       if (MATCH(@"Courier"))
2069         font_mask |= NSFixedPitchFontMask;
2070     }
2071
2072     if ((font_mask & mask) == traits) {
2073
2074       /* Check if it can do ASCII.  No good way to accomplish this!
2075          These are fonts present in iPhone Simulator as of June 2012
2076          that don't include ASCII.
2077        */
2078       if (MATCH(@"AppleGothic") ||      // Korean
2079           MATCH(@"Dingbats") ||         // Dingbats
2080           MATCH(@"Emoji") ||            // Emoticons
2081           MATCH(@"Geeza") ||            // Arabic
2082           MATCH(@"Hebrew") ||           // Hebrew
2083           MATCH(@"HiraKaku") ||         // Japanese
2084           MATCH(@"HiraMin") ||          // Japanese
2085           MATCH(@"Kailasa") ||          // Tibetan
2086           MATCH(@"Ornaments") ||        // Dingbats
2087           MATCH(@"STHeiti")             // Chinese
2088        )
2089          break;
2090
2091       *name_ret = strdup (fn.UTF8String);
2092       return [UIFont fontWithName:fn size:size];
2093     }
2094 # undef MATCH
2095   }
2096
2097 # endif
2098
2099   return NULL;
2100 }
2101
2102
2103 /* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
2104    of XLFD strings; also they can be comma-separated strings with multiple
2105    font names.  First one that exists wins.
2106  */
2107 static NSFont *
2108 try_native_font (const char *name, float scale,
2109                  char **name_ret, float *size_ret, char **xa_font)
2110 {
2111   if (!name) return 0;
2112   const char *spc = strrchr (name, ' ');
2113   if (!spc) return 0;
2114
2115   NSFont *f = 0;
2116   char *token = strdup (name);
2117   char *otoken = token;
2118   char *name2;
2119   char *lasts;
2120
2121   while ((name2 = strtok_r (token, ",", &lasts))) {
2122     token = 0;
2123
2124     while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
2125       name2++;
2126
2127     spc = strrchr (name2, ' ');
2128     if (!spc) continue;
2129
2130     int dsize = 0;
2131     if (1 != sscanf (spc, " %d ", &dsize))
2132       continue;
2133     float size = dsize;
2134
2135     if (size < 4) continue;
2136
2137     size *= scale;
2138
2139     name2[strlen(name2) - strlen(spc)] = 0;
2140
2141     NSString *nsname = [NSString stringWithCString:name2
2142                                           encoding:NSUTF8StringEncoding];
2143     f = [NSFont fontWithName:nsname size:size];
2144     if (f) {
2145       *name_ret = strdup (name2);
2146       *size_ret = size;
2147       *xa_font = strdup (name); // Maybe this should be an XLFD?
2148       break;
2149     } else {
2150       NSLog(@"No native font: \"%@\" %.0f", nsname, size);
2151 # if 0
2152       for (NSString *fam in [UIFont familyNames]) {
2153         NSLog(@"Family: %@", fam);
2154         for (NSString *f in [UIFont fontNamesForFamilyName:fam]) {
2155           NSLog(@"  Font: %@", f);
2156         }
2157       }
2158 # endif
2159     }
2160   }
2161
2162   free (otoken);
2163   return f;
2164 }
2165
2166
2167 /* Returns a random font in the given size and face.
2168  */
2169 static NSFont *
2170 random_font (NSFontTraitMask traits, NSFontTraitMask mask,
2171              float size, NSString **family_ret, char **name_ret)
2172 {
2173
2174 # ifndef USE_IPHONE
2175   // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
2176   // returns an empty list, at least on a system with default fonts only.
2177   NSArray *families = [[NSFontManager sharedFontManager]
2178                        availableFontFamilies];
2179   if (!families) return 0;
2180 # else
2181   NSArray *families = [UIFont familyNames];
2182
2183   // There are many dups in the families array -- uniquify it.
2184   {
2185     NSArray *sorted_families =
2186     [families sortedArrayUsingSelector:@selector(compare:)];
2187     NSMutableArray *new_families =
2188     [NSMutableArray arrayWithCapacity:sorted_families.count];
2189
2190     NSString *prev_family = @"";
2191     for (NSString *family in sorted_families) {
2192       if ([family compare:prev_family])
2193         [new_families addObject:family];
2194       prev_family = family;
2195     }
2196
2197     families = new_families;
2198   }
2199 # endif // USE_IPHONE
2200
2201   long n = [families count];
2202   if (n <= 0) return 0;
2203
2204   int j;
2205   for (j = 0; j < n; j++) {
2206     int i = random() % n;
2207     NSString *family_name = [families objectAtIndex:i];
2208
2209     NSFont *result = try_font (traits, mask, family_name, size, name_ret);
2210     if (result) {
2211       [*family_ret release];
2212       *family_ret = family_name;
2213       [*family_ret retain];
2214       return result;
2215     }
2216   }
2217
2218   // None of the fonts support ASCII?
2219   return 0;
2220 }
2221
2222
2223 static const char *
2224 xlfd_field_end (const char *s)
2225 {
2226   const char *s2 = strchr(s, '-');
2227   if (!s2)
2228     s2 = s + strlen(s);
2229   return s2;
2230 }
2231
2232
2233 static size_t
2234 xlfd_next (const char **s, const char **s2)
2235 {
2236   if (!**s2) {
2237     *s = *s2;
2238   } else {
2239     Assert (**s2 == '-', "xlfd parse error");
2240     *s = *s2 + 1;
2241     *s2 = xlfd_field_end (*s);
2242   }
2243
2244   return *s2 - *s;
2245 }
2246
2247
2248 static NSFont *
2249 try_xlfd_font (Display *dpy, const char *name, float scale,
2250                char **name_ret, float *size_ret, char **xa_font)
2251 {
2252   NSFont *nsfont = 0;
2253   NSString *family_name = nil;
2254   NSFontTraitMask require = 0,
2255    // Default mask is for the built-in X11 font aliases.
2256    mask = NSFixedPitchFontMask | NSBoldFontMask | NSItalicFontMask;
2257   BOOL rand  = NO;
2258   float size = 0;
2259   char *ps_name = 0;
2260
2261   const char *s = (name ? name : "");
2262
2263   size_t L = strlen (s);
2264 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2265 # define UNSPEC   (L == 0 || L == 1 && *s == '*')
2266   if      (CMP ("6x10"))     size = 8,  require |= NSFixedPitchFontMask;
2267   else if (CMP ("6x10bold")) size = 8,  require |= NSFixedPitchFontMask | NSBoldFontMask;
2268   else if (CMP ("fixed"))    size = 12, require |= NSFixedPitchFontMask;
2269   else if (CMP ("9x15"))     size = 12, require |= NSFixedPitchFontMask;
2270   else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
2271   else if (CMP ("vga"))      size = 12, require |= NSFixedPitchFontMask;
2272   else if (CMP ("console"))  size = 12, require |= NSFixedPitchFontMask;
2273   else if (CMP ("gallant"))  size = 12, require |= NSFixedPitchFontMask;
2274   else {
2275
2276     NSFontTraitMask forbid = 0;
2277
2278     // Incorrect fields are ignored.
2279
2280     if (*s == '-')
2281       ++s;
2282     const char *s2 = xlfd_field_end(s);
2283
2284     // Foundry (ignore)
2285
2286     L = xlfd_next (&s, &s2); // Family name
2287     // This used to substitute Georgia for Times. Now it doesn't.
2288     if (CMP ("random")) {
2289       rand = YES;
2290     } else if (CMP ("fixed")) {
2291       require |= NSFixedPitchFontMask;
2292       family_name = @"Courier";
2293     } else if (!UNSPEC) {
2294       family_name = [[[NSString alloc] initWithBytes:s
2295                                               length:L
2296                                             encoding:NSUTF8StringEncoding]
2297                      autorelease];
2298     }
2299
2300     L = xlfd_next (&s, &s2); // Weight name
2301     if (CMP ("bold") || CMP ("demibold"))
2302       require |= NSBoldFontMask;
2303     else if (CMP ("medium") || CMP ("regular"))
2304       forbid |= NSBoldFontMask;
2305
2306     L = xlfd_next (&s, &s2); // Slant
2307     if (CMP ("i") || CMP ("o"))
2308       require |= NSItalicFontMask;
2309     else if (CMP ("r"))
2310       forbid |= NSItalicFontMask;
2311
2312     xlfd_next (&s, &s2); // Set width name (ignore)
2313     xlfd_next (&s, &s2); // Add style name (ignore)
2314
2315     xlfd_next (&s, &s2); // Pixel size (ignore)
2316
2317     xlfd_next (&s, &s2); // Point size
2318     char *s3;
2319     uintmax_t n = strtoumax(s, &s3, 10);
2320     if (s2 == s3)
2321       size = n / 10.0;
2322
2323     xlfd_next (&s, &s2); // Resolution X (ignore)
2324     xlfd_next (&s, &s2); // Resolution Y (ignore)
2325
2326     xlfd_next (&s, &s2); // Spacing
2327     if (CMP ("p"))
2328       forbid |= NSFixedPitchFontMask;
2329     else if (CMP ("m") || CMP ("c"))
2330       require |= NSFixedPitchFontMask;
2331
2332     // Don't care about average_width or charset registry.
2333
2334     mask = require | forbid;
2335   }
2336 # undef CMP
2337 # undef UNSPEC
2338
2339   if (!family_name && !rand)
2340     family_name = default_font_family (require);
2341
2342   if (size < 6 || size > 1000)
2343     size = 12;
2344
2345   size *= scale;
2346
2347   if (rand) {
2348     nsfont   = random_font (require, mask, size, &family_name, &ps_name);
2349     [family_name autorelease];
2350   }
2351
2352   if (!nsfont)
2353     nsfont   = try_font (require, mask, family_name, size, &ps_name);
2354
2355   // if that didn't work, turn off attibutes until it does
2356   // (e.g., there is no "Monaco-Bold".)
2357   //
2358   if (!nsfont && (mask & NSItalicFontMask)) {
2359     require &= ~NSItalicFontMask;
2360     mask &= ~NSItalicFontMask;
2361     nsfont = try_font (require, mask, family_name, size, &ps_name);
2362   }
2363   if (!nsfont && (mask & NSBoldFontMask)) {
2364     require &= ~NSBoldFontMask;
2365     mask &= ~NSBoldFontMask;
2366     nsfont = try_font (require, mask, family_name, size, &ps_name);
2367   }
2368   if (!nsfont && (mask & NSFixedPitchFontMask)) {
2369     require &= ~NSFixedPitchFontMask;
2370     mask &= ~NSFixedPitchFontMask;
2371     nsfont = try_font (require, mask, family_name, size, &ps_name);
2372   }
2373
2374   if (nsfont) {
2375     unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2;
2376     unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d);
2377     *name_ret = ps_name;
2378     *size_ret = size;
2379     float actual_size = size / scale;
2380     asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
2381              family_name.UTF8String,
2382              (require & NSBoldFontMask) ? "bold" : "medium",
2383              (require & NSItalicFontMask) ? 'o' : 'r',
2384              (unsigned)(dpi * actual_size / 72.27 + 0.5),
2385              (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
2386              (require & NSFixedPitchFontMask) ? 'm' : 'p');
2387     return nsfont;
2388   } else {
2389     if (ps_name) free (ps_name);
2390     return 0;
2391   }
2392 }
2393
2394
2395 Font
2396 XLoadFont (Display *dpy, const char *name)
2397 {
2398   Font fid = (Font) calloc (1, sizeof(*fid));
2399
2400   float scale = 1;
2401
2402 # ifdef USE_IPHONE
2403   /* Since iOS screens are physically smaller than desktop screens, scale up
2404      the fonts to make them more readable.
2405
2406      Note that X11 apps on iOS also have the backbuffer sized in points
2407      instead of pixels, resulting in an effective X11 screen size of 768x1024
2408      or so, even if the display has significantly higher resolution.  That is
2409      unrelated to this hack, which is really about DPI.
2410    */
2411   scale = dpy->main_window->window.view.hackedContentScaleFactor;
2412   if (scale < 1) // iPad Pro magnifies the backbuffer by 3x, which makes text
2413     scale = 1;   // excessively blurry in BSOD.
2414 # endif
2415
2416   fid->dpy = dpy;
2417   fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
2418                                  &fid->xa_font);
2419
2420   if (!fid->nsfont && name &&
2421       strchr (name, ' ') &&
2422       !strchr (name, '*')) {
2423     // If name contains a space but no stars, it is a native font spec --
2424     // return NULL so that we know it really didn't exist.  Else, it is an
2425     //  XLFD font, so keep trying.
2426     XUnloadFont (dpy, fid);
2427     return 0;
2428   }
2429
2430   if (! fid->nsfont)
2431     fid->nsfont = try_xlfd_font (dpy, name, scale, &fid->ps_name, &fid->size,
2432                                  &fid->xa_font);
2433
2434   // We should never return NULL for XLFD fonts.
2435   if (!fid->nsfont) {
2436     Assert (0, "no font");
2437     return 0;
2438   }
2439   CFRetain (fid->nsfont);   // needed for garbage collection?
2440
2441   //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2442
2443   query_font (fid);
2444
2445   return fid;
2446 }
2447
2448
2449 XFontStruct *
2450 XLoadQueryFont (Display *dpy, const char *name)
2451 {
2452   Font fid = XLoadFont (dpy, name);
2453   if (!fid) return 0;
2454   return XQueryFont (dpy, fid);
2455 }
2456
2457 int
2458 XUnloadFont (Display *dpy, Font fid)
2459 {
2460   if (fid->ps_name)
2461     free (fid->ps_name);
2462   if (fid->metrics.per_char)
2463     free (fid->metrics.per_char);
2464
2465   // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
2466   //      crashes in [NSFont ascender] <- query_font, and it seems to go away
2467   //      if I never release the nsfont.  So, fuck it, we'll just leak fonts.
2468   //      They're probably not very big...
2469   //
2470   //  [fid->nsfont release];
2471   //  CFRelease (fid->nsfont);
2472
2473   free (fid);
2474   return 0;
2475 }
2476
2477 int
2478 XFreeFontInfo (char **names, XFontStruct *info, int n)
2479 {
2480   int i;
2481   if (names) {
2482     for (i = 0; i < n; i++)
2483       if (names[i]) free (names[i]);
2484     free (names);
2485   }
2486   if (info) {
2487     for (i = 0; i < n; i++)
2488       if (info[i].per_char) {
2489         free (info[i].per_char);
2490         free (info[i].properties);
2491       }
2492     free (info);
2493   }
2494   return 0;
2495 }
2496
2497 int
2498 XFreeFont (Display *dpy, XFontStruct *f)
2499 {
2500   Font fid = f->fid;
2501   XFreeFontInfo (0, f, 1);
2502   XUnloadFont (dpy, fid);
2503   return 0;
2504 }
2505
2506
2507 int
2508 XSetFont (Display *dpy, GC gc, Font fid)
2509 {
2510   if (gc->gcv.font)
2511     XUnloadFont (dpy, gc->gcv.font);
2512   gc->gcv.font = copy_font (fid);
2513   [gc->gcv.font->nsfont retain];
2514   CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
2515   return 0;
2516 }
2517
2518
2519 XFontSet
2520 XCreateFontSet (Display *dpy, char *name, 
2521                 char ***missing_charset_list_return,
2522                 int *missing_charset_count_return,
2523                 char **def_string_return)
2524 {
2525   char *name2 = strdup (name);
2526   char *s = strchr (name, ",");
2527   if (s) *s = 0;
2528   XFontSet set = 0;
2529   XFontStruct *f = XLoadQueryFont (dpy, name2);
2530   if (f)
2531     {
2532       set = (XFontSet) calloc (1, sizeof(*set));
2533       set->font = f;
2534     }
2535   free (name2);
2536   if (missing_charset_list_return)  *missing_charset_list_return = 0;
2537   if (missing_charset_count_return) *missing_charset_count_return = 0;
2538   if (def_string_return) *def_string_return = 0;
2539   return set;
2540 }
2541
2542
2543 void
2544 XFreeFontSet (Display *dpy, XFontSet set)
2545 {
2546   XFreeFont (dpy, set->font);
2547   free (set);
2548 }
2549
2550
2551 const char *
2552 jwxyz_nativeFontName (Font f, float *size)
2553 {
2554   if (size) *size = f->size;
2555   return f->ps_name;
2556 }
2557
2558
2559 void
2560 XFreeStringList (char **list)
2561 {
2562   int i;
2563   if (!list) return;
2564   for (i = 0; list[i]; i++)
2565     XFree (list[i]);
2566   XFree (list);
2567 }
2568
2569
2570 // Returns the verbose Unicode name of this character, like "agrave" or
2571 // "daggerdouble".  Used by fontglide debugMetrics.
2572 //
2573 char *
2574 jwxyz_unicode_character_name (Font fid, unsigned long uc)
2575 {
2576   char *ret = 0;
2577   CTFontRef ctfont =
2578     CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2579                           [fid->nsfont pointSize],
2580                           NULL);
2581   Assert (ctfont, "no CTFontRef for UIFont");
2582
2583   CGGlyph cgglyph;
2584   if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
2585     CGFontRef cgfont = CTFontCopyGraphicsFont (ctfont, 0);
2586     NSString *name = (NSString *) CGFontCopyGlyphNameForGlyph(cgfont, cgglyph);
2587     ret = (name ? strdup ([name UTF8String]) : 0);
2588     CGFontRelease (cgfont);
2589     [name release];
2590   }
2591
2592   CFRelease (ctfont);
2593   return ret;
2594 }
2595
2596
2597 // Given a UTF8 string, return an NSString.  Bogus UTF8 characters are ignored.
2598 // We have to do this because stringWithCString returns NULL if there are
2599 // any invalid characters at all.
2600 //
2601 static NSString *
2602 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
2603 {
2604   int out_len = in_len * 4;   // length of string might increase
2605   char *s2 = (char *) malloc (out_len);
2606   char *out = s2;
2607   const char *in_end  = in  + in_len;
2608   const char *out_end = out + out_len;
2609   Bool latin1_p = True;
2610
2611   while (in < in_end)
2612     {
2613       unsigned long uc;
2614       long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
2615       long L2 = utf8_encode (uc, out, out_end - out);
2616       in  += L1;
2617       out += L2;
2618       if (uc > 255) latin1_p = False;
2619     }
2620   *out = 0;
2621   NSString *nsstr =
2622     [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2623   free (s2);
2624   if (latin1_pP) *latin1_pP = latin1_p;
2625   return (nsstr ? nsstr : @"");
2626 }
2627
2628
2629 int
2630 XTextExtents (XFontStruct *f, const char *s, int length,
2631               int *dir_ret, int *ascent_ret, int *descent_ret,
2632               XCharStruct *cs)
2633 {
2634   // Unfortunately, adding XCharStructs together to get the extents for a
2635   // string doesn't work: Cocoa uses non-integral character advancements, but
2636   // XCharStruct.width is an integer. Plus that doesn't take into account
2637   // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
2638   // Zapfino.
2639
2640   NSString *nsstr = [[[NSString alloc] initWithBytes:s
2641                                               length:length
2642                                             encoding:NSISOLatin1StringEncoding]
2643                      autorelease];
2644   utf8_metrics (f->fid, nsstr, cs);
2645   *dir_ret = 0;
2646   *ascent_ret  = f->ascent;
2647   *descent_ret = f->descent;
2648   return 0;
2649 }
2650
2651 int
2652 XTextWidth (XFontStruct *f, const char *s, int length)
2653 {
2654   int ascent, descent, dir;
2655   XCharStruct cs;
2656   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2657   return cs.width;
2658 }
2659
2660
2661 int
2662 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
2663                 int *dir_ret, int *ascent_ret, int *descent_ret,
2664                 XCharStruct *cs)
2665 {
2666   // Bool latin1_p = True;
2667   int i, utf8_len = 0;
2668   char *utf8 = XChar2b_to_utf8 (s, &utf8_len);   // already sanitized
2669
2670   for (i = 0; i < length; i++)
2671     if (s[i].byte1 > 0) {
2672       // latin1_p = False;
2673       break;
2674     }
2675
2676   {
2677     NSString *nsstr = [NSString stringWithCString:utf8
2678                                 encoding:NSUTF8StringEncoding];
2679     utf8_metrics (f->fid, nsstr, cs);
2680   }
2681
2682   *dir_ret = 0;
2683   *ascent_ret  = f->ascent;
2684   *descent_ret = f->descent;
2685   free (utf8);
2686   return 0;
2687 }
2688
2689
2690 /* "Returns the distance in pixels in the primary draw direction from
2691    the drawing origin to the origin of the next character to be drawn."
2692
2693    "overall_ink_return is set to the bbox of the string's character ink."
2694
2695    "The overall_ink_return for a nondescending, horizontally drawn Latin
2696    character is conventionally entirely above the baseline; that is,
2697    overall_ink_return.height <= -overall_ink_return.y."
2698
2699      [So this means that y is the top of the ink, and height grows down:
2700       For above-the-baseline characters, y is negative.]
2701
2702    "The overall_ink_return for a nonkerned character is entirely at, and to
2703    the right of, the origin; that is, overall_ink_return.x >= 0."
2704
2705      [So this means that x is the left of the ink, and width grows right.
2706       For left-of-the-origin characters, x is negative.]
2707
2708    "A character consisting of a single pixel at the origin would set
2709    overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
2710  */
2711 int
2712 Xutf8TextExtents (XFontSet set, const char *str, int len,
2713                   XRectangle *overall_ink_return,
2714                   XRectangle *overall_logical_return)
2715 {
2716   Bool latin1_p;
2717   NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
2718   XCharStruct cs;
2719
2720   utf8_metrics (set->font->fid, nsstr, &cs);
2721
2722   /* "The overall_logical_return is the bounding box that provides minimum
2723      spacing to other graphical features for the string. Other graphical
2724      features, for example, a border surrounding the text, should not
2725      intersect this rectangle."
2726
2727      So I think that means they're the same?  Or maybe "ink" is the bounding
2728      box, and "logical" is the advancement?  But then why is the return value
2729      the advancement?
2730    */
2731   if (overall_ink_return)
2732     XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
2733   if (overall_logical_return)
2734     XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
2735
2736   return cs.width;
2737 }
2738
2739
2740 static int
2741 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2742              NSString *nsstr)
2743 {
2744   if (! nsstr) return 1;
2745
2746   XRectangle wr = d->frame;
2747   CGContextRef cgc = d->cgc;
2748
2749   unsigned long argb = gc->gcv.foreground;
2750   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2751   float rgba[4];
2752   query_color_float (dpy, argb, rgba);
2753   NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
2754                                       green:rgba[1]
2755                                        blue:rgba[2]
2756                                       alpha:rgba[3]];
2757
2758   if (!gc->gcv.font) {
2759     Assert (0, "no font");
2760     return 1;
2761   }
2762
2763   /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
2764       NSFontAttributeName, and NSAttributedString are only present on iOS 6
2765       and later.  We could resurrect the Quartz code from v5.29 and do a
2766       runtime conditional on that, but that would be a pain in the ass.
2767       Probably time to just make iOS 6 a requirement.
2768    */
2769
2770   NSDictionary *attr =
2771     [NSDictionary dictionaryWithObjectsAndKeys:
2772                     gc->gcv.font->nsfont, NSFontAttributeName,
2773                     fg, NSForegroundColorAttributeName,
2774                   nil];
2775
2776   // Don't understand why we have to do both set_color and
2777   // NSForegroundColorAttributeName, but we do.
2778   //
2779   set_color (dpy, cgc, argb, 32, NO, YES);
2780
2781   NSAttributedString *astr = [[NSAttributedString alloc]
2782                                initWithString:nsstr
2783                                    attributes:attr];
2784   CTLineRef dl = CTLineCreateWithAttributedString (
2785                    (__bridge CFAttributedStringRef) astr);
2786
2787   // Not sure why this is necessary, but xoff is positive when the first
2788   // character on the line has a negative lbearing.  Without this, the
2789   // string is rendered with the first ink at 0 instead of at lbearing.
2790   // I have not seen xoff be negative, so I'm not sure if that can happen.
2791   //
2792   // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
2793   // a letter.
2794   //
2795   CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
2796   Assert (xoff >= 0, "unexpected CTLineOffset");
2797   x -= xoff;
2798
2799   CGContextSetTextPosition (cgc,
2800                             wr.x + x,
2801                             wr.y + wr.height - y);
2802   CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
2803
2804   CTLineDraw (dl, cgc);
2805   CFRelease (dl);
2806   [astr release];
2807
2808   invalidate_drawable_cache (d);
2809   return 0;
2810 }
2811
2812
2813 int
2814 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2815              const char  *str, int len)
2816 {
2817   char *s2 = (char *) malloc (len + 1);
2818   strncpy (s2, str, len);
2819   s2[len] = 0;
2820   NSString *nsstr = [NSString stringWithCString:s2
2821                                        encoding:NSISOLatin1StringEncoding];
2822   int ret = draw_string (dpy, d, gc, x, y, nsstr);
2823   free (s2);
2824   return ret;
2825 }
2826
2827
2828 int
2829 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
2830              const XChar2b *str, int len)
2831 {
2832   char *s2 = XChar2b_to_utf8 (str, 0);   // already sanitized
2833   NSString *nsstr =
2834     [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2835   int ret = draw_string (dpy, d, gc, x, y, nsstr);
2836   free (s2);
2837   return ret;
2838 }
2839
2840
2841 void
2842 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
2843                  int x, int y, const char *str, int len)
2844 {
2845   char *s2 = (char *) malloc (len + 1);
2846   strncpy (s2, str, len);
2847   s2[len] = 0;
2848   NSString *nsstr = sanitize_utf8 (str, len, 0);
2849   draw_string (dpy, d, gc, x, y, nsstr);
2850   free (s2);
2851 }
2852
2853
2854 int
2855 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2856                   const char *str, int len)
2857 {
2858   int ascent, descent, dir;
2859   XCharStruct cs;
2860   XTextExtents (&gc->gcv.font->metrics, str, len,
2861                 &dir, &ascent, &descent, &cs);
2862   jwxyz_fill_rect (dpy, d, gc,
2863                    x + MIN (0, cs.lbearing),
2864                    y - MAX (0, ascent),
2865                    MAX (MAX (0, cs.rbearing) -
2866                         MIN (0, cs.lbearing),
2867                         cs.width),
2868                    MAX (0, ascent) + MAX (0, descent),
2869                    gc->gcv.background);
2870   return XDrawString (dpy, d, gc, x, y, str, len);
2871 }
2872
2873
2874 int
2875 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2876 {
2877   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2878
2879   if (gc->gcv.clip_mask) {
2880     XFreePixmap (dpy, gc->gcv.clip_mask);
2881     CGImageRelease (gc->clip_mask);
2882   }
2883
2884   gc->gcv.clip_mask = copy_pixmap (dpy, m);
2885   if (gc->gcv.clip_mask)
2886     gc->clip_mask =
2887       CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2888   else
2889     gc->clip_mask = 0;
2890
2891   return 0;
2892 }
2893
2894 int
2895 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2896 {
2897   gc->gcv.clip_x_origin = x;
2898   gc->gcv.clip_y_origin = y;
2899   return 0;
2900 }
2901
2902 #endif // JWXYZ_QUARTZ -- entire file