Draw window to image_surface and apply to CALayer.

This commit is contained in:
John Ralls 2021-10-28 15:15:20 -07:00
parent 2d3eb0c6a7
commit be60902805
3 changed files with 141 additions and 104 deletions

View File

@ -24,6 +24,8 @@
#include "gdkprivate-quartz.h"
#include "gdkquartz.h"
#include "gdkinternal-quartz.h"
#include <cairo/cairo-quartz.h>
#import <AppKit/AppKit.h>
@implementation GdkQuartzView
@ -185,7 +187,7 @@
-(void)doCommandBySelector: (SEL)aSelector
{
GDK_NOTE (EVENTS, g_message ("doCommandBySelector %s", aSelector));
GDK_NOTE (EVENTS, g_message ("doCommandBySelector %s", [NSStringFromSelector (aSelector) UTF8String]));
g_object_set_data (G_OBJECT (gdk_window), GIC_FILTER_KEY,
GUINT_TO_POINTER (GIC_FILTER_PASSTHRU));
}
@ -307,43 +309,32 @@
[super viewWillDraw];
}
-(void)drawRect: (NSRect)rect
-(BOOL)wantsUpdateLayer
{
return YES;
}
static void
provider_release_cb (void* info, const void* data, size_t size)
{
g_free (info);
}
-(void)updateLayer
{
GdkRectangle gdk_rect;
GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (gdk_window->impl);
const NSRect *drawn_rects;
NSInteger count;
int i;
cairo_region_t *region;
CALayer *ca_layer = [self layer];
CGRect layer_bounds = [ca_layer bounds];
CGRect backing_bounds = [self convertRectToBacking: layer_bounds];
cairo_rectangle_int_t surface_extents = { layer_bounds.origin.x,
layer_bounds.origin.y,
backing_bounds.size.width,
backing_bounds.size.height };
cairo_surface_t *image_surface = NULL;
if (GDK_WINDOW_DESTROYED (gdk_window))
return;
if (! (gdk_window->event_mask & GDK_EXPOSURE_MASK))
return;
if (NSEqualRects (rect, NSZeroRect))
return;
if (!GDK_WINDOW_IS_MAPPED (gdk_window))
{
/* If the window is not yet mapped, clip_region_with_children
* will be empty causing the usual code below to draw nothing.
* To not see garbage on the screen, we draw an aesthetic color
* here. The garbage would be visible if any widget enabled
* the NSView's CALayer in order to add sublayers for custom
* native rendering.
*/
[NSGraphicsContext saveGraphicsState];
[[NSColor windowBackgroundColor] setFill];
[NSBezierPath fillRect: rect];
[NSGraphicsContext restoreGraphicsState];
return;
}
if (impl->needs_display_region)
{
_gdk_window_process_updates_recurse (gdk_window, impl->needs_display_region);
@ -352,31 +343,66 @@
}
else
{
[self getRectsBeingDrawn: &drawn_rects count: &count];
cairo_region_t* region = cairo_region_create ();
for (i = 0; i < count; i++)
{
gdk_rect.x = drawn_rects[i].origin.x;
gdk_rect.y = drawn_rects[i].origin.y;
gdk_rect.width = drawn_rects[i].size.width;
gdk_rect.height = drawn_rects[i].size.height;
cairo_region_union_rectangle (region, &gdk_rect);
}
impl->in_paint_rect_count++;
cairo_region_t *region = cairo_region_create_rectangle (&surface_extents);
++impl->in_paint_rect_count;
_gdk_window_process_updates_recurse (gdk_window, region);
impl->in_paint_rect_count--;
cairo_region_destroy (region);
}
if (!impl || !impl->cairo_surface)
return;
if (needsInvalidateShadow)
image_surface = cairo_surface_map_to_image (impl->cairo_surface,
&surface_extents);
if (!cairo_surface_status (image_surface))
{
[[self window] invalidateShadow];
needsInvalidateShadow = NO;
cairo_format_t image_format = cairo_image_surface_get_format (image_surface);
if (image_format == CAIRO_FORMAT_ARGB32)
{
int image_width = cairo_image_surface_get_width (image_surface);
int image_height = cairo_image_surface_get_height (image_surface);
int image_stride = cairo_image_surface_get_stride (image_surface);
void* image_data = g_malloc (image_height * image_stride);
int color_bits = 8;
int pixel_bits = 32;
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB ();
CGBitmapInfo bitinfo =
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
CGDataProviderRef provider =
CGDataProviderCreateWithData (image_data, image_data,
image_height * image_stride,
provider_release_cb);
const CGFloat *decode = NULL;
bool interpolate = YES;
CGSize image_size = {image_height, image_width};
NSImage* ns_image = NULL;
if (ca_layer.contents)
[(NSImage*)ca_layer.contents release];
memcpy (image_data, cairo_image_surface_get_data (image_surface),
image_height * image_stride);
ns_image = [[NSImage alloc]
initWithCGImage:CGImageCreate (image_width,
image_height,
color_bits,
pixel_bits,
image_stride,
color_space,
bitinfo,
provider,
decode,
interpolate,
kCGRenderingIntentDefault)
size:image_size];
ca_layer.contents = ns_image;
}
}
cairo_surface_unmap_image (impl->cairo_surface, image_surface);
cairo_surface_destroy (impl->cairo_surface);
--impl->in_paint_rect_count;
}
-(void)setNeedsInvalidateShadow: (BOOL)invalidate

