diff options
author | Vladimir Vukicevic <vladimir@pobox.com> | 2008-02-25 21:06:21 -0500 |
---|---|---|
committer | Vladimir Vukicevic <vladimir@sleet.local> | 2008-02-25 21:06:21 -0500 |
commit | 63711b1d4a61f21db070f30b9c153d0923cc24bb (patch) | |
tree | de394864ab9e30c4ad12abc8671aaf52628e5b66 | |
parent | ecb54b96967ae0d1b5ec3a4d0955f46dd68f2b8b (diff) |
[quartz] Add quartz-image-surface type
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/cairo-quartz-image-surface.c | 370 | ||||
-rw-r--r-- | src/cairo-quartz-private.h | 31 | ||||
-rw-r--r-- | src/cairo-quartz-surface.c | 476 | ||||
-rw-r--r-- | src/cairo-quartz.h | 6 | ||||
-rw-r--r-- | src/cairo.h | 4 |
6 files changed, 634 insertions, 255 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 525ca66a..3656f050 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -178,7 +178,7 @@ backend_pkgconfigs += cairo-xcb.pc endif quartz_headers = cairo-quartz.h -quartz_sources = cairo-quartz-surface.c cairo-quartz-private.h +quartz_sources = cairo-quartz-surface.c cairo-quartz-image-surface.c cairo-quartz-private.h cairo_all_sources += $(quartz_headers) $(quartz_sources) if CAIRO_HAS_QUARTZ_SURFACE cairo_headers += $(quartz_headers) diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c new file mode 100644 index 00000000..7a6d07d7 --- /dev/null +++ b/src/cairo-quartz-image-surface.c @@ -0,0 +1,370 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright � 2008 Mozilla Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * + * Contributor(s): + * Vladimir Vukicevic <vladimir@mozilla.com> + */ + +#include "cairoint.h" + +#include "cairo-quartz-private.h" + +#define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY))) +#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT))) + +static void +DataProviderReleaseCallback (void *info, const void *data, size_t size) +{ + cairo_surface_t *surface = (cairo_surface_t *) info; + cairo_surface_destroy (surface); +} + +CGImageRef +_cairo_quartz_create_cgimage (cairo_format_t format, + unsigned int width, + unsigned int height, + unsigned int stride, + void *data, + cairo_bool_t interpolate, + CGColorSpaceRef colorSpaceOverride, + CGDataProviderReleaseDataCallback releaseCallback, + void *releaseInfo) +{ + CGImageRef image = NULL; + CGDataProviderRef dataProvider = NULL; + CGColorSpaceRef colorSpace = colorSpaceOverride; + CGBitmapInfo bitinfo; + int bitsPerComponent, bitsPerPixel; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + if (colorSpace == NULL) + colorSpace = CGColorSpaceCreateDeviceRGB(); + bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; + bitsPerComponent = 8; + bitsPerPixel = 32; + break; + + case CAIRO_FORMAT_RGB24: + if (colorSpace == NULL) + colorSpace = CGColorSpaceCreateDeviceRGB(); + bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; + bitsPerComponent = 8; + bitsPerPixel = 32; + break; + + /* XXX -- should use CGImageMaskCreate! */ + case CAIRO_FORMAT_A8: + if (colorSpace == NULL) + colorSpace = CGColorSpaceCreateDeviceGray(); + bitinfo = kCGImageAlphaNone; + bitsPerComponent = 8; + bitsPerPixel = 8; + break; + + case CAIRO_FORMAT_A1: + default: + return NULL; + } + + dataProvider = CGDataProviderCreateWithData (releaseInfo, + data, + height * stride, + releaseCallback); + + if (!dataProvider) { + // manually release + if (releaseCallback) + releaseCallback (releaseInfo, data, height * stride); + goto FINISH; + } + + image = CGImageCreate (width, height, + bitsPerComponent, + bitsPerPixel, + stride, + colorSpace, + bitinfo, + dataProvider, + NULL, + interpolate, + kCGRenderingIntentDefault); + +FINISH: + + CGDataProviderRelease (dataProvider); + + if (colorSpace != colorSpaceOverride) + CGColorSpaceRelease (colorSpace); + + return image; +} + + +static cairo_surface_t * +_cairo_quartz_image_surface_create_similar (void *asurface, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t *result; + cairo_surface_t *isurf = cairo_image_surface_create (_cairo_format_from_content (content), + width, + height); + if (cairo_surface_status(isurf)) + return isurf; + + result = cairo_quartz_image_surface_create (isurf); + cairo_surface_destroy (isurf); + + return result; +} + +static cairo_status_t +_cairo_quartz_image_surface_finish (void *asurface) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + + /* the imageSurface will be destroyed by the data provider's release callback */ + CGImageRelease (surface->image); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_quartz_image_surface_acquire_source_image (void *asurface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + + *image_out = surface->imageSurface; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_quartz_image_surface_acquire_dest_image (void *asurface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + + *image_out = surface->imageSurface; + *image_rect = surface->extents; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; + +} + +static cairo_int_status_t +_cairo_quartz_image_surface_get_extents (void *asurface, + cairo_rectangle_int_t *extents) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + + *extents = surface->extents; + + return CAIRO_STATUS_SUCCESS; +} + +/* we assume some drawing happened to the image buffer; make sure it's + * represented in the CGImage on flush() + */ + +static cairo_status_t +_cairo_quartz_image_surface_flush (void *asurface) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + CGImageRef oldImage = surface->image; + CGImageRef newImage = NULL; + + /* To be released by the ReleaseCallback */ + cairo_surface_reference ((cairo_surface_t*) surface->imageSurface); + + newImage = _cairo_quartz_create_cgimage (surface->imageSurface->format, + surface->imageSurface->width, + surface->imageSurface->height, + surface->imageSurface->stride, + surface->imageSurface->data, + FALSE, + NULL, + DataProviderReleaseCallback, + surface->imageSurface); + + surface->image = newImage; + CGImageRelease (oldImage); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { + CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, + _cairo_quartz_image_surface_create_similar, + _cairo_quartz_image_surface_finish, + _cairo_quartz_image_surface_acquire_source_image, + NULL, /* release_source_image */ + _cairo_quartz_image_surface_acquire_dest_image, + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* copy_page */ + NULL, /* show_page */ + NULL, /* set_clip_region */ + NULL, /* intersect_clip_path */ + _cairo_quartz_image_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + _cairo_quartz_image_surface_flush, + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + NULL, /* surface_show_glyphs */ + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* reset */ + NULL /* fill_stroke */ + +}; + +/** + * cairo_quartz_image_surface_create + * @surface: a cairo image surface to wrap with a quartz image surface + * + * Creates a Quartz surface backed by a CGImageRef that references the + * given image surface. The resulting surface can be rendered quickly + * when used as a source when rendering to a #cairo_quartz_surface. If + * the data in the image surface is every updated, cairo_surface_flush() + * must be called on the #cairo_quartz_image_surface to ensure that the + * CGImageRef refers to the updated data. + * + * Return value: the newly created surface. + * + * Since: 1.6 + */ +cairo_surface_t * +cairo_quartz_image_surface_create (cairo_surface_t *surface) +{ + cairo_quartz_image_surface_t *qisurf; + + CGImageRef image; + + CGContextRef cgContext; + CGColorSpaceRef cgColorspace; + CGBitmapInfo bitinfo; + + cairo_image_surface_t *image_surface; + int width, height, stride; + cairo_format_t format; + unsigned char *data; + + if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) + return SURFACE_ERROR_NO_MEMORY; + + image_surface = (cairo_image_surface_t*) surface; + width = image_surface->width; + height = image_surface->height; + stride = image_surface->stride; + format = image_surface->format; + data = image_surface->data; + + if (!_cairo_quartz_verify_surface_size(width, height)) + return SURFACE_ERROR_NO_MEMORY; + + if (width == 0 || height == 0) + return SURFACE_ERROR_NO_MEMORY; + + if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24) + return SURFACE_ERROR_INVALID_FORMAT; + + qisurf = malloc(sizeof(cairo_quartz_image_surface_t)); + if (qisurf == NULL) + return SURFACE_ERROR_NO_MEMORY; + + memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t)); + + /* In case the create_cgimage fails, this ref will + * be released via the callback (which will be called in + * case of failure.) + */ + cairo_surface_reference (surface); + + image = _cairo_quartz_create_cgimage (format, + width, height, + stride, + data, + FALSE, + NULL, + DataProviderReleaseCallback, + surface); + + if (!image) { + free (qisurf); + return SURFACE_ERROR_NO_MEMORY; + } + + _cairo_surface_init (&qisurf->base, + &cairo_quartz_image_surface_backend, + _cairo_content_from_format (format)); + + qisurf->extents.x = qisurf->extents.y = 0; + qisurf->extents.width = width; + qisurf->extents.height = height; + + qisurf->image = image; + qisurf->imageSurface = image_surface; + + return &qisurf->base; +} + + +cairo_surface_t * +cairo_quartz_image_surface_get_image (cairo_surface_t *asurface) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface; + + if (cairo_surface_get_type(asurface) != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) + return NULL; + + return (cairo_surface_t*) surface->imageSurface; +} diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index 52a3d7fb..cf26f4fb 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -46,11 +46,12 @@ typedef struct cairo_quartz_surface { cairo_surface_t base; - void *imageData; - CGContextRef cgContext; CGAffineTransform cgContextBaseCTM; + void *imageData; + cairo_surface_t *imageSurfaceEquiv; + cairo_rectangle_int_t extents; /* These are stored while drawing operations are in place, set up @@ -63,7 +64,33 @@ typedef struct cairo_quartz_surface { CGShadingRef sourceShading; CGPatternRef sourcePattern; + + CGInterpolationQuality oldInterpolationQuality; } cairo_quartz_surface_t; + +typedef struct cairo_quartz_image_surface { + cairo_surface_t base; + + cairo_rectangle_int_t extents; + + CGImageRef image; + cairo_image_surface_t *imageSurface; +} cairo_quartz_image_surface_t; + +cairo_bool_t +_cairo_quartz_verify_surface_size(int width, int height); + +CGImageRef +_cairo_quartz_create_cgimage (cairo_format_t format, + unsigned int width, + unsigned int height, + unsigned int stride, + void *data, + cairo_bool_t interpolate, + CGColorSpaceRef colorSpaceOverride, + CGDataProviderReleaseDataCallback releaseCallback, + void *releaseInfo); + #endif /* CAIRO_HAS_QUARTZ_SURFACE */ #if CAIRO_HAS_ATSUI_FONT diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 9aff1c3a..e918ec05 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -34,12 +34,12 @@ * Vladimir Vukicevic <vladimir@mozilla.com> */ +#include <dlfcn.h> + #include "cairoint.h" #include "cairo-quartz-private.h" -#include <dlfcn.h> - /* The 10.5 SDK includes a funky new definition of FloatToFixed which * causes all sorts of breakage; so reset to old-style definition */ @@ -140,7 +140,8 @@ static void quartz_ensure_symbols(void) #define CG_MAX_WIDTH USHRT_MAX /* is the desired size of the surface within bounds? */ -static cairo_bool_t verify_surface_size(int width, int height) +cairo_bool_t +_cairo_quartz_verify_surface_size(int width, int height) { /* hmmm, allow width, height == 0 ? */ if (width < 0 || height < 0) { @@ -295,7 +296,7 @@ _cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op) return kPrivateCGCompositeCopy; } -static CGLineCap +static inline CGLineCap _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) { switch (ccap) { @@ -307,7 +308,7 @@ _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) return kCGLineCapButt; } -static CGLineJoin +static inline CGLineJoin _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin) { switch (cjoin) { @@ -319,7 +320,28 @@ _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin) return kCGLineJoinMiter; } -static void +static inline CGInterpolationQuality +_cairo_quartz_filter_to_quartz (cairo_filter_t filter) +{ + switch (filter) { + case CAIRO_FILTER_NEAREST: + return kCGInterpolationNone; + + case CAIRO_FILTER_FAST: + return kCGInterpolationLow; + + case CAIRO_FILTER_BEST: + return kCGInterpolationHigh; + + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BILINEAR: + return kCGInterpolationDefault; + } + + return kCGInterpolationDefault; +} + +static inline void _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src, CGAffineTransform *dst) { @@ -393,114 +415,104 @@ CreateGradientFunction (cairo_gradient_pattern_t *gpat) &callbacks); } -/* generic cairo surface -> #cairo_quartz_surface_t function */ -static cairo_int_status_t -_cairo_quartz_surface_to_quartz (cairo_surface_t *target, - cairo_surface_t *pat_surf, - cairo_quartz_surface_t **quartz_surf) -{ +/* Obtain a CGImageRef from a cairo_surface_t * */ - if (cairo_surface_get_type(pat_surf) != CAIRO_SURFACE_TYPE_QUARTZ) { - /* XXXtodo/perf don't use clone if the source surface is an image surface! Instead, - * just create the CGImage directly! - */ +static CGImageRef +_cairo_surface_to_cgimage (cairo_surface_t *target, + cairo_surface_t *source) +{ + cairo_surface_type_t stype = cairo_surface_get_type (source); + cairo_image_surface_t *isurf; + CGImageRef image, image2; + void *image_extra; - cairo_surface_t *ref_type = target; - cairo_surface_t *new_surf = NULL; - cairo_rectangle_int_t rect; - cairo_status_t status; + if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) { + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source; + return CGImageRetain (surface->image); + } - if (ref_type == NULL) - ref_type = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + if (stype == CAIRO_SURFACE_TYPE_QUARTZ) { + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; + image = CGBitmapContextCreateImage (surface->cgContext); + if (image) + return image; + } - status = _cairo_surface_get_extents (pat_surf, &rect); + if (stype != CAIRO_SURFACE_TYPE_IMAGE) { + cairo_status_t status = + _cairo_surface_acquire_source_image (source, &isurf, &image_extra); if (status) - return status; - - status = _cairo_surface_clone_similar (ref_type, pat_surf, rect.x, rect.y, - rect.width, rect.height, &new_surf); - if (target == NULL) - cairo_surface_destroy(ref_type); + return NULL; + } else { + isurf = (cairo_image_surface_t *) source; + } - if (status) - return status; + image2 = _cairo_quartz_create_cgimage (isurf->format, + isurf->width, + isurf->height, + isurf->stride, + isurf->data, + FALSE, + NULL, NULL, NULL); - if (new_surf && - cairo_surface_get_type (new_surf) != CAIRO_SURFACE_TYPE_QUARTZ) - { - ND((stderr, "got a non-quartz surface, format=%d width=%u height=%u type=%d\n", cairo_surface_get_type (pat_surf), rect.width, rect.height, cairo_surface_get_type (new_surf))); - cairo_surface_destroy (new_surf); - return CAIRO_INT_STATUS_UNSUPPORTED; - } + image = CGImageCreateCopy (image2); + CGImageRelease (image2); - *quartz_surf = (cairo_quartz_surface_t *) new_surf; - } else { - /* If it's a quartz surface, we can try to see if it's a CGBitmapContext; - * we do this when we call CGBitmapContextCreateImage below. - */ - cairo_surface_reference (pat_surf); - *quartz_surf = (cairo_quartz_surface_t*) pat_surf; - } + if ((cairo_surface_t*) isurf != source) + _cairo_surface_release_source_image (source, isurf, image_extra); - return CAIRO_STATUS_SUCCESS; + return image; } /* Generic #cairo_pattern_t -> CGPattern function */ -static void -SurfacePatternDrawFunc (void *info, CGContextRef context) -{ - cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) info; - cairo_surface_t *pat_surf = spat->surface; - cairo_int_status_t status; - cairo_quartz_surface_t *quartz_surf; - CGImageRef img; +typedef struct { + CGImageRef image; CGRect imageBounds; + cairo_bool_t do_reflect; +} SurfacePatternDrawInfo; - status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf); - if (status) - return; +static void +SurfacePatternDrawFunc (void *ainfo, CGContextRef context) +{ + SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; - img = CGBitmapContextCreateImage (quartz_surf->cgContext); - if (!img) { - // ... give up. - ND((stderr, "CGBitmapContextCreateImage failed\n")); - _cairo_error (CAIRO_STATUS_NO_MEMORY); - cairo_surface_destroy ((cairo_surface_t*)quartz_surf); - return; - } + CGContextTranslateCTM (context, 0, info->imageBounds.size.height); + CGContextScaleCTM (context, 1, -1); - /* XXXtodo WHY does this need to be flipped? Writing this stuff - * to disk shows that in both this path and the path above the source image - * has an identical orientation, and the destination context at all times has a Y - * flip. So why do we need to flip in this case? - */ - if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { - CGContextTranslateCTM (context, 0, CGImageGetHeight(img)); - CGContextScaleCTM (context, 1, -1); - } - - imageBounds.size = CGSizeMake (CGImageGetWidth(img), CGImageGetHeight(img)); - imageBounds.origin.x = 0; - imageBounds.origin.y = 0; + CGContextDrawImage (context, info->imageBounds, info->image); + if (info->do_reflect) { + /* draw 3 more copies of the image, flipped. + * DrawImage draws the image according to the current Y-direction into the rectangle given + * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left + * of the base image position, and the Y axis is extending upwards. + */ - CGContextDrawImage (context, imageBounds, img); - if (spat->base.extend == CAIRO_EXTEND_REFLECT) { - /* draw 3 more copies of the image, flipped. */ - CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height); + /* Make the y axis extend downwards, and draw a flipped image below */ CGContextScaleCTM (context, 1, -1); - CGContextDrawImage (context, imageBounds, img); - CGContextTranslateCTM (context, 2 * imageBounds.size.width, 0); + CGContextDrawImage (context, info->imageBounds, info->image); + + /* Shift over to the right, and flip vertically (translation is 2x, + * since we'll be flipping and thus rendering the rectangle "backwards" + */ + CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0); CGContextScaleCTM (context, -1, 1); - CGContextDrawImage (context, imageBounds, img); - CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height); + CGContextDrawImage (context, info->imageBounds, info->image); + + /* Then unflip the Y-axis again, and draw the image above the point. */ CGContextScaleCTM (context, 1, -1); - CGContextDrawImage (context, imageBounds, img); + CGContextDrawImage (context, info->imageBounds, info->image); + } +} - CGImageRelease (img); +static void +SurfacePatternReleaseInfoFunc (void *ainfo) +{ + SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; - cairo_surface_destroy ((cairo_surface_t*) quartz_surf); + CGImageRelease (info->image); + free (info); } /* Borrowed from cairo-meta-surface */ @@ -532,50 +544,64 @@ _init_pattern_with_snapshot (cairo_pattern_t *pattern, static cairo_int_status_t _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest, - cairo_pattern_t *abspat, + cairo_pattern_t *apattern, CGPatternRef *cgpat) { - cairo_surface_pattern_t *spat; + cairo_surface_pattern_t *spattern; cairo_surface_t *pat_surf; cairo_rectangle_int_t extents; + CGImageRef image; CGRect pbounds; CGAffineTransform ptransform, stransform; CGPatternCallbacks cb = { 0, SurfacePatternDrawFunc, - (CGFunctionReleaseInfoCallback) cairo_pattern_destroy }; + SurfacePatternReleaseInfoFunc }; + SurfacePatternDrawInfo *info; float rw, rh; cairo_status_t status; - cairo_pattern_union_t *snap_pattern = NULL; - cairo_pattern_t *target_pattern = abspat; - cairo_matrix_t m; + /* SURFACE is the only type we'll handle here */ - if (abspat->type != CAIRO_PATTERN_TYPE_SURFACE) + if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_UNSUPPORTED; - spat = (cairo_surface_pattern_t *) abspat; - pat_surf = spat->surface; + spattern = (cairo_surface_pattern_t *) apattern; + pat_surf = spattern->surface; status = _cairo_surface_get_extents (pat_surf, &extents); if (status) return status; + image = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf); + if (image == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + info = malloc(sizeof(SurfacePatternDrawInfo)); + if (!info) + return CAIRO_STATUS_NO_MEMORY; + + /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure + * that the data will stick around for this image when the printer gets to it. + * Otherwise, the underlying data store may disappear from under us! + * + * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces, + * since the Quartz surfaces have a higher chance of sticking around. If the + * source is a quartz image surface, then it's set up to retain a ref to the + * image surface that it's backed by. + */ + info->image = image; + + info->imageBounds = CGRectMake (0, 0, extents.width, extents.height); + pbounds.origin.x = 0; pbounds.origin.y = 0; - // kjs seems to indicate this should work (setting to 0,0 to avoid - // tiling); however, the pattern CTM scaling ends up being NaN in - // the pattern draw function if either rw or rh are 0. - // XXXtodo get pattern drawing working with extend options - // XXXtodo/perf optimize CAIRO_EXTEND_NONE to a single DrawImage instead of a pattern - if (spat->base.extend == CAIRO_EXTEND_REFLECT) { - /* XXX broken; need to emulate by reflecting the image into 4 quadrants - * and then tiling that - */ - pbounds.size.width = 2 * extents.width; - pbounds.size.height = 2 * extents.height; + if (spattern->base.extend == CAIRO_EXTEND_REFLECT) { + pbounds.size.width = 2.0 * extents.width; + pbounds.size.height = 2.0 * extents.height; + info->do_reflect = TRUE; } else { pbounds.size.width = extents.width; pbounds.size.height = extents.height; @@ -583,7 +609,7 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t rw = pbounds.size.width; rh = pbounds.size.height; - m = spat->base.matrix; + m = spattern->base.matrix; cairo_matrix_invert(&m); _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform); @@ -601,25 +627,14 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t ND((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d)); #endif + *cgpat = CGPatternCreate (info, + pbounds, + ptransform, + rw, rh, + kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */ + TRUE, + &cb); - /* XXX fixme: only do snapshots if the context is for printing, or get rid of the - other block if it doesn't fafect performance */ - if (1 /* context is for printing */) { - snap_pattern = (cairo_pattern_union_t*) malloc(sizeof(cairo_pattern_union_t)); - target_pattern = (cairo_pattern_t*) snap_pattern; - _init_pattern_with_snapshot (target_pattern, abspat); - } else { - cairo_pattern_reference (abspat); - target_pattern = abspat; - } - - *cgpat = CGPatternCreate (target_pattern, - pbounds, - ptransform, - rw, rh, - kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */ - TRUE, - &cb); return CAIRO_STATUS_SUCCESS; } @@ -751,6 +766,9 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, { assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern)); + surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext); + CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter)); + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; @@ -785,7 +803,6 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, { cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) source; cairo_surface_t *pat_surf = spat->surface; - cairo_quartz_surface_t *quartz_surf; CGImageRef img; cairo_matrix_t m = spat->base.matrix; cairo_rectangle_int_t extents; @@ -794,16 +811,7 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, CGRect srcRect; cairo_fixed_t fw, fh; - status = _cairo_quartz_surface_to_quartz ((cairo_surface_t *) surface, pat_surf, &quartz_surf); - if (status) - return DO_UNSUPPORTED; - - surface->sourceImageSurface = (cairo_surface_t *)quartz_surf; - - if (IS_EMPTY(quartz_surf)) - return DO_NOTHING; - - img = CGBitmapContextCreateImage (quartz_surf->cgContext); + img = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf); if (!img) return DO_UNSUPPORTED; @@ -898,6 +906,8 @@ static void _cairo_quartz_teardown_source (cairo_quartz_surface_t *surface, cairo_pattern_t *source) { + CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality); + if (surface->sourceImage) { CGImageRelease(surface->sourceImage); surface->sourceImage = NULL; @@ -937,6 +947,11 @@ _cairo_quartz_get_image (cairo_quartz_surface_t *surface, return CAIRO_STATUS_SUCCESS; } + if (surface->imageSurfaceEquiv) { + *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv); + return CAIRO_STATUS_SUCCESS; + } + if (CGBitmapContextGetBitsPerPixel(surface->cgContext) != 0) { unsigned int stride; unsigned int bitinfo; @@ -945,6 +960,7 @@ _cairo_quartz_get_image (cairo_quartz_surface_t *surface, unsigned int color_comps; imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext); + #ifdef USE_10_3_WORKAROUNDS bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext); #else @@ -1029,6 +1045,11 @@ _cairo_quartz_surface_finish (void *abstract_surface) surface->cgContext = NULL; + if (surface->imageSurfaceEquiv) { + cairo_surface_destroy (surface->imageSurfaceEquiv); + surface->imageSurfaceEquiv = NULL; + } + if (surface->imageData) { free (surface->imageData); surface->imageData = NULL; @@ -1121,7 +1142,7 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, return NULL; // verify width and height of surface - if (!verify_surface_size(width, height)) { + if (!_cairo_quartz_verify_surface_size(width, height)) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return NULL; } @@ -1131,12 +1152,12 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, static cairo_status_t _cairo_quartz_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - cairo_surface_t **clone_out) + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + cairo_surface_t **clone_out) { cairo_quartz_surface_t *new_surface = NULL; cairo_format_t new_format; @@ -1145,97 +1166,58 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface, *clone_out = NULL; // verify width and height of surface - if (!verify_surface_size(width, height)) { + if (!_cairo_quartz_verify_surface_size(width, height)) { return CAIRO_INT_STATUS_UNSUPPORTED; } - if (cairo_surface_get_type(src) == CAIRO_SURFACE_TYPE_QUARTZ) { + if (width == 0 || height == 0) { + *clone_out = (cairo_surface_t*) + _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, + width, height); + return CAIRO_STATUS_SUCCESS; + } + + if (src->backend->type == CAIRO_SURFACE_TYPE_QUARTZ) { cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src; if (IS_EMPTY(qsurf)) { - *clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, qsurf->extents.width, qsurf->extents.height); + *clone_out = (cairo_surface_t*) + _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, + qsurf->extents.width, qsurf->extents.height); return CAIRO_STATUS_SUCCESS; } - - quartz_image = CGBitmapContextCreateImage (qsurf->cgContext); - new_format = CAIRO_FORMAT_ARGB32; /* XXX bogus; recover a real format from the image */ - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *isurf = (cairo_image_surface_t *) src; - CGDataProviderRef dataProvider; - CGColorSpaceRef cgColorspace; - CGBitmapInfo bitinfo; - int bitsPerComponent, bitsPerPixel; - - if (isurf->width == 0 || isurf->height == 0) { - *clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, isurf->width, isurf->height); - return CAIRO_STATUS_SUCCESS; - } - - if (isurf->format == CAIRO_FORMAT_ARGB32) { - cgColorspace = CGColorSpaceCreateDeviceRGB(); - bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; - bitsPerComponent = 8; - bitsPerPixel = 32; - } else if (isurf->format == CAIRO_FORMAT_RGB24) { - cgColorspace = CGColorSpaceCreateDeviceRGB(); - bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; - bitsPerComponent = 8; - bitsPerPixel = 32; - } else if (isurf->format == CAIRO_FORMAT_A8) { - cgColorspace = CGColorSpaceCreateDeviceGray(); - bitinfo = kCGImageAlphaNone; - bitsPerComponent = 8; - bitsPerPixel = 8; - } else { - /* SUPPORT A1, maybe */ - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - new_format = isurf->format; - - dataProvider = CGDataProviderCreateWithData (NULL, - isurf->data, - isurf->height * isurf->stride, - NULL); - - quartz_image = CGImageCreate (isurf->width, isurf->height, - bitsPerComponent, - bitsPerPixel, - isurf->stride, - cgColorspace, - bitinfo, - dataProvider, - NULL, - false, - kCGRenderingIntentDefault); - CGDataProviderRelease (dataProvider); - CGColorSpaceRelease (cgColorspace); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; } + quartz_image = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src); if (!quartz_image) return CAIRO_INT_STATUS_UNSUPPORTED; + new_format = CAIRO_FORMAT_ARGB32; /* assumed */ + if (_cairo_surface_is_image (src)) { + new_format = ((cairo_image_surface_t *) src)->format; + } + new_surface = (cairo_quartz_surface_t *) - cairo_quartz_surface_create (new_format, - CGImageGetWidth (quartz_image), - CGImageGetHeight (quartz_image)); + cairo_quartz_surface_create (new_format, width, height); if (!new_surface || new_surface->base.status) { CGImageRelease (quartz_image); return CAIRO_INT_STATUS_UNSUPPORTED; } + CGContextSaveGState (new_surface->cgContext); + CGContextSetCompositeOperation (new_surface->cgContext, kPrivateCGCompositeCopy); - quartz_image_to_png (quartz_image, NULL); - + CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y); CGContextDrawImage (new_surface->cgContext, - CGRectMake (src_x, src_y, width, height), + CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)), quartz_image); - CGImageRelease (quartz_image); + CGContextRestoreGState (new_surface->cgContext); + + CGImageRelease (quartz_image); + *clone_out = (cairo_surface_t*) new_surface; return CAIRO_STATUS_SUCCESS; @@ -1254,8 +1236,8 @@ _cairo_quartz_surface_get_extents (void *abstract_surface, static cairo_int_status_t _cairo_quartz_surface_paint (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source) + cairo_operator_t op, + cairo_pattern_t *source) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -1281,17 +1263,11 @@ _cairo_quartz_surface_paint (void *abstract_surface, } else if (action == DO_SHADING) { CGContextDrawShading (surface->cgContext, surface->sourceShading); } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) { - cairo_surface_pattern_t *surface_pattern = - (cairo_surface_pattern_t *) source; - cairo_surface_t *pat_surf = surface_pattern->surface; - CGContextSaveGState (surface->cgContext); CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform); - if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { - CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage)); - CGContextScaleCTM (surface->cgContext, 1, -1); - } + CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); + CGContextScaleCTM (surface->cgContext, 1, -1); if (action == DO_IMAGE) CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); @@ -1361,19 +1337,14 @@ _cairo_quartz_surface_fill (void *abstract_surface, CGContextDrawShading (surface->cgContext, surface->sourceShading); } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) { - cairo_surface_pattern_t *surface_pattern = - (cairo_surface_pattern_t *) source; - cairo_surface_t *pat_surf = surface_pattern->surface; if (fill_rule == CAIRO_FILL_RULE_WINDING) CGContextClip (surface->cgContext); else CGContextEOClip (surface->cgContext); CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform); - if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { - CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage)); - CGContextScaleCTM (surface->cgContext, 1, -1); - } + CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); + CGContextScaleCTM (surface->cgContext, 1, -1); if (action == DO_IMAGE) CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); @@ -1470,10 +1441,8 @@ _cairo_quartz_surface_stroke (void *abstract_surface, CGContextClip (surface->cgContext); CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform); - if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) { - CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage)); - CGContextScaleCTM (surface->cgContext, 1, -1); - } + CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); + CGContextScaleCTM (surface->cgContext, 1, -1); if (action == DO_IMAGE) CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); @@ -1622,10 +1591,8 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface, if (action == DO_IMAGE || action == DO_TILED_IMAGE) { CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform); - if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) { - CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage)); - CGContextScaleCTM (surface->cgContext, 1, -1); - } + CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); + CGContextScaleCTM (surface->cgContext, 1, -1); if (action == DO_IMAGE) CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); @@ -1659,7 +1626,6 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, cairo_surface_pattern_t *mask) { cairo_rectangle_int_t extents; - cairo_quartz_surface_t *quartz_surf; CGRect rect; CGImageRef img; cairo_surface_t *pat_surf = mask->surface; @@ -1669,37 +1635,43 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, if (status) return status; - status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf); - if (status) - return status; - // everything would be masked out, so do nothing - if (IS_EMPTY(quartz_surf)) + if (extents.width == 0 || extents.height == 0) goto BAIL; - img = CGBitmapContextCreateImage (quartz_surf->cgContext); + img = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf); if (!img) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } rect = CGRectMake (-mask->base.matrix.x0, -mask->base.matrix.y0, extents.width, extents.height); + CGContextSaveGState (surface->cgContext); + + /* ClipToMask is essentially drawing an image, so we need to flip the CTM + * to get the image to appear oriented the right way */ + CGAffineTransform ctm = CGContextGetCTM (surface->cgContext); + CGContextTranslateCTM (surface->cgContext, 0.0f, rect.size.height); + CGContextScaleCTM (surface->cgContext, 1.0f, -1.0f); + CGContextClipToMaskPtr (surface->cgContext, rect, img); + + CGContextSetCTM (surface->cgContext, ctm); + status = _cairo_quartz_surface_paint (surface, op, source); CGContextRestoreGState (surface->cgContext); CGImageRelease (img); BAIL: - cairo_surface_destroy ((cairo_surface_t*) quartz_surf); return status; } static cairo_int_status_t _cairo_quartz_surface_mask (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + cairo_operator_t op, + cairo_pattern_t *source, + cairo_pattern_t *mask) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -1871,6 +1843,7 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, surface->cgContextBaseCTM = CGContextGetCTM (cgContext); surface->imageData = NULL; + surface->imageSurfaceEquiv = NULL; return surface; } @@ -1953,7 +1926,7 @@ cairo_quartz_surface_create (cairo_format_t format, int bitsPerComponent; // verify width and height of surface - if (!verify_surface_size(width, height)) + if (!_cairo_quartz_verify_surface_size(width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); if (width == 0 || height == 0) { @@ -1994,6 +1967,7 @@ cairo_quartz_surface_create (cairo_format_t format, CGColorSpaceRelease (cgColorspace); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } + /* zero the memory to match the image surface behaviour */ memset (imageData, 0, height * stride); @@ -2025,6 +1999,7 @@ cairo_quartz_surface_create (cairo_format_t format, } surf->imageData = imageData; + surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride); return (cairo_surface_t *) surf; } @@ -2151,4 +2126,3 @@ quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest) CGImageRelease(imgref); #endif } - diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h index 5d78d391..c0edb0cc 100644 --- a/src/cairo-quartz.h +++ b/src/cairo-quartz.h @@ -57,6 +57,12 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, cairo_public CGContextRef cairo_quartz_surface_get_cg_context (cairo_surface_t *surface); +cairo_public cairo_surface_t * +cairo_quartz_image_surface_create (cairo_surface_t *image_surface); + +cairo_public cairo_surface_t * +cairo_quartz_image_surface_get_image (cairo_surface_t *surface); + CAIRO_END_DECLS #else /* CAIRO_HAS_QUARTZ_SURFACE */ diff --git a/src/cairo.h b/src/cairo.h index 5988f3aa..cf14e6f4 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1491,6 +1491,7 @@ cairo_surface_status (cairo_surface_t *surface); * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2 * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface + * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image * * #cairo_surface_type_t is used to describe the type of a given * surface. The surface types are also known as "backends" or "surface @@ -1528,7 +1529,8 @@ typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_SURFACE_TYPE_SVG, CAIRO_SURFACE_TYPE_OS2, - CAIRO_SURFACE_TYPE_WIN32_PRINTING + CAIRO_SURFACE_TYPE_WIN32_PRINTING, + CAIRO_SURFACE_TYPE_QUARTZ_IMAGE } cairo_surface_type_t; cairo_public cairo_surface_type_t |