G_DEFINE_TYPE (GdkMacosCairoContext, _gdk_macos_cairo_context, GDK_TYPE_CAIRO_CONTEXT)
-static const cairo_user_data_key_t buffer_key;
-
-static void
-unlock_buffer (gpointer data)
-{
- GdkMacosBuffer *buffer = data;
-
- g_assert (GDK_IS_MACOS_BUFFER (buffer));
-
- _gdk_macos_buffer_unlock (buffer);
- g_clear_object (&buffer);
-}
-
static cairo_t *
_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
{
stride);
cairo_surface_set_device_scale (image_surface, scale, scale);
- /* Lock the buffer so we can modify it safely */
- _gdk_macos_buffer_lock (buffer);
- cairo_surface_set_user_data (image_surface,
- &buffer_key,
- g_object_ref (buffer),
- unlock_buffer);
+ /* The buffer should already be locked at this point, and will
+ * be unlocked as part of end_frame.
+ */
if (!(cr = cairo_create (image_surface)))
goto failure;
return cr;
}
+static void
+copy_surface_data (GdkMacosBuffer *from,
+ GdkMacosBuffer *to,
+ const cairo_region_t *region,
+ int scale)
+{
+ const guint8 *from_base;
+ guint8 *to_base;
+ guint from_stride;
+ guint to_stride;
+ guint n_rects;
+
+ g_assert (GDK_IS_MACOS_BUFFER (from));
+ g_assert (GDK_IS_MACOS_BUFFER (to));
+ g_assert (region != NULL);
+ g_assert (!cairo_region_is_empty (region));
+
+ from_base = _gdk_macos_buffer_get_data (from);
+ from_stride = _gdk_macos_buffer_get_stride (from);
+
+ to_base = _gdk_macos_buffer_get_data (to);
+ to_stride = _gdk_macos_buffer_get_stride (to);
+
+ n_rects = cairo_region_num_rectangles (region);
+
+ for (guint i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t rect;
+ int y2;
+
+ cairo_region_get_rectangle (region, i, &rect);
+
+ rect.y *= scale;
+ rect.height *= scale;
+ rect.x *= scale;
+ rect.width *= scale;
+
+ y2 = rect.y + rect.height;
+
+ for (int y = rect.y; y < y2; y++)
+ memcpy (&to_base[y * to_stride + rect.x * 4],
+ &from_base[y * from_stride + rect.x * 4],
+ rect.width * 4);
+ }
+}
+
static void
_gdk_macos_cairo_context_begin_frame (GdkDrawContext *draw_context,
gboolean prefers_high_depth,
{
GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
GdkMacosBuffer *buffer;
- GdkSurface *surface;
+ GdkMacosSurface *surface;
g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
[CATransaction begin];
[CATransaction setDisableActions:YES];
- surface = gdk_draw_context_get_surface (draw_context);
- buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
+ surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (draw_context));
+ buffer = _gdk_macos_surface_get_buffer (surface);
_gdk_macos_buffer_set_damage (buffer, region);
_gdk_macos_buffer_set_flipped (buffer, FALSE);
+
+ _gdk_macos_buffer_lock (buffer);
+
+ /* If there is damage that was on the previous frame that is not on
+ * this frame, we need to copy that rendered region over to the back
+ * buffer so that when swapping buffers, we still have that content.
+ * This is done with a read-only lock on the IOSurface to avoid
+ * invalidating the buffer contents.
+ */
+ if (surface->front != NULL)
+ {
+ const cairo_region_t *previous = _gdk_macos_buffer_get_damage (surface->front);
+
+ if (previous != NULL)
+ {
+ cairo_region_t *copy;
+
+ copy = cairo_region_copy (previous);
+ cairo_region_subtract (copy, region);
+
+ if (!cairo_region_is_empty (copy))
+ {
+ int scale = gdk_surface_get_scale_factor (GDK_SURFACE (surface));
+
+ _gdk_macos_buffer_read_lock (surface->front);
+ copy_surface_data (surface->front, buffer, copy, scale);
+ _gdk_macos_buffer_read_unlock (surface->front);
+ }
+
+ cairo_region_destroy (copy);
+ }
+ }
}
static void
_gdk_macos_cairo_context_end_frame (GdkDrawContext *draw_context,
cairo_region_t *painted)
{
+ GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
+ GdkMacosSurface *surface;
GdkMacosBuffer *buffer;
- GdkSurface *surface;
- g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (draw_context));
+ g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
- surface = gdk_draw_context_get_surface (draw_context);
- buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
+ surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (draw_context));
+ buffer = _gdk_macos_surface_get_buffer (surface);
+
+ _gdk_macos_buffer_unlock (buffer);
- _gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted);
- _gdk_macos_buffer_set_damage (buffer, NULL);
+ _gdk_macos_surface_swap_buffers (surface, painted);
[CATransaction commit];
}