1
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2
/* cairo - a vector graphics library with display and print output
4
* Copyright ļæ½ 2006, 2007 Mozilla Corporation
6
* This library is free software; you can redistribute it and/or
7
* modify it either under the terms of the GNU Lesser General Public
8
* License version 2.1 as published by the Free Software Foundation
9
* (the "LGPL") or, at your option, under the terms of the Mozilla
10
* Public License Version 1.1 (the "MPL"). If you do not alter this
11
* notice, a recipient may use your version of this file under either
12
* the MPL or the LGPL.
14
* You should have received a copy of the LGPL along with this library
15
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
* You should have received a copy of the MPL along with this library
18
* in the file COPYING-MPL-1.1
20
* The contents of this file are subject to the Mozilla Public License
21
* Version 1.1 (the "License"); you may not use this file except in
22
* compliance with the License. You may obtain a copy of the License at
23
* http://www.mozilla.org/MPL/
25
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
27
* the specific language governing rights and limitations.
29
* The Original Code is the cairo graphics library.
31
* The Initial Developer of the Original Code is Mozilla Corporation.
34
* Vladimir Vukicevic <vladimir@mozilla.com>
39
#include "cairo-quartz-private.h"
41
/* The 10.5 SDK includes a funky new definition of FloatToFixed which
42
* causes all sorts of breakage; so reset to old-style definition
46
#define FloatToFixed(a) ((Fixed)((float)(a) * fixed1))
49
#include <Carbon/Carbon.h>
55
#define ND(_x) fprintf _x
57
#define ND(_x) do {} while(0)
60
#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
62
/* This method is private, but it exists. Its params are are exposed
63
* as args to the NS* method, but not as CG.
65
enum PrivateCGCompositeMode {
66
kPrivateCGCompositeClear = 0,
67
kPrivateCGCompositeCopy = 1,
68
kPrivateCGCompositeSourceOver = 2,
69
kPrivateCGCompositeSourceIn = 3,
70
kPrivateCGCompositeSourceOut = 4,
71
kPrivateCGCompositeSourceAtop = 5,
72
kPrivateCGCompositeDestinationOver = 6,
73
kPrivateCGCompositeDestinationIn = 7,
74
kPrivateCGCompositeDestinationOut = 8,
75
kPrivateCGCompositeDestinationAtop = 9,
76
kPrivateCGCompositeXOR = 10,
77
kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
78
kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
80
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
81
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
82
CG_EXTERN void CGContextResetCTM (CGContextRef);
83
CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
84
CG_EXTERN void CGContextResetClip (CGContextRef);
85
CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef);
87
/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
88
* has all the stuff we care about, just some of it isn't exported in the SDK.
90
#ifndef kCGBitmapByteOrder32Host
91
#define USE_10_3_WORKAROUNDS
92
#define kCGBitmapAlphaInfoMask 0x1F
93
#define kCGBitmapByteOrderMask 0x7000
94
#define kCGBitmapByteOrder32Host 0
96
typedef uint32_t CGBitmapInfo;
98
/* public in 10.4, present in 10.3.9 */
99
CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
100
CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
103
/* missing in 10.3.9 */
104
extern void CGContextClipToMask (CGContextRef, CGRect, CGImageRef) __attribute__((weak_import));
106
/* 10.5-only optimization */
107
extern void CGContextDrawTiledImage (CGContextRef, CGRect, CGImageRef) __attribute__((weak_import));
113
static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
114
static void quartz_image_to_png (CGImageRef, char *dest);
116
static cairo_quartz_surface_t *
117
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
118
cairo_content_t content,
120
unsigned int height);
122
/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
124
#define CG_MAX_HEIGHT SHRT_MAX
125
#define CG_MAX_WIDTH USHRT_MAX
127
/* is the desired size of the surface within bounds? */
128
static cairo_bool_t verify_surface_size(int width, int height)
130
/* hmmm, allow width, height == 0 ? */
131
if (width < 0 || height < 0) {
135
if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) {
143
* Cairo path -> Quartz path conversion helpers
146
typedef struct _quartz_stroke {
147
CGContextRef cgContext;
148
cairo_matrix_t *ctm_inverse;
151
/* cairo path -> execute in context */
152
static cairo_status_t
153
_cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point)
155
//ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
156
quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
157
double x = _cairo_fixed_to_double (point->x);
158
double y = _cairo_fixed_to_double (point->y);
160
if (stroke->ctm_inverse)
161
cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
163
CGContextMoveToPoint (stroke->cgContext, x, y);
164
return CAIRO_STATUS_SUCCESS;
167
static cairo_status_t
168
_cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point)
170
//ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
171
quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
172
double x = _cairo_fixed_to_double (point->x);
173
double y = _cairo_fixed_to_double (point->y);
175
if (stroke->ctm_inverse)
176
cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
178
if (CGContextIsPathEmpty (stroke->cgContext))
179
CGContextMoveToPoint (stroke->cgContext, x, y);
181
CGContextAddLineToPoint (stroke->cgContext, x, y);
182
return CAIRO_STATUS_SUCCESS;
185
static cairo_status_t
186
_cairo_path_to_quartz_context_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2)
188
//ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n",
189
// _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y),
190
// _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y),
191
// _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)));
192
quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
193
double x0 = _cairo_fixed_to_double (p0->x);
194
double y0 = _cairo_fixed_to_double (p0->y);
195
double x1 = _cairo_fixed_to_double (p1->x);
196
double y1 = _cairo_fixed_to_double (p1->y);
197
double x2 = _cairo_fixed_to_double (p2->x);
198
double y2 = _cairo_fixed_to_double (p2->y);
200
if (stroke->ctm_inverse) {
201
cairo_matrix_transform_point (stroke->ctm_inverse, &x0, &y0);
202
cairo_matrix_transform_point (stroke->ctm_inverse, &x1, &y1);
203
cairo_matrix_transform_point (stroke->ctm_inverse, &x2, &y2);
206
CGContextAddCurveToPoint (stroke->cgContext,
207
x0, y0, x1, y1, x2, y2);
208
return CAIRO_STATUS_SUCCESS;
211
static cairo_status_t
212
_cairo_path_to_quartz_context_close_path (void *closure)
214
//ND((stderr, "closepath\n"));
215
quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
216
CGContextClosePath (stroke->cgContext);
217
return CAIRO_STATUS_SUCCESS;
220
static cairo_status_t
221
_cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
222
quartz_stroke_t *stroke)
224
return _cairo_path_fixed_interpret (path,
225
CAIRO_DIRECTION_FORWARD,
226
_cairo_path_to_quartz_context_move_to,
227
_cairo_path_to_quartz_context_line_to,
228
_cairo_path_to_quartz_context_curve_to,
229
_cairo_path_to_quartz_context_close_path,
234
* Misc helpers/callbacks
237
static PrivateCGCompositeMode
238
_cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op)
241
case CAIRO_OPERATOR_CLEAR:
242
return kPrivateCGCompositeClear;
243
case CAIRO_OPERATOR_SOURCE:
244
return kPrivateCGCompositeCopy;
245
case CAIRO_OPERATOR_OVER:
246
return kPrivateCGCompositeSourceOver;
247
case CAIRO_OPERATOR_IN:
248
/* XXX This doesn't match image output */
249
return kPrivateCGCompositeSourceIn;
250
case CAIRO_OPERATOR_OUT:
251
/* XXX This doesn't match image output */
252
return kPrivateCGCompositeSourceOut;
253
case CAIRO_OPERATOR_ATOP:
254
return kPrivateCGCompositeSourceAtop;
256
case CAIRO_OPERATOR_DEST:
257
/* XXX this is handled specially (noop)! */
258
return kPrivateCGCompositeCopy;
259
case CAIRO_OPERATOR_DEST_OVER:
260
return kPrivateCGCompositeDestinationOver;
261
case CAIRO_OPERATOR_DEST_IN:
262
/* XXX This doesn't match image output */
263
return kPrivateCGCompositeDestinationIn;
264
case CAIRO_OPERATOR_DEST_OUT:
265
return kPrivateCGCompositeDestinationOut;
266
case CAIRO_OPERATOR_DEST_ATOP:
267
/* XXX This doesn't match image output */
268
return kPrivateCGCompositeDestinationAtop;
270
case CAIRO_OPERATOR_XOR:
271
return kPrivateCGCompositeXOR; /* This will generate strange results */
272
case CAIRO_OPERATOR_ADD:
273
return kPrivateCGCompositePlusLighter;
274
case CAIRO_OPERATOR_SATURATE:
275
/* XXX This doesn't match image output for SATURATE; there's no equivalent */
276
return kPrivateCGCompositePlusDarker; /* ??? */
280
return kPrivateCGCompositeCopy;
284
_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
287
case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break;
288
case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break;
289
case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break;
292
return kCGLineCapButt;
296
_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
299
case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break;
300
case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break;
301
case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break;
304
return kCGLineJoinMiter;
308
_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
309
CGAffineTransform *dst)
320
* Source -> Quartz setup and finish functions
324
ComputeGradientValue (void *info, const float *in, float *out)
326
float fdist = *in; /* 0.0 .. 1.0 */
327
cairo_fixed_t fdist_fix = _cairo_fixed_from_double(*in);
328
cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
331
for (i = 0; i < grad->n_stops; i++) {
332
if (grad->stops[i].x > fdist_fix)
336
if (i == 0 || i == grad->n_stops) {
337
if (i == grad->n_stops)
339
out[0] = grad->stops[i].color.red;
340
out[1] = grad->stops[i].color.green;
341
out[2] = grad->stops[i].color.blue;
342
out[3] = grad->stops[i].color.alpha;
344
float ax = _cairo_fixed_to_double(grad->stops[i-1].x);
345
float bx = _cairo_fixed_to_double(grad->stops[i].x) - ax;
346
float bp = (fdist - ax)/bx;
350
grad->stops[i-1].color.red * ap +
351
grad->stops[i].color.red * bp;
353
grad->stops[i-1].color.green * ap +
354
grad->stops[i].color.green * bp;
356
grad->stops[i-1].color.blue * ap +
357
grad->stops[i].color.blue * bp;
359
grad->stops[i-1].color.alpha * ap +
360
grad->stops[i].color.alpha * bp;
365
CreateGradientFunction (cairo_gradient_pattern_t *gpat)
367
static const float input_value_range[2] = { 0.f, 1.f };
368
static const float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
369
static const CGFunctionCallbacks callbacks = {
370
0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
373
return CGFunctionCreate (gpat,
381
/* generic cairo surface -> cairo_quartz_surface_t function */
382
static cairo_int_status_t
383
_cairo_quartz_surface_to_quartz (cairo_surface_t *target,
384
cairo_surface_t *pat_surf,
385
cairo_quartz_surface_t **quartz_surf)
388
if (cairo_surface_get_type(pat_surf) != CAIRO_SURFACE_TYPE_QUARTZ) {
389
/* XXXtodo/perf don't use clone if the source surface is an image surface! Instead,
390
* just create the CGImage directly!
393
cairo_surface_t *ref_type = target;
394
cairo_surface_t *new_surf = NULL;
395
cairo_rectangle_int_t rect;
396
cairo_status_t status;
398
if (ref_type == NULL)
399
ref_type = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
401
status = _cairo_surface_get_extents (pat_surf, &rect);
405
status = _cairo_surface_clone_similar (ref_type, pat_surf, rect.x, rect.y,
406
rect.width, rect.height, &new_surf);
408
cairo_surface_destroy(ref_type);
414
cairo_surface_get_type (new_surf) != CAIRO_SURFACE_TYPE_QUARTZ)
416
ND((stderr, "got a non-quartz surface, format=%d width=%u height=%u type=%d\n", cairo_surface_get_type (pat_surf), rect.width, rect.height, cairo_surface_get_type (new_surf)));
417
cairo_surface_destroy (new_surf);
418
return CAIRO_INT_STATUS_UNSUPPORTED;
421
*quartz_surf = (cairo_quartz_surface_t *) new_surf;
423
/* If it's a quartz surface, we can try to see if it's a CGBitmapContext;
424
* we do this when we call CGBitmapContextCreateImage below.
426
cairo_surface_reference (pat_surf);
427
*quartz_surf = (cairo_quartz_surface_t*) pat_surf;
430
return CAIRO_STATUS_SUCCESS;
433
/* Generic cairo_pattern -> CGPattern function */
435
SurfacePatternDrawFunc (void *info, CGContextRef context)
437
cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) info;
438
cairo_surface_t *pat_surf = spat->surface;
439
cairo_int_status_t status;
441
cairo_quartz_surface_t *quartz_surf;
445
status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf);
449
img = CGBitmapContextCreateImage (quartz_surf->cgContext);
452
ND((stderr, "CGBitmapContextCreateImage failed\n"));
453
_cairo_error (CAIRO_STATUS_NO_MEMORY);
454
cairo_surface_destroy ((cairo_surface_t*)quartz_surf);
458
/* XXXtodo WHY does this need to be flipped? Writing this stuff
459
* to disk shows that in both this path and the path above the source image
460
* has an identical orientation, and the destination context at all times has a Y
461
* flip. So why do we need to flip in this case?
463
if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
464
CGContextTranslateCTM (context, 0, CGImageGetHeight(img));
465
CGContextScaleCTM (context, 1, -1);
468
imageBounds.size = CGSizeMake (CGImageGetWidth(img), CGImageGetHeight(img));
469
imageBounds.origin.x = 0;
470
imageBounds.origin.y = 0;
472
CGContextDrawImage (context, imageBounds, img);
473
if (spat->base.extend == CAIRO_EXTEND_REFLECT) {
474
/* draw 3 more copies of the image, flipped. */
475
CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height);
476
CGContextScaleCTM (context, 1, -1);
477
CGContextDrawImage (context, imageBounds, img);
478
CGContextTranslateCTM (context, 2 * imageBounds.size.width, 0);
479
CGContextScaleCTM (context, -1, 1);
480
CGContextDrawImage (context, imageBounds, img);
481
CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height);
482
CGContextScaleCTM (context, 1, -1);
483
CGContextDrawImage (context, imageBounds, img);
486
CGImageRelease (img);
488
cairo_surface_destroy ((cairo_surface_t*) quartz_surf);
491
/* Borrowed from cairo-meta-surface */
492
static cairo_status_t
493
_init_pattern_with_snapshot (cairo_pattern_t *pattern,
494
const cairo_pattern_t *other)
496
cairo_status_t status;
498
status = _cairo_pattern_init_copy (pattern, other);
502
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
503
cairo_surface_pattern_t *surface_pattern =
504
(cairo_surface_pattern_t *) pattern;
505
cairo_surface_t *surface = surface_pattern->surface;
507
surface_pattern->surface = _cairo_surface_snapshot (surface);
509
cairo_surface_destroy (surface);
511
if (surface_pattern->surface->status)
512
return surface_pattern->surface->status;
515
return CAIRO_STATUS_SUCCESS;
518
static cairo_int_status_t
519
_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
520
cairo_pattern_t *abspat,
523
cairo_surface_pattern_t *spat;
524
cairo_surface_t *pat_surf;
525
cairo_rectangle_int_t extents;
528
CGAffineTransform ptransform, stransform;
529
CGPatternCallbacks cb = { 0,
530
SurfacePatternDrawFunc,
531
(CGFunctionReleaseInfoCallback) cairo_pattern_destroy };
533
cairo_status_t status;
535
cairo_pattern_union_t *snap_pattern = NULL;
536
cairo_pattern_t *target_pattern = abspat;
539
/* SURFACE is the only type we'll handle here */
540
if (abspat->type != CAIRO_PATTERN_TYPE_SURFACE)
541
return CAIRO_INT_STATUS_UNSUPPORTED;
543
spat = (cairo_surface_pattern_t *) abspat;
544
pat_surf = spat->surface;
546
status = _cairo_surface_get_extents (pat_surf, &extents);
550
pbounds.origin.x = 0;
551
pbounds.origin.y = 0;
553
// kjs seems to indicate this should work (setting to 0,0 to avoid
554
// tiling); however, the pattern CTM scaling ends up being NaN in
555
// the pattern draw function if either rw or rh are 0.
556
// XXXtodo get pattern drawing working with extend options
557
// XXXtodo/perf optimize CAIRO_EXTEND_NONE to a single DrawImage instead of a pattern
558
if (spat->base.extend == CAIRO_EXTEND_REFLECT) {
559
/* XXX broken; need to emulate by reflecting the image into 4 quadrants
560
* and then tiling that
562
pbounds.size.width = 2 * extents.width;
563
pbounds.size.height = 2 * extents.height;
565
pbounds.size.width = extents.width;
566
pbounds.size.height = extents.height;
568
rw = pbounds.size.width;
569
rh = pbounds.size.height;
571
m = spat->base.matrix;
572
cairo_matrix_invert(&m);
573
_cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
575
/* The pattern matrix is relative to the bottom left, again; the
576
* incoming cairo pattern matrix is relative to the upper left.
577
* So we take the pattern matrix and the original context matrix,
578
* which gives us the correct base translation/y flip.
580
ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM);
583
ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
584
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));
585
CGAffineTransform xform = CGContextGetCTM(dest->cgContext);
586
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));
590
/* XXX fixme: only do snapshots if the context is for printing, or get rid of the
591
other block if it doesn't fafect performance */
592
if (1 /* context is for printing */) {
593
snap_pattern = (cairo_pattern_union_t*) malloc(sizeof(cairo_pattern_union_t));
594
target_pattern = (cairo_pattern_t*) snap_pattern;
595
_init_pattern_with_snapshot (target_pattern, abspat);
597
cairo_pattern_reference (abspat);
598
target_pattern = abspat;
601
*cgpat = CGPatternCreate (target_pattern,
605
kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
608
return CAIRO_STATUS_SUCCESS;
619
} cairo_quartz_action_t;
621
static cairo_quartz_action_t
622
_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
623
cairo_linear_pattern_t *lpat)
625
cairo_pattern_t *abspat = (cairo_pattern_t *) lpat;
629
CGFunctionRef gradFunc;
631
bool extend = abspat->extend == CAIRO_EXTEND_PAD;
633
/* bandaid for mozilla bug 379321, also visible in the
634
* linear-gradient-reflect test.
636
if (abspat->extend == CAIRO_EXTEND_REFLECT ||
637
abspat->extend == CAIRO_EXTEND_REPEAT)
638
return DO_UNSUPPORTED;
640
/* We can only do this if we have an identity pattern matrix;
641
* otherwise fall back through to the generic pattern case.
642
* XXXperf we could optimize this by creating a pattern with the shading;
643
* but we'd need to know the extents to do that.
644
* ... but we don't care; we can use the surface extents for it
645
* XXXtodo - implement gradients with non-identity pattern matrices
647
cairo_pattern_get_matrix (abspat, &mat);
648
if (mat.xx != 1.0 || mat.yy != 1.0 || mat.xy != 0.0 || mat.yx != 0.0)
649
return DO_UNSUPPORTED;
651
if (!lpat->base.n_stops) {
652
CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
653
CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
659
rgb = CGColorSpaceCreateDeviceRGB();
661
start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x) - x0,
662
_cairo_fixed_to_double (lpat->p1.y) - y0);
663
end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x) - x0,
664
_cairo_fixed_to_double (lpat->p2.y) - y0);
666
cairo_pattern_reference (abspat);
667
gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat);
668
surface->sourceShading = CGShadingCreateAxial (rgb,
672
CGColorSpaceRelease(rgb);
673
CGFunctionRelease(gradFunc);
678
static cairo_quartz_action_t
679
_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
680
cairo_radial_pattern_t *rpat)
682
cairo_pattern_t *abspat = (cairo_pattern_t *)rpat;
686
CGFunctionRef gradFunc;
687
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
688
bool extend = abspat->extend == CAIRO_EXTEND_PAD;
690
/* bandaid for mozilla bug 379321, also visible in the
691
* linear-gradient-reflect test.
693
if (abspat->extend == CAIRO_EXTEND_REFLECT ||
694
abspat->extend == CAIRO_EXTEND_REPEAT)
695
return DO_UNSUPPORTED;
697
/* XXXtodo - implement gradients with non-identity pattern matrices
699
cairo_pattern_get_matrix (abspat, &mat);
700
if (mat.xx != 1.0 || mat.yy != 1.0 || mat.xy != 0.0 || mat.yx != 0.0)
701
return DO_UNSUPPORTED;
703
if (!rpat->base.n_stops) {
704
CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
705
CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
711
rgb = CGColorSpaceCreateDeviceRGB();
713
start = CGPointMake (_cairo_fixed_to_double (rpat->c1.x) - x0,
714
_cairo_fixed_to_double (rpat->c1.y) - y0);
715
end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x) - x0,
716
_cairo_fixed_to_double (rpat->c2.y) - y0);
718
cairo_pattern_reference (abspat);
719
gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat);
720
surface->sourceShading = CGShadingCreateRadial (rgb,
722
_cairo_fixed_to_double (rpat->r1),
724
_cairo_fixed_to_double (rpat->r2),
727
CGColorSpaceRelease(rgb);
728
CGFunctionRelease(gradFunc);
733
static cairo_quartz_action_t
734
_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
735
cairo_pattern_t *source)
737
assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
739
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
740
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
742
CGContextSetRGBStrokeColor (surface->cgContext,
747
CGContextSetRGBFillColor (surface->cgContext,
754
} else if (source->type == CAIRO_PATTERN_TYPE_LINEAR)
756
cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *)source;
757
return _cairo_quartz_setup_linear_source (surface, lpat);
759
} else if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
760
cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *)source;
761
return _cairo_quartz_setup_radial_source (surface, rpat);
763
} else if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
764
(source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImage && source->extend == CAIRO_EXTEND_REPEAT)))
766
cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) source;
767
cairo_surface_t *pat_surf = spat->surface;
768
cairo_quartz_surface_t *quartz_surf;
770
cairo_matrix_t m = spat->base.matrix;
771
cairo_rectangle_int_t extents;
772
cairo_status_t status;
774
status = _cairo_quartz_surface_to_quartz ((cairo_surface_t *) surface, pat_surf, &quartz_surf);
776
return DO_UNSUPPORTED;
778
surface->sourceImageSurface = (cairo_surface_t *)quartz_surf;
780
if (IS_EMPTY(quartz_surf))
783
img = CGBitmapContextCreateImage (quartz_surf->cgContext);
785
return DO_UNSUPPORTED;
787
surface->sourceImage = img;
789
cairo_matrix_invert(&m);
790
_cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceImageTransform);
792
status = _cairo_surface_get_extents (pat_surf, &extents);
794
return DO_UNSUPPORTED;
796
surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
798
if (source->extend == CAIRO_EXTEND_NONE)
801
return DO_TILED_IMAGE;
802
} else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
803
float patternAlpha = 1.0f;
804
CGColorSpaceRef patternSpace;
805
CGPatternRef pattern;
806
cairo_int_status_t status;
808
status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
810
return DO_UNSUPPORTED;
812
// Save before we change the pattern, colorspace, etc. so that
813
// we can restore and make sure that quartz releases our
814
// pattern (which may be stack allocated)
815
CGContextSaveGState(surface->cgContext);
817
patternSpace = CGColorSpaceCreatePattern(NULL);
818
CGContextSetFillColorSpace (surface->cgContext, patternSpace);
819
CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
820
CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
821
CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
822
CGColorSpaceRelease (patternSpace);
824
/* Quartz likes to munge the pattern phase (as yet unexplained
825
* why); force it to 0,0 as we've already baked in the correct
826
* pattern translation into the pattern matrix
828
CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
830
surface->sourcePattern = pattern;
834
return DO_UNSUPPORTED;
841
_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
842
cairo_pattern_t *source)
844
if (surface->sourceImage) {
845
CGImageRelease(surface->sourceImage);
846
surface->sourceImage = NULL;
848
cairo_surface_destroy(surface->sourceImageSurface);
849
surface->sourceImageSurface = NULL;
852
if (surface->sourceShading) {
853
CGShadingRelease(surface->sourceShading);
854
surface->sourceShading = NULL;
857
if (surface->sourcePattern) {
858
CGPatternRelease(surface->sourcePattern);
859
// To tear down the pattern and colorspace
860
CGContextRestoreGState(surface->cgContext);
862
surface->sourcePattern = NULL;
867
* get source/dest image implementation
871
ImageDataReleaseFunc(void *info, const void *data, size_t size)
878
/* Read the image from the surface's front buffer */
879
static cairo_int_status_t
880
_cairo_quartz_get_image (cairo_quartz_surface_t *surface,
881
cairo_image_surface_t **image_out,
882
unsigned char **data_out)
884
unsigned char *imageData;
885
cairo_image_surface_t *isurf;
887
if (IS_EMPTY(surface)) {
888
*image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
891
return CAIRO_STATUS_SUCCESS;
894
if (CGBitmapContextGetBitsPerPixel(surface->cgContext) != 0) {
896
unsigned int bitinfo;
897
unsigned int bpc, bpp;
898
CGColorSpaceRef colorspace;
899
unsigned int color_comps;
901
imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
902
#ifdef USE_10_3_WORKAROUNDS
903
bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext);
905
bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
907
stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
908
bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
909
bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
911
// let's hope they don't add YUV under us
912
colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
913
color_comps = CGColorSpaceGetNumberOfComponents(colorspace);
915
// XXX TODO: We can handle all of these by converting to
916
// pixman masks, including non-native-endian masks
918
return CAIRO_INT_STATUS_UNSUPPORTED;
920
if (bpp != 32 && bpp != 8)
921
return CAIRO_INT_STATUS_UNSUPPORTED;
923
if (color_comps != 3 && color_comps != 1)
924
return CAIRO_INT_STATUS_UNSUPPORTED;
926
if (bpp == 32 && color_comps == 3 &&
927
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
928
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
930
isurf = (cairo_image_surface_t *)
931
cairo_image_surface_create_for_data (imageData,
933
surface->extents.width,
934
surface->extents.height,
936
} else if (bpp == 32 && color_comps == 3 &&
937
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
938
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
940
isurf = (cairo_image_surface_t *)
941
cairo_image_surface_create_for_data (imageData,
943
surface->extents.width,
944
surface->extents.height,
946
} else if (bpp == 8 && color_comps == 1)
948
isurf = (cairo_image_surface_t *)
949
cairo_image_surface_create_for_data (imageData,
951
surface->extents.width,
952
surface->extents.height,
955
return CAIRO_INT_STATUS_UNSUPPORTED;
958
return CAIRO_INT_STATUS_UNSUPPORTED;
962
return CAIRO_STATUS_SUCCESS;
966
* Cairo surface backend implementations
969
static cairo_status_t
970
_cairo_quartz_surface_finish (void *abstract_surface)
972
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
974
ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
976
if (IS_EMPTY(surface))
977
return CAIRO_STATUS_SUCCESS;
979
/* Restore our saved gstate that we use to reset clipping */
980
CGContextRestoreGState (surface->cgContext);
982
CGContextRelease (surface->cgContext);
984
surface->cgContext = NULL;
986
if (surface->imageData) {
987
free (surface->imageData);
988
surface->imageData = NULL;
991
return CAIRO_STATUS_SUCCESS;
994
static cairo_status_t
995
_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
996
cairo_image_surface_t **image_out,
999
cairo_int_status_t status;
1000
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1002
//ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1004
*image_extra = NULL;
1006
status = _cairo_quartz_get_image (surface, image_out, NULL);
1008
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1010
return CAIRO_STATUS_SUCCESS;
1014
_cairo_quartz_surface_release_source_image (void *abstract_surface,
1015
cairo_image_surface_t *image,
1018
cairo_surface_destroy ((cairo_surface_t *) image);
1022
static cairo_status_t
1023
_cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
1024
cairo_rectangle_int_t *interest_rect,
1025
cairo_image_surface_t **image_out,
1026
cairo_rectangle_int_t *image_rect,
1029
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1030
cairo_int_status_t status;
1031
unsigned char *data;
1033
ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
1035
*image_rect = surface->extents;
1037
status = _cairo_quartz_get_image (surface, image_out, &data);
1039
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1041
*image_extra = data;
1043
return CAIRO_STATUS_SUCCESS;
1047
_cairo_quartz_surface_release_dest_image (void *abstract_surface,
1048
cairo_rectangle_int_t *interest_rect,
1049
cairo_image_surface_t *image,
1050
cairo_rectangle_int_t *image_rect,
1053
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1054
unsigned char *imageData = (unsigned char *) image_extra;
1056
//ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
1058
if (IS_EMPTY(surface)) {
1059
cairo_surface_destroy ((cairo_surface_t*) image);
1063
if (!CGBitmapContextGetData (surface->cgContext)) {
1064
CGDataProviderRef dataProvider;
1065
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
1068
dataProvider = CGDataProviderCreateWithData (NULL, imageData,
1069
surface->extents.width * surface->extents.height * 4,
1070
ImageDataReleaseFunc);
1072
img = CGImageCreate (surface->extents.width, surface->extents.height,
1074
surface->extents.width * 4,
1076
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
1080
kCGRenderingIntentDefault);
1081
CGColorSpaceRelease (rgb);
1083
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
1085
CGContextDrawImage (surface->cgContext,
1086
CGRectMake (0, 0, surface->extents.width, surface->extents.height),
1089
CGImageRelease (img);
1090
CGDataProviderRelease (dataProvider);
1092
ND((stderr, "Image for surface %p was recovered from a bitmap\n", surface));
1095
cairo_surface_destroy ((cairo_surface_t *) image);
1098
static cairo_surface_t *
1099
_cairo_quartz_surface_create_similar (void *abstract_surface,
1100
cairo_content_t content,
1104
/*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
1106
cairo_format_t format;
1108
if (content == CAIRO_CONTENT_COLOR_ALPHA)
1109
format = CAIRO_FORMAT_ARGB32;
1110
else if (content == CAIRO_CONTENT_COLOR)
1111
format = CAIRO_FORMAT_RGB24;
1112
else if (content == CAIRO_CONTENT_ALPHA)
1113
format = CAIRO_FORMAT_A8;
1117
// verify width and height of surface
1118
if (!verify_surface_size(width, height)) {
1119
_cairo_error (CAIRO_STATUS_NO_MEMORY);
1123
return cairo_quartz_surface_create (format, width, height);
1126
static cairo_status_t
1127
_cairo_quartz_surface_clone_similar (void *abstract_surface,
1128
cairo_surface_t *src,
1133
cairo_surface_t **clone_out)
1135
cairo_quartz_surface_t *new_surface = NULL;
1136
cairo_format_t new_format;
1137
CGImageRef quartz_image = NULL;
1141
// verify width and height of surface
1142
if (!verify_surface_size(width, height)) {
1143
return CAIRO_INT_STATUS_UNSUPPORTED;
1146
if (cairo_surface_get_type(src) == CAIRO_SURFACE_TYPE_QUARTZ) {
1147
cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
1149
if (IS_EMPTY(qsurf)) {
1150
*clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, qsurf->extents.width, qsurf->extents.height);
1151
return CAIRO_STATUS_SUCCESS;
1154
quartz_image = CGBitmapContextCreateImage (qsurf->cgContext);
1155
new_format = CAIRO_FORMAT_ARGB32; /* XXX bogus; recover a real format from the image */
1156
} else if (_cairo_surface_is_image (src)) {
1157
cairo_image_surface_t *isurf = (cairo_image_surface_t *) src;
1158
CGDataProviderRef dataProvider;
1159
CGColorSpaceRef cgColorspace;
1160
CGBitmapInfo bitinfo;
1161
int bitsPerComponent, bitsPerPixel;
1163
if (isurf->width == 0 || isurf->height == 0) {
1164
*clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, isurf->width, isurf->height);
1165
return CAIRO_STATUS_SUCCESS;
1168
if (isurf->format == CAIRO_FORMAT_ARGB32) {
1169
cgColorspace = CGColorSpaceCreateDeviceRGB();
1170
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
1171
bitsPerComponent = 8;
1173
} else if (isurf->format == CAIRO_FORMAT_RGB24) {
1174
cgColorspace = CGColorSpaceCreateDeviceRGB();
1175
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
1176
bitsPerComponent = 8;
1178
} else if (isurf->format == CAIRO_FORMAT_A8) {
1179
cgColorspace = CGColorSpaceCreateDeviceGray();
1180
bitinfo = kCGImageAlphaNone;
1181
bitsPerComponent = 8;
1184
/* SUPPORT A1, maybe */
1185
return CAIRO_INT_STATUS_UNSUPPORTED;
1188
new_format = isurf->format;
1190
dataProvider = CGDataProviderCreateWithData (NULL,
1192
isurf->height * isurf->stride,
1195
quartz_image = CGImageCreate (isurf->width, isurf->height,
1204
kCGRenderingIntentDefault);
1205
CGDataProviderRelease (dataProvider);
1206
CGColorSpaceRelease (cgColorspace);
1208
return CAIRO_INT_STATUS_UNSUPPORTED;
1212
return CAIRO_INT_STATUS_UNSUPPORTED;
1214
new_surface = (cairo_quartz_surface_t *)
1215
cairo_quartz_surface_create (new_format,
1216
CGImageGetWidth (quartz_image),
1217
CGImageGetHeight (quartz_image));
1218
if (!new_surface || new_surface->base.status) {
1219
CGImageRelease (quartz_image);
1220
return CAIRO_INT_STATUS_UNSUPPORTED;
1223
CGContextSetCompositeOperation (new_surface->cgContext,
1224
kPrivateCGCompositeCopy);
1226
quartz_image_to_png (quartz_image, NULL);
1228
CGContextDrawImage (new_surface->cgContext,
1229
CGRectMake (src_x, src_y, width, height),
1231
CGImageRelease (quartz_image);
1233
*clone_out = (cairo_surface_t*) new_surface;
1235
return CAIRO_STATUS_SUCCESS;
1238
static cairo_int_status_t
1239
_cairo_quartz_surface_get_extents (void *abstract_surface,
1240
cairo_rectangle_int_t *extents)
1242
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1244
*extents = surface->extents;
1246
return CAIRO_STATUS_SUCCESS;
1249
static cairo_int_status_t
1250
_cairo_quartz_surface_paint (void *abstract_surface,
1251
cairo_operator_t op,
1252
cairo_pattern_t *source)
1254
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1255
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1256
cairo_quartz_action_t action;
1258
ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
1260
if (IS_EMPTY(surface))
1261
return CAIRO_STATUS_SUCCESS;
1263
if (op == CAIRO_OPERATOR_DEST)
1264
return CAIRO_STATUS_SUCCESS;
1266
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1268
action = _cairo_quartz_setup_source (surface, source);
1270
if (action == DO_SOLID || action == DO_PATTERN) {
1271
CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1273
surface->extents.width,
1274
surface->extents.height));
1275
} else if (action == DO_SHADING) {
1276
CGContextDrawShading (surface->cgContext, surface->sourceShading);
1277
} else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1278
cairo_surface_pattern_t *surface_pattern =
1279
(cairo_surface_pattern_t *) source;
1280
cairo_surface_t *pat_surf = surface_pattern->surface;
1282
CGContextSaveGState (surface->cgContext);
1284
if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
1285
CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
1286
CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1288
surface->extents.width,
1289
surface->extents.height));
1292
CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
1293
if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
1294
CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
1295
CGContextScaleCTM (surface->cgContext, 1, -1);
1298
if (action == DO_IMAGE)
1299
CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1301
CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1302
CGContextRestoreGState (surface->cgContext);
1303
} else if (action != DO_NOTHING) {
1304
rv = CAIRO_INT_STATUS_UNSUPPORTED;
1307
_cairo_quartz_teardown_source (surface, source);
1309
ND((stderr, "-- paint\n"));
1313
static cairo_int_status_t
1314
_cairo_quartz_surface_fill (void *abstract_surface,
1315
cairo_operator_t op,
1316
cairo_pattern_t *source,
1317
cairo_path_fixed_t *path,
1318
cairo_fill_rule_t fill_rule,
1320
cairo_antialias_t antialias)
1322
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1323
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1324
cairo_quartz_action_t action;
1325
quartz_stroke_t stroke;
1327
ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
1329
if (IS_EMPTY(surface))
1330
return CAIRO_STATUS_SUCCESS;
1332
if (op == CAIRO_OPERATOR_DEST)
1333
return CAIRO_STATUS_SUCCESS;
1335
CGContextSaveGState (surface->cgContext);
1337
CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1338
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1340
action = _cairo_quartz_setup_source (surface, source);
1342
CGContextBeginPath (surface->cgContext);
1344
stroke.cgContext = surface->cgContext;
1345
stroke.ctm_inverse = NULL;
1346
rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
1350
if (action == DO_SOLID || action == DO_PATTERN) {
1351
if (fill_rule == CAIRO_FILL_RULE_WINDING)
1352
CGContextFillPath (surface->cgContext);
1354
CGContextEOFillPath (surface->cgContext);
1355
} else if (action == DO_SHADING) {
1357
// we have to clip and then paint the shading; we can't fill
1359
if (fill_rule == CAIRO_FILL_RULE_WINDING)
1360
CGContextClip (surface->cgContext);
1362
CGContextEOClip (surface->cgContext);
1364
CGContextDrawShading (surface->cgContext, surface->sourceShading);
1365
} else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1366
cairo_surface_pattern_t *surface_pattern =
1367
(cairo_surface_pattern_t *) source;
1368
cairo_surface_t *pat_surf = surface_pattern->surface;
1369
if (fill_rule == CAIRO_FILL_RULE_WINDING)
1370
CGContextClip (surface->cgContext);
1372
CGContextEOClip (surface->cgContext);
1374
if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
1375
CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
1376
CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1378
surface->extents.width,
1379
surface->extents.height));
1382
CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
1383
if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
1384
CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
1385
CGContextScaleCTM (surface->cgContext, 1, -1);
1388
if (action == DO_IMAGE)
1389
CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1391
CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1392
} else if (action != DO_NOTHING) {
1393
rv = CAIRO_INT_STATUS_UNSUPPORTED;
1397
_cairo_quartz_teardown_source (surface, source);
1399
CGContextRestoreGState (surface->cgContext);
1401
ND((stderr, "-- fill\n"));
1405
static cairo_int_status_t
1406
_cairo_quartz_surface_stroke (void *abstract_surface,
1407
cairo_operator_t op,
1408
cairo_pattern_t *source,
1409
cairo_path_fixed_t *path,
1410
cairo_stroke_style_t *style,
1411
cairo_matrix_t *ctm,
1412
cairo_matrix_t *ctm_inverse,
1414
cairo_antialias_t antialias)
1416
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1417
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1418
cairo_quartz_action_t action;
1419
quartz_stroke_t stroke;
1420
CGAffineTransform strokeTransform;
1422
ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
1424
if (IS_EMPTY(surface))
1425
return CAIRO_STATUS_SUCCESS;
1427
if (op == CAIRO_OPERATOR_DEST)
1428
return CAIRO_STATUS_SUCCESS;
1430
CGContextSaveGState (surface->cgContext);
1432
// Turning antialiasing off causes misrendering with
1433
// single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels)
1434
//CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1435
CGContextSetLineWidth (surface->cgContext, style->line_width);
1436
CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1437
CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1438
CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
1439
_cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1440
CGContextConcatCTM (surface->cgContext, strokeTransform);
1442
if (style->dash && style->num_dashes) {
1443
#define STATIC_DASH 32
1444
float sdash[STATIC_DASH];
1445
float *fdash = sdash;
1446
unsigned int max_dashes = style->num_dashes;
1449
if (style->num_dashes%2)
1451
if (max_dashes > STATIC_DASH)
1452
fdash = _cairo_malloc_ab (max_dashes, sizeof (float));
1454
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1456
for (k = 0; k < max_dashes; k++)
1457
fdash[k] = (float) style->dash[k % style->num_dashes];
1459
CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
1464
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1466
action = _cairo_quartz_setup_source (surface, source);
1468
CGContextBeginPath (surface->cgContext);
1470
stroke.cgContext = surface->cgContext;
1471
stroke.ctm_inverse = ctm_inverse;
1472
rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
1476
if (action == DO_SOLID || action == DO_PATTERN) {
1477
CGContextStrokePath (surface->cgContext);
1478
} else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1479
CGContextReplacePathWithStrokedPath (surface->cgContext);
1480
CGContextClip (surface->cgContext);
1482
if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
1483
CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
1484
CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1486
surface->extents.width,
1487
surface->extents.height));
1490
CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
1491
if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) {
1492
CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
1493
CGContextScaleCTM (surface->cgContext, 1, -1);
1496
if (action == DO_IMAGE)
1497
CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1499
CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1500
} else if (action == DO_SHADING) {
1501
CGContextReplacePathWithStrokedPath (surface->cgContext);
1502
CGContextClip (surface->cgContext);
1504
CGContextDrawShading (surface->cgContext, surface->sourceShading);
1505
} else if (action != DO_NOTHING) {
1506
rv = CAIRO_INT_STATUS_UNSUPPORTED;
1510
_cairo_quartz_teardown_source (surface, source);
1512
CGContextRestoreGState (surface->cgContext);
1514
ND((stderr, "-- stroke\n"));
1518
#if CAIRO_HAS_ATSUI_FONT
1519
static cairo_int_status_t
1520
_cairo_quartz_surface_show_glyphs (void *abstract_surface,
1521
cairo_operator_t op,
1522
cairo_pattern_t *source,
1523
cairo_glyph_t *glyphs,
1525
cairo_scaled_font_t *scaled_font)
1527
CGAffineTransform cairoTextTransform, textTransform, ctm;
1528
// XXXtodo/perf: stack storage for glyphs/sizes
1529
#define STATIC_BUF_SIZE 64
1530
CGGlyph glyphs_static[STATIC_BUF_SIZE];
1531
CGSize cg_advances_static[STATIC_BUF_SIZE];
1532
CGGlyph *cg_glyphs = &glyphs_static[0];
1533
CGSize *cg_advances = &cg_advances_static[0];
1535
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1536
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1537
cairo_quartz_action_t action;
1541
if (IS_EMPTY(surface))
1542
return CAIRO_STATUS_SUCCESS;
1544
if (num_glyphs <= 0)
1545
return CAIRO_STATUS_SUCCESS;
1547
if (op == CAIRO_OPERATOR_DEST)
1548
return CAIRO_STATUS_SUCCESS;
1550
if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_ATSUI)
1551
return CAIRO_INT_STATUS_UNSUPPORTED;
1553
CGContextSaveGState (surface->cgContext);
1555
action = _cairo_quartz_setup_source (surface, source);
1556
if (action == DO_SOLID || action == DO_PATTERN) {
1557
CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
1558
} else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
1559
CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
1561
if (action != DO_NOTHING)
1562
rv = CAIRO_INT_STATUS_UNSUPPORTED;
1566
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1568
/* this doesn't addref */
1569
CGFontRef cgfref = _cairo_atsui_scaled_font_get_cg_font_ref (scaled_font);
1570
CGContextSetFont (surface->cgContext, cgfref);
1572
/* So this should include the size; I don't know if I need to extract the
1573
* size from this and call CGContextSetFontSize.. will I get crappy hinting
1574
* with this 1.0 size business? Or will CG just multiply that size into the
1577
//ND((stderr, "show_glyphs: glyph 0 at: %f, %f\n", glyphs[0].x, glyphs[0].y));
1578
cairoTextTransform = CGAffineTransformMake (scaled_font->font_matrix.xx,
1579
scaled_font->font_matrix.yx,
1580
scaled_font->font_matrix.xy,
1581
scaled_font->font_matrix.yy,
1584
textTransform = CGAffineTransformMakeTranslation (glyphs[0].x, glyphs[0].y);
1585
textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
1586
textTransform = CGAffineTransformConcat (cairoTextTransform, textTransform);
1588
ctm = CGAffineTransformMake (scaled_font->ctm.xx,
1589
-scaled_font->ctm.yx,
1590
-scaled_font->ctm.xy,
1591
scaled_font->ctm.yy,
1593
textTransform = CGAffineTransformConcat (ctm, textTransform);
1595
CGContextSetTextMatrix (surface->cgContext, textTransform);
1596
CGContextSetFontSize (surface->cgContext, 1.0);
1598
if (num_glyphs > STATIC_BUF_SIZE) {
1599
cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
1600
if (cg_glyphs == NULL) {
1601
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1605
cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize));
1606
if (cg_advances == NULL) {
1607
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1612
xprev = glyphs[0].x;
1613
yprev = glyphs[0].y;
1615
cg_glyphs[0] = glyphs[0].index;
1616
cg_advances[0].width = 0;
1617
cg_advances[0].height = 0;
1619
for (i = 1; i < num_glyphs; i++) {
1620
float xf = glyphs[i].x;
1621
float yf = glyphs[i].y;
1622
cg_glyphs[i] = glyphs[i].index;
1623
cg_advances[i-1].width = xf - xprev;
1624
cg_advances[i-1].height = yf - yprev;
1630
for (i = 0; i < num_glyphs; i++) {
1631
ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
1635
CGContextShowGlyphsWithAdvances (surface->cgContext,
1640
if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1641
if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
1642
CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
1643
CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1645
surface->extents.width,
1646
surface->extents.height));
1649
CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
1650
if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) {
1651
CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
1652
CGContextScaleCTM (surface->cgContext, 1, -1);
1655
if (action == DO_IMAGE)
1656
CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1658
CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1659
} else if (action == DO_SHADING) {
1660
CGContextDrawShading (surface->cgContext, surface->sourceShading);
1664
if (cg_advances != &cg_advances_static[0]) {
1668
if (cg_glyphs != &glyphs_static[0]) {
1672
_cairo_quartz_teardown_source (surface, source);
1674
CGContextRestoreGState (surface->cgContext);
1678
#endif /* CAIRO_HAS_ATSUI_FONT */
1680
static cairo_int_status_t
1681
_cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
1682
cairo_operator_t op,
1683
cairo_pattern_t *source,
1684
cairo_surface_pattern_t *mask)
1686
cairo_rectangle_int_t extents;
1687
cairo_quartz_surface_t *quartz_surf;
1690
cairo_surface_t *pat_surf = mask->surface;
1691
cairo_status_t status = CAIRO_STATUS_SUCCESS;
1693
status = _cairo_surface_get_extents (pat_surf, &extents);
1697
status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf);
1701
// everything would be masked out, so do nothing
1702
if (IS_EMPTY(quartz_surf))
1705
img = CGBitmapContextCreateImage (quartz_surf->cgContext);
1707
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1711
rect = CGRectMake (-mask->base.matrix.x0, -mask->base.matrix.y0, extents.width, extents.height);
1712
CGContextSaveGState (surface->cgContext);
1713
CGContextClipToMask (surface->cgContext, rect, img);
1714
status = _cairo_quartz_surface_paint (surface, op, source);
1716
CGContextRestoreGState (surface->cgContext);
1717
CGImageRelease (img);
1719
cairo_surface_destroy ((cairo_surface_t*) quartz_surf);
1723
static cairo_int_status_t
1724
_cairo_quartz_surface_mask (void *abstract_surface,
1725
cairo_operator_t op,
1726
cairo_pattern_t *source,
1727
cairo_pattern_t *mask)
1729
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1730
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1732
ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
1734
if (IS_EMPTY(surface))
1735
return CAIRO_STATUS_SUCCESS;
1737
if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
1738
/* This is easy; we just need to paint with the alpha. */
1739
cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
1741
CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
1742
} else if (CGContextClipToMask &&
1743
mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
1744
mask->extend == CAIRO_EXTEND_NONE) {
1745
return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask);
1747
/* So, CGContextClipToMask is not present in 10.3.9, so we're
1748
* doomed; if we have imageData, we can do fallback, otherwise
1749
* just pretend success.
1751
if (surface->imageData)
1752
return CAIRO_INT_STATUS_UNSUPPORTED;
1754
return CAIRO_STATUS_SUCCESS;
1757
rv = _cairo_quartz_surface_paint (surface, op, source);
1759
if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
1760
CGContextSetAlpha (surface->cgContext, 1.0);
1763
ND((stderr, "-- mask\n"));
1768
static cairo_int_status_t
1769
_cairo_quartz_surface_intersect_clip_path (void *abstract_surface,
1770
cairo_path_fixed_t *path,
1771
cairo_fill_rule_t fill_rule,
1773
cairo_antialias_t antialias)
1775
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1776
quartz_stroke_t stroke;
1777
cairo_status_t status;
1779
ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
1781
if (IS_EMPTY(surface))
1782
return CAIRO_STATUS_SUCCESS;
1785
/* If we're being asked to reset the clip, we can only do it
1786
* by restoring the gstate to our previous saved one, and
1789
* Note that this assumes that ALL quartz surface creation
1790
* functions will do a SaveGState first; we do this in create_internal.
1792
CGContextRestoreGState (surface->cgContext);
1793
CGContextSaveGState (surface->cgContext);
1795
CGContextBeginPath (surface->cgContext);
1796
stroke.cgContext = surface->cgContext;
1797
stroke.ctm_inverse = NULL;
1799
/* path must not be empty. */
1800
CGContextMoveToPoint (surface->cgContext, 0, 0);
1801
status = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
1805
if (fill_rule == CAIRO_FILL_RULE_WINDING)
1806
CGContextClip (surface->cgContext);
1808
CGContextEOClip (surface->cgContext);
1811
ND((stderr, "-- intersect_clip_path\n"));
1813
return CAIRO_STATUS_SUCCESS;
1816
// XXXtodo implement show_page; need to figure out how to handle begin/end
1818
static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
1819
CAIRO_SURFACE_TYPE_QUARTZ,
1820
_cairo_quartz_surface_create_similar,
1821
_cairo_quartz_surface_finish,
1822
_cairo_quartz_surface_acquire_source_image,
1823
_cairo_quartz_surface_release_source_image,
1824
_cairo_quartz_surface_acquire_dest_image,
1825
_cairo_quartz_surface_release_dest_image,
1826
_cairo_quartz_surface_clone_similar,
1827
NULL, /* composite */
1828
NULL, /* fill_rectangles */
1829
NULL, /* composite_trapezoids */
1830
NULL, /* copy_page */
1831
NULL, /* show_page */
1832
NULL, /* set_clip_region */
1833
_cairo_quartz_surface_intersect_clip_path,
1834
_cairo_quartz_surface_get_extents,
1835
NULL, /* old_show_glyphs */
1836
NULL, /* get_font_options */
1838
NULL, /* mark_dirty_rectangle */
1839
NULL, /* scaled_font_fini */
1840
NULL, /* scaled_glyph_fini */
1842
_cairo_quartz_surface_paint,
1843
_cairo_quartz_surface_mask,
1844
_cairo_quartz_surface_stroke,
1845
_cairo_quartz_surface_fill,
1846
#if CAIRO_HAS_ATSUI_FONT
1847
_cairo_quartz_surface_show_glyphs,
1849
NULL, /* surface_show_glyphs */
1850
#endif /* CAIRO_HAS_ATSUI_FONT */
1852
NULL, /* snapshot */
1853
NULL, /* is_similar */
1855
NULL /* fill_stroke */
1858
cairo_quartz_surface_t *
1859
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
1860
cairo_content_t content,
1862
unsigned int height)
1864
cairo_quartz_surface_t *surface;
1866
/* Init the base surface */
1867
surface = malloc(sizeof(cairo_quartz_surface_t));
1868
if (surface == NULL)
1869
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1871
memset(surface, 0, sizeof(cairo_quartz_surface_t));
1873
_cairo_surface_init(&surface->base, &cairo_quartz_surface_backend,
1876
/* Save our extents */
1877
surface->extents.x = surface->extents.y = 0;
1878
surface->extents.width = width;
1879
surface->extents.height = height;
1881
if (IS_EMPTY(surface)) {
1882
surface->cgContext = NULL;
1883
surface->cgContextBaseCTM = CGAffineTransformIdentity;
1884
surface->imageData = NULL;
1888
/* Save so we can always get back to a known-good CGContext -- this is
1889
* required for proper behaviour of intersect_clip_path(NULL)
1891
CGContextSaveGState (cgContext);
1893
surface->cgContext = cgContext;
1894
surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
1896
surface->imageData = NULL;
1902
* cairo_quartz_surface_create_for_cg_context
1903
* @cgContext: the existing CGContext for which to create the surface
1904
* @width: width of the surface, in pixels
1905
* @height: height of the surface, in pixels
1907
* Creates a Quartz surface that wraps the given CGContext. The
1908
* CGContext is assumed to be in the QuickDraw coordinate space (that
1909
* is, with the origin at the upper left and the Y axis increasing
1910
* downward.) If the CGContext is in the Quartz coordinate space (with
1911
* the origin at the bottom left), then it should be flipped before
1912
* this function is called:
1914
* <informalexample><programlisting>
1915
* CGContextTranslateCTM (cgContext, 0.0, height);
1916
* CGContextScaleCTM (cgContext, 1.0, -1.0);
1917
* </programlisting></informalexample>
1919
* A very small number of Cairo operations cannot be translated to
1920
* Quartz operations; those operations will fail on this surface.
1921
* If all Cairo operations are required to succeed, consider rendering
1922
* to a surface created by cairo_quartz_surface_create() and then copying
1923
* the result to the CGContext.
1925
* Return value: the newly created Cairo surface.
1931
cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
1933
unsigned int height)
1935
cairo_quartz_surface_t *surf;
1937
CGContextRetain (cgContext);
1939
surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
1941
if (surf->base.status) {
1942
CGContextRelease (cgContext);
1943
// create_internal will have set an error
1947
return (cairo_surface_t *) surf;
1951
* cairo_quartz_surface_create
1952
* @format: format of pixels in the surface to create
1953
* @width: width of the surface, in pixels
1954
* @height: height of the surface, in pixels
1956
* Creates a Quartz surface backed by a CGBitmap. The surface is
1957
* created using the Device RGB (or Device Gray, for A8) color space.
1958
* All Cairo operations, including those that require software
1959
* rendering, will succeed on this surface.
1961
* Return value: the newly created surface.
1966
cairo_quartz_surface_create (cairo_format_t format,
1968
unsigned int height)
1970
cairo_quartz_surface_t *surf;
1972
CGColorSpaceRef cgColorspace;
1973
CGBitmapInfo bitinfo;
1976
int bitsPerComponent;
1978
// verify width and height of surface
1979
if (!verify_surface_size(width, height))
1980
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1982
if (width == 0 || height == 0) {
1983
return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
1987
if (format == CAIRO_FORMAT_ARGB32) {
1988
cgColorspace = CGColorSpaceCreateDeviceRGB();
1990
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
1991
bitsPerComponent = 8;
1992
} else if (format == CAIRO_FORMAT_RGB24) {
1993
cgColorspace = CGColorSpaceCreateDeviceRGB();
1995
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
1996
bitsPerComponent = 8;
1997
} else if (format == CAIRO_FORMAT_A8) {
1998
cgColorspace = CGColorSpaceCreateDeviceGray();
2002
stride = (width & ~3) + 4;
2003
bitinfo = kCGImageAlphaNone;
2004
bitsPerComponent = 8;
2005
} else if (format == CAIRO_FORMAT_A1) {
2006
/* I don't think we can usefully support this, as defined by
2007
* cairo_format_t -- these are 1-bit pixels stored in 32-bit
2010
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2012
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2015
imageData = _cairo_malloc_ab (height, stride);
2017
CGColorSpaceRelease (cgColorspace);
2018
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2020
/* zero the memory to match the image surface behaviour */
2021
memset (imageData, 0, height * stride);
2023
cgc = CGBitmapContextCreate (imageData,
2030
CGColorSpaceRelease (cgColorspace);
2034
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2037
/* flip the Y axis */
2038
CGContextTranslateCTM (cgc, 0.0, height);
2039
CGContextScaleCTM (cgc, 1.0, -1.0);
2041
surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2043
if (surf->base.status) {
2044
CGContextRelease (cgc);
2046
// create_internal will have set an error
2050
surf->imageData = imageData;
2052
return (cairo_surface_t *) surf;
2056
* cairo_quartz_surface_get_cg_context
2057
* @surface: the Cairo Quartz surface
2059
* Returns the CGContextRef that the given Quartz surface is backed
2062
* Return value: the CGContextRef for the given surface.
2067
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2069
cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
2071
if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
2074
return quartz->cgContext;
2084
void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest)
2086
Handle dataRef = NULL;
2088
CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII);
2090
GraphicsExportComponent grex = 0;
2091
unsigned long sizeWritten;
2093
ComponentResult result;
2095
// create the data reference
2096
result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle,
2097
0, &dataRef, &dataRefType);
2099
if (NULL != dataRef && noErr == result) {
2100
// get the PNG exporter
2101
result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG,
2105
// tell the exporter where to find its source image
2106
result = GraphicsExportSetInputCGImage(grex, inImageRef);
2108
if (noErr == result) {
2109
// tell the exporter where to save the exporter image
2110
result = GraphicsExportSetOutputDataReference(grex, dataRef,
2113
if (noErr == result) {
2114
// write the PNG file
2115
result = GraphicsExportDoExport(grex, &sizeWritten);
2119
// remember to close the component
2120
CloseComponent(grex);
2123
// remember to dispose of the data reference handle
2124
DisposeHandle(dataRef);
2130
quartz_image_to_png (CGImageRef imgref, char *dest)
2133
static int sctr = 0;
2134
char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
2137
fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
2138
sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
2143
ExportCGImageToPNGFile(imgref, dest);
2148
quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
2151
static int sctr = 0;
2152
char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
2154
if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
2155
fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
2160
fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
2161
sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
2166
CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
2167
if (imgref == NULL) {
2168
fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
2172
ExportCGImageToPNGFile(imgref, dest);
2174
CGImageRelease(imgref);