Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 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
3
 *
4
 * Copyright � 2006, 2007 Mozilla Corporation
5
 *
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.
13
 *
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17
 * You should have received a copy of the MPL along with this library
18
 * in the file COPYING-MPL-1.1
19
 *
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/
24
 *
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.
28
 *
29
 * The Original Code is the cairo graphics library.
30
 *
31
 * The Initial Developer of the Original Code is Mozilla Foundation.
32
 *
33
 * Contributor(s):
34
 *	Vladimir Vukicevic 
35
 */
36
 
37
#define _GNU_SOURCE /* required for RTLD_DEFAULT */
38
#include "cairoint.h"
39
 
40
#include "cairo-quartz-private.h"
41
 
42
#include "cairo-composite-rectangles-private.h"
43
#include "cairo-compositor-private.h"
44
#include "cairo-default-context-private.h"
45
#include "cairo-error-private.h"
46
#include "cairo-image-surface-inline.h"
47
#include "cairo-pattern-private.h"
48
#include "cairo-surface-backend-private.h"
49
#include "cairo-surface-clipper-private.h"
50
#include "cairo-recording-surface-private.h"
51
 
52
#include 
53
 
54
#ifndef RTLD_DEFAULT
55
#define RTLD_DEFAULT ((void *) 0)
56
#endif
57
 
58
#include 
59
 
60
#undef QUARTZ_DEBUG
61
 
62
#ifdef QUARTZ_DEBUG
63
#define ND(_x)	fprintf _x
64
#else
65
#define ND(_x)	do {} while(0)
66
#endif
67
 
68
#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
69
 
70
/**
71
 * SECTION:cairo-quartz
72
 * @Title: Quartz Surfaces
73
 * @Short_Description: Rendering to Quartz surfaces
74
 * @See_Also: #cairo_surface_t
75
 *
76
 * The Quartz surface is used to render cairo graphics targeting the
77
 * Apple OS X Quartz rendering system.
78
 **/
79
 
80
/**
81
 * CAIRO_HAS_QUARTZ_SURFACE:
82
 *
83
 * Defined if the Quartz surface backend is available.
84
 * This macro can be used to conditionally compile backend-specific code.
85
 *
86
 * Since: 1.6
87
 **/
88
 
89
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
90
/* This method is private, but it exists.  Its params are are exposed
91
 * as args to the NS* method, but not as CG.
92
 */
93
enum PrivateCGCompositeMode {
94
    kPrivateCGCompositeClear		= 0,
95
    kPrivateCGCompositeCopy		= 1,
96
    kPrivateCGCompositeSourceOver	= 2,
97
    kPrivateCGCompositeSourceIn		= 3,
98
    kPrivateCGCompositeSourceOut	= 4,
99
    kPrivateCGCompositeSourceAtop	= 5,
100
    kPrivateCGCompositeDestinationOver	= 6,
101
    kPrivateCGCompositeDestinationIn	= 7,
102
    kPrivateCGCompositeDestinationOut	= 8,
103
    kPrivateCGCompositeDestinationAtop	= 9,
104
    kPrivateCGCompositeXOR		= 10,
105
    kPrivateCGCompositePlusDarker	= 11, // (max (0, (1-d) + (1-s)))
106
    kPrivateCGCompositePlusLighter	= 12, // (min (1, s + d))
107
};
108
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
109
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
110
#endif
111
 
112
/* Some of these are present in earlier versions of the OS than where
113
 * they are public; other are not public at all
114
 */
115
/* public since 10.5 */
116
static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
117
 
118
/* public since 10.6 */
119
static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
120
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
121
 
122
/* not yet public */
123
static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
124
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
125
 
126
static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
127
 
128
/*
129
 * Utility functions
130
 */
131
 
132
#ifdef QUARTZ_DEBUG
133
static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
134
static void quartz_image_to_png (CGImageRef, char *dest);
135
#endif
136
 
137
static cairo_quartz_surface_t *
138
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
139
				       cairo_content_t content,
140
				       unsigned int width,
141
				       unsigned int height);
142
 
143
static cairo_bool_t
144
_cairo_surface_is_quartz (const cairo_surface_t *surface);
145
 
146
/* Load all extra symbols */
147
static void quartz_ensure_symbols (void)
148
{
149
    if (likely (_cairo_quartz_symbol_lookup_done))
150
	return;
151
 
152
    CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
153
    CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
154
    CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
155
    CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
156
    CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
157
 
158
    _cairo_quartz_symbol_lookup_done = TRUE;
159
}
160
 
161
CGImageRef
162
CairoQuartzCreateCGImage (cairo_format_t format,
163
			  unsigned int width,
164
			  unsigned int height,
165
			  unsigned int stride,
166
			  void *data,
167
			  cairo_bool_t interpolate,
168
			  CGColorSpaceRef colorSpaceOverride,
169
			  CGDataProviderReleaseDataCallback releaseCallback,
170
			  void *releaseInfo)
171
{
172
    CGImageRef image = NULL;
173
    CGDataProviderRef dataProvider = NULL;
174
    CGColorSpaceRef colorSpace = colorSpaceOverride;
175
    CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
176
    int bitsPerComponent, bitsPerPixel;
177
 
178
    switch (format) {
179
	case CAIRO_FORMAT_ARGB32:
180
	    if (colorSpace == NULL)
181
		colorSpace = CGColorSpaceCreateDeviceRGB ();
182
	    bitinfo |= kCGImageAlphaPremultipliedFirst;
183
	    bitsPerComponent = 8;
184
	    bitsPerPixel = 32;
185
	    break;
186
 
187
	case CAIRO_FORMAT_RGB24:
188
	    if (colorSpace == NULL)
189
		colorSpace = CGColorSpaceCreateDeviceRGB ();
190
	    bitinfo |= kCGImageAlphaNoneSkipFirst;
191
	    bitsPerComponent = 8;
192
	    bitsPerPixel = 32;
193
	    break;
194
 
195
	case CAIRO_FORMAT_A8:
196
	    bitsPerComponent = 8;
197
	    bitsPerPixel = 8;
198
	    break;
199
 
200
	case CAIRO_FORMAT_A1:
201
#ifdef WORDS_BIGENDIAN
202
	    bitsPerComponent = 1;
203
	    bitsPerPixel = 1;
204
	    break;
205
#endif
206
 
207
	case CAIRO_FORMAT_RGB30:
208
	case CAIRO_FORMAT_RGB16_565:
209
	case CAIRO_FORMAT_INVALID:
210
	default:
211
	    return NULL;
212
    }
213
 
214
    dataProvider = CGDataProviderCreateWithData (releaseInfo,
215
						 data,
216
						 height * stride,
217
						 releaseCallback);
218
 
219
    if (unlikely (!dataProvider)) {
220
	// manually release
221
	if (releaseCallback)
222
	    releaseCallback (releaseInfo, data, height * stride);
223
	goto FINISH;
224
    }
225
 
226
    if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
227
	cairo_quartz_float_t decode[] = {1.0, 0.0};
228
	image = CGImageMaskCreate (width, height,
229
				   bitsPerComponent,
230
				   bitsPerPixel,
231
				   stride,
232
				   dataProvider,
233
				   decode,
234
				   interpolate);
235
    } else
236
	image = CGImageCreate (width, height,
237
			       bitsPerComponent,
238
			       bitsPerPixel,
239
			       stride,
240
			       colorSpace,
241
			       bitinfo,
242
			       dataProvider,
243
			       NULL,
244
			       interpolate,
245
			       kCGRenderingIntentDefault);
246
 
247
FINISH:
248
 
249
    CGDataProviderRelease (dataProvider);
250
 
251
    if (colorSpace != colorSpaceOverride)
252
	CGColorSpaceRelease (colorSpace);
253
 
254
    return image;
255
}
256
 
257
static inline cairo_bool_t
258
_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
259
{
260
    if (unlikely (cgc == NULL))
261
	return FALSE;
262
 
263
    if (likely (CGContextGetTypePtr)) {
264
	/* 4 is the type value of a bitmap context */
265
	return CGContextGetTypePtr (cgc) == 4;
266
    }
267
 
268
    /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
269
    return CGBitmapContextGetBitsPerPixel (cgc) != 0;
270
}
271
 
272
/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
273
 
274
#define CG_MAX_HEIGHT   SHRT_MAX
275
#define CG_MAX_WIDTH    USHRT_MAX
276
 
277
/* is the desired size of the surface within bounds? */
278
cairo_bool_t
279
_cairo_quartz_verify_surface_size (int width, int height)
280
{
281
    /* hmmm, allow width, height == 0 ? */
282
    if (width < 0 || height < 0)
283
	return FALSE;
284
 
285
    if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
286
	return FALSE;
287
 
288
    return TRUE;
289
}
290
 
291
/*
292
 * Cairo path -> Quartz path conversion helpers
293
 */
294
 
295
/* cairo path -> execute in context */
296
static cairo_status_t
297
_cairo_path_to_quartz_context_move_to (void *closure,
298
				       const cairo_point_t *point)
299
{
300
    //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
301
    double x = _cairo_fixed_to_double (point->x);
302
    double y = _cairo_fixed_to_double (point->y);
303
 
304
    CGContextMoveToPoint (closure, x, y);
305
    return CAIRO_STATUS_SUCCESS;
306
}
307
 
308
static cairo_status_t
309
_cairo_path_to_quartz_context_line_to (void *closure,
310
				       const cairo_point_t *point)
311
{
312
    //ND ((stderr, "lineto: %f %f\n",  _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
313
    double x = _cairo_fixed_to_double (point->x);
314
    double y = _cairo_fixed_to_double (point->y);
315
 
316
    CGContextAddLineToPoint (closure, x, y);
317
    return CAIRO_STATUS_SUCCESS;
318
}
319
 
320
static cairo_status_t
321
_cairo_path_to_quartz_context_curve_to (void *closure,
322
					const cairo_point_t *p0,
323
					const cairo_point_t *p1,
324
					const cairo_point_t *p2)
325
{
326
    //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
327
    //		   _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
328
    //		   _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
329
    //		   _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
330
    double x0 = _cairo_fixed_to_double (p0->x);
331
    double y0 = _cairo_fixed_to_double (p0->y);
332
    double x1 = _cairo_fixed_to_double (p1->x);
333
    double y1 = _cairo_fixed_to_double (p1->y);
334
    double x2 = _cairo_fixed_to_double (p2->x);
335
    double y2 = _cairo_fixed_to_double (p2->y);
336
 
337
    CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
338
    return CAIRO_STATUS_SUCCESS;
339
}
340
 
341
static cairo_status_t
342
_cairo_path_to_quartz_context_close_path (void *closure)
343
{
344
    //ND ((stderr, "closepath\n"));
345
    CGContextClosePath (closure);
346
    return CAIRO_STATUS_SUCCESS;
347
}
348
 
349
static void
350
_cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
351
					    CGContextRef closure)
352
{
353
    cairo_status_t status;
354
 
355
    CGContextBeginPath (closure);
356
    status = _cairo_path_fixed_interpret (path,
357
					  _cairo_path_to_quartz_context_move_to,
358
					  _cairo_path_to_quartz_context_line_to,
359
					  _cairo_path_to_quartz_context_curve_to,
360
					  _cairo_path_to_quartz_context_close_path,
361
					  closure);
362
 
363
    assert (status == CAIRO_STATUS_SUCCESS);
364
}
365
 
