1 /* xscreensaver, Copyright (c) 1991-2016 Jamie Zawinski <jwz@jwz.org>
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
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.
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.
23 #ifdef JWXYZ_QUARTZ // entire file
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
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
54 # import <Cocoa/Cocoa.h>
57 #import <CoreText/CTFont.h>
58 #import <CoreText/CTLine.h>
59 #import <CoreText/CTRun.h>
62 #import "jwxyz-cocoa.h"
63 #import "jwxyz-timers.h"
70 # define MAX(a,b) ((a)>(b)?(a):(b))
71 # define MIN(a,b) ((a)<(b)?(a):(b))
74 struct jwxyz_Display {
78 struct jwxyz_sources_data *timers_data;
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. */
86 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
87 our images with this to avoid translation
90 unsigned long window_background;
95 CGBitmapInfo bitmap_info;
96 unsigned long black, white;
104 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
111 float size; // points
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.
119 struct jwxyz_XFontSet {
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.
128 // This type encodes such a conversion.
129 typedef unsigned convert_mode_t;
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;
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'.
141 convert_row (uint32_t *dest, const void *src, size_t count,
142 convert_mode_t mode, size_t src_bpp)
144 Assert (src_bpp == 24 || src_bpp == 32, "weird bpp");
146 // This works OK iff src == dest or src and dest do not overlap.
150 memcpy (dest, src, count * 4);
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;
160 uint32_t *dest_end = dest + count;
161 while (dest != dest_end) {
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;
174 # error "Can't determine system endianness."
178 src = (const uint8_t *)src + src_bpp;
180 /* The naive (i.e. ubiquitous) portable implementation of bitwise rotation,
181 for 32-bit integers, is:
183 (x << rot) | (x >> (32 - rot))
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!)
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)
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
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
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.
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.
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));
228 x = __builtin_bswap32(x); // LLVM/GCC built-in function.
236 // Converts a single pixel.
238 convert_px (uint32_t px, convert_mode_t mode)
240 convert_row (&px, &px, 1, mode, 32);
245 // This returns the inverse conversion mode, such that:
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)
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;
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)
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)
268 ((m0 + (m0 & CONVERT_MODE_SWAP ? -m1 : m1)) & CONVERT_MODE_ROTATE_MASK) |
269 ((m0 ^ m1) & CONVERT_MODE_SWAP);
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)
278 // Former default: kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
281 // green = 0x0000FF00;
282 // blue = 0x000000FF;
284 // RGBA: kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big
286 CGImageAlphaInfo alpha_info =
287 (CGImageAlphaInfo)(bitmap_info & kCGBitmapAlphaInfoMask);
289 Assert (! (bitmap_info & kCGBitmapFloatComponents),
290 "kCGBitmapFloatComponents unsupported");
291 Assert (alpha_info != kCGImageAlphaOnly, "kCGImageAlphaOnly not supported");
293 convert_mode_t rot = alpha_info == kCGImageAlphaFirst ||
294 alpha_info == kCGImageAlphaPremultipliedFirst ||
295 alpha_info == kCGImageAlphaNoneSkipFirst ?
298 CGBitmapInfo byte_order = bitmap_info & kCGBitmapByteOrderMask;
300 Assert (byte_order == kCGBitmapByteOrder32Little ||
301 byte_order == kCGBitmapByteOrder32Big,
302 "byte order not supported");
304 convert_mode_t swap = byte_order == kCGBitmapByteOrder32Little ?
305 CONVERT_MODE_SWAP : 0;
307 rot = CONVERT_MODE_ROTATE_MASK & -rot;
320 jwxyz_alloc_color (Display *dpy,
321 uint16_t r, uint16_t g, uint16_t b, uint16_t a)
323 union color_bytes color;
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;
333 color.bytes[0] = r >> 8;
334 color.bytes[1] = g >> 8;
335 color.bytes[2] = b >> 8;
336 color.bytes[3] = a >> 8;
339 convert_px (color.pixel,
340 convert_mode_invert (convert_mode_to_rgba (dpy->screen->bitmap_info)));
345 jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
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];
356 query_color_float (Display *dpy, unsigned long pixel, float *rgba)
359 jwxyz_query_color (dpy, pixel, rgba8);
360 for (unsigned i = 0; i != 4; ++i)
361 rgba[i] = rgba8[i] * (1.0f / 255.0f);
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.)
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).
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
381 static Display *jwxyz_live_displays[20] = { 0, };
386 jwxyz_make_display (Window w)
388 CGContextRef cgc = w->cgc;
390 Display *d = (Display *) calloc (1, sizeof(*d));
391 d->screen = (Screen *) calloc (1, sizeof(Screen));
395 d->screen->screen_number = 0;
398 // Find the first empty slot in live_displays and plug us in.
399 int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
401 for (i = 0; i < size; i++) {
402 if (! jwxyz_live_displays[i])
405 if (i >= size) abort();
406 jwxyz_live_displays[i] = d;
407 d->screen_count = size;
408 d->screen->screen_number = i;
410 # endif // !USE_IPHONE
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);
417 // Tests for the image conversion modes.
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);
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);
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)));
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");
451 d->screen->visual = v;
453 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
455 d->window_background = BlackPixel(d,0);
459 Assert (cgc, "no CGContext");
464 jwxyz_free_display (Display *dpy)
466 jwxyz_sources_free (dpy->timers_data);
470 // Find us in live_displays and clear that slot.
471 int size = ScreenCount(dpy);
473 for (i = 0; i < size; i++) {
474 if (dpy == jwxyz_live_displays[i]) {
475 jwxyz_live_displays[i] = 0;
479 if (i >= size) abort();
481 # endif // !USE_IPHONE
483 free (dpy->screen->visual);
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.
494 invalidate_drawable_cache (Drawable d)
497 CGImageRelease (d->cgi);
503 /* Call this when the View changes size or position.
506 jwxyz_window_resized (Display *dpy)
508 Window w = dpy->main_window;
511 // Figure out which screen the window is currently on.
514 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
520 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
524 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
526 Assert (dpy->cgdpy, "unable to find CGDisplay");
528 # endif // USE_IPHONE
532 // Figure out this screen's colorspace, and use that for every CGImage.
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");
542 // WTF? It's faster if we *do not* use the screen's colorspace!
544 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
546 invalidate_drawable_cache (w);
551 jwxyz_flush_context (Display *dpy)
553 // CGContextSynchronize is another possibility.
554 CGContextFlush(dpy->main_window->cgc);
558 display_sources_data (Display *dpy)
560 return dpy->timers_data;
565 XRootWindow (Display *dpy, int screen)
567 return dpy ? dpy->main_window : 0;
571 XDefaultScreenOfDisplay (Display *dpy)
573 return dpy ? dpy->screen : 0;
577 XDefaultVisualOfScreen (Screen *screen)
579 return screen ? screen->visual : 0;
583 XDisplayOfScreen (Screen *s)
585 return s ? s->dpy : 0;
589 XDisplayNumberOfScreen (Screen *s)
595 XScreenNumberOfScreen (Screen *s)
597 return s? s->screen_number : 0;
601 jwxyz_ScreenCount (Display *dpy)
603 return dpy ? dpy->screen_count : 0;
607 XBlackPixelOfScreen(Screen *screen)
609 return screen->black;
613 XWhitePixelOfScreen(Screen *screen)
615 return screen->white;
619 XCellsOfScreen(Screen *screen)
621 Visual *v = screen->visual;
622 return v->red_mask | v->green_mask | v->blue_mask;
627 set_color (Display *dpy, CGContextRef cgc, unsigned long argb,
628 unsigned int depth, Bool alpha_allowed_p, Bool fill_p)
630 jwxyz_validate_pixel (dpy, argb, depth, alpha_allowed_p);
633 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
635 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
638 query_color_float (dpy, argb, rgba);
640 CGContextSetRGBFillColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
642 CGContextSetRGBStrokeColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
647 set_line_mode (CGContextRef cgc, XGCValues *gcv)
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 :
654 CGContextSetLineCap (cgc,
655 gcv->cap_style == CapNotLast ? kCGLineCapButt :
656 gcv->cap_style == CapButt ? kCGLineCapButt :
657 gcv->cap_style == CapRound ? kCGLineCapRound :
662 set_clip_mask (Drawable d, GC gc)
664 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
666 Pixmap p = gc->gcv.clip_mask;
668 Assert (p->type == PIXMAP, "not a pixmap");
670 XRectangle wr = d->frame;
672 to.origin.x = wr.x + gc->gcv.clip_x_origin;
673 to.origin.y = wr.y + wr.height - gc->gcv.clip_y_origin
675 to.size.width = p->frame.width;
676 to.size.height = p->frame.height;
678 CGContextClipToMask (d->cgc, to, gc->clip_mask);
682 /* Pushes a GC context; sets BlendMode and ClipMask.
685 push_gc (Drawable d, GC gc)
687 CGContextRef cgc = d->cgc;
688 CGContextSaveGState (cgc);
690 switch (gc->gcv.function) {
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;
700 if (gc->gcv.clip_mask)
701 set_clip_mask (d, gc);
705 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
708 push_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color,
709 Bool antialias_p, Bool fill_p)
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;
719 CGContextRef cgc = d->cgc;
720 set_color (dpy, cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
721 CGContextSetShouldAntialias (cgc, antialias_p);
725 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
728 push_fg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
730 push_color_gc (dpy, d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
734 bitmap_context_p (Drawable d)
741 /* You've got to be fucking kidding me!
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!
747 I still wouldn't call it *fast*, however...
749 #define XDRAWPOINTS_IMAGES
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.)
755 #define XDRAWPOINTS_CGDATA
758 XDrawPoints (Display *dpy, Drawable d, GC gc,
759 XPoint *points, int count, int mode)
762 XRectangle wr = d->frame;
764 # ifdef XDRAWPOINTS_CGDATA
766 if (bitmap_context_p (d))
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);
774 Assert (data, "no bitmap data in Drawable");
776 unsigned long argb = gc->gcv.foreground;
777 jwxyz_validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
779 argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
782 CGFloat y0 = wr.y; // Y axis is refreshingly not flipped.
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++) {
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;
798 for (i = 0; i < count; i++, points++) {
799 CGFloat x = x0 + points->x;
800 CGFloat y = y0 + points->y;
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;
810 } else /* d->type == WINDOW */
812 # endif /* XDRAWPOINTS_CGDATA */
814 push_fg_gc (dpy, d, gc, YES);
816 # ifdef XDRAWPOINTS_IMAGES
818 unsigned long argb = gc->gcv.foreground;
819 jwxyz_validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
821 argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
823 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
825 CGImageRef cgi = CGImageCreate (1, 1,
828 /* Host-ordered, since we're using the
829 address of an int as the color data. */
830 dpy->screen->bitmap_info,
833 NO, /* interpolate */
834 kCGRenderingIntentDefault);
835 CGDataProviderRelease (prov);
837 CGContextRef cgc = d->cgc;
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;
845 rect.origin.x = wr.x + points->x;
846 rect.origin.y = wr.y + wr.height - points->y - 1;
849 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
850 CGContextDrawImage (cgc, rect, cgi);
854 CGImageRelease (cgi);
856 # else /* ! XDRAWPOINTS_IMAGES */
858 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
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;
867 r->origin.x = wr.origin.x + points->x;
868 r->origin.y = wr.origin.y + wr.size.height - points->y;
874 CGContextFillRects (d->cgc, rects, count);
877 # endif /* ! XDRAWPOINTS_IMAGES */
882 invalidate_drawable_cache (d);
889 map_point (Drawable d, int x, int y)
891 const XRectangle *wr = &d->frame;
894 p.y = wr->y + wr->height - y;
900 adjust_point_for_line (GC gc, CGPoint *p)
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.
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
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. */
926 point_for_line (Drawable d, GC gc, int x, int y)
928 CGPoint result = map_point (d, x, y);
929 adjust_point_for_line (gc, &result);
935 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
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;
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);
946 w = 1; // Actually show zero-length lines.
947 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
951 CGPoint p = point_for_line (d, gc, x1, y1);
953 push_fg_gc (dpy, d, gc, NO);
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);
963 invalidate_drawable_cache (d);
968 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
973 push_fg_gc (dpy, d, gc, NO);
975 CGContextRef cgc = d->cgc;
977 set_line_mode (cgc, &gc->gcv);
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--;
985 p = point_for_line(d, gc, points->x, points->y);
987 CGContextBeginPath (cgc);
988 CGContextMoveToPoint (cgc, p.x, p.y);
989 for (i = 1; i < count; i++) {
990 if (mode == CoordModePrevious) {
994 p = point_for_line(d, gc, points->x, points->y);
996 CGContextAddLineToPoint (cgc, p.x, p.y);
999 if (closed_p) CGContextClosePath (cgc);
1000 CGContextStrokePath (cgc);
1002 invalidate_drawable_cache (d);
1008 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1012 CGContextRef cgc = d->cgc;
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);
1024 CGContextStrokePath (cgc);
1026 invalidate_drawable_cache (d);
1032 XClearWindow (Display *dpy, Window win)
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);
1040 jwxyz_window_background (Display *dpy)
1042 return dpy->window_background;
1046 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1048 Assert (w && w->type == WINDOW, "not a window");
1049 jwxyz_validate_pixel (dpy, pixel, 32, NO);
1050 dpy->window_background = pixel;
1055 jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
1056 const XRectangle *rectangles, unsigned long nrectangles,
1057 unsigned long pixel)
1059 Assert (!gc || gc->depth == jwxyz_drawable_depth (d), "depth mismatch");
1061 CGContextRef cgc = d->cgc;
1064 bitmap_context_p (d) &&
1065 (!gc || (gc->gcv.function == GXcopy &&
1066 !gc->gcv.alpha_allowed_p &&
1067 !gc->gcv.clip_mask));
1071 push_color_gc (dpy, d, gc, pixel, gc->gcv.antialias_p, YES);
1073 set_color (dpy, d->cgc, pixel, jwxyz_drawable_depth (d), NO, YES);
1076 for (unsigned i = 0; i != nrectangles; ++i) {
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;
1084 long // negative_int > unsigned_int == 1
1085 dw = CGBitmapContextGetWidth (cgc),
1086 dh = CGBitmapContextGetHeight (cgc);
1088 if (x >= dw || y >= dh)
1101 if (width <= 0 || height <= 0)
1104 unsigned long max_width = dw - x;
1105 if (width > max_width)
1107 unsigned long max_height = dh - y;
1108 if (height > max_height)
1109 height = max_height;
1111 if (jwxyz_drawable_depth (d) == 1)
1112 pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0);
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);
1118 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
1120 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
1121 wmemset (dst, (wchar_t) pixel, width);
1123 dst = (char *) dst + dst_bytes_per_row;
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);
1136 if (!fast_fill_p && gc)
1138 invalidate_drawable_cache (d);
1143 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1145 Assert (win && win->type == WINDOW, "not a window");
1146 jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
1152 XFillPolygon (Display *dpy, Drawable d, GC gc,
1153 XPoint *points, int npoints, int shape, int mode)
1155 XRectangle wr = d->frame;
1157 push_fg_gc (dpy, d, gc, YES);
1158 CGContextRef cgc = d->cgc;
1159 CGContextBeginPath (cgc);
1161 for (i = 0; i < npoints; i++) {
1162 if (i > 0 && mode == CoordModePrevious) {
1166 x = wr.x + points[i].x;
1167 y = wr.y + wr.height - points[i].y;
1171 CGContextMoveToPoint (cgc, x, y);
1173 CGContextAddLineToPoint (cgc, x, y);
1175 CGContextClosePath (cgc);
1176 if (gc->gcv.fill_rule == EvenOddRule)
1177 CGContextEOFillPath (cgc);
1179 CGContextFillPath (cgc);
1181 invalidate_drawable_cache (d);
1185 #define radians(DEG) ((DEG) * M_PI / 180.0)
1186 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
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)
1193 XRectangle wr = d->frame;
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;
1201 ctr.x = bound.origin.x + bound.size.width /2;
1202 ctr.y = bound.origin.y + bound.size.height/2;
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);
1209 push_fg_gc (dpy, d, gc, fill_p);
1211 CGContextRef cgc = d->cgc;
1212 CGContextBeginPath (cgc);
1214 CGContextSaveGState(cgc);
1215 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1216 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1218 CGContextMoveToPoint (cgc, 0, 0);
1220 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1221 CGContextRestoreGState (cgc); // restore before stroke, for line width
1224 CGContextClosePath (cgc); // for proper line joining
1227 CGContextFillPath (cgc);
1229 set_line_mode (cgc, &gc->gcv);
1230 CGContextStrokePath (cgc);
1234 invalidate_drawable_cache (d);
1240 jwxyz_gc_gcv (GC gc)
1247 jwxyz_gc_depth (GC gc)
1254 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1256 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1257 gc->depth = jwxyz_drawable_depth (d);
1259 jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1260 XChangeGC (dpy, gc, mask, xgcv);
1266 XFreeGC (Display *dpy, GC gc)
1269 XUnloadFont (dpy, gc->gcv.font);
1271 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1273 if (gc->gcv.clip_mask) {
1274 XFreePixmap (dpy, gc->gcv.clip_mask);
1275 CGImageRelease (gc->clip_mask);
1283 flipbits (unsigned const char *in, unsigned char *out, int length)
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
1319 while (length-- > 0)
1320 *out++ = table[*in++];
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)
1329 XRectangle wr = d->frame;
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");
1339 // Clip width and height to the bounds of the Drawable
1341 if (dest_x + (int)w > wr.width) {
1342 if (dest_x > wr.width)
1344 w = wr.width - dest_x;
1346 if (dest_y + (int)h > wr.height) {
1347 if (dest_y > wr.height)
1349 h = wr.height - dest_y;
1351 if (w <= 0 || h <= 0)
1354 // Clip width and height to the bounds of the XImage
1356 if (src_x + w > ximage->width) {
1357 if (src_x > ximage->width)
1359 w = ximage->width - src_x;
1361 if (src_y + h > ximage->height) {
1362 if (src_y > ximage->height)
1364 h = ximage->height - src_y;
1366 if (w <= 0 || h <= 0)
1369 CGContextRef cgc = d->cgc;
1371 if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1374 int bpl = ximage->bytes_per_line;
1375 int bpp = ximage->bits_per_pixel;
1376 int bsize = bpl * h;
1377 char *data = ximage->data;
1380 r.origin.x = wr.x + dest_x;
1381 r.origin.y = wr.y + wr.height - dest_y - (int)h;
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.
1390 data += (src_y * bpl) + (src_x * 4);
1391 CGDataProviderRef prov =
1392 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1394 CGImageRef cgi = CGImageCreate (w, h,
1397 dpy->screen->bitmap_info,
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);
1407 } else { // (bpp == 1)
1409 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
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...
1417 Assert ((src_x % 8) == 0,
1418 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1420 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1421 unsigned char *flipped = (unsigned char *) malloc (bsize);
1423 flipbits ((unsigned char *) data, flipped, bsize);
1425 CGDataProviderRef prov =
1426 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1427 CGImageRef mask = CGImageMaskCreate (w, h,
1430 NULL, /* decode[] */
1431 NO); /* interpolate */
1432 push_fg_gc (dpy, d, gc, YES);
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
1441 CGDataProviderRelease (prov);
1442 CGImageRelease (mask);
1445 invalidate_drawable_cache (d);
1452 XGetImage (Display *dpy, Drawable d, int x, int y,
1453 unsigned int width, unsigned int height,
1454 unsigned long plane_mask, int format)
1456 const unsigned char *data = 0;
1457 size_t depth, ibpp, ibpl;
1458 convert_mode_t mode;
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");
1465 CGContextRef cgc = d->cgc;
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");
1476 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1477 data += (y * ibpl) + (x * (ibpp/8));
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);
1484 int obpl = image->bytes_per_line;
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).
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.
1496 const unsigned char *iline = data;
1497 for (yy = 0; yy < height; yy++) {
1499 const unsigned char *iline2 = iline;
1500 for (xx = 0; xx < width; xx++) {
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
1507 XPutPixel (image, xx, yy, (r ? 1 : 0));
1512 const unsigned char *iline = data;
1513 unsigned char *oline = (unsigned char *) image->data;
1515 mode = convert_mode_merge (mode,
1516 convert_mode_invert (
1517 convert_mode_to_rgba (dpy->screen->bitmap_info)));
1519 for (yy = 0; yy < height; yy++) {
1521 convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
1533 /* Returns a transformation matrix to do rotation as per the provided
1534 EXIF "Orientation" value.
1536 static CGAffineTransform
1537 exif_rotate (int rot, CGSize rect)
1539 CGAffineTransform trans = CGAffineTransformIdentity;
1541 case 2: // flip horizontal
1542 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1543 trans = CGAffineTransformScale (trans, -1, 1);
1546 case 3: // rotate 180
1547 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1548 trans = CGAffineTransformRotate (trans, M_PI);
1551 case 4: // flip vertical
1552 trans = CGAffineTransformMakeTranslation (0, rect.height);
1553 trans = CGAffineTransformScale (trans, 1, -1);
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);
1562 case 6: // rotate 90
1563 trans = CGAffineTransformMakeTranslation (0, rect.width);
1564 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1567 case 7: // transverse (UR-to-LL axis)
1568 trans = CGAffineTransformMakeScale (-1, 1);
1569 trans = CGAffineTransformRotate (trans, M_PI / 2);
1572 case 8: // rotate 270
1573 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1574 trans = CGAffineTransformRotate (trans, M_PI / 2);
1586 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
1587 Bool nsimg_p, void *img_arg,
1588 XRectangle *geom_ret, int exif_rotation)
1592 CGImageSourceRef cgsrc;
1593 # endif // USE_IPHONE
1596 CGContextRef cgc = d->cgc;
1600 NSImage *nsimg = (NSImage *) img_arg;
1601 imgr = [nsimg size];
1604 // convert the NSImage to a CGImage via the toll-free-bridging
1605 // of NSData and CFData...
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
1618 cgi = (CGImageRef) img_arg;
1619 imgr.width = CGImageGetWidth (cgi);
1620 imgr.height = CGImageGetHeight (cgi);
1623 Bool rot_p = (exif_rotation >= 5);
1626 imgr = NSMakeSize (imgr.height, imgr.width);
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);
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;
1639 dst2.origin.x = dst2.origin.y = 0;
1641 dst2.size.width = dst.size.height;
1642 dst2.size.height = dst.size.width;
1644 dst2.size = dst.size;
1647 // Clear the part not covered by the image to background or black.
1649 if (d->type == WINDOW)
1650 XClearWindow (dpy, d);
1652 jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.width, winr.height,
1653 jwxyz_drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
1656 CGAffineTransform trans =
1657 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1659 CGContextSaveGState (cgc);
1660 CGContextConcatCTM (cgc,
1661 CGAffineTransformMakeTranslation (dst.origin.x,
1663 CGContextConcatCTM (cgc, trans);
1664 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1665 CGContextDrawImage (cgc, dst2, cgi);
1666 CGContextRestoreGState (cgc);
1671 CGImageRelease (cgi);
1673 # endif // USE_IPHONE
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;
1682 invalidate_drawable_cache (d);
1688 XCreatePixmap (Display *dpy, Drawable d,
1689 unsigned int width, unsigned int height, unsigned int depth)
1692 char *data = (char *) malloc (width * height * 4);
1693 if (! data) return 0;
1695 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1697 p->frame.width = width;
1698 p->frame.height = height;
1699 p->pixmap.depth = depth;
1700 p->pixmap.cgc_buffer = data;
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. */
1706 p->cgc = CGBitmapContextCreate (data, width, height,
1707 8, /* bits per component */
1708 width * 4, /* bpl */
1710 dpy->screen->bitmap_info);
1711 Assert (p->cgc, "could not create CGBitmapContext");
1717 XFreePixmap (Display *d, Pixmap p)
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);
1730 copy_pixmap (Display *dpy, Pixmap p)
1733 Assert (p->type == PIXMAP, "not a pixmap");
1739 unsigned int width, height, border_width, depth;
1740 if (XGetGeometry (dpy, p, &root,
1741 &x, &y, &width, &height, &border_width, &depth)) {
1743 gcv.function = GXcopy;
1744 GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
1746 p2 = XCreatePixmap (dpy, p, width, height, depth);
1748 XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
1753 Assert (p2, "could not copy pixmap");
1759 /* Font metric terminology, as used by X11:
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.
1764 "rbearing" is the distance from the logical origin to the rightmost pixel.
1766 "descent" is the distance from the logical origin to the bottommost pixel.
1767 For characters with descenders, it is positive. For superscripts, it
1770 "ascent" is the distance from the logical origin to the topmost pixel.
1771 It is the number of pixels above the baseline.
1773 "width" is the distance from the logical origin to the position where
1774 the logical origin of the next character should be placed.
1776 If "rbearing" is greater than "width", then this character overlaps the
1777 following character. If smaller, then there is trailing blank space.
1780 utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs)
1782 // Returns the metrics of the multi-character, single-line UTF8 string.
1784 NSFont *nsfont = fid->nsfont;
1785 Drawable d = XRootWindow (fid->dpy, 0);
1787 CGContextRef cgc = d->cgc;
1788 NSDictionary *attr =
1789 [NSDictionary dictionaryWithObjectsAndKeys:
1790 nsfont, NSFontAttributeName,
1792 NSAttributedString *astr = [[NSAttributedString alloc]
1793 initWithString:nsstr
1795 CTLineRef ctline = CTLineCreateWithAttributedString (
1796 (__bridge CFAttributedStringRef) astr);
1797 CGContextSetTextPosition (cgc, 0, 0);
1798 CGContextSetShouldAntialias (cgc, True); // #### Guess?
1800 memset (cs, 0, sizeof(*cs));
1802 // "CTRun represents set of consecutive glyphs sharing the same
1803 // attributes and direction".
1805 // We also get multiple runs any time font subsitution happens:
1806 // E.g., if the current font is Verdana-Bold, a ← character
1807 // in the NSString will actually be rendered in LucidaGrande-Bold.
1810 for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
1811 CTRunRef run = (CTRunRef) runid;
1813 CGRect bbox = CTRunGetImageBounds (run, cgc, r);
1814 CGFloat ascent, descent, leading;
1815 CGFloat advancement =
1816 CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
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;
1825 // Create the metrics for this run:
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);
1833 // Add those metrics into the cumulative metrics:
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);
1845 // Why no y? What about vertical text?
1846 // XCharStruct doesn't encapsulate that but XGlyphInfo does.
1857 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1860 query_font (Font fid)
1862 if (!fid || !fid->nsfont) {
1863 Assert (0, "no NSFont in fid");
1866 if (![fid->nsfont fontName]) {
1867 Assert(0, "broken NSFont in fid");
1874 XFontStruct *f = &fid->metrics;
1875 XCharStruct *min = &f->min_bounds;
1876 XCharStruct *max = &f->max_bounds;
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]);
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;
1891 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1893 for (int i = first; i <= last; i++) {
1894 XCharStruct *cs = &f->per_char[i-first];
1899 NSString *nsstr = [NSString stringWithCString:s2
1900 encoding:NSISOLatin1StringEncoding];
1901 utf8_metrics (fid, nsstr, cs);
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);
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);
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);
1928 // Since 'Font' includes the metrics, this just makes a copy of that.
1931 XQueryFont (Display *dpy, Font fid)
1934 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
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;
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));
1957 copy_font (Font fid)
1959 // copy 'Font' struct
1960 Font fid2 = (Font) malloc (sizeof(*fid2));
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));
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;
1981 font_family_members (NSString *family_name)
1984 return [[NSFontManager sharedFontManager]
1985 availableMembersOfFontFamily:family_name];
1987 return [UIFont fontNamesForFamilyName:family_name];
1993 default_font_family (NSFontTraitMask require)
1995 return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
2000 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
2001 NSString *family_name, float size,
2004 Assert (size > 0, "zero font size");
2006 NSArray *family_members = font_family_members (family_name);
2007 if (!family_members.count)
2008 family_members = font_family_members (default_font_family (traits));
2011 for (unsigned k = 0; k != family_members.count; ++k) {
2013 NSArray *member = [family_members objectAtIndex:k];
2014 NSFontTraitMask font_mask =
2015 [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
2017 if ((font_mask & mask) == traits) {
2019 NSString *name = [member objectAtIndex:0];
2020 NSFont *f = [NSFont fontWithName:name size:size];
2024 /* Don't use this font if it (probably) doesn't include ASCII characters.
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);
2038 // NSLog(@"using \"%@\": %d", name, enc);
2040 // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2041 *name_ret = strdup (name.UTF8String);
2045 # else // USE_IPHONE
2047 // This trick needs iOS 3.1, see "Using SDK-Based Development".
2048 Class has_font_descriptor = [UIFontDescriptor class];
2050 for (NSString *fn in family_members) {
2052 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
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}]
2065 font_mask |= NSBoldFontMask;
2066 if (MATCH(@"Italic") || MATCH(@"Oblique"))
2067 font_mask |= NSItalicFontMask;
2068 if (MATCH(@"Courier"))
2069 font_mask |= NSFixedPitchFontMask;
2072 if ((font_mask & mask) == traits) {
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.
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
2091 *name_ret = strdup (fn.UTF8String);
2092 return [UIFont fontWithName:fn size:size];
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.
2108 try_native_font (const char *name, float scale,
2109 char **name_ret, float *size_ret, char **xa_font)
2111 if (!name) return 0;
2112 const char *spc = strrchr (name, ' ');
2116 char *token = strdup (name);
2117 char *otoken = token;
2121 while ((name2 = strtok_r (token, ",", &lasts))) {
2124 while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
2127 spc = strrchr (name2, ' ');
2131 if (1 != sscanf (spc, " %d ", &dsize))
2135 if (size < 4) continue;
2139 name2[strlen(name2) - strlen(spc)] = 0;
2141 NSString *nsname = [NSString stringWithCString:name2
2142 encoding:NSUTF8StringEncoding];
2143 f = [NSFont fontWithName:nsname size:size];
2145 *name_ret = strdup (name2);
2147 *xa_font = strdup (name); // Maybe this should be an XLFD?
2150 NSLog(@"No native font: \"%@\" %.0f", nsname, size);
2152 for (NSString *fam in [UIFont familyNames]) {
2153 NSLog(@"Family: %@", fam);
2154 for (NSString *f in [UIFont fontNamesForFamilyName:fam]) {
2155 NSLog(@" Font: %@", f);
2167 /* Returns a random font in the given size and face.
2170 random_font (NSFontTraitMask traits, NSFontTraitMask mask,
2171 float size, NSString **family_ret, char **name_ret)
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;
2181 NSArray *families = [UIFont familyNames];
2183 // There are many dups in the families array -- uniquify it.
2185 NSArray *sorted_families =
2186 [families sortedArrayUsingSelector:@selector(compare:)];
2187 NSMutableArray *new_families =
2188 [NSMutableArray arrayWithCapacity:sorted_families.count];
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;
2197 families = new_families;
2199 # endif // USE_IPHONE
2201 long n = [families count];
2202 if (n <= 0) return 0;
2205 for (j = 0; j < n; j++) {
2206 int i = random() % n;
2207 NSString *family_name = [families objectAtIndex:i];
2209 NSFont *result = try_font (traits, mask, family_name, size, name_ret);
2211 [*family_ret release];
2212 *family_ret = family_name;
2213 [*family_ret retain];
2218 // None of the fonts support ASCII?
2224 xlfd_field_end (const char *s)
2226 const char *s2 = strchr(s, '-');
2234 xlfd_next (const char **s, const char **s2)
2239 Assert (**s2 == '-', "xlfd parse error");
2241 *s2 = xlfd_field_end (*s);
2249 try_xlfd_font (Display *dpy, const char *name, float scale,
2250 char **name_ret, float *size_ret, char **xa_font)
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;
2261 const char *s = (name ? name : "");
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;
2276 NSFontTraitMask forbid = 0;
2278 // Incorrect fields are ignored.
2282 const char *s2 = xlfd_field_end(s);
2286 L = xlfd_next (&s, &s2); // Family name
2287 // This used to substitute Georgia for Times. Now it doesn't.
2288 if (CMP ("random")) {
2290 } else if (CMP ("fixed")) {
2291 require |= NSFixedPitchFontMask;
2292 family_name = @"Courier";
2293 } else if (!UNSPEC) {
2294 family_name = [[[NSString alloc] initWithBytes:s
2296 encoding:NSUTF8StringEncoding]
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;
2306 L = xlfd_next (&s, &s2); // Slant
2307 if (CMP ("i") || CMP ("o"))
2308 require |= NSItalicFontMask;
2310 forbid |= NSItalicFontMask;
2312 xlfd_next (&s, &s2); // Set width name (ignore)
2313 xlfd_next (&s, &s2); // Add style name (ignore)
2315 xlfd_next (&s, &s2); // Pixel size (ignore)
2317 xlfd_next (&s, &s2); // Point size
2319 uintmax_t n = strtoumax(s, &s3, 10);
2323 xlfd_next (&s, &s2); // Resolution X (ignore)
2324 xlfd_next (&s, &s2); // Resolution Y (ignore)
2326 xlfd_next (&s, &s2); // Spacing
2328 forbid |= NSFixedPitchFontMask;
2329 else if (CMP ("m") || CMP ("c"))
2330 require |= NSFixedPitchFontMask;
2332 // Don't care about average_width or charset registry.
2334 mask = require | forbid;
2339 if (!family_name && !rand)
2340 family_name = default_font_family (require);
2342 if (size < 6 || size > 1000)
2348 nsfont = random_font (require, mask, size, &family_name, &ps_name);
2349 [family_name autorelease];
2353 nsfont = try_font (require, mask, family_name, size, &ps_name);
2355 // if that didn't work, turn off attibutes until it does
2356 // (e.g., there is no "Monaco-Bold".)
2358 if (!nsfont && (mask & NSItalicFontMask)) {
2359 require &= ~NSItalicFontMask;
2360 mask &= ~NSItalicFontMask;
2361 nsfont = try_font (require, mask, family_name, size, &ps_name);
2363 if (!nsfont && (mask & NSBoldFontMask)) {
2364 require &= ~NSBoldFontMask;
2365 mask &= ~NSBoldFontMask;
2366 nsfont = try_font (require, mask, family_name, size, &ps_name);
2368 if (!nsfont && (mask & NSFixedPitchFontMask)) {
2369 require &= ~NSFixedPitchFontMask;
2370 mask &= ~NSFixedPitchFontMask;
2371 nsfont = try_font (require, mask, family_name, size, &ps_name);
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;
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');
2389 if (ps_name) free (ps_name);
2396 XLoadFont (Display *dpy, const char *name)
2398 Font fid = (Font) calloc (1, sizeof(*fid));
2403 /* Since iOS screens are physically smaller than desktop screens, scale up
2404 the fonts to make them more readable.
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.
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.
2417 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
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);
2431 fid->nsfont = try_xlfd_font (dpy, name, scale, &fid->ps_name, &fid->size,
2434 // We should never return NULL for XLFD fonts.
2436 Assert (0, "no font");
2439 CFRetain (fid->nsfont); // needed for garbage collection?
2441 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2450 XLoadQueryFont (Display *dpy, const char *name)
2452 Font fid = XLoadFont (dpy, name);
2454 return XQueryFont (dpy, fid);
2458 XUnloadFont (Display *dpy, Font fid)
2461 free (fid->ps_name);
2462 if (fid->metrics.per_char)
2463 free (fid->metrics.per_char);
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...
2470 // [fid->nsfont release];
2471 // CFRelease (fid->nsfont);
2478 XFreeFontInfo (char **names, XFontStruct *info, int n)
2482 for (i = 0; i < n; i++)
2483 if (names[i]) free (names[i]);
2487 for (i = 0; i < n; i++)
2488 if (info[i].per_char) {
2489 free (info[i].per_char);
2490 free (info[i].properties);
2498 XFreeFont (Display *dpy, XFontStruct *f)
2501 XFreeFontInfo (0, f, 1);
2502 XUnloadFont (dpy, fid);
2508 XSetFont (Display *dpy, GC gc, Font fid)
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?
2520 XCreateFontSet (Display *dpy, char *name,
2521 char ***missing_charset_list_return,
2522 int *missing_charset_count_return,
2523 char **def_string_return)
2525 char *name2 = strdup (name);
2526 char *s = strchr (name, ",");
2529 XFontStruct *f = XLoadQueryFont (dpy, name2);
2532 set = (XFontSet) calloc (1, sizeof(*set));
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;
2544 XFreeFontSet (Display *dpy, XFontSet set)
2546 XFreeFont (dpy, set->font);
2552 jwxyz_nativeFontName (Font f, float *size)
2554 if (size) *size = f->size;
2560 XFreeStringList (char **list)
2564 for (i = 0; list[i]; i++)
2570 // Returns the verbose Unicode name of this character, like "agrave" or
2571 // "daggerdouble". Used by fontglide debugMetrics.
2574 jwxyz_unicode_character_name (Font fid, unsigned long uc)
2578 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2579 [fid->nsfont pointSize],
2581 Assert (ctfont, "no CTFontRef for UIFont");
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);
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.
2602 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
2604 int out_len = in_len * 4; // length of string might increase
2605 char *s2 = (char *) malloc (out_len);
2607 const char *in_end = in + in_len;
2608 const char *out_end = out + out_len;
2609 Bool latin1_p = True;
2614 long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
2615 long L2 = utf8_encode (uc, out, out_end - out);
2618 if (uc > 255) latin1_p = False;
2622 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2624 if (latin1_pP) *latin1_pP = latin1_p;
2625 return (nsstr ? nsstr : @"");
2630 XTextExtents (XFontStruct *f, const char *s, int length,
2631 int *dir_ret, int *ascent_ret, int *descent_ret,
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
2640 NSString *nsstr = [[[NSString alloc] initWithBytes:s
2642 encoding:NSISOLatin1StringEncoding]
2644 utf8_metrics (f->fid, nsstr, cs);
2646 *ascent_ret = f->ascent;
2647 *descent_ret = f->descent;
2652 XTextWidth (XFontStruct *f, const char *s, int length)
2654 int ascent, descent, dir;
2656 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2662 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
2663 int *dir_ret, int *ascent_ret, int *descent_ret,
2666 // Bool latin1_p = True;
2667 int i, utf8_len = 0;
2668 char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized
2670 for (i = 0; i < length; i++)
2671 if (s[i].byte1 > 0) {
2672 // latin1_p = False;
2677 NSString *nsstr = [NSString stringWithCString:utf8
2678 encoding:NSUTF8StringEncoding];
2679 utf8_metrics (f->fid, nsstr, cs);
2683 *ascent_ret = f->ascent;
2684 *descent_ret = f->descent;
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."
2693 "overall_ink_return is set to the bbox of the string's character ink."
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."
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.]
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."
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.]
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."
2712 Xutf8TextExtents (XFontSet set, const char *str, int len,
2713 XRectangle *overall_ink_return,
2714 XRectangle *overall_logical_return)
2717 NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
2720 utf8_metrics (set->font->fid, nsstr, &cs);
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."
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
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);
2741 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2744 if (! nsstr) return 1;
2746 XRectangle wr = d->frame;
2747 CGContextRef cgc = d->cgc;
2749 unsigned long argb = gc->gcv.foreground;
2750 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2752 query_color_float (dpy, argb, rgba);
2753 NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
2758 if (!gc->gcv.font) {
2759 Assert (0, "no font");
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.
2770 NSDictionary *attr =
2771 [NSDictionary dictionaryWithObjectsAndKeys:
2772 gc->gcv.font->nsfont, NSFontAttributeName,
2773 fg, NSForegroundColorAttributeName,
2776 // Don't understand why we have to do both set_color and
2777 // NSForegroundColorAttributeName, but we do.
2779 set_color (dpy, cgc, argb, 32, NO, YES);
2781 NSAttributedString *astr = [[NSAttributedString alloc]
2782 initWithString:nsstr
2784 CTLineRef dl = CTLineCreateWithAttributedString (
2785 (__bridge CFAttributedStringRef) astr);
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.
2792 // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
2795 CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
2796 Assert (xoff >= 0, "unexpected CTLineOffset");
2799 CGContextSetTextPosition (cgc,
2801 wr.y + wr.height - y);
2802 CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
2804 CTLineDraw (dl, cgc);
2808 invalidate_drawable_cache (d);
2814 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2815 const char *str, int len)
2817 char *s2 = (char *) malloc (len + 1);
2818 strncpy (s2, str, len);
2820 NSString *nsstr = [NSString stringWithCString:s2
2821 encoding:NSISOLatin1StringEncoding];
2822 int ret = draw_string (dpy, d, gc, x, y, nsstr);
2829 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
2830 const XChar2b *str, int len)
2832 char *s2 = XChar2b_to_utf8 (str, 0); // already sanitized
2834 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2835 int ret = draw_string (dpy, d, gc, x, y, nsstr);
2842 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
2843 int x, int y, const char *str, int len)
2845 char *s2 = (char *) malloc (len + 1);
2846 strncpy (s2, str, len);
2848 NSString *nsstr = sanitize_utf8 (str, len, 0);
2849 draw_string (dpy, d, gc, x, y, nsstr);
2855 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2856 const char *str, int len)
2858 int ascent, descent, dir;
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),
2868 MAX (0, ascent) + MAX (0, descent),
2869 gc->gcv.background);
2870 return XDrawString (dpy, d, gc, x, y, str, len);
2875 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2877 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2879 if (gc->gcv.clip_mask) {
2880 XFreePixmap (dpy, gc->gcv.clip_mask);
2881 CGImageRelease (gc->clip_mask);
2884 gc->gcv.clip_mask = copy_pixmap (dpy, m);
2885 if (gc->gcv.clip_mask)
2887 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2895 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2897 gc->gcv.clip_x_origin = x;
2898 gc->gcv.clip_y_origin = y;
2902 #endif // JWXYZ_QUARTZ -- entire file