[quartz] Create similar surfaces using a CGLayer for faster drawing.

This commit is contained in:
John Ralls 2022-02-14 17:23:12 -08:00
parent 76e6a0ddf7
commit bacbe9bb2d
2 changed files with 112 additions and 31 deletions

View file

@ -55,7 +55,8 @@ typedef enum {
DO_DIRECT,
DO_SHADING,
DO_IMAGE,
DO_TILED_IMAGE
DO_TILED_IMAGE,
DO_LAYER
} cairo_quartz_action_t;
/* define CTFontRef for pre-10.5 SDKs */
@ -74,6 +75,7 @@ typedef struct cairo_quartz_surface {
cairo_surface_clipper_t clipper;
cairo_rectangle_int_t extents;
cairo_rectangle_int_t virtual_extents;
CGLayerRef cgLayer;
} cairo_quartz_surface_t;
typedef struct cairo_quartz_image_surface {

View file

@ -1170,6 +1170,7 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
state->shading = NULL;
state->cgDrawContext = NULL;
state->cgMaskContext = NULL;
state->layer = NULL;
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
@ -1277,6 +1278,18 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
cairo_fixed_t fw, fh;
cairo_bool_t is_bounded;
if (pat_surf->backend->type == CAIRO_SURFACE_TYPE_QUARTZ) {
cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
if (quartz_surf->cgLayer && source->extend == CAIRO_EXTEND_NONE) {
cairo_matrix_invert (&m);
_cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
state->rect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
state->layer = quartz_surf->cgLayer;
state->action = DO_LAYER;
return CAIRO_STATUS_SUCCESS;
}
}
_cairo_surface_get_extents (composite->surface, &extents);
status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
&m, clip, &img);
@ -1400,9 +1413,10 @@ _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
if (state->layer) {
CGContextDrawLayerInRect (surface->cgContext,
state->clipRect,
state->layer);
if (state->action != DO_LAYER)
CGContextDrawLayerInRect (surface->cgContext,
state->clipRect,
state->layer);
CGLayerRelease (state->layer);
}
@ -1416,6 +1430,29 @@ _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
CGShadingRelease (state->shading);
}
static inline void
_cairo_quartz_draw_cgcontext (cairo_quartz_drawing_state_t *state,
cairo_operator_t op)
{
if (! (op == CAIRO_OPERATOR_SOURCE &&
state->cgDrawContext == state->cgMaskContext))
return;
CGContextBeginPath (state->cgDrawContext);
CGContextAddRect (state->cgDrawContext, state->rect);
CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
CGContextScaleCTM (state->cgDrawContext, 1, -1);
CGContextConcatCTM (state->cgDrawContext,
CGAffineTransformInvert (state->transform));
CGContextAddRect (state->cgDrawContext, state->clipRect);
CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
CGContextEOFillPath (state->cgDrawContext);
}
static void
_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
cairo_operator_t op)
@ -1440,25 +1477,19 @@ _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
if (state->action == DO_IMAGE) {
CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
if (op == CAIRO_OPERATOR_SOURCE &&
state->cgDrawContext == state->cgMaskContext)
{
CGContextBeginPath (state->cgDrawContext);
CGContextAddRect (state->cgDrawContext, state->rect);
CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
CGContextScaleCTM (state->cgDrawContext, 1, -1);
CGContextConcatCTM (state->cgDrawContext,
CGAffineTransformInvert (state->transform));
CGContextAddRect (state->cgDrawContext, state->clipRect);
CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
CGContextEOFillPath (state->cgDrawContext);
}
} else {
CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
_cairo_quartz_draw_cgcontext (state, op);
return;
}
if (state->action == DO_TILED_IMAGE) {
CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
return;
}
if (state->action == DO_LAYER) {
CGContextDrawLayerInRect (state->cgDrawContext, state->rect, state->layer);
_cairo_quartz_draw_cgcontext (state, op);
return;
}
assert (FALSE); // Unreachable
}
static cairo_image_surface_t *
@ -1526,8 +1557,6 @@ static cairo_int_status_t
_cairo_quartz_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
@ -1564,6 +1593,12 @@ _cairo_quartz_surface_finish (void *abstract_surface)
}
#endif
if (surface->cgLayer)
{
CGLayerRelease (surface->cgLayer);
surface->cgLayer = NULL;
}
return CAIRO_STATUS_SUCCESS;
}
@ -1596,6 +1631,46 @@ _cairo_quartz_surface_release_source_image (void *abstract_surface,
_cairo_quartz_surface_unmap_image (abstract_surface, image);
}
static cairo_surface_t*
_cairo_quartz_surface_create_with_cglayer (cairo_quartz_surface_t *surface,
cairo_content_t content,
int width, int height)
{
CGAffineTransform xform;
CGContextRef context;
CGLayerRef layer;
cairo_quartz_surface_t* new_surface;
if (surface->cgContext == NULL || surface->cgLayer != NULL)
return NULL;
if (width <= 0 || height <= 0)
return NULL;
xform = CGContextGetUserSpaceToDeviceSpaceTransform (surface->cgContext);
layer = CGLayerCreateWithContext (surface->cgContext,
CGSizeMake (width * xform.a,
height * xform.d),
NULL);
context = CGLayerGetContext (layer);
CGContextTranslateCTM (context, 0.0, height);
CGContextScaleCTM (context, xform.a, -xform.d);
new_surface = _cairo_quartz_surface_create_internal (context, content,
width, height);
if (unlikely (new_surface->base.status))
{
CGContextRelease (context);
CGLayerRelease (layer);
return &new_surface->base;
}
new_surface->cgLayer = CGLayerRetain(layer);
CGContextRetain(context);
new_surface->virtual_extents = surface->virtual_extents;
return &new_surface->base;
}
static cairo_surface_t *
_cairo_quartz_surface_create_similar (void *abstract_surface,
cairo_content_t content,
@ -1606,6 +1681,17 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
cairo_surface_t *similar;
cairo_format_t format;
// verify width and height of surface
if (!_cairo_quartz_verify_surface_size (width, height)) {
return _cairo_surface_create_in_error (_cairo_error
(CAIRO_STATUS_INVALID_SIZE));
}
surface = (cairo_quartz_surface_t *) abstract_surface;
if (surface->cgContext && !surface->cgLayer && !(width && height))
_cairo_quartz_surface_create_with_cglayer (surface, content,
width, height);
if (content == CAIRO_CONTENT_COLOR_ALPHA)
format = CAIRO_FORMAT_ARGB32;
else if (content == CAIRO_CONTENT_COLOR)
@ -1615,17 +1701,10 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
else
return NULL;
// verify width and height of surface
if (!_cairo_quartz_verify_surface_size (width, height)) {
return _cairo_surface_create_in_error (_cairo_error
(CAIRO_STATUS_INVALID_SIZE));
}
similar = cairo_quartz_surface_create (format, width, height);
if (unlikely (similar->status))
return similar;
surface = (cairo_quartz_surface_t *) abstract_surface;
similar_quartz = (cairo_quartz_surface_t *) similar;
similar_quartz->virtual_extents = surface->virtual_extents;