366
/*
367
 * Misc helpers/callbacks
368
 */
369
 
370
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
371
static PrivateCGCompositeMode
372
_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
373
{
374
    switch (op) {
375
	case CAIRO_OPERATOR_CLEAR:
376
	    return kPrivateCGCompositeClear;
377
	case CAIRO_OPERATOR_SOURCE:
378
	    return kPrivateCGCompositeCopy;
379
	case CAIRO_OPERATOR_OVER:
380
	    return kPrivateCGCompositeSourceOver;
381
	case CAIRO_OPERATOR_IN:
382
	    return kPrivateCGCompositeSourceIn;
383
	case CAIRO_OPERATOR_OUT:
384
	    return kPrivateCGCompositeSourceOut;
385
	case CAIRO_OPERATOR_ATOP:
386
	    return kPrivateCGCompositeSourceAtop;
387
	case CAIRO_OPERATOR_DEST_OVER:
388
	    return kPrivateCGCompositeDestinationOver;
389
	case CAIRO_OPERATOR_DEST_IN:
390
	    return kPrivateCGCompositeDestinationIn;
391
	case CAIRO_OPERATOR_DEST_OUT:
392
	    return kPrivateCGCompositeDestinationOut;
393
	case CAIRO_OPERATOR_DEST_ATOP:
394
	    return kPrivateCGCompositeDestinationAtop;
395
	case CAIRO_OPERATOR_XOR:
396
	    return kPrivateCGCompositeXOR;
397
	case CAIRO_OPERATOR_ADD:
398
	    return kPrivateCGCompositePlusLighter;
399
 
400
	case CAIRO_OPERATOR_DEST:
401
	case CAIRO_OPERATOR_SATURATE:
402
	case CAIRO_OPERATOR_MULTIPLY:
403
	case CAIRO_OPERATOR_SCREEN:
404
	case CAIRO_OPERATOR_OVERLAY:
405
	case CAIRO_OPERATOR_DARKEN:
406
	case CAIRO_OPERATOR_LIGHTEN:
407
	case CAIRO_OPERATOR_COLOR_DODGE:
408
	case CAIRO_OPERATOR_COLOR_BURN:
409
	case CAIRO_OPERATOR_HARD_LIGHT:
410
	case CAIRO_OPERATOR_SOFT_LIGHT:
411
	case CAIRO_OPERATOR_DIFFERENCE:
412
	case CAIRO_OPERATOR_EXCLUSION:
413
	case CAIRO_OPERATOR_HSL_HUE:
414
	case CAIRO_OPERATOR_HSL_SATURATION:
415
	case CAIRO_OPERATOR_HSL_COLOR:
416
	case CAIRO_OPERATOR_HSL_LUMINOSITY:
417
        default:
418
	    ASSERT_NOT_REACHED;
419
    }
420
}
421
#endif
422
 
423
static CGBlendMode
424
_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
425
{
426
    switch (op) {
427
	case CAIRO_OPERATOR_MULTIPLY:
428
	    return kCGBlendModeMultiply;
429
	case CAIRO_OPERATOR_SCREEN:
430
	    return kCGBlendModeScreen;
431
	case CAIRO_OPERATOR_OVERLAY:
432
	    return kCGBlendModeOverlay;
433
	case CAIRO_OPERATOR_DARKEN:
434
	    return kCGBlendModeDarken;
435
	case CAIRO_OPERATOR_LIGHTEN:
436
	    return kCGBlendModeLighten;
437
	case CAIRO_OPERATOR_COLOR_DODGE:
438
	    return kCGBlendModeColorDodge;
439
	case CAIRO_OPERATOR_COLOR_BURN:
440
	    return kCGBlendModeColorBurn;
441
	case CAIRO_OPERATOR_HARD_LIGHT:
442
	    return kCGBlendModeHardLight;
443
	case CAIRO_OPERATOR_SOFT_LIGHT:
444
	    return kCGBlendModeSoftLight;
445
	case CAIRO_OPERATOR_DIFFERENCE:
446
	    return kCGBlendModeDifference;
447
	case CAIRO_OPERATOR_EXCLUSION:
448
	    return kCGBlendModeExclusion;
449
	case CAIRO_OPERATOR_HSL_HUE:
450
	    return kCGBlendModeHue;
451
	case CAIRO_OPERATOR_HSL_SATURATION:
452
	    return kCGBlendModeSaturation;
453
	case CAIRO_OPERATOR_HSL_COLOR:
454
	    return kCGBlendModeColor;
455
	case CAIRO_OPERATOR_HSL_LUMINOSITY:
456
	    return kCGBlendModeLuminosity;
457
 
458
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
459
	case CAIRO_OPERATOR_CLEAR:
460
	    return kCGBlendModeClear;
461
	case CAIRO_OPERATOR_SOURCE:
462
	    return kCGBlendModeCopy;
463
	case CAIRO_OPERATOR_OVER:
464
	    return kCGBlendModeNormal;
465
	case CAIRO_OPERATOR_IN:
466
	    return kCGBlendModeSourceIn;
467
	case CAIRO_OPERATOR_OUT:
468
	    return kCGBlendModeSourceOut;
469
	case CAIRO_OPERATOR_ATOP:
470
	    return kCGBlendModeSourceAtop;
471
	case CAIRO_OPERATOR_DEST_OVER:
472
	    return kCGBlendModeDestinationOver;
473
	case CAIRO_OPERATOR_DEST_IN:
474
	    return kCGBlendModeDestinationIn;
475
	case CAIRO_OPERATOR_DEST_OUT:
476
	    return kCGBlendModeDestinationOut;
477
	case CAIRO_OPERATOR_DEST_ATOP:
478
	    return kCGBlendModeDestinationAtop;
479
	case CAIRO_OPERATOR_XOR:
480
	    return kCGBlendModeXOR;
481
	case CAIRO_OPERATOR_ADD:
482
	    return kCGBlendModePlusLighter;
483
#else
484
	case CAIRO_OPERATOR_CLEAR:
485
	case CAIRO_OPERATOR_SOURCE:
486
	case CAIRO_OPERATOR_OVER:
487
	case CAIRO_OPERATOR_IN:
488
	case CAIRO_OPERATOR_OUT:
489
	case CAIRO_OPERATOR_ATOP:
490
	case CAIRO_OPERATOR_DEST_OVER:
491
	case CAIRO_OPERATOR_DEST_IN:
492
	case CAIRO_OPERATOR_DEST_OUT:
493
	case CAIRO_OPERATOR_DEST_ATOP:
494
	case CAIRO_OPERATOR_XOR:
495
	case CAIRO_OPERATOR_ADD:
496
#endif
497
 
498
	case CAIRO_OPERATOR_DEST:
499
	case CAIRO_OPERATOR_SATURATE:
500
        default:
501
	    ASSERT_NOT_REACHED;
502
    }
503
}
504
 
505
static cairo_int_status_t
506
_cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
507
{
508
    CGBlendMode blendmode;
509
 
510
    assert (op != CAIRO_OPERATOR_DEST);
511
 
512
    /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
513
     * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
514
     * uses the definition from the Adobe Supplement.
515
     */
516
    if (op == CAIRO_OPERATOR_SATURATE ||
517
	op == CAIRO_OPERATOR_COLOR_DODGE ||
518
	op == CAIRO_OPERATOR_COLOR_BURN)
519
    {
520
	return CAIRO_INT_STATUS_UNSUPPORTED;
521
    }
522
 
523
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
524
    if (op <= CAIRO_OPERATOR_ADD) {
525
	PrivateCGCompositeMode compmode;
526
 
527
	compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
528
	CGContextSetCompositeOperation (context, compmode);
529
	return CAIRO_STATUS_SUCCESS;
530
    }
531
#endif
532
 
533
    blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
534
    CGContextSetBlendMode (context, blendmode);
535
    return CAIRO_STATUS_SUCCESS;
536
}
537
 
538
static cairo_int_status_t
539
_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
540
{
541
    ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
542
 
543
    /* When the destination has no color components, we can avoid some
544
     * fallbacks, but we have to workaround operators which behave
545
     * differently in Quartz. */
546
    if (surface->base.content == CAIRO_CONTENT_ALPHA) {
547
	assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
548
 
549
	if (op == CAIRO_OPERATOR_SOURCE ||
550
	    op == CAIRO_OPERATOR_IN ||
551
	    op == CAIRO_OPERATOR_OUT ||
552
	    op == CAIRO_OPERATOR_DEST_IN ||
553
	    op == CAIRO_OPERATOR_DEST_ATOP ||
554
	    op == CAIRO_OPERATOR_XOR)
555
	{
556
	    return CAIRO_INT_STATUS_UNSUPPORTED;
557
	}
558
 
559
	if (op == CAIRO_OPERATOR_DEST_OVER)
560
	    op = CAIRO_OPERATOR_OVER;
561
	else if (op == CAIRO_OPERATOR_SATURATE)
562
	    op = CAIRO_OPERATOR_ADD;
563
	else if (op == CAIRO_OPERATOR_COLOR_DODGE)
564
	    op = CAIRO_OPERATOR_OVER;
565
	else if (op == CAIRO_OPERATOR_COLOR_BURN)
566
	    op = CAIRO_OPERATOR_OVER;
567
    }
568
 
569
    return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
570
}
571
 
572
static inline CGLineCap
573
_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
574
{
575
    switch (ccap) {
576
    default:
577
	ASSERT_NOT_REACHED;
578
 
579
    case CAIRO_LINE_CAP_BUTT:
580
	return kCGLineCapButt;
581
 
582
    case CAIRO_LINE_CAP_ROUND:
583
	return kCGLineCapRound;
584
 
585
    case CAIRO_LINE_CAP_SQUARE:
586
	return kCGLineCapSquare;
587
    }
588
}
589
 
590
static inline CGLineJoin
591
_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
592
{
593
    switch (cjoin) {
594
    default:
595
	ASSERT_NOT_REACHED;
596
 
597
    case CAIRO_LINE_JOIN_MITER:
598
	return kCGLineJoinMiter;
599
 
600
    case CAIRO_LINE_JOIN_ROUND:
601
	return kCGLineJoinRound;
602
 
603
    case CAIRO_LINE_JOIN_BEVEL:
604
	return kCGLineJoinBevel;
605
    }
606
}
607
 
608
static inline CGInterpolationQuality
609
_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
610
{
611
    switch (filter) {
612
    case CAIRO_FILTER_NEAREST:
613
    case CAIRO_FILTER_FAST:
614
	return kCGInterpolationNone;
615
 
616
    case CAIRO_FILTER_BEST:
617
    case CAIRO_FILTER_GOOD:
618
    case CAIRO_FILTER_BILINEAR:
619
    case CAIRO_FILTER_GAUSSIAN:
620
	return kCGInterpolationDefault;
621
 
622
    default:
623
	ASSERT_NOT_REACHED;
624
	return kCGInterpolationDefault;
625
    }
626
}
627
 
