#include <CoreGraphics/CoreGraphics.h>
#include <cairo-quartz.h>
-
#import "GdkMacosCairoSubview.h"
#import "GdkMacosCairoView.h"
-(void)dealloc
{
g_clear_pointer (&self->clip, cairo_region_destroy);
+ g_clear_pointer (&self->image, CGImageRelease);
[super dealloc];
}
-(void)drawRect:(NSRect)rect
{
CGContextRef cgContext;
- GdkSurface *gdk_surface;
- cairo_surface_t *dest;
- const NSRect *rects = NULL;
NSView *root_view;
- NSInteger n_rects = 0;
- NSRect abs_bounds;
- cairo_t *cr;
+ CGRect image_rect;
+ CGRect abs_bounds;
CGSize scale;
- int scale_factor;
+ int abs_height;
- if (self->cairoSurface == NULL)
+ if (self->image == NULL)
return;
- /* Acquire everything we need to do translations, drawing, etc */
- gdk_surface = [self gdkSurface];
- scale_factor = gdk_surface_get_scale_factor (gdk_surface);
- root_view = [[self window] contentView];
cgContext = [[NSGraphicsContext currentContext] CGContext];
+ root_view = [[self window] contentView];
abs_bounds = [self convertRect:[self bounds] toView:root_view];
+ abs_height = CGImageGetHeight (self->image);
CGContextSaveGState (cgContext);
- /* Translate scaling to remove HiDPI scaling from CGContext as
- * cairo will be doing that for us already.
- */
- scale = CGSizeMake (1.0, 1.0);
- scale = CGContextConvertSizeToDeviceSpace (cgContext, scale);
- CGContextScaleCTM (cgContext, 1.0 / scale.width, 1.0 / scale.height);
-
- /* Create the cairo surface to draw to the CGContext and translate
- * coordinates so we can pretend we are in the same coordinate system
- * as the GDK surface.
+ /* Clip while our context is still using matching coordinates
+ * to the self->clip region. This is usually just on the views
+ * for the shadow areas.
*/
- dest = cairo_quartz_surface_create_for_cg_context (cgContext,
- gdk_surface->width * scale_factor,
- gdk_surface->height * scale_factor);
- cairo_surface_set_device_scale (dest, scale_factor, scale_factor);
-
- /* Create cairo context and translate things into the origin of
- * the topmost contentView so that we just draw at 0,0 with a
- * clip region to paint the surface.
- */
- cr = cairo_create (dest);
- cairo_translate (cr, -abs_bounds.origin.x, -abs_bounds.origin.y);
-
- /* Apply the clip if provided one */
if (self->clip != NULL)
{
- cairo_rectangle_int_t area;
+ guint n_rects = cairo_region_num_rectangles (self->clip);
- n_rects = cairo_region_num_rectangles (self->clip);
for (guint i = 0; i < n_rects; i++)
{
+ cairo_rectangle_int_t area;
+
cairo_region_get_rectangle (self->clip, i, &area);
- cairo_rectangle (cr, area.x, area.y, area.width, area.height);
+ CGContextAddRect (cgContext,
+ CGRectMake (area.x, area.y, area.width, area.height));
}
- cairo_clip (cr);
- }
-
- /* Clip the cairo context based on the rectangles to be drawn
- * within the bounding box :rect.
- */
- [self getRectsBeingDrawn:&rects count:&n_rects];
- for (NSInteger i = 0; i < n_rects; i++)
- {
- NSRect area = [self convertRect:rects[i] toView:root_view];
- cairo_rectangle (cr,
- area.origin.x, area.origin.y,
- area.size.width, area.size.height);
+ CGContextClip (cgContext);
}
- cairo_clip (cr);
- /* Now paint the surface (without blending) as we do not need
- * any compositing here. The transparent regions (like shadows)
- * are already on non-opaque layers.
- */
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_set_source_surface (cr, self->cairoSurface, 0, 0);
- cairo_paint (cr);
+ /* Scale/Translate so that the CGImageRef draws in proper format/placement */
+ scale = CGSizeMake (1.0, 1.0);
+ scale = CGContextConvertSizeToDeviceSpace (cgContext, scale);
+ CGContextScaleCTM (cgContext, 1.0 / scale.width, 1.0 / scale.height);
+ CGContextTranslateCTM (cgContext, -abs_bounds.origin.x, -abs_bounds.origin.y);
+ image_rect = CGRectMake (-abs_bounds.origin.x,
+ -abs_bounds.origin.y,
+ CGImageGetWidth (self->image),
+ CGImageGetHeight (self->image));
+ CGContextDrawImage (cgContext, image_rect, self->image);
- /* Cleanup state, flush the surface to the backing layer, and
- * restore GState for future use.
- */
- cairo_destroy (cr);
- cairo_surface_flush (dest);
- cairo_surface_destroy (dest);
CGContextRestoreGState (cgContext);
}
--(void)setCairoSurface:(cairo_surface_t *)surface
- withDamage:(cairo_region_t *)region
+-(void)setImage:(CGImageRef)theImage
+ withDamage:(cairo_region_t *)region
{
- if (surface != self->cairoSurface)
+ if (theImage != image)
{
- g_clear_pointer (&self->cairoSurface, cairo_surface_destroy);
- if (surface != NULL)
- self->cairoSurface = cairo_surface_reference (surface);
+ g_clear_pointer (&image, CGImageRelease);
+ if (theImage)
+ image = CGImageRetain (theImage);
}
if (region != NULL)
}
for (id view in [self subviews])
- [(GdkMacosCairoSubview *)view setCairoSurface:surface
- withDamage:region];
+ [(GdkMacosCairoSubview *)view setImage:theImage withDamage:region];
}
-(void)setOpaque:(BOOL)opaque
[child setNeedsDisplay:needsDisplay];
}
+static void
+release_surface_provider (void *info,
+ const void *data,
+ size_t size)
+{
+ cairo_surface_destroy (info);
+}
+
-(void)setCairoSurface:(cairo_surface_t *)cairoSurface
withDamage:(cairo_region_t *)cairoRegion
{
+ CGImageRef image = NULL;
+
+ if (cairoSurface != NULL)
+ {
+ const CGColorRenderingIntent intent = kCGRenderingIntentDefault;
+ CGDataProviderRef provider;
+ CGColorSpaceRef rgb;
+ cairo_format_t format;
+ CGBitmapInfo bitmap = kCGBitmapByteOrder32Host;
+ guint8 *framebuffer;
+ size_t width;
+ size_t height;
+ int rowstride;
+ int bpp;
+ int bpc;
+
+ cairo_surface_flush (cairoSurface);
+
+ format = cairo_image_surface_get_format (cairoSurface);
+ framebuffer = cairo_image_surface_get_data (cairoSurface);
+ rowstride = cairo_image_surface_get_stride (cairoSurface);
+ width = cairo_image_surface_get_width (cairoSurface);
+ height = cairo_image_surface_get_height (cairoSurface);
+ rgb = CGColorSpaceCreateDeviceRGB ();
+
+ /* Assert that our image surface was created correctly with
+ * 16-byte aligned pointers and strides. This is needed to
+ * ensure that we're working with fast paths in CoreGraphics.
+ */
+ g_assert (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24);
+ g_assert (framebuffer != NULL);
+ g_assert (((intptr_t)framebuffer & (intptr_t)~0xF) == (intptr_t)framebuffer);
+ g_assert ((rowstride & ~0xF) == rowstride);
+
+ if (format == CAIRO_FORMAT_ARGB32)
+ {
+ bitmap |= kCGImageAlphaPremultipliedFirst;
+ bpp = 32;
+ bpc = 8;
+ }
+ else
+ {
+ bitmap |= kCGImageAlphaNoneSkipFirst;
+ bpp = 32;
+ bpc = 8;
+ }
+
+ provider = CGDataProviderCreateWithData (cairo_surface_reference (cairoSurface),
+ framebuffer,
+ rowstride * height,
+ release_surface_provider);
+
+ image = CGImageCreate (width, height, bpc, bpp, rowstride, rgb, bitmap, provider, NULL, FALSE, intent);
+
+ CGDataProviderRelease (provider);
+ CGColorSpaceRelease (rgb);
+ }
+
for (id view in [self subviews])
- [(GdkMacosCairoSubview *)view setCairoSurface:cairoSurface
- withDamage:cairoRegion];
+ [(GdkMacosCairoSubview *)view setImage:image
+ withDamage:cairoRegion];
+
+ if (image != NULL)
+ CGImageRelease (image);
}
-(void)removeOpaqueChildren
*/
self->transparent = [[GdkMacosCairoSubview alloc] initWithFrame:frame];
[self addSubview:self->transparent];
-
}
return self;