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 = (unsigned long)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;
2258 float size = 12; /* In points (1/72 in.) */
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 L = xlfd_next (&s, &s2); // Pixel size (ignore)
2317 uintmax_t pxsize = strtoumax(s, &s3, 10);
2318 if (UNSPEC || s2 != s3)
2319 pxsize = UINTMAX_MAX; // i.e. it's invalid.
2321 L = xlfd_next (&s, &s2); // Point size
2322 uintmax_t ptsize = strtoumax(s, &s3, 10);
2323 if (UNSPEC || s2 != s3)
2324 ptsize = UINTMAX_MAX;
2326 xlfd_next (&s, &s2); // Resolution X (ignore)
2327 xlfd_next (&s, &s2); // Resolution Y (ignore)
2329 L = xlfd_next (&s, &s2); // Spacing
2331 forbid |= NSFixedPitchFontMask;
2332 else if (CMP ("m") || CMP ("c"))
2333 require |= NSFixedPitchFontMask;
2335 xlfd_next (&s, &s2); // Average width (ignore)
2337 // -*-courier-bold-r-*-*-14-*-*-*-*-*-*-* 14 px
2338 // -*-courier-bold-r-*-*-*-140-*-*-m-*-*-* 14 pt
2339 // -*-courier-bold-r-*-*-140-* 14 pt, via wildcard
2340 // -*-courier-bold-r-*-140-* 14 pt, not handled
2341 // -*-courier-bold-r-*-*-14-180-*-*-*-*-*-* error
2343 L = xlfd_next (&s, &s2); // Charset registry
2344 if (ptsize != UINTMAX_MAX) {
2345 // It was in the ptsize field, so that's definitely what it is.
2346 size = ptsize / 10.0;
2347 } else if (pxsize != UINTMAX_MAX) {
2349 // If it's a fully qualified XLFD, then this really is the pxsize.
2350 // Otherwise, this is probably point size with a multi-field wildcard.
2355 mask = require | forbid;
2360 if (!family_name && !rand)
2361 family_name = default_font_family (require);
2363 if (size < 6 || size > 1000)
2369 nsfont = random_font (require, mask, size, &family_name, &ps_name);
2370 [family_name autorelease];
2374 nsfont = try_font (require, mask, family_name, size, &ps_name);
2376 // if that didn't work, turn off attibutes until it does
2377 // (e.g., there is no "Monaco-Bold".)
2379 if (!nsfont && (mask & NSItalicFontMask)) {
2380 require &= ~NSItalicFontMask;
2381 mask &= ~NSItalicFontMask;
2382 nsfont = try_font (require, mask, family_name, size, &ps_name);
2384 if (!nsfont && (mask & NSBoldFontMask)) {
2385 require &= ~NSBoldFontMask;
2386 mask &= ~NSBoldFontMask;
2387 nsfont = try_font (require, mask, family_name, size, &ps_name);
2389 if (!nsfont && (mask & NSFixedPitchFontMask)) {
2390 require &= ~NSFixedPitchFontMask;
2391 mask &= ~NSFixedPitchFontMask;
2392 nsfont = try_font (require, mask, family_name, size, &ps_name);
2396 unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2;
2397 unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d);
2398 *name_ret = ps_name;
2400 float actual_size = size / scale;
2401 asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
2402 family_name.UTF8String,
2403 (require & NSBoldFontMask) ? "bold" : "medium",
2404 (require & NSItalicFontMask) ? 'o' : 'r',
2405 (unsigned)(dpi * actual_size / 72.27 + 0.5),
2406 (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
2407 (require & NSFixedPitchFontMask) ? 'm' : 'p');
2410 if (ps_name) free (ps_name);
2417 XLoadFont (Display *dpy, const char *name)
2419 Font fid = (Font) calloc (1, sizeof(*fid));
2424 /* Since iOS screens are physically smaller than desktop screens, scale up
2425 the fonts to make them more readable.
2427 Note that X11 apps on iOS also have the backbuffer sized in points
2428 instead of pixels, resulting in an effective X11 screen size of 768x1024
2429 or so, even if the display has significantly higher resolution. That is
2430 unrelated to this hack, which is really about DPI.
2432 scale = dpy->main_window->window.view.hackedContentScaleFactor;
2433 if (scale < 1) // iPad Pro magnifies the backbuffer by 3x, which makes text
2434 scale = 1; // excessively blurry in BSOD.
2438 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
2441 if (!fid->nsfont && name &&
2442 strchr (name, ' ') &&
2443 !strchr (name, '*')) {
2444 // If name contains a space but no stars, it is a native font spec --
2445 // return NULL so that we know it really didn't exist. Else, it is an
2446 // XLFD font, so keep trying.
2447 XUnloadFont (dpy, fid);
2452 fid->nsfont = try_xlfd_font (dpy, name, scale, &fid->ps_name, &fid->size,
2455 // We should never return NULL for XLFD fonts.
2457 Assert (0, "no font");
2460 CFRetain (fid->nsfont); // needed for garbage collection?
2462 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2471 XLoadQueryFont (Display *dpy, const char *name)
2473 Font fid = XLoadFont (dpy, name);
2475 return XQueryFont (dpy, fid);
2479 XUnloadFont (Display *dpy, Font fid)
2482 free (fid->ps_name);
2483 if (fid->metrics.per_char)
2484 free (fid->metrics.per_char);
2486 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2487 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2488 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2489 // They're probably not very big...
2491 // [fid->nsfont release];
2492 // CFRelease (fid->nsfont);
2499 XFreeFontInfo (char **names, XFontStruct *info, int n)
2503 for (i = 0; i < n; i++)
2504 if (names[i]) free (names[i]);
2508 for (i = 0; i < n; i++)
2509 if (info[i].per_char) {
2510 free (info[i].per_char);
2511 free (info[i].properties);
2519 XFreeFont (Display *dpy, XFontStruct *f)
2522 XFreeFontInfo (0, f, 1);
2523 XUnloadFont (dpy, fid);
2529 XSetFont (Display *dpy, GC gc, Font fid)
2532 XUnloadFont (dpy, gc->gcv.font);
2533 gc->gcv.font = copy_font (fid);
2534 [gc->gcv.font->nsfont retain];
2535 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2541 XCreateFontSet (Display *dpy, char *name,
2542 char ***missing_charset_list_return,
2543 int *missing_charset_count_return,
2544 char **def_string_return)
2546 char *name2 = strdup (name);
2547 char *s = strchr (name, ',');
2550 XFontStruct *f = XLoadQueryFont (dpy, name2);
2553 set = (XFontSet) calloc (1, sizeof(*set));
2557 if (missing_charset_list_return) *missing_charset_list_return = 0;
2558 if (missing_charset_count_return) *missing_charset_count_return = 0;
2559 if (def_string_return) *def_string_return = 0;
2565 XFreeFontSet (Display *dpy, XFontSet set)
2567 XFreeFont (dpy, set->font);
2573 jwxyz_nativeFontName (Font f, float *size)
2575 if (size) *size = f->size;
2581 XFreeStringList (char **list)
2585 for (i = 0; list[i]; i++)
2591 // Returns the verbose Unicode name of this character, like "agrave" or
2592 // "daggerdouble". Used by fontglide debugMetrics.
2595 jwxyz_unicode_character_name (Display *dpy, Font fid, unsigned long uc)
2599 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2600 [fid->nsfont pointSize],
2602 Assert (ctfont, "no CTFontRef for UIFont");
2605 if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
2606 CGFontRef cgfont = CTFontCopyGraphicsFont (ctfont, 0);
2607 NSString *name = (NSString *) CGFontCopyGlyphNameForGlyph(cgfont, cgglyph);
2608 ret = (name ? strdup ([name UTF8String]) : 0);
2609 CGFontRelease (cgfont);
2618 // Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored.
2619 // We have to do this because stringWithCString returns NULL if there are
2620 // any invalid characters at all.
2623 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
2625 int out_len = in_len * 4; // length of string might increase
2626 char *s2 = (char *) malloc (out_len);
2628 const char *in_end = in + in_len;
2629 const char *out_end = out + out_len;
2630 Bool latin1_p = True;
2635 long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
2636 long L2 = utf8_encode (uc, out, out_end - out);
2639 if (uc > 255) latin1_p = False;
2643 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2645 if (latin1_pP) *latin1_pP = latin1_p;
2646 return (nsstr ? nsstr : @"");
2651 XTextExtents (XFontStruct *f, const char *s, int length,
2652 int *dir_ret, int *ascent_ret, int *descent_ret,
2655 // Unfortunately, adding XCharStructs together to get the extents for a
2656 // string doesn't work: Cocoa uses non-integral character advancements, but
2657 // XCharStruct.width is an integer. Plus that doesn't take into account
2658 // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
2661 NSString *nsstr = [[[NSString alloc] initWithBytes:s
2663 encoding:NSISOLatin1StringEncoding]
2665 utf8_metrics (f->fid, nsstr, cs);
2667 *ascent_ret = f->ascent;
2668 *descent_ret = f->descent;
2673 XTextWidth (XFontStruct *f, const char *s, int length)
2675 int ascent, descent, dir;
2677 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2683 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
2684 int *dir_ret, int *ascent_ret, int *descent_ret,
2687 // Bool latin1_p = True;
2688 int i, utf8_len = 0;
2689 char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized
2691 for (i = 0; i < length; i++)
2692 if (s[i].byte1 > 0) {
2693 // latin1_p = False;
2698 NSString *nsstr = [NSString stringWithCString:utf8
2699 encoding:NSUTF8StringEncoding];
2700 utf8_metrics (f->fid, nsstr, cs);
2704 *ascent_ret = f->ascent;
2705 *descent_ret = f->descent;
2711 /* "Returns the distance in pixels in the primary draw direction from
2712 the drawing origin to the origin of the next character to be drawn."
2714 "overall_ink_return is set to the bbox of the string's character ink."
2716 "The overall_ink_return for a nondescending, horizontally drawn Latin
2717 character is conventionally entirely above the baseline; that is,
2718 overall_ink_return.height <= -overall_ink_return.y."
2720 [So this means that y is the top of the ink, and height grows down:
2721 For above-the-baseline characters, y is negative.]
2723 "The overall_ink_return for a nonkerned character is entirely at, and to
2724 the right of, the origin; that is, overall_ink_return.x >= 0."
2726 [So this means that x is the left of the ink, and width grows right.
2727 For left-of-the-origin characters, x is negative.]
2729 "A character consisting of a single pixel at the origin would set
2730 overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
2733 Xutf8TextExtents (XFontSet set, const char *str, int len,
2734 XRectangle *overall_ink_return,
2735 XRectangle *overall_logical_return)
2738 NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
2741 utf8_metrics (set->font->fid, nsstr, &cs);
2743 /* "The overall_logical_return is the bounding box that provides minimum
2744 spacing to other graphical features for the string. Other graphical
2745 features, for example, a border surrounding the text, should not
2746 intersect this rectangle."
2748 So I think that means they're the same? Or maybe "ink" is the bounding
2749 box, and "logical" is the advancement? But then why is the return value
2752 if (overall_ink_return)
2753 XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
2754 if (overall_logical_return)
2755 XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
2762 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2765 if (! nsstr) return 1;
2767 XRectangle wr = d->frame;
2768 CGContextRef cgc = d->cgc;
2770 unsigned long argb = gc->gcv.foreground;
2771 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2773 query_color_float (dpy, argb, rgba);
2774 NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
2779 if (!gc->gcv.font) {
2780 Assert (0, "no font");
2784 /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
2785 NSFontAttributeName, and NSAttributedString are only present on iOS 6
2786 and later. We could resurrect the Quartz code from v5.29 and do a
2787 runtime conditional on that, but that would be a pain in the ass.
2788 Probably time to just make iOS 6 a requirement.
2791 NSDictionary *attr =
2792 [NSDictionary dictionaryWithObjectsAndKeys:
2793 gc->gcv.font->nsfont, NSFontAttributeName,
2794 fg, NSForegroundColorAttributeName,
2797 // Don't understand why we have to do both set_color and
2798 // NSForegroundColorAttributeName, but we do.
2800 set_color (dpy, cgc, argb, 32, NO, YES);
2802 NSAttributedString *astr = [[NSAttributedString alloc]
2803 initWithString:nsstr
2805 CTLineRef dl = CTLineCreateWithAttributedString (
2806 (__bridge CFAttributedStringRef) astr);
2808 // Not sure why this is necessary, but xoff is positive when the first
2809 // character on the line has a negative lbearing. Without this, the
2810 // string is rendered with the first ink at 0 instead of at lbearing.
2811 // I have not seen xoff be negative, so I'm not sure if that can happen.
2813 // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
2816 CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
2817 Assert (xoff >= 0, "unexpected CTLineOffset");
2820 CGContextSetTextPosition (cgc,
2822 wr.y + wr.height - y);
2823 CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
2825 CTLineDraw (dl, cgc);
2829 invalidate_drawable_cache (d);
2835 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2836 const char *str, int len)
2838 char *s2 = (char *) malloc (len + 1);
2839 strncpy (s2, str, len);
2841 NSString *nsstr = [NSString stringWithCString:s2
2842 encoding:NSISOLatin1StringEncoding];
2843 int ret = draw_string (dpy, d, gc, x, y, nsstr);
2850 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
2851 const XChar2b *str, int len)
2853 char *s2 = XChar2b_to_utf8 (str, 0); // already sanitized
2855 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2856 int ret = draw_string (dpy, d, gc, x, y, nsstr);
2863 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
2864 int x, int y, const char *str, int len)
2866 char *s2 = (char *) malloc (len + 1);
2867 strncpy (s2, str, len);
2869 NSString *nsstr = sanitize_utf8 (str, len, 0);
2870 draw_string (dpy, d, gc, x, y, nsstr);
2876 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2877 const char *str, int len)
2879 int ascent, descent, dir;
2881 XTextExtents (&gc->gcv.font->metrics, str, len,
2882 &dir, &ascent, &descent, &cs);
2883 jwxyz_fill_rect (dpy, d, gc,
2884 x + MIN (0, cs.lbearing),
2885 y - MAX (0, ascent),
2886 MAX (MAX (0, cs.rbearing) -
2887 MIN (0, cs.lbearing),
2889 MAX (0, ascent) + MAX (0, descent),
2890 gc->gcv.background);
2891 return XDrawString (dpy, d, gc, x, y, str, len);
2896 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2898 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2900 if (gc->gcv.clip_mask) {
2901 XFreePixmap (dpy, gc->gcv.clip_mask);
2902 CGImageRelease (gc->clip_mask);
2905 gc->gcv.clip_mask = copy_pixmap (dpy, m);
2906 if (gc->gcv.clip_mask)
2908 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2916 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2918 gc->gcv.clip_x_origin = x;
2919 gc->gcv.clip_y_origin = y;
2923 #endif // JWXYZ_QUARTZ -- entire file