628
static inline void
629
_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
630
				      CGAffineTransform *dst)
631
{
632
    dst->a = src->xx;
633
    dst->b = src->yx;
634
    dst->c = src->xy;
635
    dst->d = src->yy;
636
    dst->tx = src->x0;
637
    dst->ty = src->y0;
638
}
639
 
640
 
641
/*
642
 * Source -> Quartz setup and finish functions
643
 */
644
 
645
static void
646
ComputeGradientValue (void *info,
647
                      const cairo_quartz_float_t *in,
648
                      cairo_quartz_float_t *out)
649
{
650
    double fdist = *in;
651
    const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
652
    unsigned int i;
653
 
654
    /* Put fdist back in the 0.0..1.0 range if we're doing
655
     * REPEAT/REFLECT
656
     */
657
    if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
658
	fdist = fdist - floor (fdist);
659
    } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
660
	fdist = fmod (fabs (fdist), 2.0);
661
	if (fdist > 1.0)
662
	    fdist = 2.0 - fdist;
663
    }
664
 
665
    for (i = 0; i < grad->n_stops; i++)
666
	if (grad->stops[i].offset > fdist)
667
	    break;
668
 
669
    if (i == 0 || i == grad->n_stops) {
670
	if (i == grad->n_stops)
671
	    --i;
672
	out[0] = grad->stops[i].color.red;
673
	out[1] = grad->stops[i].color.green;
674
	out[2] = grad->stops[i].color.blue;
675
	out[3] = grad->stops[i].color.alpha;
676
    } else {
677
	cairo_quartz_float_t ax = grad->stops[i-1].offset;
678
	cairo_quartz_float_t bx = grad->stops[i].offset - ax;
679
	cairo_quartz_float_t bp = (fdist - ax)/bx;
680
	cairo_quartz_float_t ap = 1.0 - bp;
681
 
682
	out[0] =
683
	    grad->stops[i-1].color.red * ap +
684
	    grad->stops[i].color.red * bp;
685
	out[1] =
686
	    grad->stops[i-1].color.green * ap +
687
	    grad->stops[i].color.green * bp;
688
	out[2] =
689
	    grad->stops[i-1].color.blue * ap +
690
	    grad->stops[i].color.blue * bp;
691
	out[3] =
692
	    grad->stops[i-1].color.alpha * ap +
693
	    grad->stops[i].color.alpha * bp;
694
    }
695
}
696
 
697
static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
698
    0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
699
};
700
static const CGFunctionCallbacks gradient_callbacks = {
701
    0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
702
};
703
 
704
/* Quartz computes a small number of samples of the gradient color
705
 * function. On MacOS X 10.5 it apparently computes only 1024
706
 * samples. */
707
#define MAX_GRADIENT_RANGE 1024
708
 
709
static CGFunctionRef
710
CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
711
				   const cairo_rectangle_int_t    *extents,
712
				   cairo_circle_double_t          *start,
713
				   cairo_circle_double_t          *end)
714
{
715
    cairo_pattern_t *pat;
716
    cairo_quartz_float_t input_value_range[2];
717
 
718
    if (gradient->base.extend != CAIRO_EXTEND_NONE) {
719
	double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
720
	double t[2], tolerance;
721
 
722
	tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
723
	tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
724
 
725
	bounds_x1 = extents->x;
726
	bounds_y1 = extents->y;
727
	bounds_x2 = extents->x + extents->width;
728
	bounds_y2 = extents->y + extents->height;
729
	_cairo_matrix_transform_bounding_box (&gradient->base.matrix,
730
					      &bounds_x1, &bounds_y1,
731
					      &bounds_x2, &bounds_y2,
732
					      NULL);
733
 
734
	_cairo_gradient_pattern_box_to_parameter (gradient,
735
						  bounds_x1, bounds_y1,
736
						  bounds_x2, bounds_y2,
737
						  tolerance,
738
						  t);
739
 
740
	if (gradient->base.extend == CAIRO_EXTEND_PAD) {
741
	    t[0] = MAX (t[0], -0.5);
742
	    t[1] = MIN (t[1],  1.5);
743
	} else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
744
	    return NULL;
745
 
746
	/* set the input range for the function -- the function knows how
747
	   to map values outside of 0.0 .. 1.0 to the correct color */
748
	input_value_range[0] = t[0];
749
	input_value_range[1] = t[1];
750
    } else {
751
	input_value_range[0] = 0;
752
	input_value_range[1] = 1;
753
    }
754
 
755
    _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
756
    _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
757
 
758
    if (_cairo_pattern_create_copy (&pat, &gradient->base))
759
	return NULL;
760
 
761
    return CGFunctionCreate (pat,
762
			     1,
763
			     input_value_range,
764
			     4,
765
			     gradient_output_value_ranges,
766
			     &gradient_callbacks);
767
}
768
 
769
/* Obtain a CGImageRef from a #cairo_surface_t * */
770
 
771
typedef struct {
772
    cairo_surface_t *surface;
773
    cairo_image_surface_t *image_out;
774
    void *image_extra;
775
} quartz_source_image_t;
776
 
777
static void
778
DataProviderReleaseCallback (void *info, const void *data, size_t size)
779
{
780
    quartz_source_image_t *source_img = info;
781
    _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
782
    free (source_img);
783
}
784
 
785
static cairo_status_t
786
_cairo_surface_to_cgimage (cairo_surface_t       *source,
787
			   cairo_rectangle_int_t *extents,
788
			   cairo_format_t         format,
789
			   cairo_matrix_t        *matrix,
790
			   const cairo_clip_t    *clip,
791
			   CGImageRef            *image_out)
792
{
793
    cairo_status_t status;
794
    quartz_source_image_t *source_img;
795
    cairo_image_surface_t *image_surface;
796
 
797
    if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
798
	cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
799
	*image_out = CGImageRetain (surface->image);
800
	return CAIRO_STATUS_SUCCESS;
801
    }
802
 
803
    if (_cairo_surface_is_quartz (source)) {
804
	cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
805
	if (IS_EMPTY (surface)) {
806
	    *image_out = NULL;
807
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
808
	}
809
 
810
	if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
811
	    *image_out = CGBitmapContextCreateImage (surface->cgContext);
812
	    if (*image_out)
813
		return CAIRO_STATUS_SUCCESS;
814
	}
815
    }
816
 
817
    source_img = malloc (sizeof (quartz_source_image_t));
818
    if (unlikely (source_img == NULL))
819
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
820
 
821
    source_img->surface = source;
822
 
823
    if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
824
	image_surface = (cairo_image_surface_t *)
825
	    cairo_image_surface_create (format, extents->width, extents->height);
826
	if (unlikely (image_surface->base.status)) {
827
	    status = image_surface->base.status;
828
	    cairo_surface_destroy (&image_surface->base);
829
	    free (source_img);
830
	    return status;
831
	}
832
 
833
	status = _cairo_recording_surface_replay_with_clip (source,
834
							    matrix,
835
							    &image_surface->base,
836
							    NULL);
837
	if (unlikely (status)) {
838
	    cairo_surface_destroy (&image_surface->base);
839
	    free (source_img);
840
	    return status;
841
	}
842
 
843
	source_img->image_out = image_surface;
844
	source_img->image_extra = NULL;
845
 
846
	cairo_matrix_init_identity (matrix);
847
    }
848
    else {
849
	status = _cairo_surface_acquire_source_image (source_img->surface,
850
						      &source_img->image_out,
851
						      &source_img->image_extra);
852
	if (unlikely (status)) {
853
	    free (source_img);
854
	    return status;
855
	}
856
    }
857
 
858
    if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
859
	*image_out = NULL;
860
	DataProviderReleaseCallback (source_img,
861
				     source_img->image_out->data,
862
				     source_img->image_out->height * source_img->image_out->stride);
863
    } else {
864
	*image_out = CairoQuartzCreateCGImage (source_img->image_out->format,
865
					       source_img->image_out->width,
866
					       source_img->image_out->height,
867
					       source_img->image_out->stride,
868
					       source_img->image_out->data,
869
					       TRUE,
870
					       NULL,
871
					       DataProviderReleaseCallback,
872
					       source_img);
873
 
874
	/* TODO: differentiate memory error and unsupported surface type */
875
	if (unlikely (*image_out == NULL))
876
	    status = CAIRO_INT_STATUS_UNSUPPORTED;
877
    }
878
 
879
    return status;
880
}
881
 
882
/* Generic #cairo_pattern_t -> CGPattern function */
883
 
884
typedef struct {
885
    CGImageRef image;
886
    CGRect imageBounds;
887
    cairo_bool_t do_reflect;
888
} SurfacePatternDrawInfo;
889
 
890
static void
891
SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
892
{
893
    SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
894
 
895
    CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
896
    CGContextScaleCTM (context, 1, -1);
897
 
898
    CGContextDrawImage (context, info->imageBounds, info->image);
899
    if (info->do_reflect) {
900
	/* draw 3 more copies of the image, flipped.
901
	 * DrawImage draws the image according to the current Y-direction into the rectangle given
902
	 * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
903
	 * of the base image position, and the Y axis is extending upwards.
904
	 */
905
 
906
	/* Make the y axis extend downwards, and draw a flipped image below */
907
	CGContextScaleCTM (context, 1, -1);
908
	CGContextDrawImage (context, info->imageBounds, info->image);
909
 
910
	/* Shift over to the right, and flip vertically (translation is 2x,
911
	 * since we'll be flipping and thus rendering the rectangle "backwards"
912
	 */
913
	CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
914
	CGContextScaleCTM (context, -1, 1);
915
	CGContextDrawImage (context, info->imageBounds, info->image);
916
 
917
	/* Then unflip the Y-axis again, and draw the image above the point. */
918
	CGContextScaleCTM (context, 1, -1);
919
	CGContextDrawImage (context, info->imageBounds, info->image);
920
    }
921
}
922
 
923
static void
924
SurfacePatternReleaseInfoFunc (void *ainfo)
925
{
926
    SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
927
 
928
    CGImageRelease (info->image);
929
    free (info);
930
}
931
 
932
static cairo_int_status_t
933
_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
934
							 const cairo_pattern_t *apattern,
935
							 const cairo_clip_t *clip,
936
							 CGPatternRef *cgpat)
937
{
938
    cairo_surface_pattern_t *spattern;
939
    cairo_surface_t *pat_surf;
940
    cairo_rectangle_int_t extents;
941
    cairo_format_t format = _cairo_format_from_content (dest->base.content);
942
 
943
    CGImageRef image;
944
    CGRect pbounds;
945
    CGAffineTransform ptransform, stransform;
946
    CGPatternCallbacks cb = { 0,
947
			      SurfacePatternDrawFunc,
948
			      SurfacePatternReleaseInfoFunc };
949
    SurfacePatternDrawInfo *info;
950
    cairo_quartz_float_t rw, rh;
951
    cairo_status_t status;
952
    cairo_bool_t is_bounded;
953
 
954
    cairo_matrix_t m;
955
 
956
    /* SURFACE is the only type we'll handle here */
957
    assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
958
 
959
    spattern = (cairo_surface_pattern_t *) apattern;
960
    pat_surf = spattern->surface;
961
 
962
    if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
963
	is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
964
	assert (is_bounded);
965
    }
