michael@0: changeset: 42959:e1964291f8ff michael@0: user: Robert O'Callahan michael@0: date: Tue Jun 01 11:33:23 2010 +1200 michael@0: summary: Bug 568189. Implement CGLayer-backed cairo-quartz surfaces. r=jrmuizel michael@0: michael@0: diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: --- a/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: @@ -57,16 +57,21 @@ typedef struct cairo_quartz_surface { michael@0: michael@0: /** michael@0: * If non-null, this is a CGImage representing the contents of the surface. michael@0: * We clear this out before any painting into the surface, so that we michael@0: * don't force a copy to be created. michael@0: */ michael@0: CGImageRef bitmapContextImage; michael@0: michael@0: + /** michael@0: + * If non-null, this is the CGLayer for the surface. michael@0: + */ michael@0: + CGLayerRef cgLayer; michael@0: + michael@0: cairo_rectangle_int_t extents; michael@0: } cairo_quartz_surface_t; michael@0: michael@0: typedef struct cairo_quartz_image_surface { michael@0: cairo_surface_t base; michael@0: michael@0: cairo_rectangle_int_t extents; michael@0: michael@0: diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: @@ -1110,18 +1110,17 @@ CreateRepeatingRadialGradientFunction (c michael@0: static void michael@0: DataProviderReleaseCallback (void *info, const void *data, size_t size) michael@0: { michael@0: cairo_surface_t *surface = (cairo_surface_t *) info; michael@0: cairo_surface_destroy (surface); michael@0: } michael@0: michael@0: static cairo_status_t michael@0: -_cairo_surface_to_cgimage (cairo_surface_t *target, michael@0: - cairo_surface_t *source, michael@0: +_cairo_surface_to_cgimage (cairo_surface_t *source, michael@0: CGImageRef *image_out) michael@0: { michael@0: cairo_status_t status = CAIRO_STATUS_SUCCESS; michael@0: cairo_surface_type_t stype = cairo_surface_get_type (source); michael@0: cairo_image_surface_t *isurf; michael@0: CGImageRef image; michael@0: void *image_extra; michael@0: michael@0: @@ -1267,17 +1266,17 @@ _cairo_quartz_cairo_repeating_surface_pa michael@0: return CAIRO_INT_STATUS_UNSUPPORTED; michael@0: michael@0: spattern = (cairo_surface_pattern_t *) apattern; michael@0: pat_surf = spattern->surface; michael@0: michael@0: is_bounded = _cairo_surface_get_extents (pat_surf, &extents); michael@0: assert (is_bounded); michael@0: michael@0: - status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image); michael@0: + status = _cairo_surface_to_cgimage (pat_surf, &image); michael@0: if (status) michael@0: return status; michael@0: if (image == NULL) michael@0: return CAIRO_INT_STATUS_NOTHING_TO_DO; michael@0: michael@0: info = malloc(sizeof(SurfacePatternDrawInfo)); michael@0: if (!info) michael@0: return CAIRO_STATUS_NO_MEMORY; michael@0: @@ -1339,33 +1338,39 @@ _cairo_quartz_cairo_repeating_surface_pa michael@0: } michael@0: michael@0: typedef enum { michael@0: DO_SOLID, michael@0: DO_SHADING, michael@0: DO_PATTERN, michael@0: DO_IMAGE, michael@0: DO_TILED_IMAGE, michael@0: + DO_LAYER, michael@0: DO_UNSUPPORTED, michael@0: DO_NOTHING michael@0: } cairo_quartz_action_t; michael@0: michael@0: /* State used during a drawing operation. */ michael@0: typedef struct { michael@0: CGContextRef context; michael@0: cairo_quartz_action_t action; michael@0: michael@0: - // Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE michael@0: + // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER michael@0: CGAffineTransform transform; michael@0: michael@0: // Used with DO_IMAGE and DO_TILED_IMAGE michael@0: CGImageRef image; michael@0: cairo_surface_t *imageSurface; michael@0: + michael@0: + // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER michael@0: CGRect imageRect; michael@0: michael@0: + // Used with DO_LAYER michael@0: + CGLayerRef layer; michael@0: + michael@0: // Used with DO_SHADING michael@0: CGShadingRef shading; michael@0: michael@0: // Used with DO_PATTERN michael@0: CGPatternRef pattern; michael@0: } cairo_quartz_drawing_state_t; michael@0: michael@0: static void michael@0: @@ -1423,17 +1428,17 @@ _cairo_quartz_setup_fallback_source (cai michael@0: _cairo_pattern_transform (&pattern.base, michael@0: &fallback->device_transform_inverse); michael@0: status = _cairo_surface_paint (fallback, michael@0: CAIRO_OPERATOR_SOURCE, michael@0: &pattern.base, NULL); michael@0: } michael@0: #endif michael@0: michael@0: - status = _cairo_surface_to_cgimage (&surface->base, fallback, &img); michael@0: + status = _cairo_surface_to_cgimage (fallback, &img); michael@0: if (status) { michael@0: state->action = DO_UNSUPPORTED; michael@0: return; michael@0: } michael@0: if (img == NULL) { michael@0: state->action = DO_NOTHING; michael@0: return; michael@0: } michael@0: @@ -1624,16 +1629,17 @@ _cairo_quartz_setup_state (cairo_quartz_ michael@0: { michael@0: CGContextRef context = surface->cgContext; michael@0: cairo_quartz_drawing_state_t state; michael@0: cairo_status_t status; michael@0: michael@0: state.context = context; michael@0: state.image = NULL; michael@0: state.imageSurface = NULL; michael@0: + state.layer = NULL; michael@0: state.shading = NULL; michael@0: state.pattern = NULL; michael@0: michael@0: _cairo_quartz_surface_will_change (surface); michael@0: michael@0: // Save before we change the pattern, colorspace, etc. so that michael@0: // we can restore and make sure that quartz releases our michael@0: // pattern (which may be stack allocated) michael@0: @@ -1689,33 +1695,43 @@ _cairo_quartz_setup_state (cairo_quartz_ michael@0: CGImageRef img; michael@0: cairo_matrix_t m = spat->base.matrix; michael@0: cairo_rectangle_int_t extents; michael@0: CGAffineTransform xform; michael@0: CGRect srcRect; michael@0: cairo_fixed_t fw, fh; michael@0: cairo_bool_t is_bounded; michael@0: michael@0: - status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img); michael@0: + cairo_matrix_invert(&m); michael@0: + _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform); michael@0: + michael@0: + if (cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { michael@0: + cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf; michael@0: + if (quartz_surf->cgLayer && source->extend == CAIRO_EXTEND_NONE) { michael@0: + state.imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height); michael@0: + state.layer = quartz_surf->cgLayer; michael@0: + state.action = DO_LAYER; michael@0: + return state; michael@0: + } michael@0: + } michael@0: + michael@0: + status = _cairo_surface_to_cgimage (pat_surf, &img); michael@0: if (status) { michael@0: state.action = DO_UNSUPPORTED; michael@0: return state; michael@0: } michael@0: if (img == NULL) { michael@0: state.action = DO_NOTHING; michael@0: return state; michael@0: } michael@0: michael@0: CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1); michael@0: michael@0: state.image = img; michael@0: michael@0: - cairo_matrix_invert(&m); michael@0: - _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform); michael@0: - michael@0: is_bounded = _cairo_surface_get_extents (pat_surf, &extents); michael@0: assert (is_bounded); michael@0: michael@0: if (source->extend == CAIRO_EXTEND_NONE) { michael@0: state.imageRect = CGRectMake (0, 0, extents.width, extents.height); michael@0: state.action = DO_IMAGE; michael@0: return state; michael@0: } michael@0: @@ -1820,33 +1836,48 @@ _cairo_quartz_teardown_state (cairo_quar michael@0: michael@0: CGContextRestoreGState(state->context); michael@0: } michael@0: michael@0: michael@0: static void michael@0: _cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op) michael@0: { michael@0: - assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)); michael@0: + assert (state && michael@0: + ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) || michael@0: + (state->layer && state->action == DO_LAYER))); michael@0: michael@0: CGContextConcatCTM (state->context, state->transform); michael@0: CGContextTranslateCTM (state->context, 0, state->imageRect.size.height); michael@0: CGContextScaleCTM (state->context, 1, -1); michael@0: michael@0: - if (state->action == DO_IMAGE) { michael@0: - CGContextDrawImage (state->context, state->imageRect, state->image); michael@0: + if (state->action == DO_TILED_IMAGE) { michael@0: + CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image); michael@0: + /* no need to worry about unbounded operators, since tiled images michael@0: + fill the entire clip region */ michael@0: + } else { michael@0: + if (state->action == DO_LAYER) { michael@0: + /* Note that according to Apple docs it's completely legal michael@0: + * to draw a CGLayer to any CGContext, even one it wasn't michael@0: + * created for. michael@0: + */ michael@0: + CGContextDrawLayerAtPoint (state->context, state->imageRect.origin, michael@0: + state->layer); michael@0: + } else { michael@0: + CGContextDrawImage (state->context, state->imageRect, state->image); michael@0: + } michael@0: + michael@0: if (!_cairo_operator_bounded_by_source (op)) { michael@0: CGContextBeginPath (state->context); michael@0: CGContextAddRect (state->context, state->imageRect); michael@0: CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context)); michael@0: CGContextSetRGBFillColor (state->context, 0, 0, 0, 0); michael@0: CGContextEOFillPath (state->context); michael@0: } michael@0: - } else michael@0: - CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image); michael@0: + } michael@0: } michael@0: michael@0: michael@0: /* michael@0: * get source/dest image implementation michael@0: */ michael@0: michael@0: /* Read the image from the surface's front buffer */ michael@0: @@ -1971,95 +2002,153 @@ _cairo_quartz_surface_finish (void *abst michael@0: surface->imageSurfaceEquiv = NULL; michael@0: } michael@0: michael@0: if (surface->imageData) { michael@0: free (surface->imageData); michael@0: surface->imageData = NULL; michael@0: } michael@0: michael@0: + if (surface->cgLayer) { michael@0: + CGLayerRelease (surface->cgLayer); michael@0: + } michael@0: + michael@0: return CAIRO_STATUS_SUCCESS; michael@0: } michael@0: michael@0: static cairo_status_t michael@0: -_cairo_quartz_surface_acquire_source_image (void *abstract_surface, michael@0: - cairo_image_surface_t **image_out, michael@0: - void **image_extra) michael@0: +_cairo_quartz_surface_acquire_image (void *abstract_surface, michael@0: + cairo_image_surface_t **image_out, michael@0: + void **image_extra) michael@0: { michael@0: cairo_int_status_t status; michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: michael@0: - //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface)); michael@0: + *image_extra = NULL; michael@0: + michael@0: + /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */ michael@0: michael@0: status = _cairo_quartz_get_image (surface, image_out); michael@0: + michael@0: + if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) { michael@0: + /* copy the layer into a Quartz bitmap context so we can get the data */ michael@0: + cairo_surface_t *tmp = michael@0: + cairo_quartz_surface_create (CAIRO_CONTENT_COLOR_ALPHA, michael@0: + surface->extents.width, michael@0: + surface->extents.height); michael@0: + cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp; michael@0: + michael@0: + /* if surface creation failed, we won't have a Quartz surface here */ michael@0: + if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ && michael@0: + tmp_surface->imageSurfaceEquiv) { michael@0: + CGContextSaveGState (tmp_surface->cgContext); michael@0: + CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height); michael@0: + CGContextScaleCTM (tmp_surface->cgContext, 1, -1); michael@0: + /* Note that according to Apple docs it's completely legal michael@0: + * to draw a CGLayer to any CGContext, even one it wasn't michael@0: + * created for. michael@0: + */ michael@0: + CGContextDrawLayerAtPoint (tmp_surface->cgContext, michael@0: + CGPointMake (0.0, 0.0), michael@0: + surface->cgLayer); michael@0: + CGContextRestoreGState (tmp_surface->cgContext); michael@0: + michael@0: + *image_out = (cairo_image_surface_t*) michael@0: + cairo_surface_reference(tmp_surface->imageSurfaceEquiv); michael@0: + *image_extra = tmp; michael@0: + } else { michael@0: + cairo_surface_destroy (tmp); michael@0: + } michael@0: + } michael@0: + michael@0: if (status) michael@0: return _cairo_error (CAIRO_STATUS_NO_MEMORY); michael@0: michael@0: - *image_extra = NULL; michael@0: - michael@0: return CAIRO_STATUS_SUCCESS; michael@0: } michael@0: michael@0: static void michael@0: _cairo_quartz_surface_release_source_image (void *abstract_surface, michael@0: cairo_image_surface_t *image, michael@0: void *image_extra) michael@0: { michael@0: cairo_surface_destroy ((cairo_surface_t *) image); michael@0: + michael@0: + if (image_extra) { michael@0: + cairo_surface_destroy ((cairo_surface_t *) image_extra); michael@0: + } michael@0: } michael@0: michael@0: michael@0: static cairo_status_t michael@0: _cairo_quartz_surface_acquire_dest_image (void *abstract_surface, michael@0: cairo_rectangle_int_t *interest_rect, michael@0: cairo_image_surface_t **image_out, michael@0: cairo_rectangle_int_t *image_rect, michael@0: void **image_extra) michael@0: { michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: - cairo_int_status_t status; michael@0: michael@0: ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface)); michael@0: michael@0: - _cairo_quartz_surface_will_change (surface); michael@0: - michael@0: - status = _cairo_quartz_get_image (surface, image_out); michael@0: - if (status) michael@0: - return _cairo_error (CAIRO_STATUS_NO_MEMORY); michael@0: - michael@0: *image_rect = surface->extents; michael@0: *image_extra = NULL; michael@0: michael@0: - return CAIRO_STATUS_SUCCESS; michael@0: + _cairo_quartz_surface_will_change (surface); michael@0: + michael@0: + return _cairo_quartz_surface_acquire_image (abstract_surface, michael@0: + image_out, image_extra); michael@0: } michael@0: michael@0: static void michael@0: _cairo_quartz_surface_release_dest_image (void *abstract_surface, michael@0: cairo_rectangle_int_t *interest_rect, michael@0: cairo_image_surface_t *image, michael@0: cairo_rectangle_int_t *image_rect, michael@0: void *image_extra) michael@0: { michael@0: - //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: - michael@0: - //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); michael@0: + /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */ michael@0: michael@0: cairo_surface_destroy ((cairo_surface_t *) image); michael@0: + michael@0: + if (image_extra) { michael@0: + /* we need to write the data from the temp surface back to the layer */ michael@0: + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: + cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra; michael@0: + CGImageRef img; michael@0: + cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img); michael@0: + if (status) { michael@0: + cairo_surface_destroy (&tmp_surface->base); michael@0: + return; michael@0: + } michael@0: + michael@0: + CGContextSaveGState (surface->cgContext); michael@0: + CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height); michael@0: + CGContextScaleCTM (surface->cgContext, 1, -1); michael@0: + CGContextDrawImage (surface->cgContext, michael@0: + CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height), michael@0: + img); michael@0: + CGContextRestoreGState (surface->cgContext); michael@0: + michael@0: + cairo_surface_destroy (&tmp_surface->base); michael@0: + } michael@0: } michael@0: michael@0: static cairo_surface_t * michael@0: _cairo_quartz_surface_create_similar (void *abstract_surface, michael@0: cairo_content_t content, michael@0: int width, michael@0: int height) michael@0: { michael@0: - /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/ michael@0: - michael@0: + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: cairo_format_t format; michael@0: michael@0: + if (surface->cgLayer) michael@0: + return cairo_quartz_surface_create_cg_layer (abstract_surface, width, height); michael@0: + michael@0: if (content == CAIRO_CONTENT_COLOR_ALPHA) michael@0: format = CAIRO_FORMAT_ARGB32; michael@0: else if (content == CAIRO_CONTENT_COLOR) michael@0: format = CAIRO_FORMAT_RGB24; michael@0: else if (content == CAIRO_CONTENT_ALPHA) michael@0: format = CAIRO_FORMAT_A8; michael@0: else michael@0: return NULL; michael@0: @@ -2113,17 +2202,17 @@ _cairo_quartz_surface_clone_similar (voi michael@0: _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, michael@0: qsurf->extents.width, qsurf->extents.height); michael@0: *clone_offset_x = 0; michael@0: *clone_offset_y = 0; michael@0: return CAIRO_STATUS_SUCCESS; michael@0: } michael@0: } michael@0: michael@0: - status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image); michael@0: + status = _cairo_surface_to_cgimage (src, &quartz_image); michael@0: if (status) michael@0: return CAIRO_INT_STATUS_UNSUPPORTED; michael@0: michael@0: new_format = CAIRO_FORMAT_ARGB32; /* assumed */ michael@0: if (_cairo_surface_is_image (src)) { michael@0: new_format = ((cairo_image_surface_t *) src)->format; michael@0: } michael@0: michael@0: @@ -2194,17 +2283,18 @@ _cairo_quartz_surface_paint (void *abstr michael@0: if (state.action == DO_SOLID || state.action == DO_PATTERN) { michael@0: CGContextFillRect (state.context, CGRectMake(surface->extents.x, michael@0: surface->extents.y, michael@0: surface->extents.width, michael@0: surface->extents.height)); michael@0: } else if (state.action == DO_SHADING) { michael@0: CGContextConcatCTM (state.context, state.transform); michael@0: CGContextDrawShading (state.context, state.shading); michael@0: - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || michael@0: + state.action == DO_LAYER) { michael@0: _cairo_quartz_draw_image (&state, op); michael@0: } else if (state.action != DO_NOTHING) { michael@0: rv = CAIRO_INT_STATUS_UNSUPPORTED; michael@0: } michael@0: michael@0: _cairo_quartz_teardown_state (&state); michael@0: michael@0: ND((stderr, "-- paint\n")); michael@0: @@ -2291,17 +2381,18 @@ _cairo_quartz_surface_fill (void *abstra michael@0: // with the shading michael@0: if (fill_rule == CAIRO_FILL_RULE_WINDING) michael@0: CGContextClip (state.context); michael@0: else michael@0: CGContextEOClip (state.context); michael@0: michael@0: CGContextConcatCTM (state.context, state.transform); michael@0: CGContextDrawShading (state.context, state.shading); michael@0: - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || michael@0: + state.action == DO_LAYER) { michael@0: if (fill_rule == CAIRO_FILL_RULE_WINDING) michael@0: CGContextClip (state.context); michael@0: else michael@0: CGContextEOClip (state.context); michael@0: michael@0: _cairo_quartz_draw_image (&state, op); michael@0: } else if (state.action != DO_NOTHING) { michael@0: rv = CAIRO_INT_STATUS_UNSUPPORTED; michael@0: @@ -2416,17 +2507,18 @@ _cairo_quartz_surface_stroke (void *abst michael@0: if (rv) michael@0: goto BAIL; michael@0: michael@0: if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr) michael@0: path_for_unbounded = CGContextCopyPathPtr (state.context); michael@0: michael@0: if (state.action == DO_SOLID || state.action == DO_PATTERN) { michael@0: CGContextStrokePath (state.context); michael@0: - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || michael@0: + state.action == DO_LAYER) { michael@0: CGContextReplacePathWithStrokedPath (state.context); michael@0: CGContextClip (state.context); michael@0: michael@0: CGContextSetCTM (state.context, origCTM); michael@0: _cairo_quartz_draw_image (&state, op); michael@0: } else if (state.action == DO_SHADING) { michael@0: CGContextReplacePathWithStrokedPath (state.context); michael@0: CGContextClip (state.context); michael@0: @@ -2511,17 +2603,18 @@ _cairo_quartz_surface_show_glyphs (void michael@0: &glyph_extents, NULL); michael@0: state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents); michael@0: } else { michael@0: state = _cairo_quartz_setup_state (surface, source, op, NULL); michael@0: } michael@0: michael@0: if (state.action == DO_SOLID || state.action == DO_PATTERN) { michael@0: CGContextSetTextDrawingMode (state.context, kCGTextFill); michael@0: - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_SHADING) { michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || michael@0: + state.action == DO_SHADING || state.action == DO_LAYER) { michael@0: CGContextSetTextDrawingMode (state.context, kCGTextClip); michael@0: isClipping = TRUE; michael@0: } else { michael@0: if (state.action != DO_NOTHING) michael@0: rv = CAIRO_INT_STATUS_UNSUPPORTED; michael@0: goto BAIL; michael@0: } michael@0: michael@0: @@ -2622,17 +2715,18 @@ _cairo_quartz_surface_show_glyphs (void michael@0: michael@0: CGContextShowGlyphsWithAdvances (state.context, michael@0: cg_glyphs, michael@0: cg_advances, michael@0: num_glyphs); michael@0: michael@0: CGContextSetCTM (state.context, ctm); michael@0: michael@0: - if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: + if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || michael@0: + state.action == DO_LAYER) { michael@0: _cairo_quartz_draw_image (&state, op); michael@0: } else if (state.action == DO_SHADING) { michael@0: CGContextConcatCTM (state.context, state.transform); michael@0: CGContextDrawShading (state.context, state.shading); michael@0: } michael@0: michael@0: BAIL: michael@0: if (didForceFontSmoothing) michael@0: @@ -2679,17 +2773,17 @@ _cairo_quartz_surface_mask_with_surface michael@0: cairo_clip_t *clip) michael@0: { michael@0: CGRect rect; michael@0: CGImageRef img; michael@0: cairo_surface_t *pat_surf = mask->surface; michael@0: cairo_status_t status = CAIRO_STATUS_SUCCESS; michael@0: CGAffineTransform ctm, mask_matrix; michael@0: michael@0: - status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img); michael@0: + status = _cairo_surface_to_cgimage (pat_surf, &img); michael@0: if (status) michael@0: return status; michael@0: if (img == NULL) { michael@0: if (!_cairo_operator_bounded_by_mask (op)) michael@0: CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); michael@0: return CAIRO_STATUS_SUCCESS; michael@0: } michael@0: michael@0: @@ -2869,17 +2963,17 @@ _cairo_quartz_surface_clipper_intersect_ michael@0: } michael@0: michael@0: // XXXtodo implement show_page; need to figure out how to handle begin/end michael@0: michael@0: static const struct _cairo_surface_backend cairo_quartz_surface_backend = { michael@0: CAIRO_SURFACE_TYPE_QUARTZ, michael@0: _cairo_quartz_surface_create_similar, michael@0: _cairo_quartz_surface_finish, michael@0: - _cairo_quartz_surface_acquire_source_image, michael@0: + _cairo_quartz_surface_acquire_image, michael@0: _cairo_quartz_surface_release_source_image, michael@0: _cairo_quartz_surface_acquire_dest_image, michael@0: _cairo_quartz_surface_release_dest_image, michael@0: _cairo_quartz_surface_clone_similar, michael@0: NULL, /* composite */ michael@0: NULL, /* fill_rectangles */ michael@0: NULL, /* composite_trapezoids */ michael@0: NULL, /* create_span_renderer */ michael@0: @@ -2950,16 +3044,17 @@ _cairo_quartz_surface_create_internal (C michael@0: CGContextSaveGState (cgContext); michael@0: michael@0: surface->cgContext = cgContext; michael@0: surface->cgContextBaseCTM = CGContextGetCTM (cgContext); michael@0: michael@0: surface->imageData = NULL; michael@0: surface->imageSurfaceEquiv = NULL; michael@0: surface->bitmapContextImage = NULL; michael@0: + surface->cgLayer = NULL; michael@0: michael@0: return surface; michael@0: } michael@0: michael@0: /** michael@0: * cairo_quartz_surface_create_for_cg_context michael@0: * @cgContext: the existing CGContext for which to create the surface michael@0: * @width: width of the surface, in pixels michael@0: @@ -3002,16 +3097,88 @@ cairo_quartz_surface_create_for_cg_conte michael@0: // create_internal will have set an error michael@0: return (cairo_surface_t*) surf; michael@0: } michael@0: michael@0: return (cairo_surface_t *) surf; michael@0: } michael@0: michael@0: /** michael@0: + * cairo_quartz_cglayer_surface_create_similar michael@0: + * @surface: The returned surface can be efficiently drawn into this michael@0: + * destination surface (if tiling is not used)." michael@0: + * @width: width of the surface, in pixels michael@0: + * @height: height of the surface, in pixels michael@0: + * michael@0: + * Creates a Quartz surface backed by a CGLayer, if the given surface michael@0: + * is a Quartz surface; the CGLayer is created to match the surface's michael@0: + * Quartz context. Otherwise just calls cairo_surface_create_similar michael@0: + * with CAIRO_CONTENT_COLOR_ALPHA. michael@0: + * The returned surface can be efficiently blitted to the given surface, michael@0: + * but tiling and 'extend' modes other than NONE are not so efficient. michael@0: + * michael@0: + * Return value: the newly created surface. michael@0: + * michael@0: + * Since: 1.10 michael@0: + **/ michael@0: +cairo_surface_t * michael@0: +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, michael@0: + unsigned int width, michael@0: + unsigned int height) michael@0: +{ michael@0: + cairo_quartz_surface_t *surf; michael@0: + CGLayerRef layer; michael@0: + CGContextRef ctx; michael@0: + CGContextRef cgContext; michael@0: + michael@0: + cgContext = cairo_quartz_surface_get_cg_context (surface); michael@0: + if (!cgContext) michael@0: + return cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, michael@0: + width, height); michael@0: + michael@0: + if (!_cairo_quartz_verify_surface_size(width, height)) michael@0: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); michael@0: + michael@0: + /* If we pass zero width or height into CGLayerCreateWithContext below, michael@0: + * it will fail. michael@0: + */ michael@0: + if (width == 0 || height == 0) { michael@0: + return (cairo_surface_t*) michael@0: + _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, michael@0: + width, height); michael@0: + } michael@0: + michael@0: + layer = CGLayerCreateWithContext (cgContext, michael@0: + CGSizeMake (width, height), michael@0: + NULL); michael@0: + if (!layer) michael@0: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); michael@0: + michael@0: + ctx = CGLayerGetContext (layer); michael@0: + /* Flip it when we draw into it, so that when we finally composite it michael@0: + * to a flipped target, the directions match and Quartz will optimize michael@0: + * the composition properly michael@0: + */ michael@0: + CGContextTranslateCTM (ctx, 0, height); michael@0: + CGContextScaleCTM (ctx, 1, -1); michael@0: + michael@0: + CGContextRetain (ctx); michael@0: + surf = _cairo_quartz_surface_create_internal (ctx, CAIRO_CONTENT_COLOR_ALPHA, michael@0: + width, height); michael@0: + if (surf->base.status) { michael@0: + CGLayerRelease (layer); michael@0: + // create_internal will have set an error michael@0: + return (cairo_surface_t*) surf; michael@0: + } michael@0: + surf->cgLayer = layer; michael@0: + michael@0: + return (cairo_surface_t *) surf; michael@0: +} michael@0: + michael@0: +/** michael@0: * cairo_quartz_surface_create michael@0: * @format: format of pixels in the surface to create michael@0: * @width: width of the surface, in pixels michael@0: * @height: height of the surface, in pixels michael@0: * michael@0: * Creates a Quartz surface backed by a CGBitmap. The surface is michael@0: * created using the Device RGB (or Device Gray, for A8) color space. michael@0: * All Cairo operations, including those that require software michael@0: diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h michael@0: --- a/gfx/cairo/cairo/src/cairo-quartz.h michael@0: +++ b/gfx/cairo/cairo/src/cairo-quartz.h michael@0: @@ -45,16 +45,21 @@ michael@0: CAIRO_BEGIN_DECLS michael@0: michael@0: cairo_public cairo_surface_t * michael@0: cairo_quartz_surface_create (cairo_format_t format, michael@0: unsigned int width, michael@0: unsigned int height); michael@0: michael@0: cairo_public cairo_surface_t * michael@0: +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, michael@0: + unsigned int width, michael@0: + unsigned int height); michael@0: + michael@0: +cairo_public cairo_surface_t * michael@0: cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, michael@0: unsigned int width, michael@0: unsigned int height); michael@0: michael@0: cairo_public CGContextRef michael@0: cairo_quartz_surface_get_cg_context (cairo_surface_t *surface); michael@0: michael@0: cairo_public CGContextRef michael@0: