-/* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2012 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 <Cocoa/Cocoa.h>
+#import <stdint.h>
+
+#ifdef USE_IPHONE
+# import <UIKit/UIKit.h>
+# import <UIKit/UIScreen.h>
+# import <QuartzCore/QuartzCore.h>
+# import <CoreText/CTFont.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
+#else
+# import <Cocoa/Cocoa.h>
+#endif
+
#import "jwxyz.h"
#import "jwxyz-timers.h"
+#import "yarandom.h"
+
+#ifdef USE_IPHONE
+# define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
+#endif
#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;
};
};
Screen *screen;
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
};
+/* 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
+}
+
+
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));
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);
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_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;
CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
Assert (dpy->cgdpy, "unable to find CGDisplay");
}
+# endif // USE_IPHONE
#if 0
{
//
dpy->colorspace = CGColorSpaceCreateDeviceRGB();
# endif
+
+ 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
+
jwxyz_sources_data *
return (int) dpy->main_window->frame.size.height;
}
-
static void
validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
{
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.
}
CGContextRef cgc = d->cgc;
-
set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
CGContextSetShouldAntialias (cgc, antialias_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)
push_fg_gc (d, gc, YES);
-# ifdef XDRAWPOINTS_IMAGES
+# ifdef XDRAWPOINTS_CGDATA
- 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);
+# 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);
- 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;
+ Assert (data, "no bitmap data in Drawable");
+
+ 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));
+
+ CGFloat x0 = wr.origin.x;
+ CGFloat y0 = wr.origin.y + wr.size.height;
+
+ // 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 (0 <= x && x < w && 0 <= y && y < h) {
+ unsigned int *p = (unsigned int *)
+ ((char *) data + (size_t) y * bpr + (size_t) x * 4);
+ *p = 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 (0 <= x && x < w && 0 <= y && y < h) {
+ unsigned int *p = (unsigned int *)
+ ((char *) data + (size_t) y * bpr + (size_t) x * 4);
+ *p = argb;
+ }
+ }
}
- //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (cgc, rect, cgi);
- points++;
- }
+ } else /* d->type == WINDOW */
+
+# endif /* XDRAWPOINTS_CGDATA */
+ {
+
+# ifdef XDRAWPOINTS_IMAGES
+
+ 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);
- CGImageRelease (cgi);
+ 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);
+ invalidate_drawable_cache (d);
return 0;
}
unsigned int width, unsigned int height,
int dst_x, int dst_y)
{
+ Assert (gc, "no GC");
Assert ((width < 65535), "improbably large width");
Assert ((height < 65535), "improbably large height");
Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
if (width == 0 || height == 0)
return 0;
- if (gc && (gc->gcv.function == GXset ||
- gc->gcv.function == GXclear)) {
+ 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 (dst->cgc, (gc->gcv.function == GXset
- ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
- : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
+ set_color (dst->cgc,
+ (gc->gcv.function == GXset
+ ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
+ : (gc->depth == 1 ? 0 : BlackPixel(0,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 (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)
return 0;
NSObject *releaseme = 0;
CGImageRef cgi;
BOOL mask_p = NO;
+ BOOL free_cgi_p = NO;
- if (src->type == PIXMAP) {
- // 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);
+#ifndef USE_BACKBUFFER
+ // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+ if (src->type == PIXMAP)
+# endif
+ {
+
+ // 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);
- CGImageRelease (cgi);
- 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];
- [bm 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);
// 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 (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 (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;
}
push_fg_gc (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;
}
NSPoint p;
CGRect wr = d->frame;
push_fg_gc (d, gc, NO);
- set_line_mode (d->cgc, &gc->gcv);
+
+ 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;
+ CGContextRef cgc = d->cgc;
+
push_fg_gc (d, gc, NO);
- set_line_mode (d->cgc, &gc->gcv);
- CGContextBeginPath (d->cgc);
+ 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");
+ Assert (w && w->type == WINDOW, "not a window");
validate_pixel (pixel, 32, NO);
w->window.background = pixel;
return 0;
push_bg_gc (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;
+ CGContextRef cgc = d->cgc;
push_fg_gc (d, gc, YES);
for (i = 0; i < n; i++) {
CGRect r;
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 (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);
+ 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;
}
push_fg_gc (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
set_gcv (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 & GCBackground) validate_pixel (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");
}
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;
XAllocColor (Display *dpy, Colormap cmap, XColor *color)
{
// store 32 bit ARGB in the pixel field.
- color->pixel = (( 0xFF << 24) |
+ // (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) ));
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")) {
static unsigned long
ximage_getpixel_32 (XImage *ximage, int x, int y)
{
- return *((unsigned long *) ximage->data +
- (y * (ximage->bytes_per_line >> 2)) +
- x);
+ return ((unsigned long)
+ *((uint32_t *) ximage->data +
+ (y * (ximage->bytes_per_line >> 2)) +
+ x));
}
static int
ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
{
- *((unsigned long *) ximage->data +
+ *((uint32_t *) ximage->data +
(y * (ximage->bytes_per_line >> 2)) +
- x) = pixel;
+ x) = (uint32_t) pixel;
return 0;
}
ximage->f.put_pixel = ximage_putpixel_32;
ximage->f.get_pixel = ximage_getpixel_32;
} else {
- abort();
+ Assert (0, "unknown depth");
}
return 1;
}
{
CGRect wr = d->frame;
+ Assert (gc, "no GC");
Assert ((w < 65535), "improbably large width");
Assert ((h < 65535), "improbably large height");
Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
if (w <= 0 || h <= 0)
return 0;
- if (gc && (gc->gcv.function == GXset ||
- gc->gcv.function == GXclear)) {
+ 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
+ set_color (cgc, (gc->gcv.function == GXset
? (gc->depth == 1 ? 1 : WhitePixel(0,0))
: (gc->depth == 1 ? 0 : BlackPixel(0,0))),
gc->depth, gc->gcv.alpha_allowed_p, YES);
kCGRenderingIntentDefault);
CGDataProviderRelease (prov);
//Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (d->cgc, r, cgi);
+ CGContextDrawImage (cgc, r, cgi);
CGImageRelease (cgi);
} else { // (bpp == 1)
NO); /* interpolate */
push_fg_gc (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 (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;
+ int depth, ibpp, ibpl, alpha_first_p;
+# 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;
- 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);
+ // If it's a pixmap, we created it with kCGImageAlphaNoneSkipFirst.
+ // If it's an iPhone window, it's the other way around.
+ alpha_first_p = (d->type == PIXMAP);
+ 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
- bm = [NSBitmapImageRep alloc];
NSRect nsfrom;
nsfrom.origin.x = x;
nsfrom.origin.y = y;
nsfrom.size.width = width;
nsfrom.size.height = height;
- [bm initWithFocusedViewRect:nsfrom];
+ bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
depth = 32;
+ alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
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.
/* both PPC and Intel use word-ordered ARGB frame buffers, which
means that on Intel it is BGRA when viewed by bytes (And BGR
when using 24bpp packing).
+
+ BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
+ The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
+ indicator of this latest kink.
*/
int xx, yy;
if (depth == 1) {
const unsigned char *iline2 = iline;
for (xx = 0; xx < width; xx++) {
- iline2++; // ignore b or a
- iline2++; // ignore g or r
- unsigned char r = *iline2++; // r or g
- if (ibpp == 32) iline2++; // ignore a or b
+ iline2++; // ignore R or A or A or B
+ iline2++; // ignore G or B or R or G
+ unsigned char r = *iline2++; // use B or G or G or R
+ if (ibpp == 32) iline2++; // ignore A or R or B or A
XPutPixel (image, xx, yy, (r ? 1 : 0));
}
const unsigned char *iline2 = iline;
unsigned char *oline2 = oline;
- 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++;
- unsigned long pixel = ((a << 24) |
- (r << 16) |
- (g << 8) |
- (b << 0));
- *((unsigned int *) oline2) = pixel;
- oline2 += 4;
- }
+ 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;
+ }
+
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.
*/
void
-jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
- XRectangle *geom_ret, int exif_rotation)
+jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
+ Bool nsimg_p, void *img_arg,
+ XRectangle *geom_ret, int exif_rotation)
{
- NSImage *nsimg = (NSImage *) nsimg_arg;
+ 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...
+ //
+ NSData *nsdata = [NSBitmapImageRep
+ TIFFRepresentationOfImageRepsInArray:
+ [nsimg representations]];
+ 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;
+ imgr.width = CGImageGetWidth (cgi);
+ imgr.height = CGImageGetHeight (cgi);
+ }
- // convert the NSImage to a CGImage via the toll-free-bridging
- // of NSData and CFData...
- //
- NSData *nsdata = [NSBitmapImageRep
- TIFFRepresentationOfImageRepsInArray:
- [nsimg representations]];
- CFDataRef cfdata = (CFDataRef) nsdata;
- CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
- CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
-
- NSSize imgr = [nsimg size];
Bool rot_p = (exif_rotation >= 5);
if (rot_p)
if (d->type == WINDOW)
XClearWindow (dpy, d);
else {
- set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
+ set_color (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);
- CFRelease (cgsrc);
- CGImageRelease (cgi);
+# 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,
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,
+ // Without this, it returns 0...
+ kCGImageAlphaNoneSkipFirst
+ );
+ Assert (p2->cgc, "could not create CGBitmapContext");
+
return p2;
}
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;
f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
int i;
+# ifndef USE_IPHONE
NSBezierPath *bpath = [NSBezierPath bezierPath];
+# else // USE_IPHONE
+ CTFontRef ctfont =
+ CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+ [fid->nsfont pointSize],
+ NULL);
+ Assert (ctfont, @"no CTFontRef for UIFont");
+# endif // USE_IPHONE
for (i = first; i <= last; i++) {
unsigned char str[2];
NSString *nsstr = [NSString stringWithCString:(char *) str
encoding:NSISOLatin1StringEncoding];
+ NSPoint advancement = { 0, };
+ NSRect bbox = {{ 0, }, };
+
+# ifndef USE_IPHONE
/* I can't believe we have to go through this bullshit just to
convert a 'char' to an NSGlyph!!
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
[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];
advancement = [bpath currentPoint];
bbox = [bpath bounds];
+# else // USE_IPHONE
+
+ /* There is no way to get "lbearing", "rbearing" or "descent" out of
+ NSFont. 'sizeWithFont' gives us "width" and "height" only.
+ Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
+ width of the character and the ascent of the font.
+
+ Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
+ the CoreText library, but there's no non-CoreText way to turn a
+ unichar into a CGGlyph.
+ */
+ UniChar uchar = [nsstr characterAtIndex: 0];
+ CGGlyph cgglyph = 0;
+
+ if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
+ {
+ bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
+ kCTFontDefaultOrientation,
+ &cgglyph, NULL, 1);
+ CGSize adv = { 0, };
+ CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
+ &cgglyph, &adv, 1);
+ advancement.x = adv.width;
+ advancement.y = adv.height;
+
+ // Seems to be clipping by a pixel or two. Add a margin to be safe.
+ bbox.origin.x -= 2;
+ bbox.origin.y -= 2;
+ bbox.size.width += 4;
+ bbox.size.height += 4;
+ }
+# endif // USE_IPHONE
+
/* Now that we know the advancement and bounding box, we can compute
the lbearing and rbearing.
*/
#endif
}
+# ifdef USE_IPHONE
+ CFRelease (ctfont);
+# endif
}
}
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)
{
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
static NSFont *
random_font (BOOL bold, BOOL ital, float size, char **name_ret)
{
+# ifndef USE_IPHONE
NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
(ital ? NSItalicFontMask : NSUnitalicFontMask));
NSArray *fonts = [[NSFontManager sharedFontManager]
// None of the fonts support ASCII?
return 0;
+
+# else // USE_IPHONE
+
+ NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
+ NSArray *families = [UIFont familyNames];
+ NSMutableDictionary *famdict = [NSMutableDictionary
+ dictionaryWithCapacity:100];
+ NSObject *y = [NSNumber numberWithBool:YES];
+ for (NSString *name in families) {
+ // There are many dups in the families array -- uniquify it.
+ [famdict setValue:y forKey:name];
+ }
+
+ for (NSString *name in famdict) {
+ for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
+
+# define MATCH(X) \
+ ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
+ != NSNotFound)
+
+ BOOL bb = MATCH(@"Bold");
+ BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
+
+ if (!bold != !bb) continue;
+ if (!ital != !ii) continue;
+
+ /* 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
+ )
+ continue;
+
+ [fonts addObject:fn];
+# undef MATCH
+ }
+ }
+
+ if (! [fonts count]) return 0; // Nothing suitable?
+
+ int i = random() % [fonts count];
+ NSString *name = [fonts objectAtIndex:i];
+ UIFont *ff = [UIFont fontWithName:name size:size];
+ *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
+
+ return ff;
+
+# endif // USE_IPHONE
}
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)
{
NSFont *nsfont = 0;
BOOL bold = NO;
if (size < 6 || size > 1000)
size = 12;
+ size *= scale;
+
if (rand)
nsfont = random_font (bold, ital, size, &ps_name);
{
Font fid = (Font) calloc (1, sizeof(*fid));
- fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
- if (! fid->nsfont)
- fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
- if (!fid->nsfont) {
- NSLog(@"no NSFont for \"%s\"", name);
- abort();
- }
+ float scale = 1;
- //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
+# ifdef USE_IPHONE
+ // Scale up fonts on Retina displays.
+ scale = dpy->main_window->window.view.contentScaleFactor;
+# endif
- query_font (fid);
+ fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
- return fid;
-}
+ 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, scale, &fid->ps_name, &fid->size);
-/* This translates the NSFont into the numbers that aglUseFont() wants.
- */
-int
-jwxyz_font_info (Font f, int *size_ret, int *face_ret)
-{
- char *name = strdup (f->ps_name);
- char *dash = strchr (name, '-');
- int flags = 0;
- int size = f->size;
- if (dash) {
- // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
- if (strcasestr (dash, "bold")) flags |= 1;
- if (strcasestr (dash, "italic")) flags |= 2;
- if (strcasestr (dash, "oblique")) flags |= 2;
- *dash = 0;
+ // We should never return NULL for XLFD fonts.
+ if (!fid->nsfont) {
+ Assert (0, "no font");
+ return 0;
}
- NSString *nname = [NSString stringWithCString:name
- encoding:NSUTF8StringEncoding];
- ATSFontFamilyRef id =
- ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
-
+ CFRetain (fid->nsfont); // needed for garbage collection?
- // WTF? aglUseFont gets a BadValue if size is small!!
- if (size < 9) size = 9;
+ //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
- //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
- Assert (id >= 0, "no ATS font family");
+ query_font (fid);
- *size_ret = size;
- *face_ret = flags;
- return id;
+ return fid;
}
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
// They're probably not very big...
//
// [fid->nsfont release];
+ // CFRelease (fid->nsfont);
free (fid);
return 0;
XUnloadFont (dpy, gc->gcv.font);
gc->gcv.font = copy_font (fid);
[gc->gcv.font->nsfont retain];
+ CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
return 0;
}
static void
-set_font (CGContextRef cgc, GC gc)
+set_font (Display *dpy, CGContextRef cgc, GC gc)
{
Font font = gc->gcv.font;
if (! font) {
- font = XLoadFont (0, 0);
+ font = XLoadFont (dpy, 0);
gc->gcv.font = font;
[gc->gcv.font->nsfont retain];
+ CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
}
CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
*/
+ CGContextRef cgc = d->cgc;
push_fg_gc (d, gc, YES);
- set_font (d->cgc, gc);
+ set_font (dpy, cgc, gc);
- CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
- if (! gc->gcv.antialias_p)
- CGContextSetShouldAntialias (d->cgc, YES); // always antialias text
- CGContextShowTextAtPoint (d->cgc,
+ CGContextSetTextDrawingMode (cgc, kCGTextFill);
+ if (gc->gcv.antialias_p)
+ CGContextSetShouldAntialias (cgc, YES);
+ CGContextShowTextAtPoint (cgc,
wr.origin.x + x,
wr.origin.y + wr.size.height - y,
str, len);
pop_gc (d, gc);
-#else /* !0 */
+# else /* !0 */
/* The Cocoa way...
*/
pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
[nsstr drawAtPoint:pos withAttributes:attr];
-#endif /* 0 */
+# endif /* 0 */
+ invalidate_drawable_cache (d);
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
NSScreen *screen = (screens && [screens count] > 0
? [screens objectAtIndex:0]
: [NSScreen mainScreen]);
+#ifdef USE_IPHONE
+ double s = w->window.view.contentScaleFactor;
+#else
+ int s = 1;
+#endif
NSRect srect = [screen frame];
- vpos.y = srect.size.height - vpos.y;
+ vpos.y = (s * srect.size.height) - vpos.y;
// get the mouse position on window, from bottom left
NSEvent *e = [NSApp currentEvent];
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
NSScreen *screen = (screens && [screens count] > 0
? [screens objectAtIndex:0]
: [NSScreen mainScreen]);
+# ifdef USE_IPHONE
+ double s = w->window.view.contentScaleFactor;
+# else
+ int s = 1;
+# endif
NSRect srect = [screen frame];
- vpos.y = srect.size.height - vpos.y;
+ vpos.y = (s * srect.size.height) - vpos.y;
// point starts out relative to top left of view
NSPoint p;
// 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;