966
    else
967
	_cairo_surface_get_extents (&dest->base, &extents);
968
 
969
    m = spattern->base.matrix;
970
    status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
971
					&m, clip, &image);
972
    if (unlikely (status))
973
	return status;
974
 
975
    info = malloc (sizeof (SurfacePatternDrawInfo));
976
    if (unlikely (!info))
977
	return CAIRO_STATUS_NO_MEMORY;
978
 
979
    /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
980
     * that the data will stick around for this image when the printer gets to it.
981
     * Otherwise, the underlying data store may disappear from under us!
982
     *
983
     * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
984
     * since the Quartz surfaces have a higher chance of sticking around.  If the
985
     * source is a quartz image surface, then it's set up to retain a ref to the
986
     * image surface that it's backed by.
987
     */
988
    info->image = image;
989
    info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
990
    info->do_reflect = FALSE;
991
 
992
    pbounds.origin.x = 0;
993
    pbounds.origin.y = 0;
994
 
995
    if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
996
	pbounds.size.width = 2.0 * extents.width;
997
	pbounds.size.height = 2.0 * extents.height;
998
	info->do_reflect = TRUE;
999
    } else {
1000
	pbounds.size.width = extents.width;
1001
	pbounds.size.height = extents.height;
1002
    }
1003
    rw = pbounds.size.width;
1004
    rh = pbounds.size.height;
1005
 
1006
    cairo_matrix_invert (&m);
1007
    _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
1008
 
1009
    /* The pattern matrix is relative to the bottom left, again; the
1010
     * incoming cairo pattern matrix is relative to the upper left.
1011
     * So we take the pattern matrix and the original context matrix,
1012
     * which gives us the correct base translation/y flip.
1013
     */
1014
    ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
1015
 
1016
#ifdef QUARTZ_DEBUG
1017
    ND ((stderr, "  pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
1018
    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));
1019
    CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
1020
    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));
1021
#endif
1022
 
1023
    *cgpat = CGPatternCreate (info,
1024
			      pbounds,
1025
			      ptransform,
1026
			      rw, rh,
1027
			      kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
1028
			      TRUE,
1029
			      &cb);
1030
 
1031
    return CAIRO_STATUS_SUCCESS;
1032
}
1033
 
1034
/* State used during a drawing operation. */
1035
typedef struct {
1036
    /* The destination of the mask */
1037
    CGContextRef cgMaskContext;
1038
 
1039
    /* The destination of the drawing of the source */
1040
    CGContextRef cgDrawContext;
1041
 
1042
    /* The filter to be used when drawing the source */
1043
    CGInterpolationQuality filter;
1044
 
1045
    /* Action type */
1046
    cairo_quartz_action_t action;
1047
 
1048
    /* Destination rect */
1049
    CGRect rect;
1050
 
1051
    /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
1052
    CGAffineTransform transform;
1053
 
1054
    /* Used with DO_IMAGE and DO_TILED_IMAGE */
1055
    CGImageRef image;
1056
 
1057
    /* Used with DO_SHADING */
1058
    CGShadingRef shading;
1059
 
1060
    /* Temporary destination for unbounded operations */
1061
    CGLayerRef layer;
1062
    CGRect clipRect;
1063
} cairo_quartz_drawing_state_t;
1064
 
1065
/*
1066
Quartz does not support repeating radients. We handle repeating gradients
1067
by manually extending the gradient and repeating color stops. We need to
1068
minimize the number of repetitions since Quartz seems to sample our color
1069
function across the entire range, even if part of that range is not needed
1070
for the visible area of the gradient, and it samples with some fixed resolution,
1071
so if the gradient range is too large it samples with very low resolution and
1072
the gradient is very coarse. _cairo_quartz_create_gradient_function computes
1073
the number of repetitions needed based on the extents.
1074
*/
1075
static cairo_int_status_t
1076
_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
1077
				     const cairo_gradient_pattern_t *gradient,
1078
				     const cairo_rectangle_int_t *extents)
1079
{
1080
    cairo_matrix_t mat;
1081
    cairo_circle_double_t start, end;
1082
    CGFunctionRef gradFunc;
1083
    CGColorSpaceRef rgb;
1084
    bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
1085
 
1086
    assert (gradient->n_stops > 0);
1087
 
1088
    mat = gradient->base.matrix;
1089
    cairo_matrix_invert (&mat);
1090
    _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
1091
 
1092
    gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
1093
						  &start, &end);
1094
 
1095
    if (unlikely (gradFunc == NULL))
1096
	return CAIRO_INT_STATUS_UNSUPPORTED;
1097
 
1098
    rgb = CGColorSpaceCreateDeviceRGB ();
1099
 
1100
    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
1101
	state->shading = CGShadingCreateAxial (rgb,
1102
					       CGPointMake (start.center.x,
1103
							    start.center.y),
1104
					       CGPointMake (end.center.x,
1105
							    end.center.y),
1106
					       gradFunc,
1107
					       extend, extend);
1108
    } else {
1109
	state->shading = CGShadingCreateRadial (rgb,
1110
						CGPointMake (start.center.x,
1111
							     start.center.y),
1112
						MAX (start.radius, 0),
1113
						CGPointMake (end.center.x,
1114
							     end.center.y),
1115
						MAX (end.radius, 0),
1116
						gradFunc,
1117
						extend, extend);
1118
    }
1119
 
1120
    CGColorSpaceRelease (rgb);
1121
    CGFunctionRelease (gradFunc);
1122
 
1123
    state->action = DO_SHADING;
1124
    return CAIRO_STATUS_SUCCESS;
1125
}
1126
 
1127
static cairo_int_status_t
1128
_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
1129
			   cairo_composite_rectangles_t *composite)
1130
{
1131
    cairo_quartz_surface_t       *surface = (cairo_quartz_surface_t *) composite->surface;
1132
    cairo_operator_t              op = composite->op;
1133
    const cairo_pattern_t        *source = &composite->source_pattern.base;
1134
    const cairo_clip_t           *clip = composite->clip;
1135
    cairo_bool_t needs_temp;
1136
    cairo_status_t status;
1137
    cairo_format_t format = _cairo_format_from_content (composite->surface->content);
1138
 
1139
    state->layer = NULL;
1140
    state->image = NULL;
1141
    state->shading = NULL;
1142
    state->cgDrawContext = NULL;
1143
    state->cgMaskContext = NULL;
1144
 
1145
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1146
    if (unlikely (status))
1147
	return status;
1148
 
1149
    status = _cairo_quartz_surface_set_cairo_operator (surface, op);
1150
    if (unlikely (status))
1151
	return status;
1152
 
1153
    /* Save before we change the pattern, colorspace, etc. so that
1154
     * we can restore and make sure that quartz releases our
1155
     * pattern (which may be stack allocated)
1156
     */
1157
 
1158
    CGContextSaveGState (surface->cgContext);
1159
    state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
1160
    state->clipRect = CGRectIntegral (state->clipRect);
1161
    state->rect = state->clipRect;
1162
 
1163
    state->cgMaskContext = surface->cgContext;
1164
    state->cgDrawContext = state->cgMaskContext;
1165
 
1166
    state->filter = _cairo_quartz_filter_to_quartz (source->filter);
1167
 
1168
    if (op == CAIRO_OPERATOR_CLEAR) {
1169
	CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1170
 
1171
	state->action = DO_DIRECT;
1172
	return CAIRO_STATUS_SUCCESS;
1173
    }
1174
 
1175
    /*
1176
     * To implement mask unbounded operations Quartz needs a temporary
1177
     * surface which will be composited entirely (ignoring the mask).
1178
     * To implement source unbounded operations Quartz needs a
1179
     * temporary surface which allows extending the source to a size
1180
     * covering the whole mask, but there are some optimization
1181
     * opportunities:
1182
     *
1183
     * - CLEAR completely ignores the source, thus we can just use a
1184
     *   solid color fill.
1185
     *
1186
     * - SOURCE can be implemented by drawing the source and clearing
1187
     *   outside of the source as long as the two regions have no
1188
     *   intersection. This happens when the source is a pixel-aligned
1189
     *   rectangle. If the source is at least as big as the
1190
     *   intersection between the clip rectangle and the mask
1191
     *   rectangle, no clear operation is needed.
1192
     */
1193
    needs_temp = ! _cairo_operator_bounded_by_mask (op);
1194
 
1195
    if (needs_temp) {
1196
	state->layer = CGLayerCreateWithContext (surface->cgContext,
1197
						 state->clipRect.size,
1198
						 NULL);
1199
	state->cgDrawContext = CGLayerGetContext (state->layer);
1200
	state->cgMaskContext = state->cgDrawContext;
1201
	CGContextTranslateCTM (state->cgDrawContext,
1202
			       -state->clipRect.origin.x,
1203
			       -state->clipRect.origin.y);
1204
    }
1205
 
1206
    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1207
	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1208
 
1209
	CGContextSetRGBStrokeColor (state->cgDrawContext,
1210
				    solid->color.red,
1211
				    solid->color.green,
1212
				    solid->color.blue,
1213
				    solid->color.alpha);
1214
	CGContextSetRGBFillColor (state->cgDrawContext,
1215
				  solid->color.red,
1216
				  solid->color.green,
1217
				  solid->color.blue,
1218
				  solid->color.alpha);
1219
 
1220
	state->action = DO_DIRECT;
1221
	return CAIRO_STATUS_SUCCESS;
1222
    }
1223
 
1224
    if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
1225
	source->type == CAIRO_PATTERN_TYPE_RADIAL)
1226
    {
1227
	const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
1228
	cairo_rectangle_int_t extents;
1229
 
1230
	extents = surface->virtual_extents;
1231
	extents.x -= surface->base.device_transform.x0;
1232
	extents.y -= surface->base.device_transform.y0;
1233
	_cairo_rectangle_union (&extents, &surface->extents);
1234
 
1235
	return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
1236
    }
1237
 
1238
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1239
	(source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
1240
    {
1241
	const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
1242
	cairo_surface_t *pat_surf = spat->surface;
1243
	CGImageRef img;
1244
	cairo_matrix_t m = spat->base.matrix;
1245
	cairo_rectangle_int_t extents;
1246
	CGAffineTransform xform;
1247
	CGRect srcRect;
1248
	cairo_fixed_t fw, fh;
1249
	cairo_bool_t is_bounded;
1250
 
1251
	_cairo_surface_get_extents (composite->surface, &extents);
1252
	status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
1253
					    &m, clip, &img);
1254
	if (unlikely (status))
1255
	    return status;
1256
 
1257
	state->image = img;
1258
 
1259
	if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
1260
	    m.x0 = -ceil (m.x0 - 0.5);
1261
	    m.y0 = -ceil (m.y0 - 0.5);
1262
	} else {
1263
	    cairo_matrix_invert (&m);
1264
	}
1265
 
1266
	_cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
1267
 
1268
	if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
1269
	    is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
1270
	    assert (is_bounded);
1271
	}
1272
 
1273
	srcRect = CGRectMake (0, 0, extents.width, extents.height);
1274
 
1275
	if (source->extend == CAIRO_EXTEND_NONE) {
1276
	    int x, y;
1277
	    if (op == CAIRO_OPERATOR_SOURCE &&
1278
		(pat_surf->content == CAIRO_CONTENT_ALPHA ||
1279
		 ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
1280
	    {
1281
		state->layer = CGLayerCreateWithContext (surface->cgContext,
1282
							 state->clipRect.size,
1283
							 NULL);
1284
		state->cgDrawContext = CGLayerGetContext (state->layer);
1285
		CGContextTranslateCTM (state->cgDrawContext,
1286
				       -state->clipRect.origin.x,
1287
				       -state->clipRect.origin.y);
1288
	    }
1289
 
1290
	    CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1291
 
1292
	    state->rect = srcRect;
1293
	    state->action = DO_IMAGE;
1294
	    return CAIRO_STATUS_SUCCESS;
1295
	}
1296
 
1297
	CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1298
 
1299
	/* Quartz seems to tile images at pixel-aligned regions only -- this
1300
	 * leads to seams if the image doesn't end up scaling to fill the
1301
	 * space exactly.  The CGPattern tiling approach doesn't have this
1302
	 * problem.  Check if we're going to fill up the space (within some
1303
	 * epsilon), and if not, fall back to the CGPattern type.
1304
	 */
1305
 
1306
	xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
1307
					 state->transform);
1308
 
1309
	srcRect = CGRectApplyAffineTransform (srcRect, xform);
1310
 
1311
	fw = _cairo_fixed_from_double (srcRect.size.width);
1312
	fh = _cairo_fixed_from_double (srcRect.size.height);
1313
 
1314
	if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
1315
	    (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
1316
	{
1317
	    /* We're good to use DrawTiledImage, but ensure that
1318
	     * the math works out */
1319
 
1320
	    srcRect.size.width = round (srcRect.size.width);
1321
	    srcRect.size.height = round (srcRect.size.height);
1322
 
1323
	    xform = CGAffineTransformInvert (xform);
1324
 
1325
	    srcRect = CGRectApplyAffineTransform (srcRect, xform);
1326
 
1327
	    state->rect = srcRect;
1328
	    state->action = DO_TILED_IMAGE;
1329
	    return CAIRO_STATUS_SUCCESS;
1330
	}
1331
 
1332
	/* Fall through to generic SURFACE case */
1333
    }
1334
 
1335
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1336
	cairo_quartz_float_t patternAlpha = 1.0f;
1337
	CGColorSpaceRef patternSpace;
1338
	CGPatternRef pattern = NULL;
1339
	cairo_int_status_t status;
1340
 
1341
	status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern);
1342
	if (unlikely (status))
1343
	    return status;
1344
 
1345
	patternSpace = CGColorSpaceCreatePattern (NULL);
1346
	CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
1347
	CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
1348
	CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
1349
	CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
1350
	CGColorSpaceRelease (patternSpace);
1351
 
1352
	/* Quartz likes to munge the pattern phase (as yet unexplained
1353
	 * why); force it to 0,0 as we've already baked in the correct
1354
	 * pattern translation into the pattern matrix
1355
	 */
1356
	CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
1357
 
1358
	CGPatternRelease (pattern);
1359
 
1360
	state->action = DO_DIRECT;
1361
	return CAIRO_STATUS_SUCCESS;
1362
    }
1363
 
1364
    return CAIRO_INT_STATUS_UNSUPPORTED;
1365
}
1366
 
1367
static void
1368
_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
1369
			      cairo_composite_rectangles_t *extents)
1370
{
1371
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
1372
 
1373
    if (state->layer) {
1374
	CGContextDrawLayerInRect (surface->cgContext,
1375
				  state->clipRect,
1376
				  state->layer);
1377
	CGLayerRelease (state->layer);
1378
    }
1379
 
1380
    if (state->cgMaskContext)
1381
	CGContextRestoreGState (surface->cgContext);
1382
 
1383
    if (state->image)
1384
	CGImageRelease (state->image);
1385
 
1386
    if (state->shading)
1387
	CGShadingRelease (state->shading);
1388
}
1389
 
1390
static void
1391
_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
1392
			   cairo_operator_t              op)
1393
{
1394
    CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
1395
    CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
1396
 
1397
    if (state->action == DO_DIRECT) {
1398
	CGContextFillRect (state->cgDrawContext, state->rect);
1399
	return;
1400
    }
1401
 
1402
    CGContextConcatCTM (state->cgDrawContext, state->transform);
1403
 
1404
    if (state->action == DO_SHADING) {
1405
	CGContextDrawShading (state->cgDrawContext, state->shading);
1406
	return;
1407
    }
1408
 
1409
    CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1410
    CGContextScaleCTM (state->cgDrawContext, 1, -1);
1411
 
1412
    if (state->action == DO_IMAGE) {
1413
	CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
1414
	if (op == CAIRO_OPERATOR_SOURCE &&
1415
	    state->cgDrawContext == state->cgMaskContext)
1416
	{
1417
	    CGContextBeginPath (state->cgDrawContext);
1418
	    CGContextAddRect (state->cgDrawContext, state->rect);
1419
 
1420
	    CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1421
	    CGContextScaleCTM (state->cgDrawContext, 1, -1);
1422
	    CGContextConcatCTM (state->cgDrawContext,
1423
				CGAffineTransformInvert (state->transform));
1424
 
1425
	    CGContextAddRect (state->cgDrawContext, state->clipRect);
1426
 
1427
	    CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
1428
	    CGContextEOFillPath (state->cgDrawContext);
1429
	}
1430
    } else {
1431
	CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
1432
    }
1433
}
1434
 
1435
static cairo_image_surface_t *
1436
_cairo_quartz_surface_map_to_image (void *abstract_surface,
1437
				    const cairo_rectangle_int_t *extents)
1438
{
1439
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1440
    unsigned int stride, bitinfo, bpp, color_comps;
1441
    CGColorSpaceRef colorspace;
1442
    void *imageData;
1443
    cairo_format_t format;
1444
 
1445
    if (surface->imageSurfaceEquiv)
1446
	return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents);
1447
 
1448
    if (IS_EMPTY (surface))
1449
	return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
1450
 
1451
    if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
1452
	return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1453
 
1454
    bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
1455
    bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
1456
 
1457
    // let's hope they don't add YUV under us
1458
    colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
1459
    color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
1460
 
1461
    /* XXX TODO: We can handle many more data formats by
1462
     * converting to pixman_format_t */
1463
 
1464
    if (bpp == 32 && color_comps == 3 &&
1465
	(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
1466
	(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1467
    {
1468
	format = CAIRO_FORMAT_ARGB32;
1469
    }
1470
    else if (bpp == 32 && color_comps == 3 &&
1471
	     (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
1472
	     (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1473
    {
1474
	format = CAIRO_FORMAT_RGB24;
1475
    }
1476
    else if (bpp == 8 && color_comps == 1)
1477
    {
1478
	format = CAIRO_FORMAT_A1;
1479
    }
1480
    else
1481
    {
1482
	return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1483
    }
1484
 
1485
    imageData = CGBitmapContextGetData (surface->cgContext);
1486
    stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
1487
 
1488
    return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData,
1489
									  format,
1490
									  extents->width,
1491
									  extents->height,
1492
									  stride);
1493
}
1494
 
1495
static cairo_int_status_t
1496
_cairo_quartz_surface_unmap_image (void *abstract_surface,
1497
				   cairo_image_surface_t *image)
1498
{
1499
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1500
 
1501
    if (surface->imageSurfaceEquiv)
1502
	return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image);
1503
 
1504
    cairo_surface_finish (&image->base);
1505
    cairo_surface_destroy (&image->base);
1506
 
1507
    return CAIRO_STATUS_SUCCESS;
1508
}
1509
 
1510
 
1511
/*
1512
 * Cairo surface backend implementations
1513
 */
1514
 
1515
static cairo_status_t
1516
_cairo_quartz_surface_finish (void *abstract_surface)
1517
{
1518
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1519
 
1520
    ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
1521
 
1522
    if (IS_EMPTY (surface))
1523
	return CAIRO_STATUS_SUCCESS;
1524
 
1525
    /* Restore our saved gstate that we use to reset clipping */
1526
    CGContextRestoreGState (surface->cgContext);
1527
    _cairo_surface_clipper_reset (&surface->clipper);
1528
 
1529
    CGContextRelease (surface->cgContext);
1530
 
1531
    surface->cgContext = NULL;
1532
 
1533
    if (surface->imageSurfaceEquiv) {
1534
	cairo_surface_destroy (surface->imageSurfaceEquiv);
1535
	surface->imageSurfaceEquiv = NULL;
1536
    }
1537
 
1538
    free (surface->imageData);
1539
    surface->imageData = NULL;
1540
 
1541
    return CAIRO_STATUS_SUCCESS;
1542
}
1543
 
1544
static cairo_status_t
1545
_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1546
					     cairo_image_surface_t **image_out,
1547
					     void **image_extra)
1548
{
1549
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1550
 
1551
    //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1552
 
1553
    *image_extra = NULL;
1554
 
1555
    *image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents);
1556
    if (unlikely (cairo_surface_status(&(*image_out)->base))) {
1557
	cairo_surface_destroy (&(*image_out)->base);
1558
	*image_out = NULL;
1559
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1560
    }
1561
 
1562
    return CAIRO_STATUS_SUCCESS;
1563
}
1564
 
1565
static void
1566
_cairo_quartz_surface_release_source_image (void *abstract_surface,
1567
					    cairo_image_surface_t *image,
1568
					    void *image_extra)
1569
{
1570
    _cairo_quartz_surface_unmap_image (abstract_surface, image);
1571
}
1572
 
1573
static cairo_surface_t *
1574
_cairo_quartz_surface_create_similar (void *abstract_surface,
1575
				      cairo_content_t content,
1576
				      int width,
1577
				      int height)
1578
{
1579
    cairo_quartz_surface_t *surface, *similar_quartz;
1580
    cairo_surface_t *similar;
1581
    cairo_format_t format;
1582
 
1583
    if (content == CAIRO_CONTENT_COLOR_ALPHA)
1584
	format = CAIRO_FORMAT_ARGB32;
1585
    else if (content == CAIRO_CONTENT_COLOR)
1586
	format = CAIRO_FORMAT_RGB24;
1587
    else if (content == CAIRO_CONTENT_ALPHA)
1588
	format = CAIRO_FORMAT_A8;
1589
    else
1590
	return NULL;
1591
 
1592
    // verify width and height of surface
1593
    if (!_cairo_quartz_verify_surface_size (width, height)) {
1594
	return _cairo_surface_create_in_error (_cairo_error
1595
					       (CAIRO_STATUS_INVALID_SIZE));
1596
    }
1597
 
1598
    similar = cairo_quartz_surface_create (format, width, height);
1599
    if (unlikely (similar->status))
1600
	return similar;
1601
 
1602
    surface = (cairo_quartz_surface_t *) abstract_surface;
1603
    similar_quartz = (cairo_quartz_surface_t *) similar;
1604
    similar_quartz->virtual_extents = surface->virtual_extents;
1605
 
1606
    return similar;
1607
}
1608
 
1609
static cairo_bool_t
1610
_cairo_quartz_surface_get_extents (void *abstract_surface,
1611
				   cairo_rectangle_int_t *extents)
1612
{
1613
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1614
 
1615
    *extents = surface->extents;
1616
    return TRUE;
1617
}
1618
 
1619
static cairo_int_status_t
1620
_cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
1621
			cairo_composite_rectangles_t *extents)
