~ubuntu-branches/ubuntu/karmic/moon/karmic

« back to all changes in this revision

Viewing changes to cairo/src/cairo-quartz-surface.c

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-14 12:01:08 UTC
  • Revision ID: james.westby@ubuntu.com-20090214120108-06539vb25vhbd8bn
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

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