diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c index 322a1c42b..aff747e22 100644 --- a/boilerplate/cairo-boilerplate.c +++ b/boilerplate/cairo-boilerplate.c @@ -700,31 +700,17 @@ cleanup_cairo_glitz_wgl (void *closure) #endif /* CAIRO_HAS_GLITZ_SURFACE */ -#if 0 && CAIRO_HAS_QUARTZ_SURFACE -static cairo_surface_t * -create_quartz_surface (int width, int height, void **closure) -{ -#error Not yet implemented -} +#if CAIRO_HAS_QUARTZ_SURFACE -static void -cleanup_quartz (void *closure) -{ -#error Not yet implemented -} -#endif - -#if CAIRO_HAS_NQUARTZ_SURFACE - -#include +#include static cairo_surface_t * -create_nquartz_surface (const char *name, - cairo_content_t content, - int width, - int height, - cairo_boilerplate_mode_t mode, - void **closure) +create_quartz_surface (const char *name, + cairo_content_t content, + int width, + int height, + cairo_boilerplate_mode_t mode, + void **closure) { cairo_format_t format; @@ -739,11 +725,11 @@ create_nquartz_surface (const char *name, *closure = NULL; - return cairo_nquartz_surface_create (format, width, height); + return cairo_quartz_surface_create (format, width, height); } static void -cleanup_nquartz (void *closure) +cleanup_quartz (void *closure) { /* nothing */ } @@ -1430,19 +1416,14 @@ cairo_boilerplate_target_t targets[] = cleanup_cairo_glitz_wgl }, #endif #endif /* CAIRO_HAS_GLITZ_SURFACE */ -#if 0 && CAIRO_HAS_QUARTZ_SURFACE +#if CAIRO_HAS_QUARTZ_SURFACE + { "quartz", CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_CONTENT_COLOR_ALPHA, 0, + create_quartz_surface, cairo_surface_write_to_png, + cleanup_quartz }, { "quartz", CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_CONTENT_COLOR, 0, create_quartz_surface, cairo_surface_write_to_png, cleanup_quartz }, #endif -#if CAIRO_HAS_NQUARTZ_SURFACE - { "nquartz", CAIRO_SURFACE_TYPE_NQUARTZ, CAIRO_CONTENT_COLOR_ALPHA, 0, - create_nquartz_surface, cairo_surface_write_to_png, - cleanup_nquartz }, - { "nquartz", CAIRO_SURFACE_TYPE_NQUARTZ, CAIRO_CONTENT_COLOR, 0, - create_nquartz_surface, cairo_surface_write_to_png, - cleanup_nquartz }, -#endif #if CAIRO_HAS_WIN32_SURFACE { "win32", CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR, 0, create_win32_surface, cairo_surface_write_to_png, cleanup_win32 }, diff --git a/configure.in b/configure.in index fad10a64e..b274a21b0 100644 --- a/configure.in +++ b/configure.in @@ -278,12 +278,6 @@ CAIRO_BACKEND_ENABLE(quartz, Quartz, quartz, QUARTZ_SURFACE, no, [ quartz_LIBS="-Xlinker -framework -Xlinker Carbon" ]) -CAIRO_BACKEND_ENABLE(nquartz, NativeQuartz, nquartz, NQUARTZ_SURFACE, no, [ - dnl There is no pkgconfig for quartz; lets do a header check - AC_CHECK_HEADER(Carbon/Carbon.h, , [use_nquartz="no (Carbon headers not found)"]) - quartz_LIBS="-Xlinker -framework -Xlinker Carbon" -]) - dnl =========================================================================== AC_MSG_CHECKING([for native Win32]) @@ -889,7 +883,6 @@ echo " image: yes (always builtin)" echo " Xlib: $use_xlib" echo " Xlib Xrender: $use_xlib_xrender" echo " Quartz: $use_quartz" -echo " Native Quartz: $use_nquartz" echo " XCB: $use_xcb" echo " Win32: $use_win32" echo " OS2: $use_os2" diff --git a/src/Makefile.am b/src/Makefile.am index 259a2f321..4a24ac2f2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -66,12 +66,6 @@ libcairo_quartz_sources = cairo-quartz-surface.c cairo-quartz-private.h backend_pkgconfigs += cairo-quartz.pc endif -if CAIRO_HAS_NQUARTZ_SURFACE -libcairo_nquartz_headers = cairo-nquartz.h -libcairo_nquartz_sources = cairo-nquartz-surface.c cairo-quartz-private.h -backend_pkgconfigs += cairo-nquartz.pc -endif - if OS_WIN32 export_symbols = -export-symbols cairo.def cairo_def_dependency = cairo.def diff --git a/src/cairo-atsui-font.c b/src/cairo-atsui-font.c index f6eb3a9c8..fff7a22cf 100644 --- a/src/cairo-atsui-font.c +++ b/src/cairo-atsui-font.c @@ -935,7 +935,7 @@ _cairo_atsui_font_old_show_glyphs (void *abstract_font, CGFontRef cgFont; CGAffineTransform textTransform; - if (!_cairo_surface_is_quartz (generic_surface)) + if (cairo_surface_get_type (generic_surface) != CAIRO_SURFACE_TYPE_QUARTZ) return CAIRO_INT_STATUS_UNSUPPORTED; /* Check if we can draw directly to the destination surface */ @@ -961,7 +961,7 @@ _cairo_atsui_font_old_show_glyphs (void *abstract_font, CGContextTranslateCTM(drawingContext, 0, destImageSurface->height); CGContextScaleCTM(drawingContext, 1.0f, -1.0f); } else { - drawingContext = ((cairo_quartz_surface_t *)generic_surface)->context; + drawingContext = ((cairo_quartz_surface_t *)generic_surface)->cgContext; CGContextSaveGState (drawingContext); } @@ -992,35 +992,6 @@ _cairo_atsui_font_old_show_glyphs (void *abstract_font, CGContextSetRGBFillColor(drawingContext, 0.0f, 0.0f, 0.0f, 0.0f); } - if (surface->clip_region) { - pixman_box16_t *boxes = pixman_region_rects (surface->clip_region); - int num_boxes = pixman_region_num_rects (surface->clip_region); - CGRect stack_rects[10]; - CGRect *rects; - int i; - - /* XXX: Return-value of malloc needs to be checked for - * NULL. Can someone fix this who is more familiar with - * the cleanup needed in this function? - */ - if (num_boxes > 10) - rects = malloc (sizeof (CGRect) * num_boxes); - else - rects = stack_rects; - - for (i = 0; i < num_boxes; i++) { - rects[i].origin.x = boxes[i].x1; - rects[i].origin.y = boxes[i].y1; - rects[i].size.width = boxes[i].x2 - boxes[i].x1; - rects[i].size.height = boxes[i].y2 - boxes[i].y1; - } - - CGContextClipToRects (drawingContext, rects, num_boxes); - - if (rects != stack_rects) - free(rects); - } - /* TODO - bold and italic text * * We could draw the text using ATSUI and get bold, italics diff --git a/src/cairo-nquartz-surface.c b/src/cairo-nquartz-surface.c deleted file mode 100644 index dd05c3849..000000000 --- a/src/cairo-nquartz-surface.c +++ /dev/null @@ -1,1864 +0,0 @@ -/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2006 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 - */ - -#include - -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL -#include -#include -#endif - -#include "cairoint.h" -#include "cairo-private.h" -#include "cairo-nquartz.h" - -#include "cairo-quartz-private.h" - -#undef NQUARTZ_DEBUG - -#ifdef NQUARTZ_DEBUG -#define ND(_x) fprintf _x -#else -#define ND(_x) do {} while(0) -#endif - -/* This method is private, but it exists. Its params are are exposed - * as args to the NS* method, but not as CG. - */ -enum PrivateCGCompositeMode { - kPrivateCGCompositeClear = 0, - kPrivateCGCompositeCopy = 1, - kPrivateCGCompositeSourceOver = 2, - kPrivateCGCompositeSourceIn = 3, - kPrivateCGCompositeSourceOut = 4, - kPrivateCGCompositeSourceAtop = 5, - kPrivateCGCompositeDestinationOver = 6, - kPrivateCGCompositeDestinationIn = 7, - kPrivateCGCompositeDestinationOut = 8, - kPrivateCGCompositeDestinationAtop = 9, - kPrivateCGCompositeXOR = 10, - kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s))) - kPrivateCGCompositePlusLighter = 12, // (min (1, s + d)) -}; -typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; -CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); -CG_EXTERN void CGContextResetCTM (CGContextRef); -CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform); -CG_EXTERN void CGContextResetClip (CGContextRef); -CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef); - -/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9 - * has all the stuff we care about, just some of it isn't exported in the SDK. - */ -#ifndef kCGBitmapByteOrder32Host -#define USE_10_3_WORKAROUNDS -#define kCGBitmapAlphaInfoMask 0x1F -#define kCGBitmapByteOrderMask 0x7000 -#define kCGBitmapByteOrder32Host 0 - -typedef uint32_t CGBitmapInfo; - -/* public in 10.4, present in 10.3.9 */ -CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef); -CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef); -#endif - - -typedef struct cairo_nquartz_surface { - cairo_surface_t base; - - void *imageData; - - CGContextRef cgContext; - CGAffineTransform cgContextBaseCTM; - - cairo_rectangle_int16_t extents; - - /* These are stored while drawing operations are in place, set up - * by nquartz_setup_source() and nquartz_finish_source() - */ - CGAffineTransform imageTransform; - CGImageRef sourceImage; - CGShadingRef sourceShading; - CGPatternRef sourcePattern; -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL - AGLContext aglContext; -#else - void *_unused; -#endif -} cairo_nquartz_surface_t; - -/** - ** Utility functions - **/ - -void nquartz_surface_to_png (cairo_nquartz_surface_t *nq, char *dest); -void nquartz_image_to_png (CGImageRef, char *dest); - -/* - * Cairo path -> Quartz path conversion helpers - */ - -/* cairo path -> mutable path */ -static cairo_status_t -_cairo_path_to_quartz_path_move_to (void *closure, cairo_point_t *point) -{ - CGPathMoveToPoint ((CGMutablePathRef) closure, NULL, - _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_path_line_to (void *closure, cairo_point_t *point) -{ - CGPathAddLineToPoint ((CGMutablePathRef) closure, NULL, - _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_path_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2) -{ - CGPathAddCurveToPoint ((CGMutablePathRef) closure, NULL, - _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), - _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), - _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_path_close_path (void *closure) -{ - CGPathCloseSubpath ((CGMutablePathRef) closure); - return CAIRO_STATUS_SUCCESS; -} - -/* cairo path -> execute in context */ -static cairo_status_t -_cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point) -{ - //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); - CGContextMoveToPoint ((CGContextRef) closure, - _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point) -{ - //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); - if (CGContextIsPathEmpty ((CGContextRef) closure)) - CGContextMoveToPoint ((CGContextRef) closure, - _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); - else - CGContextAddLineToPoint ((CGContextRef) closure, - _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_context_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2) -{ - //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n", - // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), - // _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), - // _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y))); - - CGContextAddCurveToPoint ((CGContextRef) closure, - _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), - _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), - _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_context_close_path (void *closure) -{ - //ND((stderr, "closepath\n")); - CGContextClosePath ((CGContextRef) closure); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_nquartz_cairo_path_to_quartz_path (cairo_path_fixed_t *path, - CGMutablePathRef cgPath) -{ - return _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_path_to_quartz_path_move_to, - _cairo_path_to_quartz_path_line_to, - _cairo_path_to_quartz_path_curve_to, - _cairo_path_to_quartz_path_close_path, - cgPath); -} - -static cairo_status_t -_cairo_nquartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path, - CGContextRef cgc) -{ - return _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_path_to_quartz_context_move_to, - _cairo_path_to_quartz_context_line_to, - _cairo_path_to_quartz_context_curve_to, - _cairo_path_to_quartz_context_close_path, - cgc); -} - -/* - * Misc helpers/callbacks - */ - -static PrivateCGCompositeMode -_cairo_nquartz_cairo_operator_to_quartz (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return kPrivateCGCompositeClear; - case CAIRO_OPERATOR_SOURCE: - return kPrivateCGCompositeCopy; - case CAIRO_OPERATOR_OVER: - return kPrivateCGCompositeSourceOver; - case CAIRO_OPERATOR_IN: - /* XXX This doesn't match image output */ - return kPrivateCGCompositeSourceIn; - case CAIRO_OPERATOR_OUT: - /* XXX This doesn't match image output */ - return kPrivateCGCompositeSourceOut; - case CAIRO_OPERATOR_ATOP: - return kPrivateCGCompositeSourceAtop; - - case CAIRO_OPERATOR_DEST: - /* XXX this is handled specially (noop)! */ - return kPrivateCGCompositeCopy; - case CAIRO_OPERATOR_DEST_OVER: - return kPrivateCGCompositeDestinationOver; - case CAIRO_OPERATOR_DEST_IN: - /* XXX This doesn't match image output */ - return kPrivateCGCompositeDestinationIn; - case CAIRO_OPERATOR_DEST_OUT: - return kPrivateCGCompositeDestinationOut; - case CAIRO_OPERATOR_DEST_ATOP: - /* XXX This doesn't match image output */ - return kPrivateCGCompositeDestinationAtop; - - case CAIRO_OPERATOR_XOR: - return kPrivateCGCompositeXOR; /* This will generate strange results */ - case CAIRO_OPERATOR_ADD: - return kPrivateCGCompositePlusLighter; - case CAIRO_OPERATOR_SATURATE: - /* XXX This doesn't match image output for SATURATE; there's no equivalent */ - return kPrivateCGCompositePlusDarker; /* ??? */ - } - - - return kPrivateCGCompositeCopy; -} - -static CGLineCap -_cairo_nquartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) -{ - switch (ccap) { - case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break; - case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break; - case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break; - } - - return kCGLineCapButt; -} - -static CGLineJoin -_cairo_nquartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin) -{ - switch (cjoin) { - case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break; - case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break; - case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break; - } - - return kCGLineJoinMiter; -} - -static void -_cairo_nquartz_cairo_matrix_to_quartz (const cairo_matrix_t *src, - CGAffineTransform *dst) -{ - dst->a = src->xx; - dst->b = src->xy; - dst->c = src->yx; - dst->d = src->yy; - dst->tx = src->x0; - dst->ty = src->y0; -} - -/** - ** Source -> Quartz setup and finish functions - **/ - -static void -ComputeGradientValue (void *info, const float *in, float *out) -{ - float fdist = *in; /* 0.0 .. 1.0 */ - cairo_fixed_16_16_t fdist_fix = _cairo_fixed_from_double(*in); - cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info; - int i; - - for (i = 0; i < grad->n_stops; i++) { - if (grad->stops[i].x > fdist_fix) - break; - } - - if (i == 0 || i == grad->n_stops) { - if (i == grad->n_stops) - --i; - out[0] = grad->stops[i].color.red / 65535.; - out[1] = grad->stops[i].color.green / 65535.; - out[2] = grad->stops[i].color.blue / 65535.; - out[3] = grad->stops[i].color.alpha / 65535.; - } else { - float ax = _cairo_fixed_to_double(grad->stops[i-1].x); - float bx = _cairo_fixed_to_double(grad->stops[i].x) - ax; - float bp = (fdist - ax)/bx; - float ap = 1.0 - bp; - - out[0] = - (grad->stops[i-1].color.red / 65535.) * ap + - (grad->stops[i].color.red / 65535.) * bp; - out[1] = - (grad->stops[i-1].color.green / 65535.) * ap + - (grad->stops[i].color.green / 65535.) * bp; - out[2] = - (grad->stops[i-1].color.blue / 65535.) * ap + - (grad->stops[i].color.blue / 65535.) * bp; - out[3] = - (grad->stops[i-1].color.alpha / 65535.) * ap + - (grad->stops[i].color.alpha / 65535.) * bp; - } -} - -static CGFunctionRef -CreateGradientFunction (cairo_gradient_pattern_t *gpat) -{ - static const float input_value_range[2] = { 0.f, 1.f }; - static const float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f }; - static const CGFunctionCallbacks callbacks = { - 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy - }; - - return CGFunctionCreate (gpat, - 1, - input_value_range, - 4, - output_value_ranges, - &callbacks); -} - -static CGShadingRef -_cairo_nquartz_cairo_gradient_pattern_to_quartz (cairo_pattern_t *abspat) -{ - cairo_matrix_t mat; - double x0, y0; - - if (abspat->type != CAIRO_PATTERN_TYPE_LINEAR && - abspat->type != CAIRO_PATTERN_TYPE_RADIAL) - return NULL; - - /* We can only do this if we have an identity pattern matrix; - * otherwise fall back through to the generic pattern case. - * XXXperf we could optimize this by creating a pattern with the shading; - * but we'd need to know the extents to do that. - * ... but we don't care; we can use the surface extents for it - * XXXtodo - implement gradients with non-identity pattern matrices - */ - cairo_pattern_get_matrix (abspat, &mat); - if (mat.xx != 1.0 || mat.yy != 1.0 || mat.xy != 0.0 || mat.yx != 0.0) - return NULL; - - x0 = mat.x0; - y0 = mat.y0; - - if (abspat->type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t*) abspat; - CGShadingRef shading; - CGPoint start, end; - CGFunctionRef gradFunc; - CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); - - start = CGPointMake (_cairo_fixed_to_double (lpat->gradient.p1.x) - x0, - _cairo_fixed_to_double (lpat->gradient.p1.y) - y0); - end = CGPointMake (_cairo_fixed_to_double (lpat->gradient.p2.x) - x0, - _cairo_fixed_to_double (lpat->gradient.p2.y) - y0); - - cairo_pattern_reference (abspat); - gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat); - shading = CGShadingCreateAxial (rgb, - start, end, - gradFunc, - true, true); - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); - - return shading; - } - - if (abspat->type == CAIRO_PATTERN_TYPE_RADIAL) { - cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t*) abspat; - CGShadingRef shading; - CGPoint start, end; - CGFunctionRef gradFunc; - CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); - - start = CGPointMake (_cairo_fixed_to_double (rpat->gradient.inner.x) - x0, - _cairo_fixed_to_double (rpat->gradient.inner.y) - y0); - end = CGPointMake (_cairo_fixed_to_double (rpat->gradient.outer.x) - x0, - _cairo_fixed_to_double (rpat->gradient.outer.y) - y0); - - cairo_pattern_reference (abspat); - gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat); - shading = CGShadingCreateRadial (rgb, - start, - _cairo_fixed_to_double (rpat->gradient.inner.radius), - end, - _cairo_fixed_to_double (rpat->gradient.outer.radius), - gradFunc, - true, true); - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); - - return shading; - } - - /* Shouldn't be reached */ - ASSERT_NOT_REACHED; - return NULL; -} - - -/* Generic cairo_pattern -> 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_rectangle_int16_t extents; - cairo_status_t status; - - cairo_nquartz_surface_t *quartz_surf = NULL; - - cairo_bool_t flip = FALSE; - - CGImageRef img; - - if (!cairo_surface_is_nquartz (pat_surf)) { - ND((stderr, "-- source is not nquartz surface\n")); - /* This sucks; we should really store a dummy nquartz surface - * for passing in here - * XXXtodo store a dummy nquartz surface somewhere for handing off to clone_similar - * XXXtodo/perf don't use clone if the source surface is an image surface! Instead, - * just create the CGImage directly! - */ - - cairo_surface_t *dummy = cairo_nquartz_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); - - cairo_rectangle_int16_t rect; - _cairo_surface_get_extents (pat_surf, &rect); - - cairo_surface_t *new_surf = NULL; - - _cairo_surface_clone_similar (dummy, pat_surf, rect.x, rect.y, - rect.width, rect.height, &new_surf); - - cairo_surface_destroy(dummy); - - quartz_surf = (cairo_nquartz_surface_t *) new_surf; - } else { - ND((stderr, "-- source is nquartz surface\n")); - /* If it's a nquartz 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_nquartz_surface_t*) pat_surf; - - /* 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? - */ - flip = TRUE; - } - - /* this is a 10.4 API, present in 10.3.9 */ - CGContextFlush (quartz_surf->cgContext); - img = CGBitmapContextCreateImage (quartz_surf->cgContext); - - if (!img) { - // ... give up. - ND((stderr, "CGBitmapContextCreateImage failed\n")); - cairo_surface_destroy ((cairo_surface_t*)quartz_surf); - return; - } - - if (flip) { - CGContextTranslateCTM (context, 0, CGImageGetHeight(img)); - CGContextScaleCTM (context, 1, -1); - } - - CGRect imageBounds; - imageBounds.size = CGSizeMake (CGImageGetWidth(img), CGImageGetHeight(img)); - imageBounds.origin.x = 0; - imageBounds.origin.y = 0; - - CGContextDrawImage (context, imageBounds, img); - - CGImageRelease (img); - - cairo_surface_destroy ((cairo_surface_t*) quartz_surf); -} - -/* Borrowed from cairo-meta-surface */ -static cairo_status_t -_init_pattern_with_snapshot (cairo_pattern_t *pattern, - const cairo_pattern_t *other) -{ - _cairo_pattern_init_copy (pattern, other); - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = - (cairo_surface_pattern_t *) pattern; - cairo_surface_t *surface = surface_pattern->surface; - - surface_pattern->surface = _cairo_surface_snapshot (surface); - - cairo_surface_destroy (surface); - - if (surface_pattern->surface->status) - return surface_pattern->surface->status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static CGPatternRef -_cairo_nquartz_cairo_repeating_surface_pattern_to_quartz (cairo_nquartz_surface_t *dest, - cairo_pattern_t *abspat) -{ - cairo_surface_pattern_t *spat; - cairo_surface_t *pat_surf; - cairo_rectangle_int16_t extents; - - CGRect pbounds; - CGAffineTransform ptransform, stransform; - CGPatternCallbacks cb = { 0, - SurfacePatternDrawFunc, - (CGFunctionReleaseInfoCallback) cairo_pattern_destroy }; - CGPatternRef cgpat; - float rw, rh; - - cairo_pattern_union_t *snap_pattern = NULL; - cairo_pattern_t *target_pattern = abspat; - - /* SURFACE is the only type we'll handle here */ - if (abspat->type != CAIRO_PATTERN_TYPE_SURFACE) - return NULL; - - spat = (cairo_surface_pattern_t *) abspat; - pat_surf = spat->surface; - - _cairo_surface_get_extents (pat_surf, &extents); - pbounds.origin.x = 0; - pbounds.origin.y = 0; - pbounds.size.width = extents.width; - pbounds.size.height = extents.height; - - cairo_matrix_t m = spat->base.matrix; - cairo_matrix_invert(&m); - _cairo_nquartz_cairo_matrix_to_quartz (&m, &stransform); - - /* The pattern matrix is relative to the bottom left, again; the - * incoming cairo pattern matrix is relative to the upper left. - * So we take the pattern matrix and the original context matrix, - * which gives us the correct base translation/y flip. - */ - ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM); - -#ifdef NQUARTZ_DEBUG - ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height)); - ND((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d)); - CGAffineTransform xform = CGContextGetCTM(dest->cgContext); - 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 - - // 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 0 - if (spat->base.extend == CAIRO_EXTEND_NONE) { - /* XXX wasteful; this will keep drawing the pattern in the - * original location. We need to set up the clip region - * instead to do this right. - */ - rw = 0; - rh = 0; - } else if (spat->base.extend == CAIRO_EXTEND_REPEAT) { - rw = extents.width; - rh = extents.height; - } else if (spat->base.extend == CAIRO_EXTEND_REFLECT) { - /* XXX broken; need to emulate by reflecting the image into 4 quadrants - * and then tiling that - */ - rw = extents.width; - rh = extents.height; - } else { - /* CAIRO_EXTEND_PAD */ - /* XXX broken. */ - rw = 0; - rh = 0; - } -#else - rw = extents.width; - rh = extents.height; -#endif - - /* 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 (snap_pattern, abspat); - } else { - cairo_pattern_reference (abspat); - target_pattern = abspat; - } - - cgpat = CGPatternCreate (target_pattern, - pbounds, - ptransform, - rw, rh, - kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */ - TRUE, - &cb); - return cgpat; -} - -typedef enum { - DO_SOLID, - DO_SHADING, - DO_PATTERN, - DO_UNSUPPORTED -} cairo_nquartz_action_t; - -static cairo_nquartz_action_t -_cairo_nquartz_setup_source (cairo_nquartz_surface_t *surface, - cairo_pattern_t *source) -{ - assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern)); - - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; - - CGContextSetRGBStrokeColor (surface->cgContext, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - CGContextSetRGBFillColor (surface->cgContext, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - - return DO_SOLID; - } else if (source->type == CAIRO_PATTERN_TYPE_LINEAR || - source->type == CAIRO_PATTERN_TYPE_RADIAL) - { - CGShadingRef shading = _cairo_nquartz_cairo_gradient_pattern_to_quartz (source); - if (!shading) - return DO_UNSUPPORTED; - - surface->sourceShading = shading; - - return DO_SHADING; - } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - CGPatternRef pattern = _cairo_nquartz_cairo_repeating_surface_pattern_to_quartz (surface, source); - if (!pattern) - return DO_UNSUPPORTED; - - float patternAlpha = 1.0f; - - // Save before we change the pattern, colorspace, etc. so that - // we can restore and make sure that quartz releases our - // pattern (which may be stack allocated) - CGContextSaveGState(surface->cgContext); - - CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL); - CGContextSetFillColorSpace (surface->cgContext, patternSpace); - CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha); - CGContextSetStrokeColorSpace (surface->cgContext, patternSpace); - CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha); - CGColorSpaceRelease (patternSpace); - - /* Quartz likes to munge the pattern phase (as yet unexplained - * why); force it to 0,0 as we've already baked in the correct - * pattern translation into the pattern matrix - */ - CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0)); - - surface->sourcePattern = pattern; - - return DO_PATTERN; - } else { - return DO_UNSUPPORTED; - } - - ASSERT_NOT_REACHED; -} - -static void -_cairo_nquartz_teardown_source (cairo_nquartz_surface_t *surface, - cairo_pattern_t *source) -{ - if (surface->sourceImage) { - // nothing to do; we don't use sourceImage yet - } - - if (surface->sourceShading) { - CGShadingRelease(surface->sourceShading); - surface->sourceShading = NULL; - } - - if (surface->sourcePattern) { - CGPatternRelease(surface->sourcePattern); - // To tear down the pattern and colorspace - CGContextRestoreGState(surface->cgContext); - - surface->sourcePattern = NULL; - } -} - -/** - ** get source/dest image implementation - **/ - -static void -ImageDataReleaseFunc(void *info, const void *data, size_t size) -{ - if (data != NULL) { - free((void *) data); - } -} - -/* Read the image from the surface's front buffer */ -static cairo_int_status_t -_cairo_nquartz_get_image (cairo_nquartz_surface_t *surface, - cairo_image_surface_t **image_out, - unsigned char **data_out) -{ - unsigned char *imageData; - cairo_image_surface_t *isurf; - - /* If we weren't constructed with an AGL Context - * or a CCGBitmapContext, then we have no way - * of doing this - */ -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL - if (surface->aglContext) { - AGLContext oldContext; - cairo_format_masks_t masks = { 32, 0xff << 24, 0xff << 16, 0xff << 8, 0xff << 0 }; - unsigned int i; - - oldContext = aglGetCurrentContext(); - if (oldContext != surface->aglContext) - aglSetCurrentContext(surface->aglContext); - - imageData = (unsigned char *) malloc (surface->extents.width * surface->extents.height * 4); - if (!imageData) - return CAIRO_STATUS_NO_MEMORY; - - glReadBuffer (GL_FRONT); - glReadPixels (0, 0, surface->extents.width, surface->extents.height, - GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, - imageData); - - /* swap lines */ - /* XXX find some fast code to do this */ - unsigned int lineSize = surface->extents.width * 4; - unsigned char *tmpLine = malloc(lineSize); - for (i = 0; i < surface->extents.height / 2; i++) { - unsigned char *l0 = imageData + lineSize * i; - unsigned char *l1 = imageData + (lineSize * (surface->extents.height - i - 1)); - memcpy (tmpLine, l0, lineSize); - memcpy (l0, l1, lineSize); - memcpy (l1, tmpLine, lineSize); - } - free (tmpLine); - - if (oldContext && oldContext != surface->aglContext) - aglSetCurrentContext(oldContext); - - isurf = (cairo_image_surface_t *)_cairo_image_surface_create_with_masks - (imageData, - &masks, - surface->extents.width, - surface->extents.height, - surface->extents.width * 4); - - if (data_out) - *data_out = imageData; - else - _cairo_image_surface_assume_ownership_of_data (isurf); -#else - /* no AGL */ - if (0) { -#endif - } else if (CGBitmapContextGetBitsPerPixel(surface->cgContext) != 0) { - unsigned int stride; - unsigned int bitinfo; - unsigned int bpc, bpp; - CGColorSpaceRef colorspace; - unsigned int color_comps; - - imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext); -#ifdef USE_10_3_WORKAROUNDS - bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext); -#else - bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext); -#endif - stride = CGBitmapContextGetBytesPerRow (surface->cgContext); - bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext); - bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext); - - // let's hope they don't add YUV under us - colorspace = CGBitmapContextGetColorSpace (surface->cgContext); - color_comps = CGColorSpaceGetNumberOfComponents(colorspace); - - // XXX TODO: We can handle all of these by converting to - // pixman masks, including non-native-endian masks - if (bpc != 8) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (bpp != 32 && bpp != 8) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (color_comps != 3 && color_comps != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (bpp == 32 && color_comps == 3 && - (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst && - (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_ARGB32, - surface->extents.width, - surface->extents.height, - stride); - } else if (bpp == 32 && color_comps == 3 && - (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst && - (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_RGB24, - surface->extents.width, - surface->extents.height, - stride); - } else if (bpp == 8 && color_comps == 1) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_A8, - surface->extents.width, - surface->extents.height, - stride); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - *image_out = isurf; - return CAIRO_STATUS_SUCCESS; -} - -/** - ** Cairo surface backend implementations - **/ - -static cairo_status_t -_cairo_nquartz_surface_finish (void *abstract_surface) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - - ND((stderr, "_cairo_nquartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext)); - -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL - if (surface->aglContext) - aglSetCurrentContext(surface->aglContext); -#endif - - CGContextFlush (surface->cgContext); - - /* Restore our saved gstate that we use to reset clipping */ - CGContextRestoreGState (surface->cgContext); - - CGContextRelease (surface->cgContext); - - surface->cgContext = NULL; - -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL - if (surface->aglContext) - glFlush(); - - surface->aglContext = NULL; -#endif - - if (surface->imageData) { - free (surface->imageData); - surface->imageData = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_nquartz_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - - //ND((stderr, "%p _cairo_nquartz_surface_acquire_source_image\n", surface)); - - *image_extra = NULL; - - return _cairo_nquartz_get_image (surface, image_out, NULL); -} - -static cairo_status_t -_cairo_nquartz_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int16_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int16_t *image_rect, - void **image_extra) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - cairo_int_status_t status; - unsigned char *data; - - ND((stderr, "%p _cairo_nquartz_surface_acquire_dest_image\n", surface)); - - *image_rect = surface->extents; - - status = _cairo_nquartz_get_image (surface, image_out, &data); - if (status) - return status; - - *image_extra = data; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_nquartz_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int16_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int16_t *image_rect, - void *image_extra) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - unsigned char *imageData = (unsigned char *) image_extra; - - //ND((stderr, "%p _cairo_nquartz_surface_release_dest_image\n", surface)); - - if (!CGBitmapContextGetData (surface->cgContext)) { - CGDataProviderRef dataProvider; - CGImageRef img; - - dataProvider = CGDataProviderCreateWithData (NULL, imageData, - surface->extents.width * surface->extents.height * 4, - ImageDataReleaseFunc); - - img = CGImageCreate (surface->extents.width, surface->extents.height, - 8, 32, - surface->extents.width * 4, - CGColorSpaceCreateDeviceRGB(), - kCGImageAlphaPremultipliedFirst, - dataProvider, - NULL, - false, - kCGRenderingIntentDefault); - - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy); - - CGContextDrawImage (surface->cgContext, - CGRectMake (0, 0, surface->extents.width, surface->extents.height), - img); - - CGImageRelease (img); - CGDataProviderRelease (dataProvider); - - ND((stderr, "Image for surface %p was recovered from a bitmap\n", surface)); - } - - cairo_surface_destroy ((cairo_surface_t *) image); -} - -static cairo_surface_t * -_cairo_nquartz_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - - cairo_format_t format; - - if (content == CAIRO_CONTENT_COLOR_ALPHA) - format = CAIRO_FORMAT_ARGB32; - else if (content == CAIRO_CONTENT_COLOR) - format = CAIRO_FORMAT_RGB24; - else if (content == CAIRO_CONTENT_ALPHA) - format = CAIRO_FORMAT_A8; - else - return NULL; - - return cairo_nquartz_surface_create (format, width, height); -} - -static cairo_status_t -_cairo_nquartz_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_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - cairo_nquartz_surface_t *new_surface = NULL; - cairo_format_t new_format; - - CGImageRef quartz_image = NULL; - cairo_surface_t *surface_to_release = NULL; - - if (cairo_surface_is_nquartz (src)) { - cairo_nquartz_surface_t *qsurf = (cairo_nquartz_surface_t *) src; - 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->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; - } - - if (!quartz_image) - return CAIRO_INT_STATUS_UNSUPPORTED; - - new_surface = (cairo_nquartz_surface_t *) - cairo_nquartz_surface_create (new_format, - CGImageGetWidth (quartz_image), - CGImageGetHeight (quartz_image)); - if (!new_surface || new_surface->base.status) - return CAIRO_INT_STATUS_UNSUPPORTED; - - CGContextSetCompositeOperation (new_surface->cgContext, - kPrivateCGCompositeCopy); - - nquartz_image_to_png (quartz_image, NULL); - - CGContextDrawImage (new_surface->cgContext, - CGRectMake (src_x, src_y, width, height), - quartz_image); - CGImageRelease (quartz_image); - - *clone_out = (cairo_surface_t*) new_surface; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_nquartz_surface_get_extents (void *abstract_surface, - cairo_rectangle_int16_t *extents) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - - *extents = surface->extents; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_nquartz_surface_paint (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_nquartz_action_t action; - - ND((stderr, "%p _cairo_nquartz_surface_paint op %d source->type %d\n", surface, op, source->type)); - - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); - - CGRect bounds = CGContextGetClipBoundingBox (surface->cgContext); - - action = _cairo_nquartz_setup_source (surface, source); - - if (action == DO_SOLID || action == DO_PATTERN) { - CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x, - surface->extents.y, - surface->extents.width, - surface->extents.height)); - } else if (action == DO_SHADING) { - CGContextDrawShading (surface->cgContext, surface->sourceShading); - } else { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - } - - _cairo_nquartz_teardown_source (surface, source); - - ND((stderr, "-- paint\n")); - return rv; -} - -static cairo_int_status_t -_cairo_nquartz_surface_fill (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_nquartz_action_t action; - - ND((stderr, "%p _cairo_nquartz_surface_fill op %d source->type %d\n", surface, op, source->type)); - - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - CGContextSaveGState (surface->cgContext); - - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); - CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); - - action = _cairo_nquartz_setup_source (surface, source); - if (action == DO_UNSUPPORTED) { - CGContextRestoreGState (surface->cgContext); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - CGContextBeginPath (surface->cgContext); - _cairo_nquartz_cairo_path_to_quartz_context (path, surface->cgContext); - - if (action == DO_SOLID || action == DO_PATTERN) { - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextFillPath (surface->cgContext); - else - CGContextEOFillPath (surface->cgContext); - } else if (action == DO_SHADING) { - - // we have to clip and then paint the shading; we can't fill - // with the shading - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextClip (surface->cgContext); - else - CGContextEOClip (surface->cgContext); - - CGContextDrawShading (surface->cgContext, surface->sourceShading); - } else { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - } - - _cairo_nquartz_teardown_source (surface, source); - - CGContextRestoreGState (surface->cgContext); - - ND((stderr, "-- fill\n")); - return rv; -} - -static cairo_int_status_t -_cairo_nquartz_surface_stroke (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_stroke_style_t *style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_nquartz_action_t action; - - ND((stderr, "%p _cairo_nquartz_surface_stroke op %d source->type %d\n", surface, op, source->type)); - - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - CGContextSaveGState (surface->cgContext); - - // Turning antialiasing off causes misrendering with - // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels) - //CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); - CGContextSetLineWidth (surface->cgContext, style->line_width); - CGContextSetLineCap (surface->cgContext, _cairo_nquartz_cairo_line_cap_to_quartz (style->line_cap)); - CGContextSetLineJoin (surface->cgContext, _cairo_nquartz_cairo_line_join_to_quartz (style->line_join)); - CGContextSetMiterLimit (surface->cgContext, style->miter_limit); - - if (style->dash && style->num_dashes) { -#define STATIC_DASH 32 - float sdash[STATIC_DASH]; - float *fdash = sdash; - int k; - if (style->num_dashes > STATIC_DASH) - fdash = malloc (sizeof(float)*style->num_dashes); - - for (k = 0; k < style->num_dashes; k++) - fdash[k] = (float) style->dash[k]; - - CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, style->num_dashes); - - if (fdash != sdash) - free (fdash); - } - - CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); - - action = _cairo_nquartz_setup_source (surface, source); - if (action == DO_UNSUPPORTED) { - CGContextRestoreGState (surface->cgContext); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - CGContextBeginPath (surface->cgContext); - _cairo_nquartz_cairo_path_to_quartz_context (path, surface->cgContext); - - if (action == DO_SOLID || action == DO_PATTERN) { - CGContextStrokePath (surface->cgContext); - } else if (action == DO_SHADING) { - // we have to clip and then paint the shading; first we have to convert - // the stroke to a path that we can fill - CGContextReplacePathWithStrokedPath (surface->cgContext); - CGContextClip (surface->cgContext); - - CGContextDrawShading (surface->cgContext, surface->sourceShading); - } else { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - } - - _cairo_nquartz_teardown_source (surface, source); - - CGContextRestoreGState (surface->cgContext); - - ND((stderr, "-- stroke\n")); - return rv; -} - -static cairo_int_status_t -_cairo_nquartz_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_nquartz_action_t action; - int i; - - if (num_glyphs <= 0) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (!_cairo_scaled_font_is_atsui (scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - CGContextSaveGState (surface->cgContext); - - action = _cairo_nquartz_setup_source (surface, source); - if (action == DO_SOLID || action == DO_PATTERN) { - CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill); - } else if (action == DO_SHADING) { - CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip); - } else { - /* Unsupported */ - CGContextRestoreGState (surface->cgContext); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - double y_height = surface->extents.height; - - CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); - - ATSUFontID fid = _cairo_atsui_scaled_font_get_atsu_font_id (scaled_font); - ATSFontRef atsfref = FMGetATSFontRefFromFont (fid); - CGFontRef cgfref = CGFontCreateWithPlatformFont (&atsfref); - - CGContextSetFont (surface->cgContext, cgfref); - CGFontRelease (cgfref); - - /* So this should include the size; I don't know if I need to extract the - * size from this and call CGContextSetFontSize.. will I get crappy hinting - * with this 1.0 size business? Or will CG just multiply that size into the - * text matrix? - */ - //ND((stderr, "show_glyphs: glyph 0 at: %f, %f\n", glyphs[0].x, glyphs[0].y)); - CGAffineTransform cairoTextTransform, textTransform; - _cairo_nquartz_cairo_matrix_to_quartz (&scaled_font->font_matrix, &cairoTextTransform); - - textTransform = CGAffineTransformMakeTranslation (glyphs[0].x, glyphs[0].y); - textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0); - textTransform = CGAffineTransformConcat (cairoTextTransform, textTransform); - - CGContextSetTextMatrix (surface->cgContext, textTransform); - CGContextSetFontSize (surface->cgContext, 1.0); - - // XXXtodo/perf: stack storage for glyphs/sizes -#define STATIC_BUF_SIZE 64 - CGGlyph glyphs_static[STATIC_BUF_SIZE]; - CGSize cg_advances_static[STATIC_BUF_SIZE]; - CGGlyph *cg_glyphs = &glyphs_static[0]; - CGSize *cg_advances = &cg_advances_static[0]; - - if (num_glyphs > STATIC_BUF_SIZE) { - cg_glyphs = (CGGlyph*) malloc(sizeof(CGGlyph) * num_glyphs); - cg_advances = (CGSize*) malloc(sizeof(CGSize) * num_glyphs); - } - - double xprev = glyphs[0].x; - double yprev = glyphs[0].y; - - cg_glyphs[0] = glyphs[0].index; - cg_advances[0].width = 0; - cg_advances[0].height = 0; - - for (i = 1; i < num_glyphs; i++) { - cg_glyphs[i] = glyphs[i].index; - cg_advances[i-1].width = glyphs[i].x - xprev; - cg_advances[i-1].height = glyphs[i].y - yprev; - xprev = glyphs[i].x; - yprev = glyphs[i].y; - } - -#if 0 - for (i = 0; i < num_glyphs; i++) { - ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height)); - } -#endif - - CGContextShowGlyphsWithAdvances (surface->cgContext, - cg_glyphs, - cg_advances, - num_glyphs); - - if (cg_glyphs != &glyphs_static[0]) { - free (cg_glyphs); - free (cg_advances); - } - - if (action == DO_SHADING) - CGContextDrawShading (surface->cgContext, surface->sourceShading); - - _cairo_nquartz_teardown_source (surface, source); - - CGContextRestoreGState (surface->cgContext); - - return rv; -} - -static cairo_int_status_t -_cairo_nquartz_surface_mask (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - - ND((stderr, "%p _cairo_nquartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type)); - - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { - /* This is easy; we just need to paint with the alpha. */ - cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; - - CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); - } else { - /* So, CGContextClipToMask is not present in 10.3.9, so we're - * doomed; if we have imageData, we can do fallback, otherwise - * just pretend success. - */ - if (surface->imageData) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; - } - - rv = _cairo_nquartz_surface_paint (surface, op, source); - - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { - CGContextSetAlpha (surface->cgContext, 1.0); - } - - ND((stderr, "-- mask\n")); - - return rv; -} - -static cairo_int_status_t -_cairo_nquartz_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - - ND((stderr, "%p _cairo_nquartz_surface_intersect_clip_path path: %p\n", surface, path)); - - if (path == NULL) { - /* If we're being asked to reset the clip, we can only do it - * by restoring the gstate to our previous saved one, and - * saving it again. - * - * Note that this assumes that ALL nquartz surface creation - * functions will do a SaveGState first; we do this in create_internal. - */ - CGContextRestoreGState (surface->cgContext); - CGContextSaveGState (surface->cgContext); - } else { - CGContextBeginPath (surface->cgContext); - _cairo_nquartz_cairo_path_to_quartz_context (path, surface->cgContext); - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextClip (surface->cgContext); - else - CGContextEOClip (surface->cgContext); - } - - ND((stderr, "-- intersect_clip_path\n")); - - return CAIRO_STATUS_SUCCESS; -} - -// XXXtodo implement show_page; need to figure out how to handle begin/end - -static const struct _cairo_surface_backend cairo_nquartz_surface_backend = { - CAIRO_SURFACE_TYPE_NQUARTZ, - _cairo_nquartz_surface_create_similar, - _cairo_nquartz_surface_finish, - _cairo_nquartz_surface_acquire_source_image, - NULL, /* release_source_image */ - _cairo_nquartz_surface_acquire_dest_image, - _cairo_nquartz_surface_release_dest_image, - _cairo_nquartz_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* copy_page */ - NULL, /* show_page */ - NULL, /* set_clip_region */ - _cairo_nquartz_surface_intersect_clip_path, - _cairo_nquartz_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _cairo_nquartz_surface_paint, - _cairo_nquartz_surface_mask, - _cairo_nquartz_surface_stroke, - _cairo_nquartz_surface_fill, - _cairo_nquartz_surface_show_glyphs, - - NULL, /* snapshot */ -}; - -cairo_bool_t -cairo_surface_is_nquartz (cairo_surface_t *surf) -{ - return (surf->backend == &cairo_nquartz_surface_backend); -} - -static cairo_nquartz_surface_t * -_cairo_nquartz_surface_create_internal (CGContextRef cgContext, -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL - AGLContext aglContext, -#else - void * unused, -#endif - cairo_content_t content, - unsigned int width, - unsigned int height, - cairo_bool_t y_grows_down) -{ - cairo_nquartz_surface_t *surface; - - /* Init the base surface */ - surface = malloc(sizeof(cairo_nquartz_surface_t)); - if (surface == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - memset(surface, 0, sizeof(cairo_nquartz_surface_t)); - - _cairo_surface_init(&surface->base, &cairo_nquartz_surface_backend, - content); - - /* Save our extents */ - surface->extents.x = surface->extents.y = 0; - surface->extents.width = width; - surface->extents.height = height; - - if (!y_grows_down) { - /* Then make the CGContext sane */ - CGContextTranslateCTM (cgContext, 0.0, surface->extents.height); - CGContextScaleCTM (cgContext, 1.0, -1.0); - } - - /* Save so we can always get back to a known-good CGContext -- this is - * required for proper behaviour of intersect_clip_path(NULL) - */ - CGContextSaveGState (cgContext); - -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL - surface->aglContext = aglContext; -#endif - surface->cgContext = cgContext; - surface->cgContextBaseCTM = CGContextGetCTM (cgContext); - - surface->imageData = NULL; - - return surface; -} - -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL -cairo_surface_t * -cairo_nquartz_surface_create_for_agl_context (AGLContext aglContext, - unsigned int width, - unsigned int height, - cairo_bool_t y_grows_down) -{ - cairo_nquartz_surface_t *surf; - CGSize sz; - - /* Make our CGContext from the AGL context */ - sz.width = width; - sz.height = height; - - CGContextRef cgc = CGGLContextCreate (aglContext, sz, NULL /* device RGB colorspace */); - - surf = _cairo_nquartz_surface_create_internal (cgc, aglContext, CAIRO_CONTENT_COLOR_ALPHA, - width, height, y_grows_down); - if (!surf) { - CGContextRelease (cgc); - // create_internal will have set an error - return (cairo_surface_t*) &_cairo_surface_nil; - } - - return (cairo_surface_t *) surf; -} -#endif - -cairo_surface_t * -cairo_nquartz_surface_create_for_cg_context (CGContextRef cgContext, - unsigned int width, - unsigned int height, - cairo_bool_t y_grows_down) -{ - cairo_nquartz_surface_t *surf; - - CGContextRetain (cgContext); - - surf = _cairo_nquartz_surface_create_internal (cgContext, NULL, CAIRO_CONTENT_COLOR_ALPHA, - width, height, y_grows_down); - if (!surf) { - CGContextRelease (cgContext); - // create_internal will have set an error - return (cairo_surface_t*) &_cairo_surface_nil; - } - - return (cairo_surface_t *) surf; -} - -cairo_surface_t * -cairo_nquartz_surface_create (cairo_format_t format, - unsigned int width, - unsigned int height) -{ - cairo_nquartz_surface_t *surf; - CGContextRef cgc; - CGColorSpaceRef cgColorspace; - CGBitmapInfo bitinfo; - void *imageData; - int stride; - int bitsPerComponent; - - if (format == CAIRO_FORMAT_ARGB32) { - cgColorspace = CGColorSpaceCreateDeviceRGB(); - stride = width * 4; - bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; - bitsPerComponent = 8; - } else if (format == CAIRO_FORMAT_RGB24) { - cgColorspace = CGColorSpaceCreateDeviceRGB(); - stride = width * 4; - bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; - bitsPerComponent = 8; - } else if (format == CAIRO_FORMAT_A8) { - cgColorspace = CGColorSpaceCreateDeviceGray(); - if (width % 4 == 0) - stride = width; - else - stride = (width & ~3) + 4; - bitinfo = kCGImageAlphaNone; - bitsPerComponent = 8; - } else if (format == CAIRO_FORMAT_A1) { - /* I don't think we can usefully support this, as defined by - * cairo_format_t -- these are 1-bit pixels stored in 32-bit - * quantities. - */ - _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - return (cairo_surface_t*) &_cairo_surface_nil; - } else { - _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - return (cairo_surface_t*) &_cairo_surface_nil; - } - - imageData = malloc (height * stride); - if (!imageData) { - CGColorSpaceRelease (cgColorspace); - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; - } - - cgc = CGBitmapContextCreate (imageData, - width, - height, - bitsPerComponent, - stride, - cgColorspace, - bitinfo); - CGColorSpaceRelease (cgColorspace); - - if (!cgc) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; - } - - surf = _cairo_nquartz_surface_create_internal (cgc, NULL, _cairo_content_from_format (format), - width, height, FALSE); - if (!surf) { - CGContextRelease (cgc); - // create_internal will have set an error - return (cairo_surface_t*) &_cairo_surface_nil; - } - - surf->imageData = imageData; - - return (cairo_surface_t *) surf; -} - -CGContextRef -cairo_nquartz_surface_get_cg_context (cairo_surface_t *surf) -{ - cairo_nquartz_surface_t *nquartz = (cairo_nquartz_surface_t*)surf; - - if (!cairo_surface_is_nquartz(surf)) - return NULL; - - return nquartz->cgContext; -} - - -/* Debug stuff */ - -#ifdef NQUARTZ_DEBUG - -#include - -void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest) -{ - Handle dataRef = NULL; - OSType dataRefType; - CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII); - - GraphicsExportComponent grex = 0; - unsigned long sizeWritten; - - ComponentResult result; - - // create the data reference - result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle, - 0, &dataRef, &dataRefType); - - if (NULL != dataRef && noErr == result) { - // get the PNG exporter - result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG, - &grex); - - if (grex) { - // tell the exporter where to find its source image - result = GraphicsExportSetInputCGImage(grex, inImageRef); - - if (noErr == result) { - // tell the exporter where to save the exporter image - result = GraphicsExportSetOutputDataReference(grex, dataRef, - dataRefType); - - if (noErr == result) { - // write the PNG file - result = GraphicsExportDoExport(grex, &sizeWritten); - } - } - - // remember to close the component - CloseComponent(grex); - } - - // remember to dispose of the data reference handle - DisposeHandle(dataRef); - } -} -#endif - -void -nquartz_image_to_png (CGImageRef imgref, char *dest) -{ -#if 0 - static int sctr = 0; - char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png"; - - if (dest == NULL) { - fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr); - sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr); - sctr++; - dest = sptr; - } - - ExportCGImageToPNGFile(imgref, dest); -#endif -} - -void -nquartz_surface_to_png (cairo_nquartz_surface_t *nq, char *dest) -{ -#if 0 - static int sctr = 0; - char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png"; - - if (nq->base.type != CAIRO_SURFACE_TYPE_NQUARTZ) { - fprintf (stderr, "** nquartz_surface_to_png: surface %p isn't nquartz!\n", nq); - return; - } - - if (dest == NULL) { - fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr); - sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr); - sctr++; - dest = sptr; - } - - CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext); - if (imgref == NULL) { - fprintf (stderr, "nquartz surface at %p is not a bitmap context!\n", nq); - return; - } - - ExportCGImageToPNGFile(imgref, dest); - - CGImageRelease(imgref); -#endif -} - diff --git a/src/cairo-nquartz.h b/src/cairo-nquartz.h deleted file mode 100644 index c6c58e751..000000000 --- a/src/cairo-nquartz.h +++ /dev/null @@ -1,80 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2006 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 - */ - -#ifndef CAIRO_NQUARTZ_H -#define CAIRO_NQUARTZ_H - -#include - -#if CAIRO_HAS_NQUARTZ_SURFACE - -#include - -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_nquartz_surface_create (cairo_format_t format, - unsigned int width, - unsigned int height); - -#ifdef CAIRO_NQUARTZ_SUPPORT_AGL -cairo_public cairo_surface_t * -cairo_nquartz_surface_create_for_agl_context (AGLContext aglContext, - unsigned int width, - unsigned int height, - cairo_bool_t y_grows_down); -#endif - -cairo_public cairo_surface_t * -cairo_nquartz_surface_create_for_cg_context (CGContextRef cgContext, - unsigned int width, - unsigned int height, - cairo_bool_t y_grows_down); - -cairo_public cairo_bool_t -cairo_surface_is_nquartz (cairo_surface_t *surf); - -cairo_public CGContextRef -cairo_nquartz_surface_get_cg_context (cairo_surface_t *surf); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_NQUARTZ_SURFACE */ -# error Cairo was not compiled with support for the nquartz backend -#endif /* CAIRO_HAS_NQUARTZ_SURFACE */ - -#endif /* CAIRO_NQUARTZ_H */ diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index f9db501c9..f053bc0d4 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -39,20 +39,34 @@ #include #include -typedef struct cairo_quartz_surface { +#ifdef CAIRO_NQUARTZ_SUPPORT_AGL +#include +#include + +typedef AGLContext nquartz_agl_context_type; +#else +typedef void* nquartz_agl_context_type; +#endif + +typedef struct cairo_nquartz_surface { cairo_surface_t base; - CGContextRef context; + void *imageData; - cairo_bool_t y_grows_down; + CGContextRef cgContext; + CGAffineTransform cgContextBaseCTM; cairo_rectangle_int16_t extents; - pixman_region16_t *clip_region; -} cairo_quartz_surface_t; - -cairo_bool_t -_cairo_surface_is_quartz (cairo_surface_t *surface); + /* These are stored while drawing operations are in place, set up + * by nquartz_setup_source() and nquartz_finish_source() + */ + CGAffineTransform imageTransform; + CGImageRef sourceImage; + CGShadingRef sourceShading; + CGPatternRef sourcePattern; + nquartz_agl_context_type aglContext; +} cairo_nquartz_surface_t, cairo_quartz_surface_t; cairo_bool_t _cairo_scaled_font_is_atsui (cairo_scaled_font_t *sfont); diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index cff15af2b..0745d8919 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -1,6 +1,7 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * - * Copyright © 2004 Calum Robinson + * Copyright © 2006, 2007 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 @@ -27,237 +28,1841 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is Calum Robinson + * The Initial Developer of the Original Code is Mozilla Corporation. * * Contributor(s): - * Calum Robinson + * Vladimir Vukicevic + */ + +#include + +/* Support rendering to an OpenGL AGL context using CGGLContextCreate; + * Apple has deprecated CGGLContext, so CAIRO_NQUARTZ_SUPPORT_AGL is + * not defined by default. */ #include "cairoint.h" #include "cairo-private.h" + #include "cairo-quartz-private.h" -static cairo_status_t -_cairo_quartz_surface_finish(void *abstract_surface) -{ - cairo_quartz_surface_t *surface = abstract_surface; +#undef NQUARTZ_DEBUG - if (surface->clip_region) - pixman_region_destroy (surface->clip_region); +#ifdef NQUARTZ_DEBUG +#define ND(_x) fprintf _x +#else +#define ND(_x) do {} while(0) +#endif + +/* This method is private, but it exists. Its params are are exposed + * as args to the NS* method, but not as CG. + */ +enum PrivateCGCompositeMode { + kPrivateCGCompositeClear = 0, + kPrivateCGCompositeCopy = 1, + kPrivateCGCompositeSourceOver = 2, + kPrivateCGCompositeSourceIn = 3, + kPrivateCGCompositeSourceOut = 4, + kPrivateCGCompositeSourceAtop = 5, + kPrivateCGCompositeDestinationOver = 6, + kPrivateCGCompositeDestinationIn = 7, + kPrivateCGCompositeDestinationOut = 8, + kPrivateCGCompositeDestinationAtop = 9, + kPrivateCGCompositeXOR = 10, + kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s))) + kPrivateCGCompositePlusLighter = 12, // (min (1, s + d)) +}; +typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; +CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); +CG_EXTERN void CGContextResetCTM (CGContextRef); +CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform); +CG_EXTERN void CGContextResetClip (CGContextRef); +CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef); + +/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9 + * has all the stuff we care about, just some of it isn't exported in the SDK. + */ +#ifndef kCGBitmapByteOrder32Host +#define USE_10_3_WORKAROUNDS +#define kCGBitmapAlphaInfoMask 0x1F +#define kCGBitmapByteOrderMask 0x7000 +#define kCGBitmapByteOrder32Host 0 + +typedef uint32_t CGBitmapInfo; + +/* public in 10.4, present in 10.3.9 */ +CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef); +CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef); +#endif + + +/** + ** Utility functions + **/ + +void nquartz_surface_to_png (cairo_nquartz_surface_t *nq, char *dest); +void nquartz_image_to_png (CGImageRef, char *dest); + +/* + * Cairo path -> Quartz path conversion helpers + */ + +/* cairo path -> mutable path */ +static cairo_status_t +_cairo_path_to_quartz_path_move_to (void *closure, cairo_point_t *point) +{ + CGPathMoveToPoint ((CGMutablePathRef) closure, NULL, + _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_quartz_path_line_to (void *closure, cairo_point_t *point) +{ + CGPathAddLineToPoint ((CGMutablePathRef) closure, NULL, + _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_quartz_path_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2) +{ + CGPathAddCurveToPoint ((CGMutablePathRef) closure, NULL, + _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), + _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), + _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_quartz_path_close_path (void *closure) +{ + CGPathCloseSubpath ((CGMutablePathRef) closure); + return CAIRO_STATUS_SUCCESS; +} + +/* cairo path -> execute in context */ +static cairo_status_t +_cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point) +{ + //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); + CGContextMoveToPoint ((CGContextRef) closure, + _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point) +{ + //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); + if (CGContextIsPathEmpty ((CGContextRef) closure)) + CGContextMoveToPoint ((CGContextRef) closure, + _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); + else + CGContextAddLineToPoint ((CGContextRef) closure, + _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_quartz_context_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2) +{ + //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n", + // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), + // _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), + // _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y))); + + CGContextAddCurveToPoint ((CGContextRef) closure, + _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), + _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), + _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_quartz_context_close_path (void *closure) +{ + //ND((stderr, "closepath\n")); + CGContextClosePath ((CGContextRef) closure); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_nquartz_cairo_path_to_quartz_path (cairo_path_fixed_t *path, + CGMutablePathRef cgPath) +{ + return _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_path_to_quartz_path_move_to, + _cairo_path_to_quartz_path_line_to, + _cairo_path_to_quartz_path_curve_to, + _cairo_path_to_quartz_path_close_path, + cgPath); +} + +static cairo_status_t +_cairo_nquartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path, + CGContextRef cgc) +{ + return _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_path_to_quartz_context_move_to, + _cairo_path_to_quartz_context_line_to, + _cairo_path_to_quartz_context_curve_to, + _cairo_path_to_quartz_context_close_path, + cgc); +} + +/* + * Misc helpers/callbacks + */ + +static PrivateCGCompositeMode +_cairo_nquartz_cairo_operator_to_quartz (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return kPrivateCGCompositeClear; + case CAIRO_OPERATOR_SOURCE: + return kPrivateCGCompositeCopy; + case CAIRO_OPERATOR_OVER: + return kPrivateCGCompositeSourceOver; + case CAIRO_OPERATOR_IN: + /* XXX This doesn't match image output */ + return kPrivateCGCompositeSourceIn; + case CAIRO_OPERATOR_OUT: + /* XXX This doesn't match image output */ + return kPrivateCGCompositeSourceOut; + case CAIRO_OPERATOR_ATOP: + return kPrivateCGCompositeSourceAtop; + + case CAIRO_OPERATOR_DEST: + /* XXX this is handled specially (noop)! */ + return kPrivateCGCompositeCopy; + case CAIRO_OPERATOR_DEST_OVER: + return kPrivateCGCompositeDestinationOver; + case CAIRO_OPERATOR_DEST_IN: + /* XXX This doesn't match image output */ + return kPrivateCGCompositeDestinationIn; + case CAIRO_OPERATOR_DEST_OUT: + return kPrivateCGCompositeDestinationOut; + case CAIRO_OPERATOR_DEST_ATOP: + /* XXX This doesn't match image output */ + return kPrivateCGCompositeDestinationAtop; + + case CAIRO_OPERATOR_XOR: + return kPrivateCGCompositeXOR; /* This will generate strange results */ + case CAIRO_OPERATOR_ADD: + return kPrivateCGCompositePlusLighter; + case CAIRO_OPERATOR_SATURATE: + /* XXX This doesn't match image output for SATURATE; there's no equivalent */ + return kPrivateCGCompositePlusDarker; /* ??? */ + } + + + return kPrivateCGCompositeCopy; +} + +static CGLineCap +_cairo_nquartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) +{ + switch (ccap) { + case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break; + case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break; + case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break; + } + + return kCGLineCapButt; +} + +static CGLineJoin +_cairo_nquartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin) +{ + switch (cjoin) { + case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break; + case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break; + case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break; + } + + return kCGLineJoinMiter; +} + +static void +_cairo_nquartz_cairo_matrix_to_quartz (const cairo_matrix_t *src, + CGAffineTransform *dst) +{ + dst->a = src->xx; + dst->b = src->xy; + dst->c = src->yx; + dst->d = src->yy; + dst->tx = src->x0; + dst->ty = src->y0; +} + +/** + ** Source -> Quartz setup and finish functions + **/ + +static void +ComputeGradientValue (void *info, const float *in, float *out) +{ + float fdist = *in; /* 0.0 .. 1.0 */ + cairo_fixed_16_16_t fdist_fix = _cairo_fixed_from_double(*in); + cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info; + unsigned int i; + + for (i = 0; i < grad->n_stops; i++) { + if (grad->stops[i].x > fdist_fix) + break; + } + + if (i == 0 || i == grad->n_stops) { + if (i == grad->n_stops) + --i; + out[0] = grad->stops[i].color.red / 65535.; + out[1] = grad->stops[i].color.green / 65535.; + out[2] = grad->stops[i].color.blue / 65535.; + out[3] = grad->stops[i].color.alpha / 65535.; + } else { + float ax = _cairo_fixed_to_double(grad->stops[i-1].x); + float bx = _cairo_fixed_to_double(grad->stops[i].x) - ax; + float bp = (fdist - ax)/bx; + float ap = 1.0 - bp; + + out[0] = + (grad->stops[i-1].color.red / 65535.) * ap + + (grad->stops[i].color.red / 65535.) * bp; + out[1] = + (grad->stops[i-1].color.green / 65535.) * ap + + (grad->stops[i].color.green / 65535.) * bp; + out[2] = + (grad->stops[i-1].color.blue / 65535.) * ap + + (grad->stops[i].color.blue / 65535.) * bp; + out[3] = + (grad->stops[i-1].color.alpha / 65535.) * ap + + (grad->stops[i].color.alpha / 65535.) * bp; + } +} + +static CGFunctionRef +CreateGradientFunction (cairo_gradient_pattern_t *gpat) +{ + static const float input_value_range[2] = { 0.f, 1.f }; + static const float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f }; + static const CGFunctionCallbacks callbacks = { + 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy + }; + + return CGFunctionCreate (gpat, + 1, + input_value_range, + 4, + output_value_ranges, + &callbacks); +} + +static CGShadingRef +_cairo_nquartz_cairo_gradient_pattern_to_quartz (cairo_pattern_t *abspat) +{ + cairo_matrix_t mat; + double x0, y0; + + if (abspat->type != CAIRO_PATTERN_TYPE_LINEAR && + abspat->type != CAIRO_PATTERN_TYPE_RADIAL) + return NULL; + + /* We can only do this if we have an identity pattern matrix; + * otherwise fall back through to the generic pattern case. + * XXXperf we could optimize this by creating a pattern with the shading; + * but we'd need to know the extents to do that. + * ... but we don't care; we can use the surface extents for it + * XXXtodo - implement gradients with non-identity pattern matrices + */ + cairo_pattern_get_matrix (abspat, &mat); + if (mat.xx != 1.0 || mat.yy != 1.0 || mat.xy != 0.0 || mat.yx != 0.0) + return NULL; + + x0 = mat.x0; + y0 = mat.y0; + + if (abspat->type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t*) abspat; + CGShadingRef shading; + CGPoint start, end; + CGFunctionRef gradFunc; + CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); + + start = CGPointMake (_cairo_fixed_to_double (lpat->gradient.p1.x) - x0, + _cairo_fixed_to_double (lpat->gradient.p1.y) - y0); + end = CGPointMake (_cairo_fixed_to_double (lpat->gradient.p2.x) - x0, + _cairo_fixed_to_double (lpat->gradient.p2.y) - y0); + + cairo_pattern_reference (abspat); + gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat); + shading = CGShadingCreateAxial (rgb, + start, end, + gradFunc, + true, true); + CGColorSpaceRelease(rgb); + CGFunctionRelease(gradFunc); + + return shading; + } + + if (abspat->type == CAIRO_PATTERN_TYPE_RADIAL) { + cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t*) abspat; + CGShadingRef shading; + CGPoint start, end; + CGFunctionRef gradFunc; + CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); + + start = CGPointMake (_cairo_fixed_to_double (rpat->gradient.inner.x) - x0, + _cairo_fixed_to_double (rpat->gradient.inner.y) - y0); + end = CGPointMake (_cairo_fixed_to_double (rpat->gradient.outer.x) - x0, + _cairo_fixed_to_double (rpat->gradient.outer.y) - y0); + + cairo_pattern_reference (abspat); + gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat); + shading = CGShadingCreateRadial (rgb, + start, + _cairo_fixed_to_double (rpat->gradient.inner.radius), + end, + _cairo_fixed_to_double (rpat->gradient.outer.radius), + gradFunc, + true, true); + CGColorSpaceRelease(rgb); + CGFunctionRelease(gradFunc); + + return shading; + } + + /* Shouldn't be reached */ + ASSERT_NOT_REACHED; + return NULL; +} + + +/* Generic cairo_pattern -> 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_nquartz_surface_t *quartz_surf = NULL; + + cairo_bool_t flip = FALSE; + + CGImageRef img; + + if (cairo_surface_get_type(pat_surf) != CAIRO_SURFACE_TYPE_QUARTZ) { + /* This sucks; we should really store a dummy nquartz surface + * for passing in here + * XXXtodo store a dummy nquartz surface somewhere for handing off to clone_similar + * XXXtodo/perf don't use clone if the source surface is an image surface! Instead, + * just create the CGImage directly! + */ + + cairo_surface_t *dummy = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + cairo_surface_t *new_surf = NULL; + cairo_rectangle_int16_t rect; + + _cairo_surface_get_extents (pat_surf, &rect); + + _cairo_surface_clone_similar (dummy, pat_surf, rect.x, rect.y, + rect.width, rect.height, &new_surf); + + cairo_surface_destroy(dummy); + + quartz_surf = (cairo_nquartz_surface_t *) new_surf; + } else { + /* If it's a nquartz 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_nquartz_surface_t*) pat_surf; + + /* 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? + */ + flip = TRUE; + } + + /* this is a 10.4 API, present in 10.3.9 */ + CGContextFlush (quartz_surf->cgContext); + img = CGBitmapContextCreateImage (quartz_surf->cgContext); + + if (!img) { + // ... give up. + ND((stderr, "CGBitmapContextCreateImage failed\n")); + cairo_surface_destroy ((cairo_surface_t*)quartz_surf); + return; + } + + if (flip) { + CGContextTranslateCTM (context, 0, CGImageGetHeight(img)); + CGContextScaleCTM (context, 1, -1); + } + + CGRect imageBounds; + imageBounds.size = CGSizeMake (CGImageGetWidth(img), CGImageGetHeight(img)); + imageBounds.origin.x = 0; + imageBounds.origin.y = 0; + + CGContextDrawImage (context, imageBounds, img); + + CGImageRelease (img); + + cairo_surface_destroy ((cairo_surface_t*) quartz_surf); +} + +/* Borrowed from cairo-meta-surface */ +static cairo_status_t +_init_pattern_with_snapshot (cairo_pattern_t *pattern, + const cairo_pattern_t *other) +{ + _cairo_pattern_init_copy (pattern, other); + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = + (cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + + surface_pattern->surface = _cairo_surface_snapshot (surface); + + cairo_surface_destroy (surface); + + if (surface_pattern->surface->status) + return surface_pattern->surface->status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static CGPatternRef +_cairo_nquartz_cairo_repeating_surface_pattern_to_quartz (cairo_nquartz_surface_t *dest, + cairo_pattern_t *abspat) +{ + cairo_surface_pattern_t *spat; + cairo_surface_t *pat_surf; + cairo_rectangle_int16_t extents; + + CGRect pbounds; + CGAffineTransform ptransform, stransform; + CGPatternCallbacks cb = { 0, + SurfacePatternDrawFunc, + (CGFunctionReleaseInfoCallback) cairo_pattern_destroy }; + CGPatternRef cgpat; + float rw, rh; + + 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) + return NULL; + + spat = (cairo_surface_pattern_t *) abspat; + pat_surf = spat->surface; + + _cairo_surface_get_extents (pat_surf, &extents); + pbounds.origin.x = 0; + pbounds.origin.y = 0; + pbounds.size.width = extents.width; + pbounds.size.height = extents.height; + + m = spat->base.matrix; + cairo_matrix_invert(&m); + _cairo_nquartz_cairo_matrix_to_quartz (&m, &stransform); + + /* The pattern matrix is relative to the bottom left, again; the + * incoming cairo pattern matrix is relative to the upper left. + * So we take the pattern matrix and the original context matrix, + * which gives us the correct base translation/y flip. + */ + ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM); + +#ifdef NQUARTZ_DEBUG + ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height)); + ND((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d)); + CGAffineTransform xform = CGContextGetCTM(dest->cgContext); + 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 + + // 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 0 + if (spat->base.extend == CAIRO_EXTEND_NONE) { + /* XXX wasteful; this will keep drawing the pattern in the + * original location. We need to set up the clip region + * instead to do this right. + */ + rw = 0; + rh = 0; + } else if (spat->base.extend == CAIRO_EXTEND_REPEAT) { + rw = extents.width; + rh = extents.height; + } else if (spat->base.extend == CAIRO_EXTEND_REFLECT) { + /* XXX broken; need to emulate by reflecting the image into 4 quadrants + * and then tiling that + */ + rw = extents.width; + rh = extents.height; + } else { + /* CAIRO_EXTEND_PAD */ + /* XXX broken. */ + rw = 0; + rh = 0; + } +#else + rw = extents.width; + rh = extents.height; +#endif + + /* 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 cgpat; +} + +typedef enum { + DO_SOLID, + DO_SHADING, + DO_PATTERN, + DO_UNSUPPORTED +} cairo_nquartz_action_t; + +static cairo_nquartz_action_t +_cairo_nquartz_setup_source (cairo_nquartz_surface_t *surface, + cairo_pattern_t *source) +{ + assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern)); + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + + CGContextSetRGBStrokeColor (surface->cgContext, + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + CGContextSetRGBFillColor (surface->cgContext, + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + + return DO_SOLID; + } else if (source->type == CAIRO_PATTERN_TYPE_LINEAR || + source->type == CAIRO_PATTERN_TYPE_RADIAL) + { + CGShadingRef shading = _cairo_nquartz_cairo_gradient_pattern_to_quartz (source); + if (!shading) + return DO_UNSUPPORTED; + + surface->sourceShading = shading; + + return DO_SHADING; + } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + CGPatternRef pattern = _cairo_nquartz_cairo_repeating_surface_pattern_to_quartz (surface, source); + if (!pattern) + return DO_UNSUPPORTED; + + float patternAlpha = 1.0f; + + // Save before we change the pattern, colorspace, etc. so that + // we can restore and make sure that quartz releases our + // pattern (which may be stack allocated) + CGContextSaveGState(surface->cgContext); + + CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL); + CGContextSetFillColorSpace (surface->cgContext, patternSpace); + CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha); + CGContextSetStrokeColorSpace (surface->cgContext, patternSpace); + CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha); + CGColorSpaceRelease (patternSpace); + + /* Quartz likes to munge the pattern phase (as yet unexplained + * why); force it to 0,0 as we've already baked in the correct + * pattern translation into the pattern matrix + */ + CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0)); + + surface->sourcePattern = pattern; + + return DO_PATTERN; + } else { + return DO_UNSUPPORTED; + } + + ASSERT_NOT_REACHED; +} + +static void +_cairo_nquartz_teardown_source (cairo_nquartz_surface_t *surface, + cairo_pattern_t *source) +{ + if (surface->sourceImage) { + // nothing to do; we don't use sourceImage yet + } + + if (surface->sourceShading) { + CGShadingRelease(surface->sourceShading); + surface->sourceShading = NULL; + } + + if (surface->sourcePattern) { + CGPatternRelease(surface->sourcePattern); + // To tear down the pattern and colorspace + CGContextRestoreGState(surface->cgContext); + + surface->sourcePattern = NULL; + } +} + +/** + ** get source/dest image implementation + **/ + +static void +ImageDataReleaseFunc(void *info, const void *data, size_t size) +{ + if (data != NULL) { + free((void *) data); + } +} + +/* Read the image from the surface's front buffer */ +static cairo_int_status_t +_cairo_nquartz_get_image (cairo_nquartz_surface_t *surface, + cairo_image_surface_t **image_out, + unsigned char **data_out) +{ + unsigned char *imageData; + cairo_image_surface_t *isurf; + + /* If we weren't constructed with an AGL Context + * or a CCGBitmapContext, then we have no way + * of doing this + */ +#ifdef CAIRO_NQUARTZ_SUPPORT_AGL + if (surface->aglContext) { + AGLContext oldContext; + cairo_format_masks_t masks = { 32, 0xff << 24, 0xff << 16, 0xff << 8, 0xff << 0 }; + unsigned int i; + + oldContext = aglGetCurrentContext(); + if (oldContext != surface->aglContext) + aglSetCurrentContext(surface->aglContext); + + imageData = (unsigned char *) malloc (surface->extents.width * surface->extents.height * 4); + if (!imageData) + return CAIRO_STATUS_NO_MEMORY; + + glReadBuffer (GL_FRONT); + glReadPixels (0, 0, surface->extents.width, surface->extents.height, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, + imageData); + + /* swap lines */ + /* XXX find some fast code to do this */ + unsigned int lineSize = surface->extents.width * 4; + unsigned char *tmpLine = malloc(lineSize); + for (i = 0; i < surface->extents.height / 2; i++) { + unsigned char *l0 = imageData + lineSize * i; + unsigned char *l1 = imageData + (lineSize * (surface->extents.height - i - 1)); + memcpy (tmpLine, l0, lineSize); + memcpy (l0, l1, lineSize); + memcpy (l1, tmpLine, lineSize); + } + free (tmpLine); + + if (oldContext && oldContext != surface->aglContext) + aglSetCurrentContext(oldContext); + + isurf = (cairo_image_surface_t *)_cairo_image_surface_create_with_masks + (imageData, + &masks, + surface->extents.width, + surface->extents.height, + surface->extents.width * 4); + + if (data_out) + *data_out = imageData; + else + _cairo_image_surface_assume_ownership_of_data (isurf); +#else + /* no AGL */ + if (0) { +#endif + } else if (CGBitmapContextGetBitsPerPixel(surface->cgContext) != 0) { + unsigned int stride; + unsigned int bitinfo; + unsigned int bpc, bpp; + CGColorSpaceRef colorspace; + unsigned int color_comps; + + imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext); +#ifdef USE_10_3_WORKAROUNDS + bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext); +#else + bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext); +#endif + stride = CGBitmapContextGetBytesPerRow (surface->cgContext); + bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext); + bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext); + + // let's hope they don't add YUV under us + colorspace = CGBitmapContextGetColorSpace (surface->cgContext); + color_comps = CGColorSpaceGetNumberOfComponents(colorspace); + + // XXX TODO: We can handle all of these by converting to + // pixman masks, including non-native-endian masks + if (bpc != 8) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (bpp != 32 && bpp != 8) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (color_comps != 3 && color_comps != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (bpp == 32 && color_comps == 3 && + (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst && + (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) + { + isurf = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (imageData, + CAIRO_FORMAT_ARGB32, + surface->extents.width, + surface->extents.height, + stride); + } else if (bpp == 32 && color_comps == 3 && + (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst && + (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) + { + isurf = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (imageData, + CAIRO_FORMAT_RGB24, + surface->extents.width, + surface->extents.height, + stride); + } else if (bpp == 8 && color_comps == 1) + { + isurf = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (imageData, + CAIRO_FORMAT_A8, + surface->extents.width, + surface->extents.height, + stride); + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *image_out = isurf; + return CAIRO_STATUS_SUCCESS; +} + +/** + ** Cairo surface backend implementations + **/ + +static cairo_status_t +_cairo_nquartz_surface_finish (void *abstract_surface) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + + ND((stderr, "_cairo_nquartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext)); + +#ifdef CAIRO_NQUARTZ_SUPPORT_AGL + if (surface->aglContext) + aglSetCurrentContext(surface->aglContext); +#endif + + CGContextFlush (surface->cgContext); + + /* Restore our saved gstate that we use to reset clipping */ + CGContextRestoreGState (surface->cgContext); + + CGContextRelease (surface->cgContext); + + surface->cgContext = NULL; + +#ifdef CAIRO_NQUARTZ_SUPPORT_AGL + if (surface->aglContext) + glFlush(); + + surface->aglContext = NULL; +#endif + + if (surface->imageData) { + free (surface->imageData); + surface->imageData = NULL; + } return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_quartz_surface_acquire_source_image(void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) +_cairo_nquartz_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { - cairo_quartz_surface_t *surface = abstract_surface; + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - if (CGBitmapContextGetBitmapInfo (surface->context) != 0) { - /* XXX: We can create an image out of the bitmap here */ - } + //ND((stderr, "%p _cairo_nquartz_surface_acquire_source_image\n", surface)); - return CAIRO_INT_STATUS_UNSUPPORTED; + *image_extra = NULL; + + return _cairo_nquartz_get_image (surface, image_out, NULL); } static cairo_status_t -_cairo_quartz_surface_acquire_dest_image(void *abstract_surface, - cairo_rectangle_int16_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int16_t *image_rect, - void **image_extra) +_cairo_nquartz_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int16_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int16_t *image_rect, + void **image_extra) { - cairo_quartz_surface_t *surface = abstract_surface; - cairo_surface_t *image_surface; + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + cairo_int_status_t status; unsigned char *data; - int x1, y1, x2, y2; - x1 = surface->extents.x; - x2 = surface->extents.x + surface->extents.width; - y1 = surface->extents.y; - y2 = surface->extents.y + surface->extents.height; + ND((stderr, "%p _cairo_nquartz_surface_acquire_dest_image\n", surface)); - if (interest_rect->x > x1) - x1 = interest_rect->x; - if (interest_rect->y > y1) - y1 = interest_rect->y; - if (interest_rect->x + interest_rect->width < x2) - x2 = interest_rect->x + interest_rect->width; - if (interest_rect->y + interest_rect->height < y2) - y2 = interest_rect->y + interest_rect->height; + *image_rect = surface->extents; - if (x1 >= x2 || y1 >= y2) { - *image_out = NULL; - *image_extra = NULL; + status = _cairo_nquartz_get_image (surface, image_out, &data); + if (status) + return status; + + *image_extra = data; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_nquartz_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int16_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int16_t *image_rect, + void *image_extra) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + unsigned char *imageData = (unsigned char *) image_extra; + + //ND((stderr, "%p _cairo_nquartz_surface_release_dest_image\n", surface)); + + if (!CGBitmapContextGetData (surface->cgContext)) { + CGDataProviderRef dataProvider; + CGImageRef img; + + dataProvider = CGDataProviderCreateWithData (NULL, imageData, + surface->extents.width * surface->extents.height * 4, + ImageDataReleaseFunc); + + img = CGImageCreate (surface->extents.width, surface->extents.height, + 8, 32, + surface->extents.width * 4, + CGColorSpaceCreateDeviceRGB(), + kCGImageAlphaPremultipliedFirst, + dataProvider, + NULL, + false, + kCGRenderingIntentDefault); + + CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy); + + CGContextDrawImage (surface->cgContext, + CGRectMake (0, 0, surface->extents.width, surface->extents.height), + img); + + CGImageRelease (img); + CGDataProviderRelease (dataProvider); + + ND((stderr, "Image for surface %p was recovered from a bitmap\n", surface)); + } + + cairo_surface_destroy ((cairo_surface_t *) image); +} + +static cairo_surface_t * +_cairo_nquartz_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + /*cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface;*/ + + cairo_format_t format; + + if (content == CAIRO_CONTENT_COLOR_ALPHA) + format = CAIRO_FORMAT_ARGB32; + else if (content == CAIRO_CONTENT_COLOR) + format = CAIRO_FORMAT_RGB24; + else if (content == CAIRO_CONTENT_ALPHA) + format = CAIRO_FORMAT_A8; + else + return NULL; + + return cairo_quartz_surface_create (format, width, height); +} + +static cairo_status_t +_cairo_nquartz_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_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + cairo_nquartz_surface_t *new_surface = NULL; + cairo_format_t new_format; + + CGImageRef quartz_image = NULL; + + if (cairo_surface_get_type(src) == CAIRO_SURFACE_TYPE_QUARTZ) { + cairo_nquartz_surface_t *qsurf = (cairo_nquartz_surface_t *) src; + 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->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; + } + + if (!quartz_image) + return CAIRO_INT_STATUS_UNSUPPORTED; + + new_surface = (cairo_nquartz_surface_t *) + cairo_quartz_surface_create (new_format, + CGImageGetWidth (quartz_image), + CGImageGetHeight (quartz_image)); + if (!new_surface || new_surface->base.status) + return CAIRO_INT_STATUS_UNSUPPORTED; + + CGContextSetCompositeOperation (new_surface->cgContext, + kPrivateCGCompositeCopy); + + nquartz_image_to_png (quartz_image, NULL); + + CGContextDrawImage (new_surface->cgContext, + CGRectMake (src_x, src_y, width, height), + quartz_image); + CGImageRelease (quartz_image); + + *clone_out = (cairo_surface_t*) new_surface; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_nquartz_surface_get_extents (void *abstract_surface, + cairo_rectangle_int16_t *extents) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + + *extents = surface->extents; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_nquartz_surface_paint (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + cairo_nquartz_action_t action; + + ND((stderr, "%p _cairo_nquartz_surface_paint op %d source->type %d\n", surface, op, source->type)); + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); + + action = _cairo_nquartz_setup_source (surface, source); + + if (action == DO_SOLID || action == DO_PATTERN) { + CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x, + surface->extents.y, + surface->extents.width, + surface->extents.height)); + } else if (action == DO_SHADING) { + CGContextDrawShading (surface->cgContext, surface->sourceShading); + } else { + rv = CAIRO_INT_STATUS_UNSUPPORTED; + } + + _cairo_nquartz_teardown_source (surface, source); + + ND((stderr, "-- paint\n")); + return rv; +} + +static cairo_int_status_t +_cairo_nquartz_surface_fill (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + cairo_nquartz_action_t action; + + ND((stderr, "%p _cairo_nquartz_surface_fill op %d source->type %d\n", surface, op, source->type)); + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + CGContextSaveGState (surface->cgContext); + + CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); + CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); + + action = _cairo_nquartz_setup_source (surface, source); + if (action == DO_UNSUPPORTED) { + CGContextRestoreGState (surface->cgContext); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + CGContextBeginPath (surface->cgContext); + _cairo_nquartz_cairo_path_to_quartz_context (path, surface->cgContext); + + if (action == DO_SOLID || action == DO_PATTERN) { + if (fill_rule == CAIRO_FILL_RULE_WINDING) + CGContextFillPath (surface->cgContext); + else + CGContextEOFillPath (surface->cgContext); + } else if (action == DO_SHADING) { + + // we have to clip and then paint the shading; we can't fill + // with the shading + if (fill_rule == CAIRO_FILL_RULE_WINDING) + CGContextClip (surface->cgContext); + else + CGContextEOClip (surface->cgContext); + + CGContextDrawShading (surface->cgContext, surface->sourceShading); + } else { + rv = CAIRO_INT_STATUS_UNSUPPORTED; + } + + _cairo_nquartz_teardown_source (surface, source); + + CGContextRestoreGState (surface->cgContext); + + ND((stderr, "-- fill\n")); + return rv; +} + +static cairo_int_status_t +_cairo_nquartz_surface_stroke (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + cairo_nquartz_action_t action; + + ND((stderr, "%p _cairo_nquartz_surface_stroke op %d source->type %d\n", surface, op, source->type)); + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + CGContextSaveGState (surface->cgContext); + + // Turning antialiasing off causes misrendering with + // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels) + //CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); + CGContextSetLineWidth (surface->cgContext, style->line_width); + CGContextSetLineCap (surface->cgContext, _cairo_nquartz_cairo_line_cap_to_quartz (style->line_cap)); + CGContextSetLineJoin (surface->cgContext, _cairo_nquartz_cairo_line_join_to_quartz (style->line_join)); + CGContextSetMiterLimit (surface->cgContext, style->miter_limit); + + if (style->dash && style->num_dashes) { +#define STATIC_DASH 32 + float sdash[STATIC_DASH]; + float *fdash = sdash; + unsigned int k; + if (style->num_dashes > STATIC_DASH) + fdash = malloc (sizeof(float)*style->num_dashes); + + for (k = 0; k < style->num_dashes; k++) + fdash[k] = (float) style->dash[k]; + + CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, style->num_dashes); + + if (fdash != sdash) + free (fdash); + } + + CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); + + action = _cairo_nquartz_setup_source (surface, source); + if (action == DO_UNSUPPORTED) { + CGContextRestoreGState (surface->cgContext); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + CGContextBeginPath (surface->cgContext); + _cairo_nquartz_cairo_path_to_quartz_context (path, surface->cgContext); + + if (action == DO_SOLID || action == DO_PATTERN) { + CGContextStrokePath (surface->cgContext); + } else if (action == DO_SHADING) { + // we have to clip and then paint the shading; first we have to convert + // the stroke to a path that we can fill + CGContextReplacePathWithStrokedPath (surface->cgContext); + CGContextClip (surface->cgContext); + + CGContextDrawShading (surface->cgContext, surface->sourceShading); + } else { + rv = CAIRO_INT_STATUS_UNSUPPORTED; + } + + _cairo_nquartz_teardown_source (surface, source); + + CGContextRestoreGState (surface->cgContext); + + ND((stderr, "-- stroke\n")); + return rv; +} + +static cairo_int_status_t +_cairo_nquartz_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + cairo_nquartz_action_t action; + int i; + + if (num_glyphs <= 0) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (!_cairo_scaled_font_is_atsui (scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + CGContextSaveGState (surface->cgContext); + + action = _cairo_nquartz_setup_source (surface, source); + if (action == DO_SOLID || action == DO_PATTERN) { + CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill); + } else if (action == DO_SHADING) { + CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip); + } else { + /* Unsupported */ + CGContextRestoreGState (surface->cgContext); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + CGContextSetCompositeOperation (surface->cgContext, _cairo_nquartz_cairo_operator_to_quartz (op)); + + ATSUFontID fid = _cairo_atsui_scaled_font_get_atsu_font_id (scaled_font); + ATSFontRef atsfref = FMGetATSFontRefFromFont (fid); + CGFontRef cgfref = CGFontCreateWithPlatformFont (&atsfref); + + CGContextSetFont (surface->cgContext, cgfref); + CGFontRelease (cgfref); + + /* So this should include the size; I don't know if I need to extract the + * size from this and call CGContextSetFontSize.. will I get crappy hinting + * with this 1.0 size business? Or will CG just multiply that size into the + * text matrix? + */ + //ND((stderr, "show_glyphs: glyph 0 at: %f, %f\n", glyphs[0].x, glyphs[0].y)); + CGAffineTransform cairoTextTransform, textTransform; + _cairo_nquartz_cairo_matrix_to_quartz (&scaled_font->font_matrix, &cairoTextTransform); + + textTransform = CGAffineTransformMakeTranslation (glyphs[0].x, glyphs[0].y); + textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0); + textTransform = CGAffineTransformConcat (cairoTextTransform, textTransform); + + CGContextSetTextMatrix (surface->cgContext, textTransform); + CGContextSetFontSize (surface->cgContext, 1.0); + + // XXXtodo/perf: stack storage for glyphs/sizes +#define STATIC_BUF_SIZE 64 + CGGlyph glyphs_static[STATIC_BUF_SIZE]; + CGSize cg_advances_static[STATIC_BUF_SIZE]; + CGGlyph *cg_glyphs = &glyphs_static[0]; + CGSize *cg_advances = &cg_advances_static[0]; + + if (num_glyphs > STATIC_BUF_SIZE) { + cg_glyphs = (CGGlyph*) malloc(sizeof(CGGlyph) * num_glyphs); + cg_advances = (CGSize*) malloc(sizeof(CGSize) * num_glyphs); + } + + double xprev = glyphs[0].x; + double yprev = glyphs[0].y; + + cg_glyphs[0] = glyphs[0].index; + cg_advances[0].width = 0; + cg_advances[0].height = 0; + + for (i = 1; i < num_glyphs; i++) { + cg_glyphs[i] = glyphs[i].index; + cg_advances[i-1].width = glyphs[i].x - xprev; + cg_advances[i-1].height = glyphs[i].y - yprev; + xprev = glyphs[i].x; + yprev = glyphs[i].y; + } + +#if 0 + for (i = 0; i < num_glyphs; i++) { + ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height)); + } +#endif + + CGContextShowGlyphsWithAdvances (surface->cgContext, + cg_glyphs, + cg_advances, + num_glyphs); + + if (cg_glyphs != &glyphs_static[0]) { + free (cg_glyphs); + free (cg_advances); + } + + if (action == DO_SHADING) + CGContextDrawShading (surface->cgContext, surface->sourceShading); + + _cairo_nquartz_teardown_source (surface, source); + + CGContextRestoreGState (surface->cgContext); + + return rv; +} + +static cairo_int_status_t +_cairo_nquartz_surface_mask (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_pattern_t *mask) +{ + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; + cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + + ND((stderr, "%p _cairo_nquartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type)); + + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + /* This is easy; we just need to paint with the alpha. */ + cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; + + CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); + } else { + /* So, CGContextClipToMask is not present in 10.3.9, so we're + * doomed; if we have imageData, we can do fallback, otherwise + * just pretend success. + */ + if (surface->imageData) + return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } - image_rect->x = x1; - image_rect->y = y1; - image_rect->width = x2 - x1; - image_rect->height = y2 - y1; + rv = _cairo_nquartz_surface_paint (surface, op, source); - data = calloc (image_rect->width * image_rect->height * 4, 1); - image_surface = cairo_image_surface_create_for_data (data, - CAIRO_FORMAT_ARGB32, - image_rect->width, - image_rect->height, - image_rect->width * 4); - - *image_out = (cairo_image_surface_t *)image_surface; - *image_extra = data; - - return CAIRO_STATUS_SUCCESS; - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static CGImageRef -create_image_from_surface (cairo_image_surface_t *image_surface, void *data) -{ - CGImageRef image; - CGColorSpaceRef color_space; - CGDataProviderRef data_provider; - int width, height; - - width = cairo_image_surface_get_width ((cairo_surface_t *)image_surface); - height = cairo_image_surface_get_height ((cairo_surface_t *)image_surface); - - color_space = CGColorSpaceCreateDeviceRGB(); - data_provider = CGDataProviderCreateWithData (NULL, data, - width * height * 4, NULL); - image = CGImageCreate (width, height, - 8, 32, - width * 4, - color_space, - kCGImageAlphaPremultipliedFirst, - data_provider, - NULL, - FALSE, kCGRenderingIntentDefault); - - CGColorSpaceRelease (color_space); - CGDataProviderRelease (data_provider); - - return image; -} - -static void -_cairo_quartz_surface_release_dest_image(void *abstract_surface, - cairo_rectangle_int16_t *intersect_rect, - cairo_image_surface_t *image, - cairo_rectangle_int16_t *image_rect, - void *image_extra) -{ - cairo_quartz_surface_t *surface = abstract_surface; - CGImageRef image_ref; - CGRect rect; - - image_ref = create_image_from_surface (image, image_extra); - - rect = CGRectMake (image_rect->x, image_rect->y, image_rect->width, image_rect->height); - - if (surface->y_grows_down) { - CGContextSaveGState (surface->context); - CGContextTranslateCTM (surface->context, 0, image_rect->height + 2 * image_rect->y); - CGContextScaleCTM (surface->context, 1, -1); + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + CGContextSetAlpha (surface->cgContext, 1.0); } - CGContextDrawImage(surface->context, rect, image_ref); - CFRelease (image_ref); + ND((stderr, "-- mask\n")); - if (surface->y_grows_down) { - CGContextRestoreGState (surface->context); - } - - cairo_surface_destroy ((cairo_surface_t *)image); - free (image_extra); + return rv; } static cairo_int_status_t -_cairo_quartz_surface_set_clip_region(void *abstract_surface, - pixman_region16_t * region) +_cairo_nquartz_surface_intersect_clip_path (void *abstract_surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) { - cairo_quartz_surface_t *surface = abstract_surface; + cairo_nquartz_surface_t *surface = (cairo_nquartz_surface_t *) abstract_surface; - if (surface->clip_region) - pixman_region_destroy (surface->clip_region); + ND((stderr, "%p _cairo_nquartz_surface_intersect_clip_path path: %p\n", surface, path)); - if (region) { - surface->clip_region = pixman_region_create (); - pixman_region_copy (surface->clip_region, region); - } else - surface->clip_region = NULL; + if (path == NULL) { + /* If we're being asked to reset the clip, we can only do it + * by restoring the gstate to our previous saved one, and + * saving it again. + * + * Note that this assumes that ALL nquartz surface creation + * functions will do a SaveGState first; we do this in create_internal. + */ + CGContextRestoreGState (surface->cgContext); + CGContextSaveGState (surface->cgContext); + } else { + CGContextBeginPath (surface->cgContext); + _cairo_nquartz_cairo_path_to_quartz_context (path, surface->cgContext); + if (fill_rule == CAIRO_FILL_RULE_WINDING) + CGContextClip (surface->cgContext); + else + CGContextEOClip (surface->cgContext); + } + + ND((stderr, "-- intersect_clip_path\n")); return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_quartz_surface_get_extents (void *abstract_surface, - cairo_rectangle_int16_t *rectangle) -{ - cairo_quartz_surface_t *surface = abstract_surface; +// XXXtodo implement show_page; need to figure out how to handle begin/end - *rectangle = surface->extents; - - return CAIRO_STATUS_SUCCESS; -} - -static const struct _cairo_surface_backend cairo_quartz_surface_backend = { +static const struct _cairo_surface_backend cairo_nquartz_surface_backend = { CAIRO_SURFACE_TYPE_QUARTZ, - NULL, /* create_similar */ - _cairo_quartz_surface_finish, - _cairo_quartz_surface_acquire_source_image, + _cairo_nquartz_surface_create_similar, + _cairo_nquartz_surface_finish, + _cairo_nquartz_surface_acquire_source_image, NULL, /* release_source_image */ - _cairo_quartz_surface_acquire_dest_image, - _cairo_quartz_surface_release_dest_image, - NULL, /* clone_similar */ + _cairo_nquartz_surface_acquire_dest_image, + _cairo_nquartz_surface_release_dest_image, + _cairo_nquartz_surface_clone_similar, NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ NULL, /* copy_page */ NULL, /* show_page */ - _cairo_quartz_surface_set_clip_region, - NULL, /* intersect_clip_path */ - _cairo_quartz_surface_get_extents, - NULL /* old_show_glyphs */ + NULL, /* set_clip_region */ + _cairo_nquartz_surface_intersect_clip_path, + _cairo_nquartz_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + _cairo_nquartz_surface_paint, + _cairo_nquartz_surface_mask, + _cairo_nquartz_surface_stroke, + _cairo_nquartz_surface_fill, + _cairo_nquartz_surface_show_glyphs, + + NULL, /* snapshot */ }; -cairo_surface_t *cairo_quartz_surface_create(CGContextRef context, - int width, - int height, - cairo_bool_t y_grows_down) +static cairo_nquartz_surface_t * +_cairo_nquartz_surface_create_internal (CGContextRef cgContext, + nquartz_agl_context_type aglContext, + cairo_content_t content, + unsigned int width, + unsigned int height) { - cairo_quartz_surface_t *surface; - CGRect clip_box; + cairo_nquartz_surface_t *surface; - surface = malloc(sizeof(cairo_quartz_surface_t)); + /* Init the base surface */ + surface = malloc(sizeof(cairo_nquartz_surface_t)); if (surface == NULL) { _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; + return NULL; } - /* XXX: The content value here might be totally wrong. */ - _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend, - CAIRO_CONTENT_COLOR_ALPHA); + memset(surface, 0, sizeof(cairo_nquartz_surface_t)); - surface->context = context; - surface->clip_region = NULL; - surface->y_grows_down = y_grows_down; + _cairo_surface_init(&surface->base, &cairo_nquartz_surface_backend, + content); - clip_box = CGContextGetClipBoundingBox (context); - surface->extents.x = clip_box.origin.x; - surface->extents.y = clip_box.origin.y; - surface->extents.width = clip_box.size.width; - surface->extents.height = clip_box.size.height; + /* Save our extents */ + surface->extents.x = surface->extents.y = 0; + surface->extents.width = width; + surface->extents.height = height; - return (cairo_surface_t *) surface; + /* Save so we can always get back to a known-good CGContext -- this is + * required for proper behaviour of intersect_clip_path(NULL) + */ + CGContextSaveGState (cgContext); + + surface->aglContext = aglContext; + surface->cgContext = cgContext; + surface->cgContextBaseCTM = CGContextGetCTM (cgContext); + + surface->imageData = NULL; + + return surface; } - -int -_cairo_surface_is_quartz (cairo_surface_t *surface) + +#ifdef CAIRO_NQUARTZ_SUPPORT_AGL +cairo_surface_t * +cairo_quartz_surface_create_for_agl_context (AGLContext aglContext, + unsigned int width, + unsigned int height) { - return surface->backend == &cairo_quartz_surface_backend; + cairo_nquartz_surface_t *surf; + CGSize sz; + + /* Make our CGContext from the AGL context */ + sz.width = width; + sz.height = height; + + CGContextRef cgc = CGGLContextCreate (aglContext, sz, NULL /* device RGB colorspace */); + + surf = _cairo_nquartz_surface_create_internal (cgc, aglContext, CAIRO_CONTENT_COLOR_ALPHA, + width, height); + if (!surf) { + CGContextRelease (cgc); + // create_internal will have set an error + return (cairo_surface_t*) &_cairo_surface_nil; + } + + return (cairo_surface_t *) surf; } +#endif + +/** + * cairo_quartz_surface_create_for_cg_context + * @cgContext: the existing CGContext for which to create the surface + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a Quartz surface that wraps the given CGContext. The + * CGContext is assumed to be in the QuickDraw coordinate space (that + * is, with the origin at the upper left and the Y axis increasing + * downward.) If the CGContext is in the Quartz coordinate space (with + * the origin at the bottom left), then it should be flipped before + * this function is called: + * + * + * GContextTranslateCTM (cgContext, 0.0, height); + * CGContextScaleCTM (cgContext, 1.0, -1.0); + * + * + * A very small number of Cairo operations cannot be translated to + * Quartz operations; those operations will fail on this surface. + * If all Cairo operations are required to succeed, consider rendering + * to a surface created by cairo_quartz_surface_create() and then copying + * the result to the CGContext. + * + * Return value: the newly created Cairo surface. + * + * Since: 1.4 + **/ + +cairo_surface_t * +cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, + unsigned int width, + unsigned int height) +{ + cairo_nquartz_surface_t *surf; + + CGContextRetain (cgContext); + + surf = _cairo_nquartz_surface_create_internal (cgContext, NULL, CAIRO_CONTENT_COLOR_ALPHA, + width, height); + if (!surf) { + CGContextRelease (cgContext); + // create_internal will have set an error + return (cairo_surface_t*) &_cairo_surface_nil; + } + + 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 + * @height: height of the surface, in pixels + * + * Creates a Quartz surface backed by a CGBitmap. The surface is + * created using the Device RGB (or Device Gray, for A8) color space. + * All Cairo operations, including those that require software + * rendering, will succeed on this surface. + * + * Return value: the newly created surface. + * + * Since: 1.4 + **/ +cairo_surface_t * +cairo_quartz_surface_create (cairo_format_t format, + unsigned int width, + unsigned int height) +{ + cairo_nquartz_surface_t *surf; + CGContextRef cgc; + CGColorSpaceRef cgColorspace; + CGBitmapInfo bitinfo; + void *imageData; + int stride; + int bitsPerComponent; + + if (format == CAIRO_FORMAT_ARGB32) { + cgColorspace = CGColorSpaceCreateDeviceRGB(); + stride = width * 4; + bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; + bitsPerComponent = 8; + } else if (format == CAIRO_FORMAT_RGB24) { + cgColorspace = CGColorSpaceCreateDeviceRGB(); + stride = width * 4; + bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; + bitsPerComponent = 8; + } else if (format == CAIRO_FORMAT_A8) { + cgColorspace = CGColorSpaceCreateDeviceGray(); + if (width % 4 == 0) + stride = width; + else + stride = (width & ~3) + 4; + bitinfo = kCGImageAlphaNone; + bitsPerComponent = 8; + } else if (format == CAIRO_FORMAT_A1) { + /* I don't think we can usefully support this, as defined by + * cairo_format_t -- these are 1-bit pixels stored in 32-bit + * quantities. + */ + _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + return (cairo_surface_t*) &_cairo_surface_nil; + } else { + _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + imageData = malloc (height * stride); + if (!imageData) { + CGColorSpaceRelease (cgColorspace); + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + cgc = CGBitmapContextCreate (imageData, + width, + height, + bitsPerComponent, + stride, + cgColorspace, + bitinfo); + CGColorSpaceRelease (cgColorspace); + + if (!cgc) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + /* flip the Y axis */ + CGContextTranslateCTM (cgc, 0.0, height); + CGContextScaleCTM (cgc, 1.0, -1.0); + + surf = _cairo_nquartz_surface_create_internal (cgc, NULL, _cairo_content_from_format (format), + width, height); + if (!surf) { + CGContextRelease (cgc); + // create_internal will have set an error + return (cairo_surface_t*) &_cairo_surface_nil; + } + + surf->imageData = imageData; + + return (cairo_surface_t *) surf; +} + +/** + * cairo_quartz_surface_get_cg_context + * @surf: the Cairo Quartz surface + * + * Returns the CGContextRef that the given Quartz surface is backed + * by. + * + * Return value: the CGContextRef for the given surface. + * + * Since: 1.4 + **/ +CGContextRef +cairo_quartz_surface_get_cg_context (cairo_surface_t *surf) +{ + cairo_nquartz_surface_t *nquartz = (cairo_nquartz_surface_t*)surf; + + if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_QUARTZ) + return NULL; + + return nquartz->cgContext; +} + + +/* Debug stuff */ + +#ifdef NQUARTZ_DEBUG + +#include + +void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest) +{ + Handle dataRef = NULL; + OSType dataRefType; + CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII); + + GraphicsExportComponent grex = 0; + unsigned long sizeWritten; + + ComponentResult result; + + // create the data reference + result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle, + 0, &dataRef, &dataRefType); + + if (NULL != dataRef && noErr == result) { + // get the PNG exporter + result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG, + &grex); + + if (grex) { + // tell the exporter where to find its source image + result = GraphicsExportSetInputCGImage(grex, inImageRef); + + if (noErr == result) { + // tell the exporter where to save the exporter image + result = GraphicsExportSetOutputDataReference(grex, dataRef, + dataRefType); + + if (noErr == result) { + // write the PNG file + result = GraphicsExportDoExport(grex, &sizeWritten); + } + } + + // remember to close the component + CloseComponent(grex); + } + + // remember to dispose of the data reference handle + DisposeHandle(dataRef); + } +} +#endif + +void +nquartz_image_to_png (CGImageRef imgref, char *dest) +{ +#if 0 + static int sctr = 0; + char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png"; + + if (dest == NULL) { + fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr); + sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr); + sctr++; + dest = sptr; + } + + ExportCGImageToPNGFile(imgref, dest); +#endif +} + +void +nquartz_surface_to_png (cairo_nquartz_surface_t *nq, char *dest) +{ +#if 0 + static int sctr = 0; + char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png"; + + if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) { + fprintf (stderr, "** nquartz_surface_to_png: surface %p isn't nquartz!\n", nq); + return; + } + + if (dest == NULL) { + fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr); + sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr); + sctr++; + dest = sptr; + } + + CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext); + if (imgref == NULL) { + fprintf (stderr, "nquartz surface at %p is not a bitmap context!\n", nq); + return; + } + + ExportCGImageToPNGFile(imgref, dest); + + CGImageRelease(imgref); +#endif +} + diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h index 286efe8b8..d0d87c26e 100644 --- a/src/cairo-quartz.h +++ b/src/cairo-quartz.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2002 University of Southern California + * Copyright © 2006, 2007 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 @@ -27,15 +27,14 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is University of Southern - * California. + * The Initial Developer of the Original Code is Mozilla Corporation. * * Contributor(s): - * Carl D. Worth + * Vladimir Vukicevic */ -#ifndef CAIRO_QUARTZ_H -#define CAIRO_QUARTZ_H +#ifndef CAIRO_NQUARTZ_H +#define CAIRO_NQUARTZ_H #include @@ -43,13 +42,31 @@ #include +#ifdef CAIRO_NQUARTZ_SUPPORT_AGL +#include +#endif + CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * -cairo_quartz_surface_create (CGContextRef context, - int width, - int height, - cairo_bool_t y_grows_down); +cairo_quartz_surface_create (cairo_format_t format, + unsigned int width, + unsigned int height); + +#ifdef CAIRO_NQUARTZ_SUPPORT_AGL +cairo_public cairo_surface_t * +cairo_quartz_surface_create_for_agl_context (AGLContext aglContext, + unsigned int width, + unsigned int height); +#endif + +cairo_public cairo_surface_t * +cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, + unsigned int width, + unsigned int height); + +cairo_public CGContextRef +cairo_quartz_surface_get_cg_context (cairo_surface_t *surf); CAIRO_END_DECLS @@ -57,4 +74,4 @@ CAIRO_END_DECLS # error Cairo was not compiled with support for the quartz backend #endif /* CAIRO_HAS_QUARTZ_SURFACE */ -#endif /* CAIRO_QUARTZ_H */ +#endif /* CAIRO_NQUARTZ_H */ diff --git a/src/cairo.h b/src/cairo.h index 1c3fb25ea..2cb8e96f6 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1321,7 +1321,6 @@ typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_BEOS, CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_SURFACE_TYPE_SVG, - CAIRO_SURFACE_TYPE_NQUARTZ, CAIRO_SURFACE_TYPE_OS2 } cairo_surface_type_t; diff --git a/test/get-clip.c b/test/get-clip.c index f6a2f5a4d..0de84e904 100644 --- a/test/get-clip.c +++ b/test/get-clip.c @@ -135,16 +135,15 @@ draw (cairo_t *cr, int width, int height) case CAIRO_SURFACE_TYPE_XLIB: case CAIRO_SURFACE_TYPE_XCB: case CAIRO_SURFACE_TYPE_GLITZ: - case CAIRO_SURFACE_TYPE_QUARTZ: case CAIRO_SURFACE_TYPE_WIN32: case CAIRO_SURFACE_TYPE_BEOS: case CAIRO_SURFACE_TYPE_DIRECTFB: uses_clip_rects = TRUE; break; + case CAIRO_SURFACE_TYPE_QUARTZ: case CAIRO_SURFACE_TYPE_PDF: case CAIRO_SURFACE_TYPE_PS: case CAIRO_SURFACE_TYPE_SVG: - case CAIRO_SURFACE_TYPE_NQUARTZ: case CAIRO_SURFACE_TYPE_OS2: default: uses_clip_rects = FALSE;