1622
{
1623
    cairo_quartz_drawing_state_t state;
1624
    cairo_int_status_t rv;
1625
 
1626
    ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
1627
	 extents->surface, extents->op, extents->source_pattern.base.type));
1628
 
1629
    rv = _cairo_quartz_setup_state (&state, extents);
1630
    if (unlikely (rv))
1631
	goto BAIL;
1632
 
1633
    _cairo_quartz_draw_source (&state, extents->op);
1634
 
1635
BAIL:
1636
    _cairo_quartz_teardown_state (&state, extents);
1637
 
1638
    ND ((stderr, "-- paint\n"));
1639
    return rv;
1640
}
1641
 
1642
static cairo_int_status_t
1643
_cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
1644
				    cairo_surface_t              *mask_surf,
1645
				    const cairo_matrix_t         *mask_mat,
1646
				    CGInterpolationQuality        filter)
1647
{
1648
    CGRect rect;
1649
    CGImageRef img;
1650
    cairo_status_t status;
1651
    CGAffineTransform mask_matrix;
1652
    cairo_quartz_drawing_state_t state;
1653
    cairo_format_t format = _cairo_format_from_content (extents->surface->content);
1654
    cairo_rectangle_int_t dest_extents;
1655
    cairo_matrix_t m = *mask_mat;
1656
 
1657
    _cairo_surface_get_extents (extents->surface, &dest_extents);
1658
    status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format,
1659
					&m, extents->clip, &img);
1660
    if (unlikely (status))
1661
	return status;
1662
 
1663
    status = _cairo_quartz_setup_state (&state, extents);
1664
    if (unlikely (status))
1665
	goto BAIL;
1666
 
1667
    rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
1668
    _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix);
1669
 
1670
    /* ClipToMask is essentially drawing an image, so we need to flip the CTM
1671
     * to get the image to appear oriented the right way */
1672
    CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
1673
    CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
1674
    CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1675
 
1676
    state.filter = filter;
1677
 
1678
    CGContextSetInterpolationQuality (state.cgMaskContext, filter);
1679
    CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
1680
 
1681
    CGContextClipToMask (state.cgMaskContext, rect, img);
1682
 
1683
    CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1684
    CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
1685
    CGContextConcatCTM (state.cgMaskContext, mask_matrix);
1686
 
1687
    _cairo_quartz_draw_source (&state, extents->op);
1688
 
1689
BAIL:
1690
    _cairo_quartz_teardown_state (&state, extents);
1691
 
1692
    CGImageRelease (img);
1693
 
1694
    return status;
1695
}
1696
 
1697
static cairo_int_status_t
1698
_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
1699
				  cairo_composite_rectangles_t *extents)
1700
{
1701
    cairo_quartz_drawing_state_t state;
1702
    double alpha = extents->mask_pattern.solid.color.alpha;
1703
    cairo_status_t status;
1704
 
1705
    status = _cairo_quartz_setup_state (&state, extents);
1706
    if (unlikely (status))
1707
	return status;
1708
 
1709
    CGContextSetAlpha (surface->cgContext, alpha);
1710
    _cairo_quartz_draw_source (&state, extents->op);
1711
 
1712
    _cairo_quartz_teardown_state (&state, extents);
1713
 
1714
    return CAIRO_STATUS_SUCCESS;
1715
}
1716
 
1717
static cairo_int_status_t
1718
_cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
1719
		       cairo_composite_rectangles_t *extents)
1720
{
1721
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
1722
    const cairo_pattern_t *source = &extents->source_pattern.base;
1723
    const cairo_pattern_t *mask = &extents->mask_pattern.base;
1724
    cairo_surface_t *mask_surf;
1725
    cairo_matrix_t matrix;
1726
    cairo_status_t status;
1727
    cairo_bool_t need_temp;
1728
    CGInterpolationQuality filter;
1729
 
1730
    ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
1731
	 extents->surface, extents->op, extents->source_pattern.base.type,
1732
	 extents->mask_pattern.base.type));
1733
 
1734
    if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
1735
	return _cairo_quartz_cg_mask_with_solid (surface, extents);
1736
 
1737
    need_temp = (mask->type   != CAIRO_PATTERN_TYPE_SURFACE ||
1738
		 mask->extend != CAIRO_EXTEND_NONE);
1739
 
1740
    filter = _cairo_quartz_filter_to_quartz (source->filter);
1741
 
1742
    if (! need_temp) {
1743
	mask_surf = extents->mask_pattern.surface.surface;
1744
 
1745
	/* When an opaque surface used as a mask in Quartz, its
1746
	 * luminosity is used as the alpha value, so we con only use
1747
	 * surfaces with alpha without creating a temporary mask. */
1748
	need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
1749
    }
1750
 
1751
    if (! need_temp) {
1752
	CGInterpolationQuality mask_filter;
1753
	cairo_bool_t simple_transform;
1754
 
1755
	matrix = mask->matrix;
1756
 
1757
	mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
1758
	if (mask_filter == kCGInterpolationNone) {
1759
	    simple_transform = _cairo_matrix_is_translation (&matrix);
1760
	    if (simple_transform) {
1761
		matrix.x0 = ceil (matrix.x0 - 0.5);
1762
		matrix.y0 = ceil (matrix.y0 - 0.5);
1763
	    }
1764
	} else {
1765
	    simple_transform = _cairo_matrix_is_integer_translation (&matrix,
1766
								     NULL,
1767
								     NULL);
1768
	}
1769
 
1770
	/* Quartz only allows one interpolation to be set for mask and
1771
	 * source, so we can skip the temp surface only if the source
1772
	 * filtering makes the mask look correct. */
1773
	if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
1774
	    need_temp = ! (simple_transform || filter == mask_filter);
1775
	else
1776
	    filter = mask_filter;
1777
    }
1778
 
1779
    if (need_temp) {
1780
	/* Render the mask to a surface */
1781
	mask_surf = _cairo_quartz_surface_create_similar (surface,
1782
							  CAIRO_CONTENT_ALPHA,
1783
							  surface->extents.width,
1784
							  surface->extents.height);
1785
	status = mask_surf->status;
1786
	if (unlikely (status))
1787
	    goto BAIL;
1788
 
1789
	/* mask_surf is clear, so use OVER instead of SOURCE to avoid a
1790
	 * temporary layer or fallback to cairo-image. */
1791
	status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
1792
	if (unlikely (status))
1793
	    goto BAIL;
1794
 
1795
	cairo_matrix_init_identity (&matrix);
1796
    }
1797
 
1798
    status = _cairo_quartz_cg_mask_with_surface (extents,
1799
						 mask_surf, &matrix, filter);
1800
 
1801
BAIL:
1802
 
1803
    if (need_temp)
1804
	cairo_surface_destroy (mask_surf);
1805
 
1806
    return status;
1807
}
1808
 
1809
static cairo_int_status_t
1810
_cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
1811
		       cairo_composite_rectangles_t *extents,
1812
		       const cairo_path_fixed_t *path,
1813
		       cairo_fill_rule_t fill_rule,
1814
		       double tolerance,
1815
		       cairo_antialias_t antialias)
1816
{
1817
    cairo_quartz_drawing_state_t state;
1818
    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1819
 
1820
    ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
1821
	 extents->surface, extents->op, extents->source_pattern.base.type));
1822
 
1823
    rv = _cairo_quartz_setup_state (&state, extents);
1824
    if (unlikely (rv))
1825
	goto BAIL;
1826
 
1827
    CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1828
 
1829
    _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1830
 
1831
    if (state.action == DO_DIRECT) {
1832
	assert (state.cgDrawContext == state.cgMaskContext);
1833
	if (fill_rule == CAIRO_FILL_RULE_WINDING)
1834
	    CGContextFillPath (state.cgMaskContext);
1835
	else
1836
	    CGContextEOFillPath (state.cgMaskContext);
1837
    } else {
1838
	if (fill_rule == CAIRO_FILL_RULE_WINDING)
1839
	    CGContextClip (state.cgMaskContext);
1840
	else
1841
	    CGContextEOClip (state.cgMaskContext);
1842
 
1843
	_cairo_quartz_draw_source (&state, extents->op);
1844
    }
1845
 
1846
BAIL:
1847
    _cairo_quartz_teardown_state (&state, extents);
1848
 
1849
    ND ((stderr, "-- fill\n"));
1850
    return rv;
1851
}
1852
 
1853
static cairo_int_status_t
1854
_cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
1855
			 cairo_composite_rectangles_t *extents,
1856
			 const cairo_path_fixed_t *path,
1857
			 const cairo_stroke_style_t *style,
1858
			 const cairo_matrix_t *ctm,
1859
			 const cairo_matrix_t *ctm_inverse,
1860
			 double tolerance,
1861
			 cairo_antialias_t antialias)
1862
{
1863
    cairo_quartz_drawing_state_t state;
1864
    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1865
    CGAffineTransform strokeTransform, invStrokeTransform;
1866
 
1867
    ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
1868
	 extents->surface, extents->op, extents->source_pattern.base.type));
1869
 
1870
    rv = _cairo_quartz_setup_state (&state, extents);
1871
    if (unlikely (rv))
1872
	goto BAIL;
1873
 
1874
    // Turning antialiasing off used to cause misrendering with
1875
    // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1876
    // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1877
    CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1878
    CGContextSetLineWidth (state.cgMaskContext, style->line_width);
1879
    CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1880
    CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1881
    CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
1882
 
1883
    if (style->dash && style->num_dashes) {
1884
	cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
1885
	cairo_quartz_float_t *fdash = sdash;
1886
	unsigned int max_dashes = style->num_dashes;
1887
	unsigned int k;
1888
 
1889
	if (style->num_dashes%2)
1890
	    max_dashes *= 2;
1891
	if (max_dashes > ARRAY_LENGTH (sdash))
1892
	    fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
1893
	if (unlikely (fdash == NULL)) {
1894
	    rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1895
	    goto BAIL;
1896
	}
1897
 
1898
	for (k = 0; k < max_dashes; k++)
1899
	    fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
1900
 
1901
	CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
1902
	if (fdash != sdash)
1903
	    free (fdash);
1904
    } else
1905
	CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
1906
 
1907
    _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1908
 
1909
    _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1910
    CGContextConcatCTM (state.cgMaskContext, strokeTransform);
1911
 
1912
    if (state.action == DO_DIRECT) {
1913
	assert (state.cgDrawContext == state.cgMaskContext);
1914
	CGContextStrokePath (state.cgMaskContext);
1915
    } else {
1916
	CGContextReplacePathWithStrokedPath (state.cgMaskContext);
1917
	CGContextClip (state.cgMaskContext);
1918
 
1919
	_cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
1920
	CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
1921
 
1922
	_cairo_quartz_draw_source (&state, extents->op);
1923
    }
1924
 
