-/* xscreensaver, Copyright (c) 1991-2010 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2015 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#import <stdlib.h>
#import <stdint.h>
-#import <Cocoa/Cocoa.h>
+#import <wchar.h>
+
+#ifdef USE_IPHONE
+# import <UIKit/UIKit.h>
+# import <UIKit/UIScreen.h>
+# import <QuartzCore/QuartzCore.h>
+# define NSView UIView
+# define NSRect CGRect
+# define NSPoint CGPoint
+# define NSSize CGSize
+# define NSColor UIColor
+# define NSImage UIImage
+# define NSEvent UIEvent
+# define NSFont UIFont
+# define NSGlyph CGGlyph
+# define NSWindow UIWindow
+# define NSMakeSize CGSizeMake
+# define NSBezierPath UIBezierPath
+# define colorWithDeviceRed colorWithRed
+
+# define NSFontTraitMask UIFontDescriptorSymbolicTraits
+// The values for the flags for NSFontTraitMask and
+// UIFontDescriptorSymbolicTraits match up, not that it really matters here.
+# define NSBoldFontMask UIFontDescriptorTraitBold
+# define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
+# define NSItalicFontMask UIFontDescriptorTraitItalic
+#else
+# import <Cocoa/Cocoa.h>
+#endif
+
+#import <CoreText/CTFont.h>
+#import <CoreText/CTLine.h>
+#import <CoreText/CTRun.h>
+
#import "jwxyz.h"
#import "jwxyz-timers.h"
+#import "yarandom.h"
+#import "utf8wc.h"
+#import "xft.h"
+
+# define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
#undef Assert
-#define Assert(C,S) do { \
- if (!(C)) { \
- NSLog(@"%s",S); \
- abort(); \
- }} while(0)
+#define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
# undef MAX
# undef MIN
struct jwxyz_Drawable {
enum { WINDOW, PIXMAP } type;
CGContextRef cgc;
+ CGImageRef cgi;
CGRect frame;
union {
struct {
NSView *view;
unsigned long background;
+ int last_mouse_x, last_mouse_y;
} window;
struct {
int depth;
+ void *cgc_buffer; // the bits to which CGContextRef renders
} pixmap;
};
};
struct jwxyz_Display {
Window main_window;
Screen *screen;
+ int screen_count;
struct jwxyz_sources_data *timers_data;
+# ifndef USE_IPHONE
CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
This can change if the window is dragged to
a different screen. */
+# endif
CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
our images with this to avoid translation
struct jwxyz_Screen {
Display *dpy;
+ CGBitmapInfo bitmap_info;
+ unsigned long black, white;
Visual *visual;
+ int screen_number;
};
struct jwxyz_GC {
};
struct jwxyz_Font {
+ Display *dpy;
char *ps_name;
NSFont *nsfont;
float size; // points
+ char *xa_font;
// In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
// But we need the metrics on both of them, so they go here.
XFontStruct metrics;
};
+struct jwxyz_XFontSet {
+ XFontStruct *font;
+};
+
+
+/* Instead of calling abort(), throw a real exception, so that
+ XScreenSaverView can catch it and display a dialog.
+ */
+void
+jwxyz_abort (const char *fmt, ...)
+{
+ char s[10240];
+ if (!fmt || !*fmt)
+ strcpy (s, "abort");
+ else
+ {
+ va_list args;
+ va_start (args, fmt);
+ vsprintf (s, fmt, args);
+ va_end (args);
+ }
+ [[NSException exceptionWithName: NSInternalInconsistencyException
+ reason: [NSString stringWithCString: s
+ encoding:NSUTF8StringEncoding]
+ userInfo: nil]
+ raise];
+ abort(); // not reached
+}
+
+// 24/32bpp -> 32bpp image conversion.
+// Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24
+// bits and an optional byte order swap.
+
+// This type encodes such a conversion.
+typedef unsigned convert_mode_t;
+
+// It's rotate, then swap.
+// A rotation here shifts bytes forward in memory. On x86/ARM, that's a left
+// rotate, and on PowerPC, a rightward rotation.
+static const convert_mode_t CONVERT_MODE_ROTATE_MASK = 0x3;
+static const convert_mode_t CONVERT_MODE_SWAP = 0x4;
+
+
+// Converts an array of pixels ('src') from one format to another, placing the
+// result in 'dest', according to the pixel conversion mode 'mode'.
+static void
+convert_row (uint32_t *dest, const void *src, size_t count,
+ convert_mode_t mode, size_t src_bpp)
+{
+ Assert (src_bpp == 24 || src_bpp == 32, "weird bpp");
+
+ // This works OK iff src == dest or src and dest do not overlap.
+
+ if (!mode) {
+ if (src != dest)
+ memcpy (dest, src, count * 4);
+ return;
+ }
+
+ // This is correct, but not fast.
+ convert_mode_t rot = (mode & CONVERT_MODE_ROTATE_MASK) * 8;
+ convert_mode_t flip = mode & CONVERT_MODE_SWAP;
+
+ src_bpp /= 8;
+
+ uint32_t *dest_end = dest + count;
+ while (dest != dest_end) {
+ uint32_t x;
+
+ if (src_bpp == 4)
+ x = *(const uint32_t *)src;
+ else { // src_bpp == 3
+ const uint8_t *src8 = (const uint8_t *)src;
+ // __LITTLE/BIG_ENDIAN__ are defined by the compiler.
+# if defined __LITTLE_ENDIAN__
+ x = src8[0] | (src8[1] << 8) | (src8[2] << 16) | 0xff000000;
+# elif defined __BIG_ENDIAN__
+ x = (src8[0] << 24) | (src8[1] << 16) | (src8[2] << 8) | 0xff;
+# else
+# error "Can't determine system endianness."
+# endif
+ }
+
+ src = (const uint8_t *)src + src_bpp;
+
+ /* The naive (i.e. ubiquitous) portable implementation of bitwise rotation,
+ for 32-bit integers, is:
+
+ (x << rot) | (x >> (32 - rot))
+
+ This works nearly everywhere. Compilers on x86 wil generally recognize
+ the idiom and convert it to a ROL instruction. But there's a problem
+ here: according to the C specification, bit shifts greater than or equal
+ to the length of the integer are undefined. And if rot = 0:
+ 1. (x << 0) | (x >> (32 - 0))
+ 2. (x << 0) | (x >> 32)
+ 3. (x << 0) | (Undefined!)
+
+ Still, when the compiler converts this to a ROL on x86, everything works
+ as intended. But, there are two additional problems when Clang does
+ compile-time constant expression evaluation with the (x >> 32)
+ expression:
+ 1. Instead of evaluating it to something reasonable (either 0, like a
+ human would intuitively expect, or x, like x86 would with SHR), Clang
+ seems to pull a value out of nowhere, like -1, or some other random
+ number.
+ 2. Clang's warning for this, -Wshift-count-overflow, only works when the
+ shift count is a literal constant, as opposed to an arbitrary
+ expression that is optimized down to a constant.
+ Put together, this means that the assertions in jwxyz_make_display with
+ convert_px break with the above naive rotation, but only for a release
+ build.
+
+ http://blog.regehr.org/archives/1063
+ http://llvm.org/bugs/show_bug.cgi?id=17332
+ As described in those links, there is a solution here: Masking the
+ undefined shift with '& 31' as below makes the experesion well-defined
+ again. And LLVM is set to pick up on this safe version of the idiom and
+ use a rotation instruction on architectures (like x86) that support it,
+ just like it does with the unsafe version.
+
+ Too bad LLVM doesn't want to pick up on that particular optimization
+ here. Oh well. At least this code usually isn't critical w.r.t.
+ performance.
+ */
+
+# if defined __LITTLE_ENDIAN__
+ x = (x << rot) | (x >> ((32 - rot) & 31));
+# elif defined __BIG_ENDIAN__
+ x = (x >> rot) | (x << ((32 - rot) & 31));
+# endif
+
+ if (flip)
+ x = __builtin_bswap32(x); // LLVM/GCC built-in function.
+
+ *dest = x;
+ ++dest;
+ }
+}
+
+
+// Converts a single pixel.
+static uint32_t
+convert_px (uint32_t px, convert_mode_t mode)
+{
+ convert_row (&px, &px, 1, mode, 32);
+ return px;
+}
+
+
+// This returns the inverse conversion mode, such that:
+// pixel
+// == convert_px(convert_px(pixel, mode), convert_mode_invert(mode))
+// == convert_px(convert_px(pixel, convert_mode_invert(mode)), mode)
+static convert_mode_t
+convert_mode_invert (convert_mode_t mode)
+{
+ // swap(0); rot(n) == rot(n); swap(0)
+ // swap(1); rot(n) == rot(-n); swap(1)
+ return mode & CONVERT_MODE_SWAP ? mode : CONVERT_MODE_ROTATE_MASK & -mode;
+}
+
+
+// This combines two conversions into one, such that:
+// convert_px(convert_px(pixel, mode0), mode1)
+// == convert_px(pixel, convert_mode_merge(mode0, mode1))
+static convert_mode_t
+convert_mode_merge (convert_mode_t m0, convert_mode_t m1)
+{
+ // rot(r0); swap(s0); rot(r1); swap(s1)
+ // rot(r0); rot(s0 ? -r1 : r1); swap(s0); swap(s1)
+ // rot(r0 + (s0 ? -r1 : r1)); swap(s0 + s1)
+ return
+ ((m0 + (m0 & CONVERT_MODE_SWAP ? -m1 : m1)) & CONVERT_MODE_ROTATE_MASK) |
+ ((m0 ^ m1) & CONVERT_MODE_SWAP);
+}
+
+
+// This returns a conversion mode that converts an arbitrary 32-bit format
+// specified by bitmap_info to RGBA.
+static convert_mode_t
+convert_mode_to_rgba (CGBitmapInfo bitmap_info)
+{
+ // Former default: kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
+ // i.e. BGRA
+ // red = 0x00FF0000;
+ // green = 0x0000FF00;
+ // blue = 0x000000FF;
+
+ // RGBA: kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big
+
+ CGImageAlphaInfo alpha_info =
+ (CGImageAlphaInfo)(bitmap_info & kCGBitmapAlphaInfoMask);
+
+ Assert (! (bitmap_info & kCGBitmapFloatComponents),
+ "kCGBitmapFloatComponents unsupported");
+ Assert (alpha_info != kCGImageAlphaOnly, "kCGImageAlphaOnly not supported");
+
+ convert_mode_t rot = alpha_info == kCGImageAlphaFirst ||
+ alpha_info == kCGImageAlphaPremultipliedFirst ||
+ alpha_info == kCGImageAlphaNoneSkipFirst ?
+ 3 : 0;
+
+ CGBitmapInfo byte_order = bitmap_info & kCGBitmapByteOrderMask;
+
+ Assert (byte_order == kCGBitmapByteOrder32Little ||
+ byte_order == kCGBitmapByteOrder32Big,
+ "byte order not supported");
+
+ convert_mode_t swap = byte_order == kCGBitmapByteOrder32Little ?
+ CONVERT_MODE_SWAP : 0;
+ if (swap)
+ rot = CONVERT_MODE_ROTATE_MASK & -rot;
+ return swap | rot;
+}
+
+
+union color_bytes
+{
+ uint32_t pixel;
+ uint8_t bytes[4];
+};
+
+
+static uint32_t
+alloc_color (Display *dpy, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
+{
+ union color_bytes color;
+
+ /* Instead of (int)(c / 256.0), another possibility is
+ (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
+ uint8_t integer_math(uint16_t c) {
+ unsigned c0 = c + 128;
+ return (c0 - (c0 >> 8)) >> 8;
+ }
+ */
+
+ color.bytes[0] = r >> 8;
+ color.bytes[1] = g >> 8;
+ color.bytes[2] = b >> 8;
+ color.bytes[3] = a >> 8;
+
+ return
+ convert_px (color.pixel,
+ convert_mode_invert (convert_mode_to_rgba (dpy->screen->bitmap_info)));
+}
+
+
+static void
+query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
+{
+ union color_bytes color;
+ color.pixel = convert_px ((uint32_t)pixel,
+ convert_mode_to_rgba (dpy->screen->bitmap_info));
+ for (unsigned i = 0; i != 4; ++i)
+ rgba[i] = color.bytes[i];
+}
+
+
+static void
+query_color_float (Display *dpy, unsigned long pixel, float *rgba)
+{
+ uint8_t rgba8[4];
+ query_color (dpy, pixel, rgba8);
+ for (unsigned i = 0; i != 4; ++i)
+ rgba[i] = rgba8[i] * (1.0f / 255.0f);
+}
+
+
+/* We keep a list of all of the Displays that have been created and not
+ yet freed so that they can have sensible display numbers. If three
+ displays are created (0, 1, 2) and then #1 is closed, then the fourth
+ display will be given the now-unused display number 1. (Everything in
+ here assumes a 1:1 Display/Screen mapping.)
+
+ The size of this array is the most number of live displays at one time.
+ So if it's 20, then we'll blow up if the system has 19 monitors and also
+ has System Preferences open (the small preview window).
+
+ Note that xlockmore-style savers tend to allocate big structures, so
+ setting this to 1000 will waste a few megabytes. Also some of them assume
+ that the number of screens never changes, so dynamically expanding this
+ array won't work.
+ */
+# ifndef USE_IPHONE
+static Display *jwxyz_live_displays[20] = { 0, };
+# endif
+
Display *
-jwxyz_make_display (void *nsview_arg)
+jwxyz_make_display (void *nsview_arg, void *cgc_arg)
{
+ CGContextRef cgc = (CGContextRef) cgc_arg;
NSView *view = (NSView *) nsview_arg;
- if (!view) abort();
+ Assert (view, "no view");
+ if (!view) return 0;
Display *d = (Display *) calloc (1, sizeof(*d));
d->screen = (Screen *) calloc (1, sizeof(Screen));
d->screen->dpy = d;
+ d->screen_count = 1;
+ d->screen->screen_number = 0;
+# ifndef USE_IPHONE
+ {
+ // Find the first empty slot in live_displays and plug us in.
+ int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
+ int i;
+ for (i = 0; i < size; i++) {
+ if (! jwxyz_live_displays[i])
+ break;
+ }
+ if (i >= size) abort();
+ jwxyz_live_displays[i] = d;
+ d->screen_count = size;
+ d->screen->screen_number = i;
+ }
+# endif // !USE_IPHONE
+
+# ifdef USE_BACKBUFFER
+ d->screen->bitmap_info = CGBitmapContextGetBitmapInfo (cgc);
+# else
+ d->screen->bitmap_info = (kCGImageAlphaNoneSkipFirst |
+ kCGBitmapByteOrder32Little);
+# endif
+ d->screen->black = alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
+ d->screen->white = alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
+
+# if 0
+ // Tests for the image conversion modes.
+ {
+ const uint32_t key = 0x04030201;
+# ifdef __LITTLE_ENDIAN__
+ assert (convert_px (key, 0) == key);
+ assert (convert_px (key, 1) == 0x03020104);
+ assert (convert_px (key, 3) == 0x01040302);
+ assert (convert_px (key, 4) == 0x01020304);
+ assert (convert_px (key, 5) == 0x04010203);
+# endif
+ for (unsigned i = 0; i != 8; ++i) {
+ assert (convert_px(convert_px(key, i), convert_mode_invert(i)) == key);
+ assert (convert_mode_invert(convert_mode_invert(i)) == i);
+ }
+
+ for (unsigned i = 0; i != 8; ++i) {
+ for (unsigned j = 0; j != 8; ++j)
+ assert (convert_px(convert_px(key, i), j) ==
+ convert_px(key, convert_mode_merge(i, j)));
+ }
+ }
+# endif
+
Visual *v = (Visual *) calloc (1, sizeof(Visual));
v->class = TrueColor;
- v->red_mask = 0x00FF0000;
- v->green_mask = 0x0000FF00;
- v->blue_mask = 0x000000FF;
+ v->red_mask = alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
+ v->green_mask = alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
+ v->blue_mask = alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
+ CGBitmapInfo byte_order = d->screen->bitmap_info & kCGBitmapByteOrderMask;
+ Assert ( ! (d->screen->bitmap_info & kCGBitmapFloatComponents) &&
+ (byte_order == kCGBitmapByteOrder32Little ||
+ byte_order == kCGBitmapByteOrder32Big),
+ "invalid bits per channel");
v->bits_per_rgb = 8;
d->screen->visual = v;
Window w = (Window) calloc (1, sizeof(*w));
w->type = WINDOW;
- // kludge! this needs to be set late, so we do it in XClearWindow!
- // w->cgc = [[[view window] graphicsContext] graphicsPort];
- w->cgc = 0;
w->window.view = view;
CFRetain (w->window.view); // needed for garbage collection?
- w->window.background = BlackPixel(0,0);
+ w->window.background = BlackPixel(d,0);
d->main_window = w;
- [view lockFocus];
- w->cgc = [[[view window] graphicsContext] graphicsPort];
- [view unlockFocus];
-
- jwxyz_window_resized (d, w);
+# ifndef USE_IPHONE
+ if (! cgc) {
+ [view lockFocus];
+ cgc = [[[view window] graphicsContext] graphicsPort];
+ [view unlockFocus];
+ w->cgc = cgc;
+ }
+# endif
+ Assert (cgc, "no CGContext");
return d;
}
void
jwxyz_free_display (Display *dpy)
{
- jwxyz_XtRemoveInput_all (dpy);
- // #### jwxyz_XtRemoveTimeOut_all ();
+ jwxyz_sources_free (dpy->timers_data);
+# ifndef USE_IPHONE
+ {
+ // Find us in live_displays and clear that slot.
+ int size = ScreenCount(dpy);
+ int i;
+ for (i = 0; i < size; i++) {
+ if (dpy == jwxyz_live_displays[i]) {
+ jwxyz_live_displays[i] = 0;
+ break;
+ }
+ }
+ if (i >= size) abort();
+ }
+# endif // !USE_IPHONE
+
free (dpy->screen->visual);
free (dpy->screen);
+ CFRelease (dpy->main_window->window.view);
free (dpy->main_window);
free (dpy);
}
void *
jwxyz_window_view (Window w)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
return w->window.view;
}
+
+/* Call this after any modification to the bits on a Pixmap or Window.
+ Most Pixmaps are used frequently as sources and infrequently as
+ destinations, so it pays to cache the data as a CGImage as needed.
+ */
+static void
+invalidate_drawable_cache (Drawable d)
+{
+ if (d && d->cgi) {
+ CGImageRelease (d->cgi);
+ d->cgi = 0;
+ }
+}
+
+
/* Call this when the View changes size or position.
*/
void
-jwxyz_window_resized (Display *dpy, Window w)
+jwxyz_window_resized (Display *dpy, Window w,
+ int new_x, int new_y, int new_width, int new_height,
+ void *cgc_arg)
{
- Assert (w->type == WINDOW, "not a window");
- NSRect r = [w->window.view frame];
- w->frame.origin.x = r.origin.x; // NSRect -> CGRect
- w->frame.origin.y = r.origin.y;
- w->frame.size.width = r.size.width;
- w->frame.size.height = r.size.height;
+ CGContextRef cgc = (CGContextRef) cgc_arg;
+ Assert (w && w->type == WINDOW, "not a window");
+ w->frame.origin.x = new_x;
+ w->frame.origin.y = new_y;
+ w->frame.size.width = new_width;
+ w->frame.size.height = new_height;
+ if (cgc) w->cgc = cgc;
+ Assert (w->cgc, "no CGContext");
+
+# ifndef USE_IPHONE
// Figure out which screen the window is currently on.
{
int wx, wy;
CGDisplayCount n;
dpy->cgdpy = 0;
CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
+ // Auuugh!
+ if (! dpy->cgdpy) {
+ p.x = p.y = 0;
+ CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
+ }
Assert (dpy->cgdpy, "unable to find CGDisplay");
}
+# endif // USE_IPHONE
+
+# ifndef USE_BACKBUFFER
+ // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
+ // then this one's faster.
-#if 0
{
// Figure out this screen's colorspace, and use that for every CGImage.
//
dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
Assert (dpy->colorspace, "unable to find colorspace");
}
-# else
+# else // USE_BACKBUFFER
// WTF? It's faster if we *do not* use the screen's colorspace!
//
dpy->colorspace = CGColorSpaceCreateDeviceRGB();
-# endif
+# endif // USE_BACKBUFFER
+
+ invalidate_drawable_cache (w);
}
+#ifdef USE_IPHONE
+void
+jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
+{
+ Assert (w && w->type == WINDOW, "not a window");
+ w->window.last_mouse_x = x;
+ w->window.last_mouse_y = y;
+}
+#endif // USE_IPHONE
+
+
+void
+jwxyz_flush_context (Display *dpy)
+{
+ // This is only used when USE_BACKBUFFER is off.
+ // CGContextSynchronize is another possibility.
+ CGContextFlush(dpy->main_window->cgc);
+}
+
jwxyz_sources_data *
display_sources_data (Display *dpy)
{
int
XScreenNumberOfScreen (Screen *s)
{
- return 0;
+ return s->screen_number;
+}
+
+int
+jwxyz_ScreenCount (Display *dpy)
+{
+ return dpy->screen_count;
}
int
return (int) dpy->main_window->frame.size.height;
}
+unsigned long
+XBlackPixelOfScreen(Screen *screen)
+{
+ return screen->black;
+}
+
+unsigned long
+XWhitePixelOfScreen(Screen *screen)
+{
+ return screen->white;
+}
+
+unsigned long
+XCellsOfScreen(Screen *screen)
+{
+ Visual *v = screen->visual;
+ return v->red_mask | v->green_mask | v->blue_mask;
+}
static void
-validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
+validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth,
+ BOOL alpha_allowed_p)
{
if (depth == 1)
Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
else if (!alpha_allowed_p)
- Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
+ Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)),
"bogus color pixel");
}
static void
-set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
- BOOL alpha_allowed_p, BOOL fill_p)
+set_color (Display *dpy, CGContextRef cgc, unsigned long argb,
+ unsigned int depth, BOOL alpha_allowed_p, BOOL fill_p)
{
- validate_pixel (argb, depth, alpha_allowed_p);
+ validate_pixel (dpy, argb, depth, alpha_allowed_p);
if (depth == 1) {
if (fill_p)
CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
else
CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
} else {
- float a = ((argb >> 24) & 0xFF) / 255.0;
- float r = ((argb >> 16) & 0xFF) / 255.0;
- float g = ((argb >> 8) & 0xFF) / 255.0;
- float b = ((argb ) & 0xFF) / 255.0;
+ float rgba[4];
+ query_color_float (dpy, argb, rgba);
if (fill_p)
- CGContextSetRGBFillColor (cgc, r, g, b, a);
+ CGContextSetRGBFillColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
else
- CGContextSetRGBStrokeColor (cgc, r, g, b, a);
+ CGContextSetRGBStrokeColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
}
}
case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
- default: abort(); break;
+ default: Assert(0, "unknown gcv function"); break;
}
if (gc->gcv.clip_mask)
set_clip_mask (d, gc);
}
-#define pop_gc(d,gc) CGContextRestoreGState ((d)->cgc)
+#define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
/* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
*/
static void
-push_color_gc (Drawable d, GC gc, unsigned long color,
+push_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color,
BOOL antialias_p, Bool fill_p)
{
push_gc (d, gc);
int depth = gc->depth;
switch (gc->gcv.function) {
- case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
- case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
+ case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
+ case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
}
CGContextRef cgc = d->cgc;
-
- set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
+ set_color (dpy, cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
CGContextSetShouldAntialias (cgc, antialias_p);
}
/* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
*/
static void
-push_fg_gc (Drawable d, GC gc, Bool fill_p)
+push_fg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
{
- push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
+ push_color_gc (dpy, d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
}
/* Pushes a GC context; sets Fill and Stroke colors to the background color.
*/
static void
-push_bg_gc (Drawable d, GC gc, Bool fill_p)
+push_bg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
{
- push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
+ push_color_gc (dpy, d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
}
It is *way* faster to draw points by creating and drawing a 1x1 CGImage
with repeated calls to CGContextDrawImage than it is to make a single
- call to CGContextFillRects()!
+ call to CGContextFillRects() with a list of 1x1 rectangles!
I still wouldn't call it *fast*, however...
*/
#define XDRAWPOINTS_IMAGES
+/* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
+ the bitmap data directly is faster. This only works on Pixmaps, though,
+ not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
+ */
+#define XDRAWPOINTS_CGDATA
+
int
XDrawPoints (Display *dpy, Drawable d, GC gc,
XPoint *points, int count, int mode)
int i;
CGRect wr = d->frame;
- push_fg_gc (d, gc, YES);
+# ifdef XDRAWPOINTS_CGDATA
-# ifdef XDRAWPOINTS_IMAGES
+# ifdef USE_BACKBUFFER
+ if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+# else
+ if (d->type == PIXMAP)
+# endif
+ {
+ CGContextRef cgc = d->cgc;
+ void *data = CGBitmapContextGetData (cgc);
+ size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
+ size_t w = CGBitmapContextGetWidth (cgc);
+ size_t h = CGBitmapContextGetHeight (cgc);
- unsigned int argb = gc->gcv.foreground;
- validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
- if (gc->depth == 1)
- argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
-
- CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
- CGImageRef cgi = CGImageCreate (1, 1,
- 8, 32, 4,
- dpy->colorspace,
- /* Host-ordered, since we're using the
- address of an int as the color data. */
- (kCGImageAlphaNoneSkipFirst |
- kCGBitmapByteOrder32Host),
- prov,
- NULL, /* decode[] */
- NO, /* interpolate */
- kCGRenderingIntentDefault);
- CGDataProviderRelease (prov);
+ Assert (data, "no bitmap data in Drawable");
- CGContextRef cgc = d->cgc;
- CGRect rect;
- rect.size.width = rect.size.height = 1;
- for (i = 0; i < count; i++) {
- if (i > 0 && mode == CoordModePrevious) {
- rect.origin.x += points->x;
- rect.origin.x -= points->y;
+ unsigned long argb = gc->gcv.foreground;
+ validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
+ if (gc->depth == 1)
+ argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
+
+ CGFloat x0 = wr.origin.x;
+ CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
+
+ // It's uglier, but faster, to hoist the conditional out of the loop.
+ if (mode == CoordModePrevious) {
+ CGFloat x = x0, y = y0;
+ for (i = 0; i < count; i++, points++) {
+ x += points->x;
+ y += points->y;
+
+ if (x >= 0 && x < w && y >= 0 && y < h) {
+ unsigned int *p = (unsigned int *)
+ ((char *) data + (size_t) y * bpr + (size_t) x * 4);
+ *p = (unsigned int) argb;
+ }
+ }
} else {
- rect.origin.x = wr.origin.x + points->x;
- rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
+ for (i = 0; i < count; i++, points++) {
+ CGFloat x = x0 + points->x;
+ CGFloat y = y0 + points->y;
+
+ if (x >= 0 && x < w && y >= 0 && y < h) {
+ unsigned int *p = (unsigned int *)
+ ((char *) data + (size_t) y * bpr + (size_t) x * 4);
+ *p = (unsigned int) argb;
+ }
+ }
}
- //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (cgc, rect, cgi);
- points++;
- }
+ } else /* d->type == WINDOW */
- CGImageRelease (cgi);
+# endif /* XDRAWPOINTS_CGDATA */
+ {
+ push_fg_gc (dpy, d, gc, YES);
+
+# ifdef XDRAWPOINTS_IMAGES
+
+ unsigned int argb = gc->gcv.foreground;
+ validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
+ if (gc->depth == 1)
+ argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
+
+ CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
+ NULL);
+ CGImageRef cgi = CGImageCreate (1, 1,
+ 8, 32, 4,
+ dpy->colorspace,
+ /* Host-ordered, since we're using the
+ address of an int as the color data. */
+ dpy->screen->bitmap_info,
+ prov,
+ NULL, /* decode[] */
+ NO, /* interpolate */
+ kCGRenderingIntentDefault);
+ CGDataProviderRelease (prov);
+
+ CGContextRef cgc = d->cgc;
+ CGRect rect;
+ rect.size.width = rect.size.height = 1;
+ for (i = 0; i < count; i++) {
+ if (i > 0 && mode == CoordModePrevious) {
+ rect.origin.x += points->x;
+ rect.origin.x -= points->y;
+ } else {
+ rect.origin.x = wr.origin.x + points->x;
+ rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
+ }
+
+ //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
+ CGContextDrawImage (cgc, rect, cgi);
+ points++;
+ }
+
+ CGImageRelease (cgi);
# else /* ! XDRAWPOINTS_IMAGES */
- CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
- CGRect *r = rects;
+ CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
+ CGRect *r = rects;
- for (i = 0; i < count; i++) {
- r->size.width = r->size.height = 1;
- if (i > 0 && mode == CoordModePrevious) {
- r->origin.x = r[-1].origin.x + points->x;
- r->origin.y = r[-1].origin.x - points->y;
- } else {
- r->origin.x = wr.origin.x + points->x;
- r->origin.y = wr.origin.y + wr.size.height - points->y;
+ for (i = 0; i < count; i++) {
+ r->size.width = r->size.height = 1;
+ if (i > 0 && mode == CoordModePrevious) {
+ r->origin.x = r[-1].origin.x + points->x;
+ r->origin.y = r[-1].origin.x - points->y;
+ } else {
+ r->origin.x = wr.origin.x + points->x;
+ r->origin.y = wr.origin.y + wr.size.height - points->y;
+ }
+ points++;
+ r++;
}
- points++;
- r++;
- }
- CGContextFillRects (d->cgc, rects, count);
- free (rects);
+ CGContextFillRects (d->cgc, rects, count);
+ free (rects);
# endif /* ! XDRAWPOINTS_IMAGES */
- pop_gc (d, gc);
+ pop_gc (d, gc);
+ }
+
+ invalidate_drawable_cache (d);
return 0;
}
int x, int y, unsigned int width, unsigned int height,
BOOL foreground_p, BOOL fill_p);
+static Bool
+bitmap_context_p (Drawable d)
+{
+# ifdef USE_BACKBUFFER
+ return True;
+# else
+ // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+ return d->type == PIXMAP;
+# endif
+}
+
+static void
+fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
+ size_t fill_width, size_t fill_height)
+{
+ Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
+ while (fill_height) {
+ // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
+ wmemset (dst, fill_data, fill_width);
+ --fill_height;
+ dst = (char *) dst + dst_pitch;
+ }
+}
+
+static void *
+seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
+{
+ return (char *)dst + dst_pitch * y + x * 4;
+}
+
+static unsigned int
+drawable_depth (Drawable d)
+{
+ return (d->type == WINDOW
+ ? visual_depth (NULL, NULL)
+ : d->pixmap.depth);
+}
+
+
int
XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
int src_x, int src_y,
gc->gcv.function == GXclear) {
// "set" and "clear" are dumb drawing modes that ignore the source
// bits and just draw solid rectangles.
- set_color (dst->cgc, (gc->gcv.function == GXset
- ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
- : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
+ set_color (dpy, dst->cgc,
+ (gc->gcv.function == GXset
+ ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
+ : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))),
gc->depth, gc->gcv.alpha_allowed_p, YES);
draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
return 0;
// Clip rects to frames...
//
-// CGRect orig_src_rect = src_rect;
- CGRect orig_dst_rect = dst_rect;
# define CLIP(THIS,THAT,VAL,SIZE) do { \
float off = THIS##_rect.origin.VAL; \
CLIP (dst, src, x, width);
CLIP (dst, src, y, height);
+
+ // Not actually the original dst_rect, just the one before it's clipped to
+ // the src_frame.
+ CGRect orig_dst_rect = dst_rect;
+
CLIP (src, dst, x, width);
CLIP (src, dst, y, height);
# undef CLIP
-#if 0
- Assert (src_rect.size.width == dst_rect.size.width, "width out of sync");
- Assert (src_rect.size.height == dst_rect.size.height, "height out of sync");
- Assert (src_rect.origin.x >= 0 && src_rect.origin.y >= 0, "clip failed src_x");
- Assert (dst_rect.origin.x >= 0 && dst_rect.origin.y >= 0, "clip failed dst_x");
- Assert (src_rect.origin.y >= 0 && src_rect.origin.y >= 0, "clip failed src_y");
- Assert (dst_rect.origin.y >= 0 && dst_rect.origin.y >= 0, "clip failed dst_y");
- Assert (src_rect.origin.x + src_rect.size.width <=
- src_frame.origin.x + src_frame.size.width, "clip failed src_width");
-#endif
-
- if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
+ if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
return 0;
+
+ // Sort-of-special case where no pixels can be grabbed from the source,
+ // and the whole destination is filled with the background color.
+ if (src_rect.size.width < 0 || src_rect.size.height < 0) {
+
+ Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
+ (int)src_rect.size.height == (int)dst_rect.size.height,
+ "size mismatch");
+
+ src_rect.size.width = 0;
+ src_rect.size.height = 0;
+ dst_rect.size.width = 0;
+ dst_rect.size.height = 0;
+ }
NSObject *releaseme = 0;
CGImageRef cgi;
BOOL mask_p = NO;
+ BOOL free_cgi_p = NO;
+
+
+ /* If we're copying from a bitmap to a bitmap, and there's nothing funny
+ going on with clipping masks or depths or anything, optimize it by
+ just doing a memcpy instead of going through a CGI.
+ */
+ if (bitmap_context_p (src)) {
+
+ if (bitmap_context_p (dst) &&
+ gc->gcv.function == GXcopy &&
+ !gc->gcv.clip_mask &&
+ drawable_depth (src) == drawable_depth (dst)) {
+
+ Assert(!(int)src_frame.origin.x &&
+ !(int)src_frame.origin.y &&
+ !(int)dst_frame.origin.x &&
+ !(int)dst_frame.origin.y,
+ "unexpected non-zero origin");
+
+ char *src_data = CGBitmapContextGetData(src->cgc);
+ char *dst_data = CGBitmapContextGetData(dst->cgc);
+ size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
+ size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
+
+ // Int to float and back again. It's not very safe, but it seems to work.
+ int src_x0 = src_rect.origin.x;
+ int dst_x0 = dst_rect.origin.x;
+
+ // Flip the Y-axis a second time.
+ int src_y0 = (src_frame.origin.y + src_frame.size.height -
+ src_rect.size.height - src_rect.origin.y);
+ int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
+ dst_rect.size.height - dst_rect.origin.y);
+
+ unsigned width0 = (int) src_rect.size.width;
+ unsigned height0 = (int) src_rect.size.height;
+
+ Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
+ (int)src_rect.size.height == (int)dst_rect.size.height,
+ "size mismatch");
+ {
+ char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
+ char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
+ size_t src_pitch0 = src_pitch;
+ size_t dst_pitch0 = dst_pitch;
+ size_t bytes = width0 * 4;
+
+ if (src == dst && dst_y0 > src_y0) {
+ // Copy upwards if the areas might overlap.
+ src_data0 += src_pitch0 * (height0 - 1);
+ dst_data0 += dst_pitch0 * (height0 - 1);
+ src_pitch0 = -src_pitch0;
+ dst_pitch0 = -dst_pitch0;
+ }
+
+ size_t lines0 = height0;
+ while (lines0) {
+ // memcpy is an alias for memmove on OS X.
+ memmove(dst_data0, src_data0, bytes);
+ src_data0 += src_pitch0;
+ dst_data0 += dst_pitch0;
+ --lines0;
+ }
+ }
+
+ if (clipped) {
+ int orig_dst_x = orig_dst_rect.origin.x;
+ int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
+ orig_dst_rect.origin.y - orig_dst_rect.size.height);
+ int orig_width = orig_dst_rect.size.width;
+ int orig_height = orig_dst_rect.size.height;
+
+ Assert (orig_dst_x >= 0 &&
+ orig_dst_x + orig_width <= (int) dst_frame.size.width &&
+ orig_dst_y >= 0 &&
+ orig_dst_y + orig_height <= (int) dst_frame.size.height,
+ "wrong dimensions");
+
+ if (orig_dst_y < dst_y0) {
+ fill_rect_memset (seek_xy (dst_data, dst_pitch,
+ orig_dst_x, orig_dst_y), dst_pitch,
+ (uint32_t) gc->gcv.background, orig_width,
+ dst_y0 - orig_dst_y);
+ }
+
+ if (orig_dst_y + orig_height > dst_y0 + height0) {
+ fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
+ dst_y0 + height0),
+ dst_pitch,
+ (uint32_t) gc->gcv.background, orig_width,
+ orig_dst_y + orig_height - dst_y0 - height0);
+ }
- if (src->type == PIXMAP) {
+ if (orig_dst_x < dst_x0) {
+ fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
+ dst_pitch, (uint32_t) gc->gcv.background,
+ dst_x0 - orig_dst_x, height0);
+ }
- // get a CGImage out of the pixmap CGContext -- it's the whole pixmap,
- // but it presumably shares the data pointer instead of copying it.
- cgi = CGBitmapContextCreateImage (src->cgc);
+ if (dst_x0 + width0 < orig_dst_x + orig_width) {
+ fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
+ dst_y0),
+ dst_pitch, (uint32_t) gc->gcv.background,
+ orig_dst_x + orig_width - dst_x0 - width0,
+ height0);
+ }
+ }
+
+ invalidate_drawable_cache (dst);
+ return 0;
+ }
+
+
+ // If we are copying from a Pixmap to a Pixmap or Window, we must first
+ // copy the bits to an intermediary CGImage object, then copy that to the
+ // destination drawable's CGContext.
+ //
+ // (It doesn't seem to be possible to use NSCopyBits() to optimize the
+ // case of copying from a Pixmap back to itself, but I don't think that
+ // happens very often anyway.)
+ //
+ // First we get a CGImage out of the pixmap CGContext -- it's the whole
+ // pixmap, but it presumably shares the data pointer instead of copying
+ // it. We then cache that CGImage it inside the Pixmap object. Note:
+ // invalidate_drawable_cache() must be called to discard this any time a
+ // modification is made to the pixmap, or we'll end up re-using old bits.
+ //
+ if (!src->cgi)
+ src->cgi = CGBitmapContextCreateImage (src->cgc);
+ cgi = src->cgi;
// if doing a sub-rect, trim it down.
if (src_rect.origin.x != src_frame.origin.x ||
src_rect.origin.y = (src_frame.size.height -
src_rect.size.height - src_rect.origin.y);
// This does not copy image data, so it should be fast.
- CGImageRef cgi2 = CGImageCreateWithImageInRect (cgi, src_rect);
- cgi = cgi2;
+ cgi = CGImageCreateWithImageInRect (cgi, src_rect);
+ free_cgi_p = YES;
}
- if (src->pixmap.depth == 1)
+ if (src->type == PIXMAP && src->pixmap.depth == 1)
mask_p = YES;
+# ifndef USE_BACKBUFFER
} else { /* (src->type == WINDOW) */
- NSRect nsfrom;
+ NSRect nsfrom; // NSRect != CGRect on 10.4
nsfrom.origin.x = src_rect.origin.x;
nsfrom.origin.y = src_rect.origin.y;
nsfrom.size.width = src_rect.size.width;
nsfrom.size.height = src_rect.size.height;
-#if 1
- // get the bits (desired sub-rectangle) out of the NSView via Cocoa.
- //
- NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
- initWithFocusedViewRect:nsfrom];
- unsigned char *data = [bm bitmapData];
- int bps = [bm bitsPerSample];
- int bpp = [bm bitsPerPixel];
- int bpl = [bm bytesPerRow];
- releaseme = bm;
-#endif
+ if (src == dst) {
-#if 0
- // QuickDraw way (doesn't work, need NSQuickDrawView)
- PixMapHandle pix = GetPortPixMap([src->window.view qdPort]);
- char **data = GetPortPixMap (pix);
- int bps = 8;
- int bpp = 32;
- int bpl = GetPixRowBytes (pix) & 0x3FFF;
-#endif
+ // If we are copying from a window to itself, we can use NSCopyBits()
+ // without first copying the rectangle to an intermediary CGImage.
+ // This is ~28% faster (but I *expected* it to be twice as fast...)
+ // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
+ //
+ cgi = 0;
-#if 0
- // get the bits (desired sub-rectangle) out of the raw frame buffer.
- // (This renders wrong, and appears to be even slower anyway.)
- //
- int window_x, window_y;
- XTranslateCoordinates (dpy, src, NULL, 0, 0, &window_x, &window_y, NULL);
- window_x += nsfrom.origin.x;
- window_y += (dst->frame.size.height
- - (nsfrom.origin.y + nsfrom.size.height));
-
- unsigned char *data = (unsigned char *)
- CGDisplayAddressForPosition (dpy->cgdpy, window_x, window_y);
- int bps = CGDisplayBitsPerSample (dpy->cgdpy);
- int bpp = CGDisplayBitsPerPixel (dpy->cgdpy);
- int bpl = CGDisplayBytesPerRow (dpy->cgdpy);
+ } else {
-#endif
+ // If we are copying from a Window to a Pixmap, we must first copy
+ // the bits to an intermediary CGImage object, then copy that to the
+ // Pixmap's CGContext.
+ //
+ NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
+ initWithFocusedViewRect:nsfrom];
+ unsigned char *data = [bm bitmapData];
+ int bps = [bm bitsPerSample];
+ int bpp = [bm bitsPerPixel];
+ int bpl = [bm bytesPerRow];
+ releaseme = bm;
+
+ // create a CGImage from those bits.
+ // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
+ // but that method didn't exist in 10.4.)
+
+ CGDataProviderRef prov =
+ CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
+ NULL);
+ cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
+ bps, bpp, bpl,
+ dpy->colorspace,
+ /* Use whatever default bit ordering we got from
+ initWithFocusedViewRect. I would have assumed
+ that it was (kCGImageAlphaNoneSkipFirst |
+ kCGBitmapByteOrder32Host), but on Intel,
+ it's not!
+ */
+ 0,
+ prov,
+ NULL, /* decode[] */
+ NO, /* interpolate */
+ kCGRenderingIntentDefault);
+ free_cgi_p = YES;
+ //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
+ CGDataProviderRelease (prov);
+ }
- // create a CGImage from those bits
-
- CGDataProviderRef prov =
- CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
- NULL);
- cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
- bps, bpp, bpl,
- dpy->colorspace,
- /* Use whatever default bit ordering we got from
- initWithFocusedViewRect. I would have assumed
- that it was (kCGImageAlphaNoneSkipFirst |
- kCGBitmapByteOrder32Host), but on Intel,
- it's not!
- */
- 0,
- prov,
- NULL, /* decode[] */
- NO, /* interpolate */
- kCGRenderingIntentDefault);
- //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGDataProviderRelease (prov);
+# endif // !USE_BACKBUFFER
}
+ CGContextRef cgc = dst->cgc;
+
if (mask_p) { // src depth == 1
- push_bg_gc (dst, gc, YES);
+ push_bg_gc (dpy, dst, gc, YES);
// fill the destination rectangle with solid background...
- CGContextFillRect (dst->cgc, orig_dst_rect);
+ CGContextFillRect (cgc, orig_dst_rect);
+
+ Assert (cgc, "no CGC with 1-bit XCopyArea");
// then fill in a solid rectangle of the fg color, using the image as an
// alpha mask. (the image has only values of BlackPixel or WhitePixel.)
- set_color (dst->cgc, gc->gcv.foreground, gc->depth,
+ set_color (dpy, cgc, gc->gcv.foreground, gc->depth,
gc->gcv.alpha_allowed_p, YES);
- CGContextClipToMask (dst->cgc, dst_rect, cgi);
- CGContextFillRect (dst->cgc, dst_rect);
+ CGContextClipToMask (cgc, dst_rect, cgi);
+ CGContextFillRect (cgc, dst_rect);
pop_gc (dst, gc);
// being copied.
//
if (clipped) {
- set_color (dst->cgc, gc->gcv.background, gc->depth,
+ set_color (dpy, cgc, gc->gcv.background, gc->depth,
gc->gcv.alpha_allowed_p, YES);
- CGContextFillRect (dst->cgc, orig_dst_rect);
+ CGContextFillRect (cgc, orig_dst_rect);
}
- // copy the CGImage onto the destination CGContext
- //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (dst->cgc, dst_rect, cgi);
+ if (cgi) {
+ // copy the CGImage onto the destination CGContext
+ //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
+ CGContextDrawImage (cgc, dst_rect, cgi);
+ } else {
+ // No cgi means src == dst, and both are Windows.
+
+# ifdef USE_BACKBUFFER
+ Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
+ return 0;
+# else // !USE_BACKBUFFER
+ NSRect nsfrom;
+ nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
+ nsfrom.origin.y = src_rect.origin.y;
+ nsfrom.size.width = src_rect.size.width;
+ nsfrom.size.height = src_rect.size.height;
+ NSPoint nsto;
+ nsto.x = dst_rect.origin.x;
+ nsto.y = dst_rect.origin.y;
+ NSCopyBits (0, nsfrom, nsto);
+# endif // !USE_BACKBUFFER
+ }
pop_gc (dst, gc);
}
-
- CGImageRelease (cgi);
+
+ if (free_cgi_p) CGImageRelease (cgi);
+
if (releaseme) [releaseme release];
+ invalidate_drawable_cache (dst);
return 0;
}
p.x = wr.origin.x + x1;
p.y = wr.origin.y + wr.size.height - y1;
- push_fg_gc (d, gc, NO);
+ push_fg_gc (dpy, d, gc, NO);
- set_line_mode (d->cgc, &gc->gcv);
- CGContextBeginPath (d->cgc);
- CGContextMoveToPoint (d->cgc, p.x, p.y);
+ CGContextRef cgc = d->cgc;
+ set_line_mode (cgc, &gc->gcv);
+ CGContextBeginPath (cgc);
+ CGContextMoveToPoint (cgc, p.x, p.y);
p.x = wr.origin.x + x2;
p.y = wr.origin.y + wr.size.height - y2;
- CGContextAddLineToPoint (d->cgc, p.x, p.y);
- CGContextStrokePath (d->cgc);
+ CGContextAddLineToPoint (cgc, p.x, p.y);
+ CGContextStrokePath (cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
int i;
NSPoint p;
CGRect wr = d->frame;
- push_fg_gc (d, gc, NO);
- set_line_mode (d->cgc, &gc->gcv);
+ push_fg_gc (dpy, d, gc, NO);
+
+ CGContextRef cgc = d->cgc;
+
+ set_line_mode (cgc, &gc->gcv);
// if the first and last points coincide, use closepath to get
// the proper line-joining.
p.x = wr.origin.x + points->x;
p.y = wr.origin.y + wr.size.height - points->y;
points++;
- CGContextBeginPath (d->cgc);
- CGContextMoveToPoint (d->cgc, p.x, p.y);
+ CGContextBeginPath (cgc);
+ CGContextMoveToPoint (cgc, p.x, p.y);
for (i = 1; i < count; i++) {
if (mode == CoordModePrevious) {
p.x += points->x;
p.x = wr.origin.x + points->x;
p.y = wr.origin.y + wr.size.height - points->y;
}
- CGContextAddLineToPoint (d->cgc, p.x, p.y);
+ CGContextAddLineToPoint (cgc, p.x, p.y);
points++;
}
- if (closed_p) CGContextClosePath (d->cgc);
- CGContextStrokePath (d->cgc);
+ if (closed_p) CGContextClosePath (cgc);
+ CGContextStrokePath (cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
int i;
CGRect wr = d->frame;
- push_fg_gc (d, gc, NO);
- set_line_mode (d->cgc, &gc->gcv);
- CGContextBeginPath (d->cgc);
+ CGContextRef cgc = d->cgc;
+
+ push_fg_gc (dpy, d, gc, NO);
+ set_line_mode (cgc, &gc->gcv);
+ CGContextBeginPath (cgc);
for (i = 0; i < count; i++) {
- CGContextMoveToPoint (d->cgc,
+ CGContextMoveToPoint (cgc,
wr.origin.x + segments->x1,
wr.origin.y + wr.size.height - segments->y1);
- CGContextAddLineToPoint (d->cgc,
+ CGContextAddLineToPoint (cgc,
wr.origin.x + segments->x2,
wr.origin.y + wr.size.height - segments->y2);
segments++;
}
- CGContextStrokePath (d->cgc);
+ CGContextStrokePath (cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
int
XClearWindow (Display *dpy, Window win)
{
- Assert (win->type == WINDOW, "not a window");
+ Assert (win && win->type == WINDOW, "not a window");
CGRect wr = win->frame;
return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
}
int
XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
{
- Assert (w->type == WINDOW, "not a window");
- validate_pixel (pixel, 32, NO);
+ Assert (w && w->type == WINDOW, "not a window");
+ validate_pixel (dpy, pixel, 32, NO);
w->window.background = pixel;
return 0;
}
if (gc) {
if (foreground_p)
- push_fg_gc (d, gc, fill_p);
+ push_fg_gc (dpy, d, gc, fill_p);
else
- push_bg_gc (d, gc, fill_p);
+ push_bg_gc (dpy, d, gc, fill_p);
}
+ CGContextRef cgc = d->cgc;
if (fill_p)
- CGContextFillRect (d->cgc, r);
+ CGContextFillRect (cgc, r);
else {
if (gc)
- set_line_mode (d->cgc, &gc->gcv);
- CGContextStrokeRect (d->cgc, r);
+ set_line_mode (cgc, &gc->gcv);
+ CGContextStrokeRect (cgc, r);
}
if (gc)
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
}
{
CGRect wr = d->frame;
int i;
- push_fg_gc (d, gc, YES);
+ CGContextRef cgc = d->cgc;
+ push_fg_gc (dpy, d, gc, YES);
for (i = 0; i < n; i++) {
CGRect r;
r.origin.x = wr.origin.x + rects->x;
r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
r.size.width = rects->width;
r.size.height = rects->height;
- CGContextFillRect (d->cgc, r);
+ CGContextFillRect (cgc, r);
rects++;
}
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
int
XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
{
- Assert (win->type == WINDOW, "not a window");
- set_color (win->cgc, win->window.background, 32, NO, YES);
+ Assert (win && win->type == WINDOW, "not a window");
+ CGContextRef cgc = win->cgc;
+ set_color (dpy, cgc, win->window.background, 32, NO, YES);
draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
return 0;
}
{
CGRect wr = d->frame;
int i;
- push_fg_gc (d, gc, YES);
- CGContextBeginPath (d->cgc);
+ push_fg_gc (dpy, d, gc, YES);
+ CGContextRef cgc = d->cgc;
+ CGContextBeginPath (cgc);
+ float x = 0, y = 0;
for (i = 0; i < npoints; i++) {
- float x, y;
if (i > 0 && mode == CoordModePrevious) {
x += points[i].x;
y -= points[i].y;
}
if (i == 0)
- CGContextMoveToPoint (d->cgc, x, y);
+ CGContextMoveToPoint (cgc, x, y);
else
- CGContextAddLineToPoint (d->cgc, x, y);
+ CGContextAddLineToPoint (cgc, x, y);
}
- CGContextClosePath (d->cgc);
+ CGContextClosePath (cgc);
if (gc->gcv.fill_rule == EvenOddRule)
- CGContextEOFillPath (d->cgc);
+ CGContextEOFillPath (cgc);
else
- CGContextFillPath (d->cgc);
+ CGContextFillPath (cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
BOOL clockwise = angle2 < 0;
BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
- push_fg_gc (d, gc, fill_p);
+ push_fg_gc (dpy, d, gc, fill_p);
- CGContextBeginPath (d->cgc);
+ CGContextRef cgc = d->cgc;
+ CGContextBeginPath (cgc);
- CGContextSaveGState(d->cgc);
- CGContextTranslateCTM (d->cgc, ctr.x, ctr.y);
- CGContextScaleCTM (d->cgc, width/2.0, height/2.0);
+ CGContextSaveGState(cgc);
+ CGContextTranslateCTM (cgc, ctr.x, ctr.y);
+ CGContextScaleCTM (cgc, width/2.0, height/2.0);
if (fill_p)
- CGContextMoveToPoint (d->cgc, 0, 0);
+ CGContextMoveToPoint (cgc, 0, 0);
- CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise);
- CGContextRestoreGState (d->cgc); // restore before stroke, for line width
+ CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
+ CGContextRestoreGState (cgc); // restore before stroke, for line width
if (closed_p)
- CGContextClosePath (d->cgc); // for proper line joining
+ CGContextClosePath (cgc); // for proper line joining
if (fill_p) {
- CGContextFillPath (d->cgc);
+ CGContextFillPath (cgc);
} else {
- set_line_mode (d->cgc, &gc->gcv);
- CGContextStrokePath (d->cgc);
+ set_line_mode (cgc, &gc->gcv);
+ CGContextStrokePath (cgc);
}
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
static void
-gcv_defaults (XGCValues *gcv, int depth)
+gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
{
memset (gcv, 0, sizeof(*gcv));
gcv->function = GXcopy;
- gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
- gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
+ gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
+ gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
gcv->line_width = 1;
gcv->cap_style = CapNotLast;
gcv->join_style = JoinMiter;
}
static void
-set_gcv (GC gc, XGCValues *from, unsigned long mask)
+set_gcv (Display *dpy, GC gc, XGCValues *from, unsigned long mask)
{
+ if (! mask) return;
+ Assert (gc && from, "no gc");
+ if (!gc || !from) return;
+
if (mask & GCFunction) gc->gcv.function = from->function;
if (mask & GCForeground) gc->gcv.foreground = from->foreground;
if (mask & GCBackground) gc->gcv.background = from->background;
if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
if (mask & GCFont) XSetFont (0, gc, from->font);
- if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
+ if (mask & GCForeground) validate_pixel (dpy, from->foreground, gc->depth,
gc->gcv.alpha_allowed_p);
- if (mask & GCBackground) validate_pixel (from->background, gc->depth,
+ if (mask & GCBackground) validate_pixel (dpy, from->background, gc->depth,
gc->gcv.alpha_allowed_p);
- if (mask & GCLineStyle) abort();
- if (mask & GCPlaneMask) abort();
- if (mask & GCFillStyle) abort();
- if (mask & GCTile) abort();
- if (mask & GCStipple) abort();
- if (mask & GCTileStipXOrigin) abort();
- if (mask & GCTileStipYOrigin) abort();
- if (mask & GCGraphicsExposures) abort();
- if (mask & GCDashOffset) abort();
- if (mask & GCDashList) abort();
- if (mask & GCArcMode) abort();
+ Assert ((! (mask & (GCLineStyle |
+ GCPlaneMask |
+ GCFillStyle |
+ GCTile |
+ GCStipple |
+ GCTileStipXOrigin |
+ GCTileStipYOrigin |
+ GCGraphicsExposures |
+ GCDashOffset |
+ GCDashList |
+ GCArcMode))),
+ "unimplemented gcvalues mask");
}
gc->depth = d->pixmap.depth;
}
- gcv_defaults (&gc->gcv, gc->depth);
- set_gcv (gc, xgcv, mask);
+ gcv_defaults (dpy, &gc->gcv, gc->depth);
+ set_gcv (dpy, gc, xgcv, mask);
return gc;
}
int
XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
{
- set_gcv (gc, gcv, mask);
+ set_gcv (dpy, gc, gcv, mask);
return 0;
}
Status
XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
memset (xgwa, 0, sizeof(*xgwa));
xgwa->x = w->frame.origin.x;
xgwa->y = w->frame.origin.y;
Status
XAllocColor (Display *dpy, Colormap cmap, XColor *color)
{
- // store 32 bit ARGB in the pixel field.
- // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
- color->pixel = (uint32_t)
- (( 0xFF << 24) |
- (((color->red >> 8) & 0xFF) << 16) |
- (((color->green >> 8) & 0xFF) << 8) |
- (((color->blue >> 8) & 0xFF) ));
+ color->pixel = alloc_color (dpy,
+ color->red, color->green, color->blue, 0xFFFF);
return 1;
}
g = (hex[spec[3]] << 4) | hex[spec[4]];
b = (hex[spec[5]] << 4) | hex[spec[6]];
} else if (!strcasecmp(spec,"black")) {
- r = g = b = 0;
+// r = g = b = 0;
} else if (!strcasecmp(spec,"white")) {
r = g = b = 255;
} else if (!strcasecmp(spec,"red")) {
int
XQueryColor (Display *dpy, Colormap cmap, XColor *color)
{
- validate_pixel (color->pixel, 32, NO);
- unsigned char r = ((color->pixel >> 16) & 0xFF);
- unsigned char g = ((color->pixel >> 8) & 0xFF);
- unsigned char b = ((color->pixel ) & 0xFF);
- color->red = (r << 8) | r;
- color->green = (g << 8) | g;
- color->blue = (b << 8) | b;
+ validate_pixel (dpy, color->pixel, 32, NO);
+ uint8_t rgba[4];
+ query_color(dpy, color->pixel, rgba);
+ color->red = (rgba[0] << 8) | rgba[0];
+ color->green = (rgba[1] << 8) | rgba[1];
+ color->blue = (rgba[2] << 8) | rgba[2];
color->flags = DoRed|DoGreen|DoBlue;
return 0;
}
ximage->f.put_pixel = ximage_putpixel_32;
ximage->f.get_pixel = ximage_getpixel_32;
} else {
- abort();
+ Assert (0, "unknown depth");
}
return 1;
}
ximage->format = format;
ximage->data = data;
ximage->bitmap_unit = 8;
- ximage->byte_order = MSBFirst;
+ ximage->byte_order = LSBFirst;
ximage->bitmap_bit_order = ximage->byte_order;
ximage->bitmap_pad = bitmap_pad;
ximage->depth = depth;
- ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
- ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
- ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
+ ximage->red_mask = (depth == 1 ? 0 : dpy->screen->visual->red_mask);
+ ximage->green_mask = (depth == 1 ? 0 : dpy->screen->visual->green_mask);
+ ximage->blue_mask = (depth == 1 ? 0 : dpy->screen->visual->blue_mask);
ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
ximage->bytes_per_line = bytes_per_line;
XImage *
XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
{
- XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
- w, h, from->bitmap_pad, 0);
+ XImage *to = (XImage *) malloc (sizeof(*to));
+ memcpy (to, from, sizeof(*from));
+ to->width = w;
+ to->height = h;
+ to->bytes_per_line = 0;
+ XInitImage (to);
+
to->data = (char *) malloc (h * to->bytes_per_line);
if (x >= from->width)
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
- while (--length > 0)
+ while (length-- > 0)
*out++ = table[*in++];
}
if (w <= 0 || h <= 0)
return 0;
+ CGContextRef cgc = d->cgc;
+
if (gc->gcv.function == GXset ||
gc->gcv.function == GXclear) {
// "set" and "clear" are dumb drawing modes that ignore the source
// bits and just draw solid rectangles.
- set_color (d->cgc, (gc->gcv.function == GXset
- ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
- : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
+ set_color (dpy, cgc, (gc->gcv.function == GXset
+ ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
+ : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))),
gc->depth, gc->gcv.alpha_allowed_p, YES);
draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
return 0;
CGImageRef cgi = CGImageCreate (w, h,
bpp/4, bpp, bpl,
dpy->colorspace,
- /* Need this for XPMs to have the right
- colors, e.g. the logo in "maze". */
- (kCGImageAlphaNoneSkipFirst |
- kCGBitmapByteOrder32Host),
+ dpy->screen->bitmap_info,
prov,
NULL, /* decode[] */
NO, /* interpolate */
kCGRenderingIntentDefault);
CGDataProviderRelease (prov);
//Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (d->cgc, r, cgi);
+ CGContextDrawImage (cgc, r, cgi);
CGImageRelease (cgi);
} else { // (bpp == 1)
prov,
NULL, /* decode[] */
NO); /* interpolate */
- push_fg_gc (d, gc, YES);
+ push_fg_gc (dpy, d, gc, YES);
- CGContextFillRect (d->cgc, r); // foreground color
- CGContextClipToMask (d->cgc, r, mask);
- set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
- CGContextFillRect (d->cgc, r); // background color
+ CGContextFillRect (cgc, r); // foreground color
+ CGContextClipToMask (cgc, r, mask);
+ set_color (dpy, cgc, gc->gcv.background, gc->depth, NO, YES);
+ CGContextFillRect (cgc, r); // background color
pop_gc (d, gc);
free (flipped);
CGImageRelease (mask);
}
+ invalidate_drawable_cache (d);
+
return 0;
}
unsigned long plane_mask, int format)
{
const unsigned char *data = 0;
- int depth, ibpp, ibpl, alpha_first_p;
+ size_t depth, ibpp, ibpl;
+ convert_mode_t mode;
+# ifndef USE_BACKBUFFER
NSBitmapImageRep *bm = 0;
+# endif
Assert ((width < 65535), "improbably large width");
Assert ((height < 65535), "improbably large height");
Assert ((x < 65535 && x > -65535), "improbably large x");
Assert ((y < 65535 && y > -65535), "improbably large y");
- if (d->type == PIXMAP) {
- depth = d->pixmap.depth;
- alpha_first_p = 1; // we created it with kCGImageAlphaNoneSkipFirst.
- ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
- ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
- data = CGBitmapContextGetData (d->cgc);
+ CGContextRef cgc = d->cgc;
+
+#ifndef USE_BACKBUFFER
+ // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+ if (d->type == PIXMAP)
+# endif
+ {
+ depth = (d->type == PIXMAP
+ ? d->pixmap.depth
+ : 32);
+ mode = convert_mode_to_rgba (dpy->screen->bitmap_info);
+ ibpp = CGBitmapContextGetBitsPerPixel (cgc);
+ ibpl = CGBitmapContextGetBytesPerRow (cgc);
+ data = CGBitmapContextGetData (cgc);
Assert (data, "CGBitmapContextGetData failed");
- } else {
+
+# ifndef USE_BACKBUFFER
+ } else { /* (d->type == WINDOW) */
+
// get the bits (desired sub-rectangle) out of the NSView
NSRect nsfrom;
nsfrom.origin.x = x;
- nsfrom.origin.y = y;
+// nsfrom.origin.y = y;
+ nsfrom.origin.y = d->frame.size.height - height - y;
nsfrom.size.width = width;
nsfrom.size.height = height;
bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
depth = 32;
- alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
+ mode = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? 3 : 0;
ibpp = [bm bitsPerPixel];
ibpl = [bm bytesPerRow];
data = [bm bitmapData];
Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
+# endif // !USE_BACKBUFFER
}
// data points at (x,y) with ibpl rowstride. ignore x,y from now on.
data += (y * ibpl) + (x * (ibpp/8));
format = (depth == 1 ? XYPixmap : ZPixmap);
- XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
- 0, 0);
+ XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
+ format, 0, 0, width, height, 0, 0);
image->data = (char *) malloc (height * image->bytes_per_line);
int obpl = image->bytes_per_line;
iline += ibpl;
}
} else {
- Assert (ibpp == 24 || ibpp == 32, "weird obpp");
const unsigned char *iline = data;
unsigned char *oline = (unsigned char *) image->data;
+
+ mode = convert_mode_merge (mode,
+ convert_mode_invert (
+ convert_mode_to_rgba (dpy->screen->bitmap_info)));
+
for (yy = 0; yy < height; yy++) {
- const unsigned char *iline2 = iline;
- unsigned char *oline2 = oline;
-
- if (alpha_first_p) // ARGB
- for (xx = 0; xx < width; xx++) {
- unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
- unsigned char r = *iline2++;
- unsigned char g = *iline2++;
- unsigned char b = *iline2++;
- uint32_t pixel = ((a << 24) |
- (r << 16) |
- (g << 8) |
- (b << 0));
- *((uint32_t *) oline2) = pixel;
- oline2 += 4;
- }
- else // RGBA
- for (xx = 0; xx < width; xx++) {
- unsigned char r = *iline2++;
- unsigned char g = *iline2++;
- unsigned char b = *iline2++;
- unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
- uint32_t pixel = ((a << 24) |
- (r << 16) |
- (g << 8) |
- (b << 0));
- *((uint32_t *) oline2) = pixel;
- oline2 += 4;
- }
+ convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
oline += obpl;
iline += ibpl;
}
}
+# ifndef USE_BACKBUFFER
if (bm) [bm release];
+# endif
return image;
}
+
/* Returns a transformation matrix to do rotation as per the provided
EXIF "Orientation" value.
*/
XRectangle *geom_ret, int exif_rotation)
{
CGImageRef cgi;
+# ifndef USE_IPHONE
CGImageSourceRef cgsrc;
+# endif // USE_IPHONE
NSSize imgr;
+ CGContextRef cgc = d->cgc;
+
if (nsimg_p) {
NSImage *nsimg = (NSImage *) img_arg;
imgr = [nsimg size];
+# ifndef USE_IPHONE
// convert the NSImage to a CGImage via the toll-free-bridging
// of NSData and CFData...
//
CFDataRef cfdata = (CFDataRef) nsdata;
cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
+# else // USE_IPHONE
+ cgi = nsimg.CGImage;
+# endif // USE_IPHONE
} else {
cgi = (CGImageRef) img_arg;
if (d->type == WINDOW)
XClearWindow (dpy, d);
else {
- set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
+ set_color (dpy, cgc, BlackPixel(dpy,0), 32, NO, YES);
draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
}
CGAffineTransform trans =
exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
- CGContextSaveGState (d->cgc);
- CGContextConcatCTM (d->cgc,
+ CGContextSaveGState (cgc);
+ CGContextConcatCTM (cgc,
CGAffineTransformMakeTranslation (dst.origin.x,
dst.origin.y));
- CGContextConcatCTM (d->cgc, trans);
+ CGContextConcatCTM (cgc, trans);
//Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (d->cgc, dst2, cgi);
- CGContextRestoreGState (d->cgc);
+ CGContextDrawImage (cgc, dst2, cgi);
+ CGContextRestoreGState (cgc);
+# ifndef USE_IPHONE
if (nsimg_p) {
CFRelease (cgsrc);
CGImageRelease (cgi);
}
+# endif // USE_IPHONE
if (geom_ret) {
geom_ret->x = dst.origin.x;
geom_ret->width = dst.size.width;
geom_ret->height = dst.size.height;
}
+
+ invalidate_drawable_cache (d);
}
+
Pixmap
XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
const char *data,
p->frame.size.width = width;
p->frame.size.height = height;
p->pixmap.depth = depth;
+ p->pixmap.cgc_buffer = data;
/* Quartz doesn't have a 1bpp image type.
- We used to use 8bpp gray images instead of 1bpp, but some Mac video
+ Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
don't support that! So we always use 32bpp, regardless of depth. */
p->cgc = CGBitmapContextCreate (data, width, height,
8, /* bits per component */
width * 4, /* bpl */
dpy->colorspace,
- // Without this, it returns 0...
- kCGImageAlphaNoneSkipFirst
- );
+ dpy->screen->bitmap_info);
Assert (p->cgc, "could not create CGBitmapContext");
return p;
}
int
XFreePixmap (Display *d, Pixmap p)
{
- Assert (p->type == PIXMAP, "not a pixmap");
+ Assert (p && p->type == PIXMAP, "not a pixmap");
+ invalidate_drawable_cache (p);
CGContextRelease (p->cgc);
+ if (p->pixmap.cgc_buffer)
+ free (p->pixmap.cgc_buffer);
free (p);
return 0;
}
static Pixmap
-copy_pixmap (Pixmap p)
+copy_pixmap (Display *dpy, Pixmap p)
{
if (!p) return 0;
Assert (p->type == PIXMAP, "not a pixmap");
+
+ int width = p->frame.size.width;
+ int height = p->frame.size.height;
+ char *data = (char *) malloc (width * height * 4);
+ if (! data) return 0;
+
+ memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
+
Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
*p2 = *p;
- CGContextRetain (p2->cgc); // #### is this ok? need to copy it instead?
+ p2->cgi = 0;
+ p2->pixmap.cgc_buffer = data;
+ p2->cgc = CGBitmapContextCreate (data, width, height,
+ 8, /* bits per component */
+ width * 4, /* bpl */
+ dpy->colorspace,
+ dpy->screen->bitmap_info);
+ Assert (p2->cgc, "could not create CGBitmapContext");
+
return p2;
}
+char *
+XGetAtomName (Display *dpy, Atom atom)
+{
+ if (atom == XA_FONT)
+ return strdup ("FONT");
+
+ // Note that atoms (that aren't predefined) are just char *.
+ return strdup ((char *) atom);
+}
+
+
/* Font metric terminology, as used by X11:
- "lbearing" is the distance from the logical origin to the leftmost pixel.
- If a character's ink extends to the left of the origin, it is negative.
+ "lbearing" is the distance from the logical origin to the leftmost pixel.
+ If a character's ink extends to the left of the origin, it is negative.
+
+ "rbearing" is the distance from the logical origin to the rightmost pixel.
+
+ "descent" is the distance from the logical origin to the bottommost pixel.
+ For characters with descenders, it is positive. For superscripts, it
+ is negative.
+
+ "ascent" is the distance from the logical origin to the topmost pixel.
+ It is the number of pixels above the baseline.
+
+ "width" is the distance from the logical origin to the position where
+ the logical origin of the next character should be placed.
+
+ If "rbearing" is greater than "width", then this character overlaps the
+ following character. If smaller, then there is trailing blank space.
+ */
+static void
+utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs)
+{
+ // Returns the metrics of the multi-character, single-line UTF8 string.
+
+ NSFont *nsfont = fid->nsfont;
+ Drawable d = XRootWindow (fid->dpy, 0);
+
+ CGContextRef cgc = d->cgc;
+ NSDictionary *attr =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ nsfont, NSFontAttributeName,
+ nil];
+ NSAttributedString *astr = [[NSAttributedString alloc]
+ initWithString:nsstr
+ attributes:attr];
+ CTLineRef ctline = CTLineCreateWithAttributedString (
+ (__bridge CFAttributedStringRef) astr);
+ CGContextSetTextPosition (cgc, 0, 0);
+ CGContextSetShouldAntialias (cgc, True); // #### Guess?
+
+ memset (cs, 0, sizeof(*cs));
+
+ // "CTRun represents set of consecutive glyphs sharing the same
+ // attributes and direction".
+ //
+ // We also get multiple runs any time font subsitution happens:
+ // E.g., if the current font is Verdana-Bold, a ← character
+ // in the NSString will actually be rendered in LucidaGrande-Bold.
+ //
+ int count = 0;
+ for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
+ CTRunRef run = (CTRunRef) runid;
+ CFRange r = { 0, };
+ CGRect bbox = CTRunGetImageBounds (run, cgc, r);
+ CGFloat ascent, descent, leading;
+ CGFloat advancement =
+ CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
+
+# ifndef USE_IPHONE
+ // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
+ bbox.origin.x -= 2.0/3.0;
+ bbox.size.width += 4.0/3.0;
+ bbox.size.height += 1.0/2.0;
+# endif
- "rbearing" is the distance from the logical origin to the rightmost pixel.
+ // Create the metrics for this run:
+ XCharStruct cc;
+ cc.ascent = ceil (bbox.origin.y + bbox.size.height);
+ cc.descent = ceil (-bbox.origin.y);
+ cc.lbearing = floor (bbox.origin.x);
+ cc.rbearing = ceil (bbox.origin.x + bbox.size.width);
+ cc.width = floor (advancement + 0.5);
+
+ // Add those metrics into the cumulative metrics:
+ if (count == 0)
+ *cs = cc;
+ else
+ {
+ cs->ascent = MAX (cs->ascent, cc.ascent);
+ cs->descent = MAX (cs->descent, cc.descent);
+ cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing);
+ cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing);
+ cs->width = MAX (cs->width, cs->width + cc.width);
+ }
- "descent" is the distance from the logical origin to the bottommost pixel.
- For characters with descenders, it is negative.
+ // Why no y? What about vertical text?
+ // XCharStruct doesn't encapsulate that but XGlyphInfo does.
- "ascent" is the distance from the logical origin to the topmost pixel.
- It is the number of pixels above the baseline.
+ count++;
+ }
- "width" is the distance from the logical origin to the position where
- the logical origin of the next character should be placed.
+ CFRelease (ctline);
+}
- If "rbearing" is greater than "width", then this character overlaps the
- following character. If smaller, then there is trailing blank space.
- */
// This is XQueryFont, but for the XFontStruct embedded in 'Font'
query_font (Font fid)
{
if (!fid || !fid->nsfont) {
- NSLog(@"no NSFont in fid");
- abort();
+ Assert (0, "no NSFont in fid");
+ return;
}
if (![fid->nsfont fontName]) {
- NSLog(@"broken NSFont in fid");
- abort();
+ Assert(0, @"broken NSFont in fid");
+ return;
}
int first = 32;
XCharStruct *min = &f->min_bounds;
XCharStruct *max = &f->max_bounds;
-#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
-
f->fid = fid;
f->min_char_or_byte2 = first;
f->max_char_or_byte2 = last;
f->default_char = 'M';
- f->ascent = CEIL ([fid->nsfont ascender]);
- f->descent = -CEIL ([fid->nsfont descender]);
+ f->ascent = ceil ([fid->nsfont ascender]);
+ f->descent = -floor ([fid->nsfont descender]);
- min->width = 255; // set to smaller values in the loop
- min->ascent = 255;
- min->descent = 255;
- min->lbearing = 255;
- min->rbearing = 255;
+ min->width = 32767; // set to smaller values in the loop
+ min->ascent = 32767;
+ min->descent = 32767;
+ min->lbearing = 32767;
+ min->rbearing = 32767;
f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
- int i;
-
- NSBezierPath *bpath = [NSBezierPath bezierPath];
-
- for (i = first; i <= last; i++) {
- unsigned char str[2];
- str[0] = i;
- str[1] = 0;
-
- NSString *nsstr = [NSString stringWithCString:(char *) str
- encoding:NSISOLatin1StringEncoding];
- /* I can't believe we have to go through this bullshit just to
- convert a 'char' to an NSGlyph!!
-
- You might think that we could do
- NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
- but that doesn't work; my guess is that glyphWithName expects
- full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
- */
- NSGlyph glyph;
- {
- NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
- [ts setFont:fid->nsfont];
- NSLayoutManager *lm = [[NSLayoutManager alloc] init];
-
- /* Without this, the layout manager ends up on a queue somewhere and is
- referenced again after we return to the command loop. Since we don't
- use this layout manager again, by that time it may have been garbage
- collected, and we crash. Setting this seems to cause `lm' to no
- longer be referenced once we exit this block. */
- [lm setBackgroundLayoutEnabled:NO];
-
- NSTextContainer *tc = [[NSTextContainer alloc] init];
- [lm addTextContainer:tc];
- [tc release]; // lm retains tc
- [ts addLayoutManager:lm];
- [lm release]; // ts retains lm
- glyph = [lm glyphAtIndex:0];
- [ts release];
- }
-
- /* Compute the bounding box and advancement by converting the glyph
- to a bezier path. There appears to be *no other way* to find out
- the bounding box of a character: [NSFont boundingRectForGlyph] and
- [NSString sizeWithAttributes] both return an advancement-sized
- rectangle, not a rectangle completely enclosing the glyph's ink.
- */
- NSPoint advancement;
- NSRect bbox;
- advancement.x = advancement.y = 0;
- [bpath removeAllPoints];
- [bpath moveToPoint:advancement];
- [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
- advancement = [bpath currentPoint];
- bbox = [bpath bounds];
-
- /* Now that we know the advancement and bounding box, we can compute
- the lbearing and rbearing.
- */
+ for (int i = first; i <= last; i++) {
XCharStruct *cs = &f->per_char[i-first];
- cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
- cs->descent = CEIL(-bbox.origin.y);
- cs->lbearing = CEIL (bbox.origin.x);
- cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
- cs->width = CEIL (advancement.x);
-
- Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
- "bbox w wrong");
- Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
- "bbox h wrong");
+ char s2[2];
+ s2[0] = i;
+ s2[1] = 0;
+ NSString *nsstr = [NSString stringWithCString:s2
+ encoding:NSISOLatin1StringEncoding];
+ utf8_metrics (fid, nsstr, cs);
max->width = MAX (max->width, cs->width);
max->ascent = MAX (max->ascent, cs->ascent);
min->lbearing = MIN (min->lbearing, cs->lbearing);
min->rbearing = MIN (min->rbearing, cs->rbearing);
-# undef CEIL
-
-#if 0
+# if 0
fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
- " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
+ " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
i, i, cs->width, cs->lbearing, cs->rbearing,
cs->ascent, cs->descent,
- (int) bbox.size.width, (int) bbox.size.height,
- (int) bbox.origin.x, (int) bbox.origin.y,
- (int) advancement.x, (int) advancement.y);
-#endif
+ bbox.size.width, bbox.size.height,
+ bbox.origin.x, bbox.origin.y,
+ advancement.width, advancement.height);
+# endif // 0
}
-
}
XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
*f = fid->metrics;
+ // build XFontProps
+ f->n_properties = 1;
+ f->properties = malloc (sizeof(*f->properties) * f->n_properties);
+ f->properties[0].name = XA_FONT;
+ Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
+ "atoms probably needs a real implementation");
+ // If XInternAtom is ever implemented, use it here.
+ f->properties[0].card32 = (char *)fid->xa_font;
+
// copy XCharStruct array
- int size = f->max_char_or_byte2 - f->min_char_or_byte2;
+ int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
memcpy (f->per_char, fid->metrics.per_char,
size * sizeof (XCharStruct));
// copy the other pointers
fid2->ps_name = strdup (fid->ps_name);
+ fid2->xa_font = strdup (fid->xa_font);
// [fid2->nsfont retain];
fid2->metrics.fid = fid2;
}
+static NSArray *
+font_family_members (NSString *family_name)
+{
+# ifndef USE_IPHONE
+ return [[NSFontManager sharedFontManager]
+ availableMembersOfFontFamily:family_name];
+# else
+ return [UIFont fontNamesForFamilyName:family_name];
+# endif
+}
+
+
+static NSString *
+default_font_family (NSFontTraitMask require)
+{
+ return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
+}
+
+
static NSFont *
-try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
+try_font (NSFontTraitMask traits, NSFontTraitMask mask,
+ NSString *family_name, float size,
char **name_ret)
{
Assert (size > 0, "zero font size");
- const char *name;
-
- if (fixed) {
- //
- // "Monaco" only exists in plain.
- // "LucidaSansTypewriterStd" gets an AGL bad value error.
- //
- if (bold && ital) name = "Courier-BoldOblique";
- else if (bold) name = "Courier-Bold";
- else if (ital) name = "Courier-Oblique";
- else name = "Courier";
-
- } else if (serif) {
- //
- // "Georgia" looks better than "Times".
- //
- if (bold && ital) name = "Georgia-BoldItalic";
- else if (bold) name = "Georgia-Bold";
- else if (ital) name = "Georgia-Italic";
- else name = "Georgia";
- } else {
- //
- // "Geneva" only exists in plain.
- // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
- // "Verdana" renders smoother than "Helvetica" for some reason.
- //
- if (bold && ital) name = "Verdana-BoldItalic";
- else if (bold) name = "Verdana-Bold";
- else if (ital) name = "Verdana-Italic";
- else name = "Verdana";
- }
-
- NSString *nsname = [NSString stringWithCString:name
- encoding:NSUTF8StringEncoding];
- NSFont *f = [NSFont fontWithName:nsname size:size];
- if (f)
- *name_ret = strdup(name);
- return f;
+ NSArray *family_members = font_family_members (family_name);
+ if (!family_members.count)
+ family_members = font_family_members (default_font_family (traits));
+
+# ifndef USE_IPHONE
+ for (unsigned k = 0; k != family_members.count; ++k) {
+
+ NSArray *member = [family_members objectAtIndex:k];
+ NSFontTraitMask font_mask =
+ [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
+
+ if ((font_mask & mask) == traits) {
+
+ NSString *name = [member objectAtIndex:0];
+ NSFont *f = [NSFont fontWithName:name size:size];
+ if (!f)
+ break;
+
+ /* Don't use this font if it (probably) doesn't include ASCII characters.
+ */
+ NSStringEncoding enc = [f mostCompatibleStringEncoding];
+ if (! (enc == NSUTF8StringEncoding ||
+ enc == NSISOLatin1StringEncoding ||
+ enc == NSNonLossyASCIIStringEncoding ||
+ enc == NSISOLatin2StringEncoding ||
+ enc == NSUnicodeStringEncoding ||
+ enc == NSWindowsCP1250StringEncoding ||
+ enc == NSWindowsCP1252StringEncoding ||
+ enc == NSMacOSRomanStringEncoding)) {
+ // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
+ break;
+ }
+ // NSLog(@"using \"%@\": %d", name, enc);
+
+ // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
+ *name_ret = strdup (name.UTF8String);
+ return f;
+ }
+ }
+# else // USE_IPHONE
+
+ for (NSString *fn in family_members) {
+# define MATCH(X) \
+ ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
+ != NSNotFound)
+
+ // The magic invocation for getting font names is
+ // [[UIFontDescriptor
+ // fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
+ // symbolicTraits]
+ // ...but this only works on iOS 7 and later.
+ NSFontTraitMask font_mask = 0;
+ if (MATCH(@"Bold"))
+ font_mask |= NSBoldFontMask;
+ if (MATCH(@"Italic") || MATCH(@"Oblique"))
+ font_mask |= NSItalicFontMask;
+
+ if ((font_mask & mask) == traits) {
+
+ /* Check if it can do ASCII. No good way to accomplish this!
+ These are fonts present in iPhone Simulator as of June 2012
+ that don't include ASCII.
+ */
+ if (MATCH(@"AppleGothic") || // Korean
+ MATCH(@"Dingbats") || // Dingbats
+ MATCH(@"Emoji") || // Emoticons
+ MATCH(@"Geeza") || // Arabic
+ MATCH(@"Hebrew") || // Hebrew
+ MATCH(@"HiraKaku") || // Japanese
+ MATCH(@"HiraMin") || // Japanese
+ MATCH(@"Kailasa") || // Tibetan
+ MATCH(@"Ornaments") || // Dingbats
+ MATCH(@"STHeiti") // Chinese
+ )
+ break;
+
+ *name_ret = strdup (fn.UTF8String);
+ return [UIFont fontWithName:fn size:size];
+ }
+# undef MATCH
+ }
+
+# endif
+
+ return NULL;
}
static NSFont *
-try_native_font (const char *name, char **name_ret, float *size_ret)
+try_native_font (const char *name, float scale,
+ char **name_ret, float *size_ret, char **xa_font)
{
if (!name) return 0;
const char *spc = strrchr (name, ' ');
if (!spc) return 0;
- int size = 0;
- if (1 != sscanf (spc, " %d ", &size)) return 0;
+ int dsize = 0;
+ if (1 != sscanf (spc, " %d ", &dsize)) return 0;
+ float size = dsize;
+
if (size <= 4) return 0;
+ size *= scale;
+
char *name2 = strdup (name);
name2[strlen(name2) - strlen(spc)] = 0;
NSString *nsname = [NSString stringWithCString:name2
if (f) {
*name_ret = name2;
*size_ret = size;
+ *xa_font = strdup (name); // Maybe this should be an XLFD?
return f;
} else {
free (name2);
/* Returns a random font in the given size and face.
*/
static NSFont *
-random_font (BOOL bold, BOOL ital, float size, char **name_ret)
+random_font (NSFontTraitMask traits, NSFontTraitMask mask,
+ float size, NSString **family_ret, char **name_ret)
{
- NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
- (ital ? NSItalicFontMask : NSUnitalicFontMask));
- NSArray *fonts = [[NSFontManager sharedFontManager]
- availableFontNamesWithTraits:mask];
- if (!fonts) return 0;
- int n = [fonts count];
+# ifndef USE_IPHONE
+ // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
+ // returns an empty list, at least on a system with default fonts only.
+ NSArray *families = [[NSFontManager sharedFontManager]
+ availableFontFamilies];
+ if (!families) return 0;
+# else
+ NSArray *families = [UIFont familyNames];
+
+ // There are many dups in the families array -- uniquify it.
+ {
+ NSArray *sorted_families =
+ [families sortedArrayUsingSelector:@selector(compare:)];
+ NSMutableArray *new_families =
+ [NSMutableArray arrayWithCapacity:sorted_families.count];
+
+ NSString *prev_family = nil;
+ for (NSString *family in sorted_families) {
+ if ([family compare:prev_family])
+ [new_families addObject:family];
+ }
+
+ families = new_families;
+ }
+# endif // USE_IPHONE
+
+ long n = [families count];
if (n <= 0) return 0;
int j;
for (j = 0; j < n; j++) {
int i = random() % n;
- NSString *name = [fonts objectAtIndex:i];
- NSFont *f = [NSFont fontWithName:name size:size];
- if (!f) continue;
-
- /* Don't use this font if it (probably) doesn't include ASCII characters.
- */
- NSStringEncoding enc = [f mostCompatibleStringEncoding];
- if (! (enc == NSUTF8StringEncoding ||
- enc == NSISOLatin1StringEncoding ||
- enc == NSNonLossyASCIIStringEncoding ||
- enc == NSISOLatin2StringEncoding ||
- enc == NSUnicodeStringEncoding ||
- enc == NSWindowsCP1250StringEncoding ||
- enc == NSWindowsCP1252StringEncoding ||
- enc == NSMacOSRomanStringEncoding)) {
- // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
- continue;
+ NSString *family_name = [families objectAtIndex:i];
+
+ NSFont *result = try_font (traits, mask, family_name, size, name_ret);
+ if (result) {
+ [*family_ret release];
+ *family_ret = family_name;
+ [*family_ret retain];
+ return result;
}
- // NSLog(@"using \"%@\": %d", name, enc);
-
- *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
- return f;
}
// None of the fonts support ASCII?
}
+// Fonts need this. XDisplayHeightMM and friends should probably be consistent
+// with this as well if they're ever implemented.
+static const unsigned dpi = 75;
+
+
+static const char *
+xlfd_field_end (const char *s)
+{
+ const char *s2 = strchr(s, '-');
+ if (!s2)
+ s2 = s + strlen(s);
+ return s2;
+}
+
+
+static size_t
+xlfd_next (const char **s, const char **s2)
+{
+ if (!**s2) {
+ *s = *s2;
+ } else {
+ Assert (**s2 == '-', "xlfd parse error");
+ *s = *s2 + 1;
+ *s2 = xlfd_field_end (*s);
+ }
+
+ return *s2 - *s;
+}
+
+
static NSFont *
-try_xlfd_font (const char *name, char **name_ret, float *size_ret)
+try_xlfd_font (const char *name, float scale,
+ char **name_ret, float *size_ret, char **xa_font)
{
NSFont *nsfont = 0;
- BOOL bold = NO;
- BOOL ital = NO;
- BOOL fixed = NO;
- BOOL serif = NO;
+ NSString *family_name = nil;
+ NSFontTraitMask require = 0, forbid = 0;
BOOL rand = NO;
float size = 0;
char *ps_name = 0;
const char *s = (name ? name : "");
- while (*s) {
- while (*s && (*s == '*' || *s == '-'))
- s++;
- const char *s2 = s;
- while (*s2 && (*s2 != '*' && *s2 != '-'))
- s2++;
-
- int L = s2-s;
- if (s == s2)
- ;
+
+ size_t L = strlen (s);
# define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
- else if (CMP ("random")) rand = YES;
- else if (CMP ("bold")) bold = YES;
- else if (CMP ("i")) ital = YES;
- else if (CMP ("o")) ital = YES;
- else if (CMP ("courier")) fixed = YES;
- else if (CMP ("fixed")) fixed = YES;
- else if (CMP ("m")) fixed = YES;
- else if (CMP ("times")) serif = YES;
- else if (CMP ("6x10")) fixed = YES, size = 8;
- else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
- else if (CMP ("9x15")) fixed = YES, size = 12;
- else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
- else if (CMP ("vga")) fixed = YES, size = 12;
- else if (CMP ("console")) fixed = YES, size = 12;
- else if (CMP ("gallant")) fixed = YES, size = 12;
-# undef CMP
- else if (size == 0) {
- int n = 0;
- if (1 == sscanf (s, " %d ", &n))
- size = n / 10.0;
+# define UNSPEC (L == 0 || L == 1 && *s == '*')
+ if (CMP ("6x10")) size = 8, require |= NSFixedPitchFontMask;
+ else if (CMP ("6x10bold")) size = 8, require |= NSFixedPitchFontMask | NSBoldFontMask;
+ else if (CMP ("fixed")) size = 12, require |= NSFixedPitchFontMask;
+ else if (CMP ("9x15")) size = 12, require |= NSFixedPitchFontMask;
+ else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
+ else if (CMP ("vga")) size = 12, require |= NSFixedPitchFontMask;
+ else if (CMP ("console")) size = 12, require |= NSFixedPitchFontMask;
+ else if (CMP ("gallant")) size = 12, require |= NSFixedPitchFontMask;
+ else {
+
+ // Incorrect fields are ignored.
+
+ if (*s == '-')
+ ++s;
+ const char *s2 = xlfd_field_end(s);
+
+ // Foundry (ignore)
+
+ L = xlfd_next (&s, &s2); // Family name
+ // This used to substitute Georgia for Times. Now it doesn't.
+ if (CMP ("random")) {
+ rand = YES;
+ } else if (CMP ("fixed")) {
+ require |= NSFixedPitchFontMask;
+ family_name = @"Courier";
+ } else if (!UNSPEC) {
+ family_name = [[[NSString alloc] initWithBytes:s
+ length:L
+ encoding:NSUTF8StringEncoding]
+ autorelease];
}
- s = s2;
+ L = xlfd_next (&s, &s2); // Weight name
+ if (CMP ("bold") || CMP ("demibold"))
+ require |= NSBoldFontMask;
+ else if (CMP ("medium") || CMP ("regular"))
+ forbid |= NSBoldFontMask;
+
+ L = xlfd_next (&s, &s2); // Slant
+ if (CMP ("i") || CMP ("o"))
+ require |= NSItalicFontMask;
+ else if (CMP ("r"))
+ forbid |= NSItalicFontMask;
+
+ xlfd_next (&s, &s2); // Set width name (ignore)
+ xlfd_next (&s, &s2); // Add style name (ignore)
+
+ xlfd_next (&s, &s2); // Pixel size (ignore)
+
+ xlfd_next (&s, &s2); // Point size
+ char *s3;
+ uintmax_t n = strtoumax(s, &s3, 10);
+ if (s2 == s3)
+ size = n / 10.0;
+
+ xlfd_next (&s, &s2); // Resolution X (ignore)
+ xlfd_next (&s, &s2); // Resolution Y (ignore)
+
+ xlfd_next (&s, &s2); // Spacing
+ if (CMP ("p"))
+ forbid |= NSFixedPitchFontMask;
+ else if (CMP ("m") || CMP ("c"))
+ require |= NSFixedPitchFontMask;
+
+ // Don't care about average_width or charset registry.
}
+# undef CMP
+# undef UNSPEC
+
+ if (!family_name && !rand)
+ family_name = default_font_family (require);
if (size < 6 || size > 1000)
size = 12;
- if (rand)
- nsfont = random_font (bold, ital, size, &ps_name);
+ size *= scale;
+
+ NSFontTraitMask mask = require | forbid;
+
+ if (rand) {
+ nsfont = random_font (require, mask, size, &family_name, &ps_name);
+ [family_name autorelease];
+ }
if (!nsfont)
- nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
+ nsfont = try_font (require, mask, family_name, size, &ps_name);
// if that didn't work, turn off attibutes until it does
// (e.g., there is no "Monaco-Bold".)
//
- if (!nsfont && serif) {
- serif = NO;
- nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
- }
- if (!nsfont && ital) {
- ital = NO;
- nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
+ if (!nsfont && (mask & NSItalicFontMask)) {
+ require &= ~NSItalicFontMask;
+ mask &= ~NSItalicFontMask;
+ nsfont = try_font (require, mask, family_name, size, &ps_name);
}
- if (!nsfont && bold) {
- bold = NO;
- nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
+ if (!nsfont && (mask & NSBoldFontMask)) {
+ require &= ~NSBoldFontMask;
+ mask &= ~NSBoldFontMask;
+ nsfont = try_font (require, mask, family_name, size, &ps_name);
}
- if (!nsfont && fixed) {
- fixed = NO;
- nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
+ if (!nsfont && (mask & NSFixedPitchFontMask)) {
+ require &= ~NSFixedPitchFontMask;
+ mask &= ~NSFixedPitchFontMask;
+ nsfont = try_font (require, mask, family_name, size, &ps_name);
}
if (nsfont) {
*name_ret = ps_name;
*size_ret = size;
+ float actual_size = size / scale;
+ asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
+ family_name.UTF8String,
+ (require & NSBoldFontMask) ? "bold" : "medium",
+ (require & NSItalicFontMask) ? 'o' : 'r',
+ (unsigned)(dpi * actual_size / 72.27 + 0.5),
+ (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
+ (require & NSFixedPitchFontMask) ? 'm' : 'p');
return nsfont;
} else {
return 0;
{
Font fid = (Font) calloc (1, sizeof(*fid));
- fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
+ float scale = 1;
+
+# ifdef USE_IPHONE
+ /* Since iOS screens are physically smaller than desktop screens, scale up
+ the fonts to make them more readable.
+
+ Note that X11 apps on iOS also have the backbuffer sized in points
+ instead of pixels, resulting in an effective X11 screen size of 768x1024
+ or so, even if the display has significantly higher resolution. That is
+ unrelated to this hack, which is really about DPI.
+ */
+ scale = 2;
+# endif
+
+ fid->dpy = dpy;
+ fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
+ &fid->xa_font);
+
+ if (!fid->nsfont && name &&
+ strchr (name, ' ') &&
+ !strchr (name, '*')) {
+ // If name contains a space but no stars, it is a native font spec --
+ // return NULL so that we know it really didn't exist. Else, it is an
+ // XLFD font, so keep trying.
+ XUnloadFont (dpy, fid);
+ return 0;
+ }
+
if (! fid->nsfont)
- fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
+ fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size,
+ &fid->xa_font);
+
+ // We should never return NULL for XLFD fonts.
if (!fid->nsfont) {
- NSLog(@"no NSFont for \"%s\"", name);
- abort();
+ Assert (0, "no font");
+ return 0;
}
CFRetain (fid->nsfont); // needed for garbage collection?
XLoadQueryFont (Display *dpy, const char *name)
{
Font fid = XLoadFont (dpy, name);
+ if (!fid) return 0;
return XQueryFont (dpy, fid);
}
int
XUnloadFont (Display *dpy, Font fid)
{
- free (fid->ps_name);
- free (fid->metrics.per_char);
+ if (fid->ps_name)
+ free (fid->ps_name);
+ if (fid->metrics.per_char)
+ free (fid->metrics.per_char);
// #### DAMMIT! I can't tell what's going wrong here, but I keep getting
// crashes in [NSFont ascender] <- query_font, and it seems to go away
}
if (info) {
for (i = 0; i < n; i++)
- if (info[i].per_char)
+ if (info[i].per_char) {
free (info[i].per_char);
+ free (info[i].properties);
+ }
free (info);
}
return 0;
return 0;
}
+
+XFontSet
+XCreateFontSet (Display *dpy, char *name,
+ char ***missing_charset_list_return,
+ int *missing_charset_count_return,
+ char **def_string_return)
+{
+ char *name2 = strdup (name);
+ char *s = strchr (name, ",");
+ if (s) *s = 0;
+ XFontSet set = 0;
+ XFontStruct *f = XLoadQueryFont (dpy, name2);
+ if (f)
+ {
+ set = (XFontSet) calloc (1, sizeof(*set));
+ set->font = f;
+ }
+ free (name2);
+ if (missing_charset_list_return) *missing_charset_list_return = 0;
+ if (missing_charset_count_return) *missing_charset_count_return = 0;
+ if (def_string_return) *def_string_return = 0;
+ return set;
+}
+
+
+void
+XFreeFontSet (Display *dpy, XFontSet set)
+{
+ XFreeFont (dpy, set->font);
+ free (set);
+}
+
+
+const char *
+jwxyz_nativeFontName (Font f, float *size)
+{
+ if (size) *size = f->size;
+ return f->ps_name;
+}
+
+
+void
+XFreeStringList (char **list)
+{
+ int i;
+ if (!list) return;
+ for (i = 0; list[i]; i++)
+ XFree (list[i]);
+ XFree (list);
+}
+
+
+// Returns the verbose Unicode name of this character, like "agrave" or
+// "daggerdouble". Used by fontglide debugMetrics.
+//
+char *
+jwxyz_unicode_character_name (Font fid, unsigned long uc)
+{
+ char *ret = 0;
+ CTFontRef ctfont =
+ CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+ [fid->nsfont pointSize],
+ NULL);
+ Assert (ctfont, @"no CTFontRef for UIFont");
+
+ CGGlyph cgglyph;
+ if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
+ NSString *name = (NSString *)
+ CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
+ cgglyph);
+ ret = (name ? strdup ([name UTF8String]) : 0);
+ }
+
+ CFRelease (ctfont);
+ return ret;
+}
+
+
+// Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored.
+// We have to do this because stringWithCString returns NULL if there are
+// any invalid characters at all.
+//
+static NSString *
+sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
+{
+ int out_len = in_len * 4; // length of string might increase
+ char *s2 = (char *) malloc (out_len);
+ char *out = s2;
+ const char *in_end = in + in_len;
+ const char *out_end = out + out_len;
+ Bool latin1_p = True;
+
+ while (in < in_end)
+ {
+ unsigned long uc;
+ long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
+ long L2 = utf8_encode (uc, out, out_end - out);
+ in += L1;
+ out += L2;
+ if (uc > 255) latin1_p = False;
+ }
+ *out = 0;
+ NSString *nsstr =
+ [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
+ free (s2);
+ if (latin1_pP) *latin1_pP = latin1_p;
+ return (nsstr ? nsstr : @"");
+}
+
+
int
XTextExtents (XFontStruct *f, const char *s, int length,
int *dir_ret, int *ascent_ret, int *descent_ret,
XCharStruct *cs)
{
- memset (cs, 0, sizeof(*cs));
- int i;
- for (i = 0; i < length; i++) {
- unsigned char c = (unsigned char) s[i];
- if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
- c = f->default_char;
- const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
- if (i == 0) {
- *cs = *cc;
- } else {
- cs->ascent = MAX (cs->ascent, cc->ascent);
- cs->descent = MAX (cs->descent, cc->descent);
- cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
- cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
- cs->width += cc->width;
- }
- }
+ // Unfortunately, adding XCharStructs together to get the extents for a
+ // string doesn't work: Cocoa uses non-integral character advancements, but
+ // XCharStruct.width is an integer. Plus that doesn't take into account
+ // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
+ // Zapfino.
+
+ NSString *nsstr = [[[NSString alloc] initWithBytes:s
+ length:length
+ encoding:NSISOLatin1StringEncoding]
+ autorelease];
+ utf8_metrics (f->fid, nsstr, cs);
*dir_ret = 0;
*ascent_ret = f->ascent;
*descent_ret = f->descent;
}
-static void
-set_font (CGContextRef cgc, GC gc)
+int
+XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
+ int *dir_ret, int *ascent_ret, int *descent_ret,
+ XCharStruct *cs)
{
- Font font = gc->gcv.font;
- if (! font) {
- font = XLoadFont (0, 0);
- gc->gcv.font = font;
- [gc->gcv.font->nsfont retain];
- CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
+ Bool latin1_p = True;
+ int i, utf8_len = 0;
+ char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized
+
+ for (i = 0; i < length; i++)
+ if (s[i].byte1 > 0) {
+ latin1_p = False;
+ break;
+ }
+
+ {
+ NSString *nsstr = [NSString stringWithCString:utf8
+ encoding:NSUTF8StringEncoding];
+ utf8_metrics (f->fid, nsstr, cs);
}
- CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
- CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+
+ *dir_ret = 0;
+ *ascent_ret = f->ascent;
+ *descent_ret = f->descent;
+ free (utf8);
+ return 0;
}
-static int
-draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
- const char *str, int len, BOOL clear_background_p)
-{
- if (clear_background_p) {
- int ascent, descent, dir;
- XCharStruct cs;
- XTextExtents (&gc->gcv.font->metrics, str, len,
- &dir, &ascent, &descent, &cs);
- draw_rect (dpy, d, gc,
- x + MIN (0, cs.lbearing),
- y - MAX (0, ascent),
- MAX (MAX (0, cs.rbearing) -
- MIN (0, cs.lbearing),
- cs.width),
- MAX (0, ascent) + MAX (0, descent),
- NO, YES);
- }
+/* "Returns the distance in pixels in the primary draw direction from
+ the drawing origin to the origin of the next character to be drawn."
- CGRect wr = d->frame;
+ "overall_ink_return is set to the bbox of the string's character ink."
-# if 1
- /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
- But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
- */
+ "The overall_ink_return for a nondescending, horizontally drawn Latin
+ character is conventionally entirely above the baseline; that is,
+ overall_ink_return.height <= -overall_ink_return.y."
- push_fg_gc (d, gc, YES);
- set_font (d->cgc, gc);
+ [So this means that y is the top of the ink, and height grows down:
+ For above-the-baseline characters, y is negative.]
- CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
- if (gc->gcv.antialias_p)
- CGContextSetShouldAntialias (d->cgc, YES);
- CGContextShowTextAtPoint (d->cgc,
- wr.origin.x + x,
- wr.origin.y + wr.size.height - y,
- str, len);
- pop_gc (d, gc);
+ "The overall_ink_return for a nonkerned character is entirely at, and to
+ the right of, the origin; that is, overall_ink_return.x >= 0."
+
+ [So this means that x is the left of the ink, and width grows right.
+ For left-of-the-origin characters, x is negative.]
+
+ "A character consisting of a single pixel at the origin would set
+ overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
+ */
+int
+Xutf8TextExtents (XFontSet set, const char *str, int len,
+ XRectangle *overall_ink_return,
+ XRectangle *overall_logical_return)
+{
+ Bool latin1_p;
+ NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
+ XCharStruct cs;
+
+ utf8_metrics (set->font->fid, nsstr, &cs);
-#else /* !0 */
+ /* "The overall_logical_return is the bounding box that provides minimum
+ spacing to other graphical features for the string. Other graphical
+ features, for example, a border surrounding the text, should not
+ intersect this rectangle."
- /* The Cocoa way...
+ So I think that means they're the same? Or maybe "ink" is the bounding
+ box, and "logical" is the advancement? But then why is the return value
+ the advancement?
*/
+ if (overall_ink_return)
+ XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
+ if (overall_logical_return)
+ XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
+
+ return cs.width;
+}
+
+
+static int
+draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
+ NSString *nsstr)
+{
+ if (! nsstr) return 1;
+
+ CGRect wr = d->frame;
+ CGContextRef cgc = d->cgc;
unsigned long argb = gc->gcv.foreground;
if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
- float a = ((argb >> 24) & 0xFF) / 255.0;
- float r = ((argb >> 16) & 0xFF) / 255.0;
- float g = ((argb >> 8) & 0xFF) / 255.0;
- float b = ((argb ) & 0xFF) / 255.0;
- NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
+ float rgba[4];
+ query_color_float (dpy, argb, rgba);
+ NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
+ green:rgba[1]
+ blue:rgba[2]
+ alpha:rgba[3]];
+
+ if (!gc->gcv.font) {
+ Assert (0, "no font");
+ return 1;
+ }
+
+ /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
+ NSFontAttributeName, and NSAttributedString are only present on iOS 6
+ and later. We could resurrect the Quartz code from v5.29 and do a
+ runtime conditional on that, but that would be a pain in the ass.
+ Probably time to just make iOS 6 a requirement.
+ */
+
NSDictionary *attr =
[NSDictionary dictionaryWithObjectsAndKeys:
gc->gcv.font->nsfont, NSFontAttributeName,
fg, NSForegroundColorAttributeName,
nil];
+
+ // Don't understand why we have to do both set_color and
+ // NSForegroundColorAttributeName, but we do.
+ //
+ set_color (dpy, cgc, argb, 32, NO, YES);
+
+ NSAttributedString *astr = [[NSAttributedString alloc]
+ initWithString:nsstr
+ attributes:attr];
+ CTLineRef dl = CTLineCreateWithAttributedString (
+ (__bridge CFAttributedStringRef) astr);
+
+ // Not sure why this is necessary, but xoff is positive when the first
+ // character on the line has a negative lbearing. Without this, the
+ // string is rendered with the first ink at 0 instead of at lbearing.
+ // I have not seen xoff be negative, so I'm not sure if that can happen.
+ //
+ // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
+ // a letter.
+ //
+ CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
+ Assert (xoff >= 0, "unexpected CTLineOffset");
+ x -= xoff;
+
+ CGContextSetTextPosition (cgc,
+ wr.origin.x + x,
+ wr.origin.y + wr.size.height - y);
+ CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
+
+ CTLineDraw (dl, cgc);
+ CFRelease (dl);
+
+ invalidate_drawable_cache (d);
+ return 0;
+}
+
+
+int
+XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
+ const char *str, int len)
+{
char *s2 = (char *) malloc (len + 1);
strncpy (s2, str, len);
s2[len] = 0;
NSString *nsstr = [NSString stringWithCString:s2
- encoding:NSISOLatin1StringEncoding];
+ encoding:NSISOLatin1StringEncoding];
+ int ret = draw_string (dpy, d, gc, x, y, nsstr);
free (s2);
- NSPoint pos;
- pos.x = wr.origin.x + x;
- pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
- [nsstr drawAtPoint:pos withAttributes:attr];
+ return ret;
+}
-#endif /* 0 */
- return 0;
+int
+XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
+ const XChar2b *str, int len)
+{
+ char *s2 = XChar2b_to_utf8 (str, 0); // already sanitized
+ NSString *nsstr =
+ [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
+ int ret = draw_string (dpy, d, gc, x, y, nsstr);
+ free (s2);
+ return ret;
}
-int
-XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
- const char *str, int len)
+void
+Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
+ int x, int y, const char *str, int len)
{
- return draw_string (dpy, d, gc, x, y, str, len, NO);
+ char *s2 = (char *) malloc (len + 1);
+ strncpy (s2, str, len);
+ s2[len] = 0;
+ NSString *nsstr = sanitize_utf8 (str, len, 0);
+ draw_string (dpy, d, gc, x, y, nsstr);
+ free (s2);
}
+
int
XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
const char *str, int len)
{
- return draw_string (dpy, d, gc, x, y, str, len, YES);
+ int ascent, descent, dir;
+ XCharStruct cs;
+ XTextExtents (&gc->gcv.font->metrics, str, len,
+ &dir, &ascent, &descent, &cs);
+ draw_rect (dpy, d, gc,
+ x + MIN (0, cs.lbearing),
+ y - MAX (0, ascent),
+ MAX (MAX (0, cs.rbearing) -
+ MIN (0, cs.lbearing),
+ cs.width),
+ MAX (0, ascent) + MAX (0, descent),
+ NO, YES);
+ return XDrawString (dpy, d, gc, x, y, str, len);
}
int
XSetForeground (Display *dpy, GC gc, unsigned long fg)
{
- validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
+ validate_pixel (dpy, fg, gc->depth, gc->gcv.alpha_allowed_p);
gc->gcv.foreground = fg;
return 0;
}
int
XSetBackground (Display *dpy, GC gc, unsigned long bg)
{
- validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
+ validate_pixel (dpy, bg, gc->depth, gc->gcv.alpha_allowed_p);
gc->gcv.background = bg;
return 0;
}
CGImageRelease (gc->clip_mask);
}
- gc->gcv.clip_mask = copy_pixmap (m);
+ gc->gcv.clip_mask = copy_pixmap (dpy, m);
if (gc->gcv.clip_mask)
- gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
+ gc->clip_mask =
+ CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
else
gc->clip_mask = 0;
int *root_x_ret, int *root_y_ret,
int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
+
+# ifdef USE_IPHONE
+ int x = w->window.last_mouse_x;
+ int y = w->window.last_mouse_y;
+ if (root_x_ret) *root_x_ret = x;
+ if (root_y_ret) *root_y_ret = y;
+ if (win_x_ret) *win_x_ret = x;
+ if (win_y_ret) *win_y_ret = y;
+
+# else // !USE_IPHONE
+
NSWindow *nsw = [w->window.view window];
NSPoint wpos;
// get bottom left of window on screen, from bottom left
if (root_y_ret) *root_y_ret = (int) p.y;
if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
+# endif // !USE_IPHONE
- if (mask_ret) *mask_ret = 0; // ####
+ if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
if (root_ret) *root_ret = 0;
if (child_ret) *child_ret = 0;
return True;
int *dest_x_ret, int *dest_y_ret,
Window *child_ret)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
+
+# ifdef USE_IPHONE
+
+ NSPoint p;
+ p.x = src_x;
+ p.y = src_y;
+
+# else // !USE_IPHONE
+
NSWindow *nsw = [w->window.view window];
NSPoint wpos;
// get bottom left of window on screen, from bottom left
// get point relative to top left of screen
p.x += vpos.x;
p.y += vpos.y;
+# endif // !USE_IPHONE
*dest_x_ret = p.x;
*dest_y_ret = p.y;
XComposeStatus *xc)
{
KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
- char c = (char) ks; // could be smarter about modifiers here...
+ char c = 0;
+ // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
+ if ((unsigned int) ks <= 255)
+ c = (char) ks;
+
+ // Put control characters in the string. Not meta.
+ if (e->state & ControlMask) {
+ if (c >= 'a' && c <= 'z') // Upcase control.
+ c -= 'a'-'A';
+ if (c >= '@' && c <= '_') // Shift to control page.
+ c -= '@';
+ if (c == ' ') // C-SPC is NULL.
+ c = 0;
+ }
+
if (k_ret) *k_ret = ks;
if (size > 0) buf[0] = c;
if (size > 1) buf[1] = 0;
- return 0;
+ return (size > 0 ? 1 : 0);
}
int
visual_cells (Screen *s, Visual *v)
{
- return 0xFFFFFF;
+ return (int)(v->red_mask | v->green_mask | v->blue_mask);
}
int
return TrueColor;
}
+int
+get_bits_per_pixel (Display *dpy, int depth)
+{
+ Assert (depth == 32 || depth == 1, "unexpected depth");
+ return depth;
+}
+
+int
+screen_number (Screen *screen)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int i;
+ for (i = 0; i < ScreenCount (dpy); i++)
+ if (ScreenOfDisplay (dpy, i) == screen)
+ return i;
+ abort ();
+ return 0;
+}
+
// declared in utils/grabclient.h
Bool
use_subwindow_mode_p (Screen *screen, Window window)