View File

@ -148,7 +148,7 @@ gdk_window_impl_quartz_get_context (GdkWindowImplQuartz *window_impl,
gboolean antialias)
{
CGContextRef cg_context = NULL;
CGSize scale;
// CGSize scale;
if (GDK_WINDOW_DESTROYED (window_impl->wrapper))
return NULL;
@ -186,9 +186,8 @@ gdk_window_impl_quartz_get_context (GdkWindowImplQuartz *window_impl,
/* Undo the default scaling transform, since we apply our own
* in gdk_quartz_ref_cairo_surface () */
scale = CGContextConvertSizeToDeviceSpace (cg_context,
CGSizeMake (1.0, 1.0));
CGContextScaleCTM (cg_context, 1.0 / fabs(scale.width), 1.0 / fabs(scale.height));
// scale = CGContextConvertSizeToDeviceSpace (cg_context, CGSizeMake (1.0, 1.0));
// CGContextScaleCTM (cg_context, 1.0 / fabs(scale.width), 1.0 / fabs(scale.height));
return cg_context;
}
@ -247,10 +246,6 @@ gdk_quartz_cairo_surface_destroy (void *data)
surface_data->window_impl->cairo_surface = NULL;
if (surface_data->cg_context)
gdk_quartz_window_release_context (surface_data->window_impl,
surface_data->cg_context);
g_free (surface_data);
}
@ -259,21 +254,15 @@ gdk_quartz_create_cairo_surface (GdkWindowImplQuartz *impl,
int width,
int height)
{
CGContextRef cg_context;
GdkQuartzCairoSurfaceData *surface_data;
cairo_surface_t *surface;
cg_context = gdk_quartz_window_get_context (impl, TRUE);
surface_data = g_new (GdkQuartzCairoSurfaceData, 1);
surface_data->window_impl = impl;
surface_data->cg_context = cg_context;
surface_data->cg_context = NULL;
if (cg_context)
surface = cairo_quartz_surface_create_for_cg_context (cg_context,
width, height);
else
surface = cairo_quartz_surface_create(CAIRO_FORMAT_ARGB32, width, height);
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_surface_set_user_data (surface, &gdk_quartz_cairo_key,
surface_data,
@ -286,19 +275,17 @@ static cairo_surface_t *
gdk_quartz_ref_cairo_surface (GdkWindow *window)
{
GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
gint scale = gdk_window_get_scale_factor (impl->wrapper);
if (GDK_WINDOW_DESTROYED (window))
return NULL;
if (!impl->cairo_surface)
{
gint scale = gdk_window_get_scale_factor (impl->wrapper);
impl->cairo_surface =
gdk_quartz_create_cairo_surface (impl,
gdk_window_get_width (impl->wrapper) * scale,
gdk_window_get_height (impl->wrapper) * scale);
impl->cairo_surface =
gdk_quartz_create_cairo_surface (impl,
gdk_window_get_width (impl->wrapper) * scale,
gdk_window_get_height (impl->wrapper) * scale);
cairo_surface_set_device_scale (impl->cairo_surface, scale, scale);
}
else
@ -316,6 +303,7 @@ gdk_window_impl_quartz_init (GdkWindowImplQuartz *impl)
static gboolean
gdk_window_impl_quartz_begin_paint (GdkWindow *window)
{
gdk_quartz_ref_cairo_surface (window);
return FALSE;
}
@ -3204,37 +3192,15 @@ gdk_quartz_window_release_context (GdkWindowImplQuartz *window,
GDK_WINDOW_IMPL_QUARTZ_GET_CLASS (window)->release_context (window, cg_context);
}
/* macOS doesn't define a root window, but Gdk needs one for two
* purposes: To be a parent reference for some toplevels and to be a
* fallback window when gdk_window_create_image_surface is called with
* a NULL GdkWindow.
*
*/
static CGContextRef
gdk_root_window_impl_quartz_get_context (GdkWindowImplQuartz *window,
gboolean antialias)
{
CGColorSpaceRef colorspace;
CGContextRef cg_context;
GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (window);
if (GDK_WINDOW_DESTROYED (window_impl->wrapper))
return NULL;
/* We do not have the notion of a root window on OS X. We fake this
* by creating a 1x1 bitmap and return a context to that.
*/
colorspace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
cg_context = CGBitmapContextCreate (NULL,
1, 1, 8, 4, colorspace,
(CGBitmapInfo)kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease (colorspace);
return cg_context;
}
static void
gdk_root_window_impl_quartz_release_context (GdkWindowImplQuartz *window,
CGContextRef cg_context)
{
CGContextRelease (cg_context);
}
static CGContextRef gdk_root_window_impl_quartz_get_context (GdkWindowImplQuartz *window, gboolean antialias);
static void gdk_root_window_impl_quartz_release_context (GdkWindowImplQuartz *window, CGContextRef cg_context);
static void
gdk_root_window_impl_quartz_class_init (GdkRootWindowImplQuartzClass *klass)
@ -3250,6 +3216,22 @@ gdk_root_window_impl_quartz_class_init (GdkRootWindowImplQuartzClass *klass)
static void
gdk_root_window_impl_quartz_init (GdkRootWindowImplQuartz *impl)
{
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB ();
/* Alpha channel Info: Cairo, CGImage, and CVPixelBuffer all use
* kCGImageAlphaPremultipliedFirst, CALayer.contents wants
* kCGImageAlphaPremultipliedLast.
*/
CGBitmapInfo info = (CGBitmapInfo)kCGImageAlphaPremultipliedLast;
impl->cg_context = CGBitmapContextCreate (NULL, 1, 1, 8, 4,
colorspace, info);
CGColorSpaceRelease (colorspace);
impl->cg_layers = NULL;
}
static void
gdk_root_window_impl_quartz_dispose (GdkRootWindowImplQuartz *impl)
{
g_list_free_full (impl->cg_layers, (GDestroyNotify)CGLayerRelease);
}
GType
@ -3279,3 +3261,30 @@ _gdk_root_window_impl_quartz_get_type (void)
return object_type;
}
static CGContextRef
gdk_root_window_impl_quartz_get_context (GdkWindowImplQuartz *window,
gboolean antialias)
{
GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (window);
GdkRootWindowImplQuartz *impl = GDK_ROOT_WINDOW_IMPL_QUARTZ (window);
CGSize size;
CGLayerRef layer;
if (GDK_WINDOW_DESTROYED (window_impl->wrapper))
return NULL;
size.width = gdk_window_get_width (window_impl->wrapper);
size.height = gdk_window_get_height (window_impl->wrapper);
layer = CGLayerCreateWithContext(impl->cg_context, size, NULL);
impl->cg_layers = g_list_prepend(impl->cg_layers, CGLayerRetain (layer));
return CGContextRetain (CGLayerGetContext (layer));
}
static void
gdk_root_window_impl_quartz_release_context (GdkWindowImplQuartz *window,
CGContextRef cg_context)
{
CGContextRelease (cg_context);
}

View File

@ -103,6 +103,8 @@ typedef struct _GdkRootWindowImplQuartzClass GdkRootWindowImplQuartzClass;
struct _GdkRootWindowImplQuartz
{
GdkWindowImplQuartz parent_instance;
CGContextRef cg_context;
GList* cg_layers;
};
struct _GdkRootWindowImplQuartzClass