1925
BAIL:
1926
    _cairo_quartz_teardown_state (&state, extents);
1927
 
1928
    ND ((stderr, "-- stroke\n"));
1929
    return rv;
1930
}
1931
 
1932
#if CAIRO_HAS_QUARTZ_FONT
1933
static cairo_int_status_t
1934
_cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
1935
			 cairo_composite_rectangles_t *extents,
1936
			 cairo_scaled_font_t *scaled_font,
1937
			 cairo_glyph_t *glyphs,
1938
			 int num_glyphs,
1939
			 cairo_bool_t overlap)
1940
{
1941
    CGAffineTransform textTransform, invTextTransform;
1942
    CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1943
    CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1944
    CGGlyph *cg_glyphs = &glyphs_static[0];
1945
    CGSize *cg_advances = &cg_advances_static[0];
1946
    COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
1947
 
1948
    cairo_quartz_drawing_state_t state;
1949
    cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
1950
    cairo_quartz_float_t xprev, yprev;
1951
    int i;
1952
    CGFontRef cgfref = NULL;
1953
 
1954
    cairo_bool_t didForceFontSmoothing = FALSE;
1955
 
1956
    if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
1957
	return CAIRO_INT_STATUS_UNSUPPORTED;
1958
 
1959
    rv = _cairo_quartz_setup_state (&state, extents);
1960
    if (unlikely (rv))
1961
	goto BAIL;
1962
 
1963
    if (state.action == DO_DIRECT) {
1964
	assert (state.cgDrawContext == state.cgMaskContext);
1965
	CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
1966
    } else {
1967
	CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
1968
    }
1969
 
1970
    /* this doesn't addref */
1971
    cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
1972
    CGContextSetFont (state.cgMaskContext, cgfref);
1973
    CGContextSetFontSize (state.cgMaskContext, 1.0);
1974
 
1975
    switch (scaled_font->options.antialias) {
1976
	case CAIRO_ANTIALIAS_SUBPIXEL:
1977
	case CAIRO_ANTIALIAS_BEST:
1978
	    CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1979
	    CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
1980
	    if (CGContextSetAllowsFontSmoothingPtr &&
1981
		!CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
1982
	    {
1983
		didForceFontSmoothing = TRUE;
1984
		CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
1985
	    }
1986
	    break;
1987
	case CAIRO_ANTIALIAS_NONE:
1988
	    CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
1989
	    break;
1990
	case CAIRO_ANTIALIAS_GRAY:
1991
	case CAIRO_ANTIALIAS_GOOD:
1992
	case CAIRO_ANTIALIAS_FAST:
1993
	    CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1994
	    CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
1995
	    break;
1996
	case CAIRO_ANTIALIAS_DEFAULT:
1997
	    /* Don't do anything */
1998
	    break;
1999
    }
2000
 
2001
    if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
2002
	cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
2003
	if (unlikely (cg_glyphs == NULL)) {
2004
	    rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2005
	    goto BAIL;
2006
	}
2007
 
2008
	cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
2009
    }
2010
 
2011
    /* scale(1,-1) * scaled_font->scale */
2012
    textTransform = CGAffineTransformMake (scaled_font->scale.xx,
2013
					   scaled_font->scale.yx,
2014
					   -scaled_font->scale.xy,
2015
					   -scaled_font->scale.yy,
2016
					   0.0, 0.0);
2017
 
2018
    /* scaled_font->scale_inverse * scale(1,-1) */
2019
    invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
2020
					      -scaled_font->scale_inverse.yx,
2021
					      scaled_font->scale_inverse.xy,
2022
					      -scaled_font->scale_inverse.yy,
2023
					      0.0, 0.0);
2024
 
2025
    CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
2026
    CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
2027
 
2028
    /* Convert our glyph positions to glyph advances.  We need n-1 advances,
2029
     * since the advance at index 0 is applied after glyph 0. */
2030
    xprev = glyphs[0].x;
2031
    yprev = glyphs[0].y;
2032
 
2033
    cg_glyphs[0] = glyphs[0].index;
2034
 
2035
    for (i = 1; i < num_glyphs; i++) {
2036
	cairo_quartz_float_t xf = glyphs[i].x;
2037
	cairo_quartz_float_t yf = glyphs[i].y;
2038
	cg_glyphs[i] = glyphs[i].index;
2039
	cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
2040
	xprev = xf;
2041
	yprev = yf;
2042
    }
2043
 
2044
    /* Translate to the first glyph's position before drawing */
2045
    CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
2046
    CGContextConcatCTM (state.cgMaskContext, textTransform);
2047
 
2048
    CGContextShowGlyphsWithAdvances (state.cgMaskContext,
2049
				     cg_glyphs,
2050
				     cg_advances,
2051
				     num_glyphs);
2052
 
2053
    CGContextConcatCTM (state.cgMaskContext, invTextTransform);
2054
    CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
2055
 
2056
    if (state.action != DO_DIRECT)
2057
	_cairo_quartz_draw_source (&state, extents->op);
2058
 
2059
BAIL:
2060
    if (didForceFontSmoothing)
2061
	CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
2062
 
2063
    _cairo_quartz_teardown_state (&state, extents);
2064
 
2065
    if (cg_glyphs != glyphs_static)
2066
	free (cg_glyphs);
2067
 
2068
    return rv;
2069
}
2070
#endif /* CAIRO_HAS_QUARTZ_FONT */
2071
 
2072
static const cairo_compositor_t _cairo_quartz_cg_compositor = {
2073
    &_cairo_fallback_compositor,
2074
 
2075
    _cairo_quartz_cg_paint,
2076
    _cairo_quartz_cg_mask,
2077
    _cairo_quartz_cg_stroke,
2078
    _cairo_quartz_cg_fill,
2079
#if CAIRO_HAS_QUARTZ_FONT
2080
    _cairo_quartz_cg_glyphs,
2081
#else
2082
    NULL,
2083
#endif
2084
};
2085
 
2086
static cairo_int_status_t
2087
_cairo_quartz_surface_paint (void *surface,
2088
			     cairo_operator_t op,
2089
			     const cairo_pattern_t *source,
2090
			     const cairo_clip_t *clip)
2091
{
2092
    return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
2093
				    surface, op, source, clip);
2094
}
2095
 
2096
static cairo_int_status_t
2097
_cairo_quartz_surface_mask (void *surface,
2098
			    cairo_operator_t op,
2099
			    const cairo_pattern_t *source,
2100
			    const cairo_pattern_t *mask,
2101
			    const cairo_clip_t *clip)
2102
{
2103
    return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
2104
				   surface, op, source, mask,
2105
				   clip);
2106
}
2107
 
2108
static cairo_int_status_t
2109
_cairo_quartz_surface_fill (void *surface,
2110
			    cairo_operator_t op,
2111
			    const cairo_pattern_t *source,
2112
			    const cairo_path_fixed_t *path,
2113
			    cairo_fill_rule_t fill_rule,
2114
			    double tolerance,
2115
			    cairo_antialias_t antialias,
2116
			    const cairo_clip_t *clip)
2117
{
2118
    return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
2119
				   surface, op, source, path,
2120
				   fill_rule, tolerance, antialias,
2121
				   clip);
2122
}
2123
 
2124
static cairo_int_status_t
2125
_cairo_quartz_surface_stroke (void *surface,
2126
			      cairo_operator_t op,
2127
			      const cairo_pattern_t *source,
2128
			      const cairo_path_fixed_t *path,
2129
			      const cairo_stroke_style_t *style,
2130
			      const cairo_matrix_t *ctm,
2131
			      const cairo_matrix_t *ctm_inverse,
2132
			      double tolerance,
2133
			      cairo_antialias_t antialias,
2134
			      const cairo_clip_t *clip)
2135
{
2136
    return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
2137
				     surface, op, source, path,
2138
				     style, ctm,ctm_inverse,
2139
				     tolerance, antialias, clip);
2140
}
2141
 
2142
static cairo_int_status_t
2143
_cairo_quartz_surface_glyphs (void *surface,
2144
			      cairo_operator_t op,
2145
			      const cairo_pattern_t *source,
2146
			      cairo_glyph_t *glyphs,
2147
			      int num_glyphs,
2148
			      cairo_scaled_font_t *scaled_font,
2149
			      const cairo_clip_t *clip)
2150
{
2151
    return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
2152
				     surface, op, source,
2153
				     glyphs, num_glyphs, scaled_font,
2154
				     clip);
2155
}
2156
 
2157
static cairo_status_t
2158
_cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
2159
						   cairo_path_fixed_t *path,
2160
						   cairo_fill_rule_t fill_rule,
2161
						   double tolerance,
2162
						   cairo_antialias_t antialias)
2163
{
2164
    cairo_quartz_surface_t *surface =
2165
	cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
2166
 
2167
    ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
2168
 
2169
    if (IS_EMPTY (surface))
2170
	return CAIRO_STATUS_SUCCESS;
2171
 
2172
    if (path == NULL) {
2173
	/* If we're being asked to reset the clip, we can only do it
2174
	 * by restoring the gstate to our previous saved one, and
2175
	 * saving it again.
2176
	 *
2177
	 * Note that this assumes that ALL quartz surface creation
2178
	 * functions will do a SaveGState first; we do this in create_internal.
2179
	 */
2180
	CGContextRestoreGState (surface->cgContext);
2181
	CGContextSaveGState (surface->cgContext);
2182
    } else {
2183
	CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
2184
 
2185
	_cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
2186
 
2187
	if (fill_rule == CAIRO_FILL_RULE_WINDING)
2188
	    CGContextClip (surface->cgContext);
2189
	else
2190
	    CGContextEOClip (surface->cgContext);
2191
    }
2192
 
2193
    ND ((stderr, "-- intersect_clip_path\n"));
2194
 
2195
    return CAIRO_STATUS_SUCCESS;
2196
}
2197
 
2198
// XXXtodo implement show_page; need to figure out how to handle begin/end
2199
 
2200
static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
2201
    CAIRO_SURFACE_TYPE_QUARTZ,
2202
    _cairo_quartz_surface_finish,
2203
 
2204
    _cairo_default_context_create,
2205
 
2206
    _cairo_quartz_surface_create_similar,
2207
    NULL, /* similar image */
2208
    _cairo_quartz_surface_map_to_image,
2209
    _cairo_quartz_surface_unmap_image,
2210
 
2211
    _cairo_surface_default_source,
2212
    _cairo_quartz_surface_acquire_source_image,
2213
    _cairo_quartz_surface_release_source_image,
2214
    NULL, /* snapshot */
2215
 
2216
    NULL, /* copy_page */
2217
    NULL, /* show_page */
2218
 
2219
    _cairo_quartz_surface_get_extents,
2220
    NULL, /* get_font_options */
2221
 
2222
    NULL, /* flush */
2223
    NULL, /* mark_dirty_rectangle */
2224
 
2225
    _cairo_quartz_surface_paint,
2226
    _cairo_quartz_surface_mask,
2227
    _cairo_quartz_surface_stroke,
2228
    _cairo_quartz_surface_fill,
2229
    NULL,  /* fill-stroke */
2230
    _cairo_quartz_surface_glyphs,
2231
};
2232
 
