struct jwxyz_Drawable {
enum { WINDOW, PIXMAP } type;
CGContextRef cgc;
+ CGImageRef cgi;
CGRect frame;
union {
struct {
} window;
struct {
int depth;
+ void *cgc_buffer; // the bits to which CGContextRef renders
} pixmap;
};
};
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
//
dpy->colorspace = CGColorSpaceCreateDeviceRGB();
# endif
+
+ invalidate_drawable_cache (w);
}
return (int) dpy->main_window->frame.size.height;
}
-
static void
validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_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...
*/
# endif /* ! XDRAWPOINTS_IMAGES */
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
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);
+ // 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)
} 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 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 (src == dst) {
-#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);
+ // 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;
-#endif
+ } else {
- // 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);
+ // 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);
+ }
}
if (mask_p) { // src depth == 1
CGContextFillRect (dst->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 (dst->cgc, dst_rect, cgi);
+ } else {
+ // No cgi means src == dst, and both are Windows.
+ 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);
+ }
pop_gc (dst, gc);
}
-
- CGImageRelease (cgi);
+
+ if (free_cgi_p) CGImageRelease (cgi);
if (releaseme) [releaseme release];
+ invalidate_drawable_cache (dst);
return 0;
}
CGContextAddLineToPoint (d->cgc, p.x, p.y);
CGContextStrokePath (d->cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
if (closed_p) CGContextClosePath (d->cgc);
CGContextStrokePath (d->cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
}
CGContextStrokePath (d->cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
if (gc)
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
}
rects++;
}
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
else
CGContextFillPath (d->cgc);
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
}
pop_gc (d, gc);
+ invalidate_drawable_cache (d);
return 0;
}
CGImageRelease (mask);
}
+ invalidate_drawable_cache (d);
+
return 0;
}
geom_ret->width = dst.size.width;
geom_ret->height = dst.size.height;
}
+
+ invalidate_drawable_cache (d);
}
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,
XFreePixmap (Display *d, Pixmap p)
{
Assert (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;
}
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);
else