[cairo-commit] boilerplate/cairo-boilerplate.c configure.in
src/cairo-atsui-font.c src/cairo.h src/cairo-nquartz.h
src/cairo-nquartz-surface.c src/cairo-quartz-private.h
src/Makefile.am
Vladimir Vukicevic
vladimir at kemper.freedesktop.org
Wed Sep 13 14:49:27 PDT 2006
boilerplate/cairo-boilerplate.c | 44
configure.in | 7
src/Makefile.am | 8
src/cairo-atsui-font.c | 122 ++
src/cairo-nquartz-surface.c | 1797 ++++++++++++++++++++++++++++++++++++++++
src/cairo-nquartz.h | 80 +
src/cairo-quartz-private.h | 9
src/cairo.h | 3
8 files changed, 2037 insertions(+), 33 deletions(-)
New commits:
diff-tree 3623da441a2b9dcec1effef71a41967ad5ff1363 (from f1bd0b9f9815ac838f30216d810bdd4eb2b67997)
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date: Wed Sep 13 14:48:40 2006 -0700
[nquartz] Initial commit of native quartz surface
Inital commit of Native Quartz surface. The main missing functionality
is mask() support (which is just a noop right now, except for the simple
solid-alpha case).
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index a6acd56..0bb479b 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/*
* Copyright © 2004,2006 Red Hat, Inc.
*
@@ -710,6 +711,41 @@ cleanup_quartz (void *closure)
}
#endif
+#if CAIRO_HAS_NQUARTZ_SURFACE
+
+#include <cairo-nquartz.h>
+
+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)
+{
+ cairo_format_t format;
+
+ switch (content) {
+ case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break;
+ case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
+ case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
+ default:
+ assert (0); /* not reached */
+ return NULL;
+ }
+
+ *closure = NULL;
+
+ return cairo_nquartz_surface_create (format, width, height);
+}
+
+static void
+cleanup_nquartz (void *closure)
+{
+ /* nothing */
+}
+#endif
+
/* Testing the win32 surface isn't interesting, since for
* ARGB images it just chains to the image backend
*/
@@ -1477,6 +1513,14 @@ cairo_boilerplate_target_t targets[] =
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 fe5d38b..50c74a1 100644
--- a/configure.in
+++ b/configure.in
@@ -260,6 +260,12 @@ CAIRO_BACKEND_ENABLE(quartz, Quartz, qua
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 ===========================================================================
CAIRO_BACKEND_ENABLE(xcb, XCB, xcb, XCB_SURFACE, no, [
@@ -817,6 +823,7 @@ echo " image: yes (always built
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 " PostScript: $use_ps"
diff --git a/src/Makefile.am b/src/Makefile.am
index e2484b5..23516c1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -59,6 +59,12 @@ libcairo_quartz_sources = cairo-quartz-s
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 CAIRO_HAS_XCB_SURFACE
libcairo_xcb_headers = cairo-xcb.h cairo-xcb-xrender.h
libcairo_xcb_sources = cairo-xcb-surface.c
@@ -132,6 +138,7 @@ cairo_headers = \
$(libcairo_svg_headers) \
$(libcairo_ps_headers) \
$(libcairo_quartz_headers) \
+ $(libcairo_nquartz_headers) \
$(libcairo_win32_headers) \
$(libcairo_beos_headers) \
$(libcairo_xcb_headers) \
@@ -215,6 +222,7 @@ libcairo_la_SOURCES = \
$(libcairo_font_subset_sources) \
$(libcairo_xlib_sources) \
$(libcairo_quartz_sources) \
+ $(libcairo_nquartz_sources) \
$(libcairo_xcb_sources) \
$(libcairo_glitz_sources) \
$(libcairo_win32_sources) \
diff --git a/src/cairo-atsui-font.c b/src/cairo-atsui-font.c
index ff57eab..973160d 100644
--- a/src/cairo-atsui-font.c
+++ b/src/cairo-atsui-font.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Calum Robinson
@@ -53,8 +54,11 @@
/* If this isn't defined, we must be building on non-intel,
* hence it will be 0.
*/
-#ifndef kCGBitmapByteOrder32Host
-#define kCGBitmapByteOrder32Host 0
+#ifdef __BIG_ENDIAN__
+#define CG_BITMAP_BYTE_ORDER_FLAG 0
+#else /* Little endian. */
+/* x86, and will be a 10.4u SDK; ByteOrder32Host will be defined */
+#define CG_BITMAP_BYTE_ORDER_FLAG kCGBitmapByteOrder32Host
#endif
typedef struct _cairo_atsui_font_face cairo_atsui_font_face_t;
@@ -223,22 +227,30 @@ _cairo_atsui_font_create_scaled (cairo_f
font->style = CreateSizedCopyOfStyle(style, &font->base.scale);
- Fixed theSize = FloatToFixed(1.0);
- const ATSUAttributeTag theFontStyleTags[] = { kATSUSizeTag };
- const ByteCount theFontStyleSizes[] = { sizeof(Fixed) };
- ATSUAttributeValuePtr theFontStyleValues[] = { &theSize };
- err = ATSUSetAttributes(style,
- sizeof(theFontStyleTags) /
- sizeof(ATSUAttributeTag), theFontStyleTags,
- theFontStyleSizes, theFontStyleValues);
+ {
+ Fixed theSize = FloatToFixed(1.0);
+ const ATSUAttributeTag theFontStyleTags[] = { kATSUSizeTag };
+ const ByteCount theFontStyleSizes[] = { sizeof(Fixed) };
+ ATSUAttributeValuePtr theFontStyleValues[] = { &theSize };
+
+ err = ATSUSetAttributes(style,
+ sizeof(theFontStyleTags) /
+ sizeof(ATSUAttributeTag), theFontStyleTags,
+ theFontStyleSizes, theFontStyleValues);
+ if (err != noErr) {
+ status = CAIRO_STATUS_NO_MEMORY;
+ goto FAIL;
+ }
+ }
font->unscaled_style = style;
-
font->fontID = font_id;
*font_out = &font->base;
status = _cairo_atsui_font_set_metrics (font);
+
+ FAIL:
if (status) {
cairo_scaled_font_destroy (&font->base);
return status;
@@ -314,15 +326,17 @@ _cairo_atsui_font_create_toy(cairo_toy_f
kFontNoLanguageCode, &fontID);
}
- ATSUAttributeTag styleTags[] =
- { kATSUQDItalicTag, kATSUQDBoldfaceTag, kATSUFontTag };
- ATSUAttributeValuePtr styleValues[] = { &isItalic, &isBold, &fontID };
- ByteCount styleSizes[] =
- { sizeof(Boolean), sizeof(Boolean), sizeof(ATSUFontID) };
-
- err = ATSUSetAttributes(style,
- sizeof(styleTags) / sizeof(styleTags[0]),
- styleTags, styleSizes, styleValues);
+ {
+ ATSUAttributeTag styleTags[] =
+ { kATSUQDItalicTag, kATSUQDBoldfaceTag, kATSUFontTag };
+ ATSUAttributeValuePtr styleValues[] = { &isItalic, &isBold, &fontID };
+ ByteCount styleSizes[] =
+ { sizeof(Boolean), sizeof(Boolean), sizeof(ATSUFontID) };
+
+ err = ATSUSetAttributes(style,
+ sizeof(styleTags) / sizeof(styleTags[0]),
+ styleTags, styleSizes, styleValues);
+ }
return _cairo_atsui_font_create_scaled (&toy_face->base, fontID, style,
font_matrix, ctm, options, font_out);
@@ -569,13 +583,17 @@ _cairo_atsui_font_old_show_glyphs (void
{
cairo_atsui_font_t *font = abstract_font;
CGContextRef myBitmapContext = 0, drawingContext;
- CGColorSpaceRef colorSpace = 0;;
+ CGColorSpaceRef colorSpace = 0;
cairo_image_surface_t *destImageSurface;
- int i;
+ int i, bits_per_comp, alpha;
void *extra = NULL;
cairo_bool_t can_draw_directly;
cairo_rectangle_int16_t rect;
+ ATSFontRef atsFont;
+ CGFontRef cgFont;
+ CGAffineTransform textTransform;
+
/* Check if we can draw directly to the destination surface */
can_draw_directly = _cairo_surface_is_quartz (generic_surface) &&
_cairo_pattern_is_opaque_solid (pattern) &&
@@ -594,15 +612,36 @@ _cairo_atsui_font_old_show_glyphs (void
&extra);
/* Create a CGBitmapContext for the dest surface for drawing into */
- colorSpace = CGColorSpaceCreateDeviceRGB();
+ if (destImageSurface->depth == 1) {
+ colorSpace = CGColorSpaceCreateDeviceGray();
+ bits_per_comp = 1;
+ alpha = kCGImageAlphaNone;
+ } else if (destImageSurface->depth == 8) {
+ colorSpace = CGColorSpaceCreateDeviceGray();
+ bits_per_comp = 8;
+ alpha = kCGImageAlphaNone;
+ } else if (destImageSurface->depth == 24) {
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+ bits_per_comp = 8;
+ alpha = kCGImageAlphaNoneSkipFirst | CG_BITMAP_BYTE_ORDER_FLAG;
+ } else if (destImageSurface->depth == 32) {
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+ bits_per_comp = 8;
+ alpha = kCGImageAlphaPremultipliedFirst | CG_BITMAP_BYTE_ORDER_FLAG;
+ } else {
+ // not reached
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
myBitmapContext = CGBitmapContextCreate(destImageSurface->data,
destImageSurface->width,
destImageSurface->height,
- destImageSurface->depth / 4,
+ bits_per_comp,
destImageSurface->stride,
colorSpace,
- kCGImageAlphaPremultipliedFirst);
+ alpha);
+ CGColorSpaceRelease(colorSpace);
+
CGContextTranslateCTM(myBitmapContext, 0, destImageSurface->height);
CGContextScaleCTM(myBitmapContext, 1.0f, -1.0f);
@@ -612,14 +651,12 @@ _cairo_atsui_font_old_show_glyphs (void
CGContextSaveGState (drawingContext);
}
- ATSFontRef atsFont = FMGetATSFontRefFromFont(font->fontID);
- CGFontRef cgFont = CGFontCreateWithPlatformFont(&atsFont);
+ atsFont = FMGetATSFontRefFromFont(font->fontID);
+ cgFont = CGFontCreateWithPlatformFont(&atsFont);
CGContextSetFont(drawingContext, cgFont);
- CGAffineTransform textTransform =
- CGAffineTransformMakeWithCairoFontScale(&font->base.scale);
-
+ textTransform = CGAffineTransformMakeWithCairoFontScale(&font->base.scale);
textTransform = CGAffineTransformScale(textTransform, 1.0f, -1.0f);
CGContextSetFontSize(drawingContext, 1.0);
@@ -688,7 +725,6 @@ _cairo_atsui_font_old_show_glyphs (void
}
if (!can_draw_directly) {
- CGColorSpaceRelease(colorSpace);
CGContextRelease(myBitmapContext);
_cairo_surface_release_dest_image(generic_surface,
@@ -703,6 +739,28 @@ _cairo_atsui_font_old_show_glyphs (void
return CAIRO_STATUS_SUCCESS;
}
+cairo_bool_t
+_cairo_scaled_font_is_atsui (cairo_scaled_font_t *sfont)
+{
+ return (sfont->backend == &cairo_atsui_scaled_font_backend);
+}
+
+ATSUStyle
+_cairo_atsui_scaled_font_get_atsu_style (cairo_scaled_font_t *sfont)
+{
+ cairo_atsui_font_t *afont = (cairo_atsui_font_t *) sfont;
+
+ return afont->style;
+}
+
+ATSUFontID
+_cairo_atsui_scaled_font_get_atsu_font_id (cairo_scaled_font_t *sfont)
+{
+ cairo_atsui_font_t *afont = (cairo_atsui_font_t *) sfont;
+
+ return afont->fontID;
+}
+
const cairo_scaled_font_backend_t cairo_atsui_scaled_font_backend = {
CAIRO_FONT_TYPE_ATSUI,
_cairo_atsui_font_create_toy,
@@ -711,5 +769,5 @@ const cairo_scaled_font_backend_t cairo_
_cairo_atsui_font_text_to_glyphs,
NULL, /* ucs4_to_index */
_cairo_atsui_font_old_show_glyphs,
- NULL, /* load_truetype_table */
};
+
diff --git a/src/cairo-nquartz-surface.c b/src/cairo-nquartz-surface.c
new file mode 100644
index 0000000..9f87f8c
--- /dev/null
+++ b/src/cairo-nquartz-surface.c
@@ -0,0 +1,1797 @@
+/* -*- 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 <vladimir at mozilla.com>
+ */
+
+#include <Carbon/Carbon.h>
+
+#ifdef CAIRO_NQUARTZ_SUPPORT_AGL
+#include <AGL/agl.h>
+#include <OpenGL/gl.h>
+#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()
+ */
+ 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, &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);
+}
+
+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;
+
+ /* 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
+
+ cairo_pattern_reference (abspat);
+ cgpat = CGPatternCreate (abspat,
+ 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);
+ } 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 CAIRO_INT_STATUS_UNSUPPORTED;
+
+ surface->sourceShading = shading;
+ } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ CGPatternRef pattern = _cairo_nquartz_cairo_repeating_surface_pattern_to_quartz (surface, source);
+ if (!pattern)
+ return CAIRO_INT_STATUS_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);
+ 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;
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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,
+ 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 (0, 0, CGImageGetWidth (quartz_image), CGImageGetHeight (quartz_image)),
+ 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);
+
+ 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;
+ }
+
+ 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,
+ const 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
+ CGGlyph *cg_glyphs = (CGGlyph*) malloc(sizeof(CGGlyph) * num_glyphs);
+ CGSize *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);
+
+ 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; we could
+ * do fallback, if we implemented _composite, but for now let's just not support
+ * this. (But pretend we did.)
+ */
+ 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) + 1;
+ 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 <Movies.h>
+
+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
new file mode 100644
index 0000000..c8f981b
--- /dev/null
+++ b/src/cairo-nquartz.h
@@ -0,0 +1,80 @@
+/* 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 <vladimir at mozilla.com>
+ */
+
+#ifndef CAIRO_NQUARTZ_H
+#define CAIRO_NQUARTZ_H
+
+#include <cairo.h>
+
+#if CAIRO_HAS_NQUARTZ_SURFACE
+
+#include <Carbon/Carbon.h>
+
+#include <AGL/agl.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+cairo_nquartz_surface_create (cairo_format_t format,
+ unsigned int width,
+ unsigned int height);
+
+#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);
+#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_bool_t
+cairo_surface_is_nquartz (cairo_surface_t *surf);
+
+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 ed66700..f9db501 100644
--- a/src/cairo-quartz-private.h
+++ b/src/cairo-quartz-private.h
@@ -54,4 +54,13 @@ typedef struct cairo_quartz_surface {
cairo_bool_t
_cairo_surface_is_quartz (cairo_surface_t *surface);
+cairo_bool_t
+_cairo_scaled_font_is_atsui (cairo_scaled_font_t *sfont);
+
+ATSUStyle
+_cairo_atsui_scaled_font_get_atsu_style (cairo_scaled_font_t *sfont);
+
+ATSUFontID
+_cairo_atsui_scaled_font_get_atsu_font_id (cairo_scaled_font_t *sfont);
+
#endif /* CAIRO_QUARTZ_PRIVATE_H */
diff --git a/src/cairo.h b/src/cairo.h
index ca8c375..0325138 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1275,7 +1275,8 @@ typedef enum _cairo_surface_type {
CAIRO_SURFACE_TYPE_WIN32,
CAIRO_SURFACE_TYPE_BEOS,
CAIRO_SURFACE_TYPE_DIRECTFB,
- CAIRO_SURFACE_TYPE_SVG
+ CAIRO_SURFACE_TYPE_SVG,
+ CAIRO_SURFACE_TYPE_NQUARTZ
} cairo_surface_type_t;
cairo_public cairo_surface_type_t
More information about the cairo-commit
mailing list