2233
cairo_quartz_surface_t *
2234
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
2235
				       cairo_content_t content,
2236
				       unsigned int width,
2237
				       unsigned int height)
2238
{
2239
    cairo_quartz_surface_t *surface;
2240
 
2241
    quartz_ensure_symbols ();
2242
 
2243
    /* Init the base surface */
2244
    surface = malloc (sizeof (cairo_quartz_surface_t));
2245
    if (unlikely (surface == NULL))
2246
	return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2247
 
2248
    memset (surface, 0, sizeof (cairo_quartz_surface_t));
2249
 
2250
    _cairo_surface_init (&surface->base,
2251
			 &cairo_quartz_surface_backend,
2252
			 NULL, /* device */
2253
			 content);
2254
 
2255
    _cairo_surface_clipper_init (&surface->clipper,
2256
				 _cairo_quartz_surface_clipper_intersect_clip_path);
2257
 
2258
    /* Save our extents */
2259
    surface->extents.x = surface->extents.y = 0;
2260
    surface->extents.width = width;
2261
    surface->extents.height = height;
2262
    surface->virtual_extents = surface->extents;
2263
 
2264
    if (IS_EMPTY (surface)) {
2265
	surface->cgContext = NULL;
2266
	surface->cgContextBaseCTM = CGAffineTransformIdentity;
2267
	surface->imageData = NULL;
2268
	surface->base.is_clear = TRUE;
2269
	return surface;
2270
    }
2271
 
2272
    /* Save so we can always get back to a known-good CGContext -- this is
2273
     * required for proper behaviour of intersect_clip_path(NULL)
2274
     */
2275
    CGContextSaveGState (cgContext);
2276
 
2277
    surface->cgContext = cgContext;
2278
    surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
2279
 
2280
    surface->imageData = NULL;
2281
    surface->imageSurfaceEquiv = NULL;
2282
 
2283
    return surface;
2284
}
2285
 
2286
/**
2287
 * cairo_quartz_surface_create_for_cg_context:
2288
 * @cgContext: the existing CGContext for which to create the surface
2289
 * @width: width of the surface, in pixels
2290
 * @height: height of the surface, in pixels
2291
 *
2292
 * Creates a Quartz surface that wraps the given CGContext.  The
2293
 * CGContext is assumed to be in the standard Cairo coordinate space
2294
 * (that is, with the origin at the upper left and the Y axis
2295
 * increasing downward).  If the CGContext is in the Quartz coordinate
2296
 * space (with the origin at the bottom left), then it should be
2297
 * flipped before this function is called.  The flip can be accomplished
2298
 * using a translate and a scale; for example:
2299
 *
2300
 * 
2301
 * CGContextTranslateCTM (cgContext, 0.0, height);
2302
 * CGContextScaleCTM (cgContext, 1.0, -1.0);
2303
 * 
2304
 *
2305
 * All Cairo operations are implemented in terms of Quartz operations,
2306
 * as long as Quartz-compatible elements are used (such as Quartz fonts).
2307
 *
2308
 * Return value: the newly created Cairo surface.
2309
 *
2310
 * Since: 1.6
2311
 **/
2312
 
2313
cairo_surface_t *
2314
cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2315
					    unsigned int width,
2316
					    unsigned int height)
2317
{
2318
    cairo_quartz_surface_t *surf;
2319
 
2320
    surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
2321
						  width, height);
2322
    if (likely (!surf->base.status))
2323
	CGContextRetain (cgContext);
2324
 
2325
    return &surf->base;
2326
}
2327
 
2328
/**
2329
 * cairo_quartz_surface_create:
2330
 * @format: format of pixels in the surface to create
2331
 * @width: width of the surface, in pixels
2332
 * @height: height of the surface, in pixels
2333
 *
2334
 * Creates a Quartz surface backed by a CGBitmap.  The surface is
2335
 * created using the Device RGB (or Device Gray, for A8) color space.
2336
 * All Cairo operations, including those that require software
2337
 * rendering, will succeed on this surface.
2338
 *
2339
 * Return value: the newly created surface.
2340
 *
2341
 * Since: 1.6
2342
 **/
2343
cairo_surface_t *
2344
cairo_quartz_surface_create (cairo_format_t format,
2345
			     unsigned int width,
2346
			     unsigned int height)
2347
{
2348
    cairo_quartz_surface_t *surf;
2349
    CGContextRef cgc;
2350
    CGColorSpaceRef cgColorspace;
2351
    CGBitmapInfo bitinfo;
2352
    void *imageData;
2353
    int stride;
2354
    int bitsPerComponent;
2355
 
2356
    if (!_cairo_quartz_verify_surface_size (width, height))
2357
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
2358
 
2359
    if (width == 0 || height == 0) {
2360
	return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
2361
						       width, height)->base;
2362
    }
2363
 
2364
    if (format == CAIRO_FORMAT_ARGB32 ||
2365
	format == CAIRO_FORMAT_RGB24)
2366
    {
2367
	cgColorspace = CGColorSpaceCreateDeviceRGB ();
2368
	bitinfo = kCGBitmapByteOrder32Host;
2369
	if (format == CAIRO_FORMAT_ARGB32)
2370
	    bitinfo |= kCGImageAlphaPremultipliedFirst;
2371
	else
2372
	    bitinfo |= kCGImageAlphaNoneSkipFirst;
2373
	bitsPerComponent = 8;
2374
	stride = width * 4;
2375
    } else if (format == CAIRO_FORMAT_A8) {
2376
	cgColorspace = NULL;
2377
	stride = width;
2378
	bitinfo = kCGImageAlphaOnly;
2379
	bitsPerComponent = 8;
2380
    } else if (format == CAIRO_FORMAT_A1) {
2381
	/* I don't think we can usefully support this, as defined by
2382
	 * cairo_format_t -- these are 1-bit pixels stored in 32-bit
2383
	 * quantities.
2384
	 */
2385
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2386
    } else {
2387
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2388
    }
2389
 
2390
    /* The Apple docs say that for best performance, the stride and the data
2391
     * pointer should be 16-byte aligned.  malloc already aligns to 16-bytes,
2392
     * so we don't have to anything special on allocation.
2393
     */
2394
    stride = (stride + 15) & ~15;
2395
 
2396
    imageData = _cairo_malloc_ab (height, stride);
2397
    if (unlikely (!imageData)) {
2398
	CGColorSpaceRelease (cgColorspace);
2399
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2400
    }
2401
 
2402
    /* zero the memory to match the image surface behaviour */
2403
    memset (imageData, 0, height * stride);
2404
 
2405
    cgc = CGBitmapContextCreate (imageData,
2406
				 width,
2407
				 height,
2408
				 bitsPerComponent,
2409
				 stride,
2410
				 cgColorspace,
2411
				 bitinfo);
2412
    CGColorSpaceRelease (cgColorspace);
2413
 
2414
    if (!cgc) {
2415
	free (imageData);
2416
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2417
    }
2418
 
2419
    /* flip the Y axis */
2420
    CGContextTranslateCTM (cgc, 0.0, height);
2421
    CGContextScaleCTM (cgc, 1.0, -1.0);
2422
 
2423
    surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2424
						  width, height);
2425
    if (surf->base.status) {
2426
	CGContextRelease (cgc);
2427
	free (imageData);
2428
	// create_internal will have set an error
2429
	return &surf->base;
2430
    }
2431
 
2432
    surf->base.is_clear = TRUE;
2433
 
2434
    surf->imageData = imageData;
2435
    surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2436
 
2437
    return &surf->base;
2438
}
2439
 
2440
/**
2441
 * cairo_quartz_surface_get_cg_context:
2442
 * @surface: the Cairo Quartz surface
2443
 *
2444
 * Returns the CGContextRef that the given Quartz surface is backed
2445
 * by.
2446
 *
2447
 * A call to cairo_surface_flush() is required before using the
2448
 * CGContextRef to ensure that all pending drawing operations are
2449
 * finished and to restore any temporary modification cairo has made
2450
 * to its state. A call to cairo_surface_mark_dirty() is required
2451
 * after the state or the content of the CGContextRef has been
2452
 * modified.
2453
 *
2454
 * Return value: the CGContextRef for the given surface.
2455
 *
2456
 * Since: 1.6
2457
 **/
2458
CGContextRef
2459
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2460
{
2461
    if (surface && _cairo_surface_is_quartz (surface)) {
2462
	cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
2463
	return quartz->cgContext;
2464
    } else
2465
	return NULL;
2466
}
2467
 
2468
static cairo_bool_t
2469
_cairo_surface_is_quartz (const cairo_surface_t *surface)
2470
{
2471
    return surface->backend == &cairo_quartz_surface_backend;
2472
}
2473
 
2474
/* Debug stuff */
2475
 
2476
#ifdef QUARTZ_DEBUG
2477
 
2478
#include 
2479
 
2480
void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
2481
{
2482
    Handle  dataRef = NULL;
2483
    OSType  dataRefType;
2484
    CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
2485
 
2486
    GraphicsExportComponent grex = 0;
2487
    unsigned long sizeWritten;
2488
 
2489
    ComponentResult result;
2490
 
2491
    // create the data reference
2492
    result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
2493
						     0, &dataRef, &dataRefType);
2494
 
2495
    if (NULL != dataRef && noErr == result) {
2496
	// get the PNG exporter
2497
	result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
2498
					&grex);
2499
 
2500
	if (grex) {
2501
	    // tell the exporter where to find its source image
2502
	    result = GraphicsExportSetInputCGImage (grex, inImageRef);
2503
 
2504
	    if (noErr == result) {
2505
		// tell the exporter where to save the exporter image
2506
		result = GraphicsExportSetOutputDataReference (grex, dataRef,
2507
							       dataRefType);
2508
 
2509
		if (noErr == result) {
2510
		    // write the PNG file
2511
		    result = GraphicsExportDoExport (grex, &sizeWritten);
2512
		}
2513
	    }
2514
 
2515
	    // remember to close the component
2516
	    CloseComponent (grex);
2517
	}
2518
 
2519
	// remember to dispose of the data reference handle
2520
	DisposeHandle (dataRef);
2521
    }
2522
}
2523
 
2524
void
2525
quartz_image_to_png (CGImageRef imgref, char *dest)
2526
{
2527
    static int sctr = 0;
2528
    char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
2529
 
2530
    if (dest == NULL) {
2531
	fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
2532
	sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
2533
	sctr++;
2534
	dest = sptr;
2535
    }
2536
 
2537
    ExportCGImageToPNGFile (imgref, dest);
2538
}
2539
 
2540
void
2541
quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
2542
{
2543
    static int sctr = 0;
2544
    char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
2545
 
2546
    if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
2547
	fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
2548
	return;
2549
    }
2550
 
2551
    if (dest == NULL) {
2552
	fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
2553
	sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
2554
	sctr++;
2555
	dest = sptr;
2556
    }
2557
 
2558
    CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
2559
    if (imgref == NULL) {
2560
	fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
2561
	return;
2562
    }
2563
 
2564
    ExportCGImageToPNGFile (imgref, dest);
2565
 
2566
    CGImageRelease (imgref);
2567
}
2568
 
2569
#endif /* QUARTZ_DEBUG */