diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h --- a/gfx/cairo/cairo/src/cairo-quartz-private.h +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h @@ -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 */ @@ -72,6 +73,11 @@ typedef struct cairo_quartz_surface { cairo_surface_clipper_t clipper; + /** + * If non-null, this is the CGLayer for the surface. + */ + CGLayerRef cgLayer; + cairo_rectangle_int_t extents; cairo_rectangle_int_t virtual_extents; diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -503,6 +503,7 @@ static CGBlendMode default: ASSERT_NOT_REACHED; } + return kCGBlendModeNormal; /* just to silence clang warning [-Wreturn-type] */ } static cairo_int_status_t @@ -1065,7 +1066,7 @@ typedef struct { /* Destination rect */ CGRect rect; - /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */ + /* Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE, DO_LAYER */ CGAffineTransform transform; /* Used with DO_IMAGE and DO_TILED_IMAGE */ @@ -1077,6 +1078,11 @@ typedef struct { /* Temporary destination for unbounded operations */ CGLayerRef layer; CGRect clipRect; + + /* Source layer to be rendered when using DO_LAYER. + Unlike 'layer' above, this is not owned by the drawing state + but by the source surface. */ + CGLayerRef sourceLayer; } cairo_quartz_drawing_state_t; /* @@ -1253,7 +1259,9 @@ static cairo_int_status_t } if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) + (source->extend == CAIRO_EXTEND_NONE || + source->extend == CAIRO_EXTEND_PAD || + (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) { const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; cairo_surface_t *pat_surf = spat->surface; @@ -1265,6 +1273,20 @@ static cairo_int_status_t cairo_fixed_t fw, fh; cairo_bool_t is_bounded; + /* Draw nonrepeating CGLayer surface using DO_LAYER */ + if (source->extend != CAIRO_EXTEND_REPEAT && + cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { + cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf; + if (quartz_surf->cgLayer) { + 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->sourceLayer = 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); @@ -1426,7 +1448,14 @@ static void CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height); CGContextScaleCTM (state->cgDrawContext, 1, -1); - if (state->action == DO_IMAGE) { + if (state->action == DO_LAYER) { + /* Note that according to Apple docs it's completely legal to draw a CGLayer + * to any CGContext, even one it wasn't created for. + */ + assert (state->sourceLayer); + CGContextDrawLayerAtPoint (state->cgDrawContext, state->rect.origin, + state->sourceLayer); + } else if (state->action == DO_IMAGE) { CGContextDrawImage (state->cgDrawContext, state->rect, state->image); if (op == CAIRO_OPERATOR_SOURCE && state->cgDrawContext == state->cgMaskContext) @@ -1655,6 +1684,10 @@ static cairo_status_t surface->imageData = NULL; + if (surface->cgLayer) { + CGLayerRelease (surface->cgLayer); + } + return CAIRO_STATUS_SUCCESS; } @@ -1693,9 +1726,14 @@ static cairo_surface_t * int width, int height) { - cairo_quartz_surface_t *surface, *similar_quartz; + cairo_quartz_surface_t *similar_quartz; cairo_surface_t *similar; cairo_format_t format; + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + + if (surface->cgLayer) + return cairo_quartz_surface_create_cg_layer (abstract_surface, content, + width, height); if (content == CAIRO_CONTENT_COLOR_ALPHA) format = CAIRO_FORMAT_ARGB32; @@ -2068,7 +2106,6 @@ static cairo_int_status_t cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; int i; - CGFontRef cgfref = NULL; cairo_bool_t didForceFontSmoothing = FALSE; cairo_antialias_t effective_antialiasing; @@ -2087,10 +2124,12 @@ static cairo_int_status_t CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip); } - /* this doesn't addref */ - cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); - CGContextSetFont (state.cgMaskContext, cgfref); - CGContextSetFontSize (state.cgMaskContext, 1.0); + if (!CTFontDrawGlyphsPtr) { + /* this doesn't addref */ + CGFontRef cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); + CGContextSetFont (state.cgMaskContext, cgfref); + CGContextSetFontSize (state.cgMaskContext, 1.0); + } effective_antialiasing = scaled_font->options.antialias; if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && @@ -2625,6 +2664,79 @@ cairo_quartz_surface_create_for_cg_conte } /** + * cairo_quartz_surface_create_cg_layer + * @surface: The returned surface can be efficiently drawn into this + * destination surface (if tiling is not used)." + * @content: the content type of the surface + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a Quartz surface backed by a CGLayer, if the given surface + * is a Quartz surface; the CGLayer is created to match the surface's + * Quartz context. Otherwise just calls cairo_surface_create_similar. + * The returned surface can be efficiently blitted to the given surface, + * but tiling and 'extend' modes other than NONE are not so efficient. + * + * Return value: the newly created surface. + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, + cairo_content_t content, + unsigned int width, + unsigned int height) +{ + cairo_quartz_surface_t *surf; + CGLayerRef layer; + CGContextRef ctx; + CGContextRef cgContext; + + cgContext = cairo_quartz_surface_get_cg_context (surface); + if (!cgContext) + return cairo_surface_create_similar (surface, content, + width, height); + + if (!_cairo_quartz_verify_surface_size(width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + /* If we pass zero width or height into CGLayerCreateWithContext below, + * it will fail. + */ + if (width == 0 || height == 0) { + return (cairo_surface_t*) + _cairo_quartz_surface_create_internal (NULL, content, + width, height); + } + + layer = CGLayerCreateWithContext (cgContext, + CGSizeMake (width, height), + NULL); + if (!layer) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + ctx = CGLayerGetContext (layer); + /* Flip it when we draw into it, so that when we finally composite it + * to a flipped target, the directions match and Quartz will optimize + * the composition properly + */ + CGContextTranslateCTM (ctx, 0, height); + CGContextScaleCTM (ctx, 1, -1); + + CGContextRetain (ctx); + surf = _cairo_quartz_surface_create_internal (ctx, content, + width, height); + if (surf->base.status) { + CGLayerRelease (layer); + // create_internal will have set an error + return (cairo_surface_t*) surf; + } + surf->cgLayer = layer; + + return (cairo_surface_t *) surf; +} + +/** * cairo_quartz_surface_create: * @format: format of pixels in the surface to create * @width: width of the surface, in pixels diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h --- a/gfx/cairo/cairo/src/cairo-quartz.h +++ b/gfx/cairo/cairo/src/cairo-quartz.h @@ -61,6 +61,12 @@ cairo_quartz_surface_create_for_data (un unsigned int height, unsigned int stride); +cairo_public cairo_surface_t * +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, + cairo_content_t content, + unsigned int width, + unsigned int height); + cairo_public CGContextRef cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);