~ubuntu-branches/ubuntu/lucid/cairo/lucid

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2008-01-17 13:00:59 UTC
  • Revision ID: james.westby@ubuntu.com-20080117130059-3gbudaudr2w8bl4w
Tags: upstream-1.5.6
ImportĀ upstreamĀ versionĀ 1.5.6

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
/* The 10.5 SDK includes a funky new definition of FloatToFixed which
 
42
 * causes all sorts of breakage; so reset to old-style definition
 
43
 */
 
44
#ifdef FloatToFixed
 
45
#undef FloatToFixed
 
46
#define FloatToFixed(a)     ((Fixed)((float)(a) * fixed1))
 
47
#endif
 
48
 
 
49
#include <Carbon/Carbon.h>
 
50
#include <limits.h>
 
51
 
 
52
#undef QUARTZ_DEBUG
 
53
 
 
54
#ifdef QUARTZ_DEBUG
 
55
#define ND(_x)  fprintf _x
 
56
#else
 
57
#define ND(_x)  do {} while(0)
 
58
#endif
 
59
 
 
60
#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
 
61
 
 
62
/* This method is private, but it exists.  Its params are are exposed
 
63
 * as args to the NS* method, but not as CG.
 
64
 */
 
65
enum PrivateCGCompositeMode {
 
66
    kPrivateCGCompositeClear            = 0,
 
67
    kPrivateCGCompositeCopy             = 1,
 
68
    kPrivateCGCompositeSourceOver       = 2,
 
69
    kPrivateCGCompositeSourceIn         = 3,
 
70
    kPrivateCGCompositeSourceOut        = 4,
 
71
    kPrivateCGCompositeSourceAtop       = 5,
 
72
    kPrivateCGCompositeDestinationOver  = 6,
 
73
    kPrivateCGCompositeDestinationIn    = 7,
 
74
    kPrivateCGCompositeDestinationOut   = 8,
 
75
    kPrivateCGCompositeDestinationAtop  = 9,
 
76
    kPrivateCGCompositeXOR              = 10,
 
77
    kPrivateCGCompositePlusDarker       = 11, // (max (0, (1-d) + (1-s)))
 
78
    kPrivateCGCompositePlusLighter      = 12, // (min (1, s + d))
 
79
};
 
80
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
 
81
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
 
82
CG_EXTERN void CGContextResetCTM (CGContextRef);
 
83
CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
 
84
CG_EXTERN void CGContextResetClip (CGContextRef);
 
85
CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef);
 
86
 
 
87
/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
 
88
 * has all the stuff we care about, just some of it isn't exported in the SDK.
 
89
 */
 
90
#ifndef kCGBitmapByteOrder32Host
 
91
#define USE_10_3_WORKAROUNDS
 
92
#define kCGBitmapAlphaInfoMask 0x1F
 
93
#define kCGBitmapByteOrderMask 0x7000
 
94
#define kCGBitmapByteOrder32Host 0
 
95
 
 
96
typedef uint32_t CGBitmapInfo;
 
97
 
 
98
/* public in 10.4, present in 10.3.9 */
 
99
CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
 
100
CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
 
101
#endif
 
102
 
 
103
/* missing in 10.3.9 */
 
104
extern void CGContextClipToMask (CGContextRef, CGRect, CGImageRef) __attribute__((weak_import));
 
105
 
 
106
/* 10.5-only optimization */
 
107
extern void CGContextDrawTiledImage (CGContextRef, CGRect, CGImageRef) __attribute__((weak_import));
 
108
 
 
109
/*
 
110
 * Utility functions
 
111
 */
 
112
 
 
113
static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
 
114
static void quartz_image_to_png (CGImageRef, char *dest);
 
115
 
 
116
static cairo_quartz_surface_t *
 
117
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
 
118
                                       cairo_content_t content,
 
119
                                       unsigned int width,
 
120
                                       unsigned int height);
 
121
 
 
122
/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
 
123
 
 
124
#define CG_MAX_HEIGHT   SHRT_MAX
 
125
#define CG_MAX_WIDTH    USHRT_MAX
 
126
 
 
127
/* is the desired size of the surface within bounds? */
 
128
static cairo_bool_t verify_surface_size(int width, int height)
 
129
{
 
130
    /* hmmm, allow width, height == 0 ? */
 
131
    if (width < 0 || height < 0) {
 
132
        return FALSE;
 
133
    }
 
134
 
 
135
    if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) {
 
136
        return FALSE;
 
137
    }
 
138
 
 
139
    return TRUE;
 
140
}
 
141
 
 
142
/*
 
143
 * Cairo path -> Quartz path conversion helpers
 
144
 */
 
145
 
 
146
typedef struct _quartz_stroke {
 
147
    CGContextRef cgContext;
 
148
    cairo_matrix_t *ctm_inverse;
 
149
} quartz_stroke_t;
 
150
 
 
151
/* cairo path -> execute in context */
 
152
static cairo_status_t
 
153
_cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point)
 
154
{
 
155
    //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
 
156
    quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
 
157
    double x = _cairo_fixed_to_double (point->x);
 
158
    double y = _cairo_fixed_to_double (point->y);
 
159
 
 
160
    if (stroke->ctm_inverse)
 
161
        cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
 
162
 
 
163
    CGContextMoveToPoint (stroke->cgContext, x, y);
 
164
    return CAIRO_STATUS_SUCCESS;
 
165
}
 
166
 
 
167
static cairo_status_t
 
168
_cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point)
 
169
{
 
170
    //ND((stderr, "lineto: %f %f\n",  _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
 
171
    quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
 
172
    double x = _cairo_fixed_to_double (point->x);
 
173
    double y = _cairo_fixed_to_double (point->y);
 
174
 
 
175
    if (stroke->ctm_inverse)
 
176
        cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
 
177
 
 
178
    if (CGContextIsPathEmpty (stroke->cgContext))
 
179
        CGContextMoveToPoint (stroke->cgContext, x, y);
 
180
    else
 
181
        CGContextAddLineToPoint (stroke->cgContext, x, y);
 
182
    return CAIRO_STATUS_SUCCESS;
 
183
}
 
184
 
 
185
static cairo_status_t
 
186
_cairo_path_to_quartz_context_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2)
 
187
{
 
188
    //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n",
 
189
    //             _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y),
 
190
    //             _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y),
 
191
    //             _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)));
 
192
    quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
 
193
    double x0 = _cairo_fixed_to_double (p0->x);
 
194
    double y0 = _cairo_fixed_to_double (p0->y);
 
195
    double x1 = _cairo_fixed_to_double (p1->x);
 
196
    double y1 = _cairo_fixed_to_double (p1->y);
 
197
    double x2 = _cairo_fixed_to_double (p2->x);
 
198
    double y2 = _cairo_fixed_to_double (p2->y);
 
199
 
 
200
    if (stroke->ctm_inverse) {
 
201
        cairo_matrix_transform_point (stroke->ctm_inverse, &x0, &y0);
 
202
        cairo_matrix_transform_point (stroke->ctm_inverse, &x1, &y1);
 
203
        cairo_matrix_transform_point (stroke->ctm_inverse, &x2, &y2);
 
204
    }
 
205
 
 
206
    CGContextAddCurveToPoint (stroke->cgContext,
 
207
                              x0, y0, x1, y1, x2, y2);
 
208
    return CAIRO_STATUS_SUCCESS;
 
209
}
 
210
 
 
211
static cairo_status_t
 
212
_cairo_path_to_quartz_context_close_path (void *closure)
 
213
{
 
214
    //ND((stderr, "closepath\n"));
 
215
    quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
 
216
    CGContextClosePath (stroke->cgContext);
 
217
    return CAIRO_STATUS_SUCCESS;
 
218
}
 
219
 
 
220
static cairo_status_t
 
221
_cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
 
222
                                            quartz_stroke_t *stroke)
 
223
{
 
224
    return _cairo_path_fixed_interpret (path,
 
225
                                        CAIRO_DIRECTION_FORWARD,
 
226
                                        _cairo_path_to_quartz_context_move_to,
 
227
                                        _cairo_path_to_quartz_context_line_to,
 
228
                                        _cairo_path_to_quartz_context_curve_to,
 
229
                                        _cairo_path_to_quartz_context_close_path,
 
230
                                        stroke);
 
231
}
 
232
 
 
233
/*
 
234
 * Misc helpers/callbacks
 
235
 */
 
236
 
 
237
static PrivateCGCompositeMode
 
238
_cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op)
 
239
{
 
240
    switch (op) {
 
241
        case CAIRO_OPERATOR_CLEAR:
 
242
            return kPrivateCGCompositeClear;
 
243
        case CAIRO_OPERATOR_SOURCE:
 
244
            return kPrivateCGCompositeCopy;
 
245
        case CAIRO_OPERATOR_OVER:
 
246
            return kPrivateCGCompositeSourceOver;
 
247
        case CAIRO_OPERATOR_IN:
 
248
            /* XXX This doesn't match image output */
 
249
            return kPrivateCGCompositeSourceIn;
 
250
        case CAIRO_OPERATOR_OUT:
 
251
            /* XXX This doesn't match image output */
 
252
            return kPrivateCGCompositeSourceOut;
 
253
        case CAIRO_OPERATOR_ATOP:
 
254
            return kPrivateCGCompositeSourceAtop;
 
255
 
 
256
        case CAIRO_OPERATOR_DEST:
 
257
            /* XXX this is handled specially (noop)! */
 
258
            return kPrivateCGCompositeCopy;
 
259
        case CAIRO_OPERATOR_DEST_OVER:
 
260
            return kPrivateCGCompositeDestinationOver;
 
261
        case CAIRO_OPERATOR_DEST_IN:
 
262
            /* XXX This doesn't match image output */
 
263
            return kPrivateCGCompositeDestinationIn;
 
264
        case CAIRO_OPERATOR_DEST_OUT:
 
265
            return kPrivateCGCompositeDestinationOut;
 
266
        case CAIRO_OPERATOR_DEST_ATOP:
 
267
            /* XXX This doesn't match image output */
 
268
            return kPrivateCGCompositeDestinationAtop;
 
269
 
 
270
        case CAIRO_OPERATOR_XOR:
 
271
            return kPrivateCGCompositeXOR; /* This will generate strange results */
 
272
        case CAIRO_OPERATOR_ADD:
 
273
            return kPrivateCGCompositePlusLighter;
 
274
        case CAIRO_OPERATOR_SATURATE:
 
275
            /* XXX This doesn't match image output for SATURATE; there's no equivalent */
 
276
            return kPrivateCGCompositePlusDarker;  /* ??? */
 
277
    }
 
278
 
 
279
 
 
280
    return kPrivateCGCompositeCopy;
 
281
}
 
282
 
 
283
static CGLineCap
 
284
_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
 
285
{
 
286
    switch (ccap) {
 
287
        case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break;
 
288
        case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break;
 
289
        case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break;
 
290
    }
 
291
 
 
292
    return kCGLineCapButt;
 
293
}
 
294
 
 
295
static CGLineJoin
 
296
_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
 
297
{
 
298
    switch (cjoin) {
 
299
        case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break;
 
300
        case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break;
 
301
        case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break;
 
302
    }
 
303
 
 
304
    return kCGLineJoinMiter;
 
305
}
 
306
 
 
307
static void
 
308
_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
 
309
                                       CGAffineTransform *dst)
 
310
{
 
311
    dst->a = src->xx;
 
312
    dst->b = src->yx;
 
313
    dst->c = src->xy;
 
314
    dst->d = src->yy;
 
315
    dst->tx = src->x0;
 
316
    dst->ty = src->y0;
 
317
}
 
318
 
 
319
/*
 
320
 * Source -> Quartz setup and finish functions
 
321
 */
 
322
 
 
323
static void
 
324
ComputeGradientValue (void *info, const float *in, float *out)
 
325
{
 
326
    float fdist = *in; /* 0.0 .. 1.0 */
 
327
    cairo_fixed_t fdist_fix = _cairo_fixed_from_double(*in);
 
328
    cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
 
329
    unsigned int i;
 
330
 
 
331
    for (i = 0; i < grad->n_stops; i++) {
 
332
        if (grad->stops[i].x > fdist_fix)
 
333
            break;
 
334
    }
 
335
 
 
336
    if (i == 0 || i == grad->n_stops) {
 
337
        if (i == grad->n_stops)
 
338
            --i;
 
339
        out[0] = grad->stops[i].color.red;
 
340
        out[1] = grad->stops[i].color.green;
 
341
        out[2] = grad->stops[i].color.blue;
 
342
        out[3] = grad->stops[i].color.alpha;
 
343
    } else {
 
344
        float ax = _cairo_fixed_to_double(grad->stops[i-1].x);
 
345
        float bx = _cairo_fixed_to_double(grad->stops[i].x) - ax;
 
346
        float bp = (fdist - ax)/bx;
 
347
        float ap = 1.0 - bp;
 
348
 
 
349
        out[0] =
 
350
            grad->stops[i-1].color.red * ap +
 
351
            grad->stops[i].color.red * bp;
 
352
        out[1] =
 
353
            grad->stops[i-1].color.green * ap +
 
354
            grad->stops[i].color.green * bp;
 
355
        out[2] =
 
356
            grad->stops[i-1].color.blue * ap +
 
357
            grad->stops[i].color.blue * bp;
 
358
        out[3] =
 
359
            grad->stops[i-1].color.alpha * ap +
 
360
            grad->stops[i].color.alpha * bp;
 
361
    }
 
362
}
 
363
 
 
364
static CGFunctionRef
 
365
CreateGradientFunction (cairo_gradient_pattern_t *gpat)
 
366
{
 
367
    static const float input_value_range[2] = { 0.f, 1.f };
 
368
    static const float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
 
369
    static const CGFunctionCallbacks callbacks = {
 
370
        0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
 
371
    };
 
372
 
 
373
    return CGFunctionCreate (gpat,
 
374
                             1,
 
375
                             input_value_range,
 
376
                             4,
 
377
                             output_value_ranges,
 
378
                             &callbacks);
 
379
}
 
380
 
 
381
/* generic cairo surface -> cairo_quartz_surface_t function */
 
382
static cairo_int_status_t
 
383
_cairo_quartz_surface_to_quartz (cairo_surface_t *target,
 
384
                                 cairo_surface_t *pat_surf,
 
385
                                 cairo_quartz_surface_t **quartz_surf)
 
386
{
 
387
 
 
388
    if (cairo_surface_get_type(pat_surf) != CAIRO_SURFACE_TYPE_QUARTZ) {
 
389
        /* XXXtodo/perf don't use clone if the source surface is an image surface!  Instead,
 
390
         * just create the CGImage directly!
 
391
         */
 
392
 
 
393
        cairo_surface_t *ref_type = target;
 
394
        cairo_surface_t *new_surf = NULL;
 
395
        cairo_rectangle_int_t rect;
 
396
        cairo_status_t status;
 
397
 
 
398
        if (ref_type == NULL)
 
399
            ref_type = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
 
400
 
 
401
        status = _cairo_surface_get_extents (pat_surf, &rect);
 
402
        if (status)
 
403
            return status;
 
404
 
 
405
        status = _cairo_surface_clone_similar (ref_type, pat_surf, rect.x, rect.y,
 
406
                                      rect.width, rect.height, &new_surf);
 
407
        if (target == NULL)
 
408
            cairo_surface_destroy(ref_type);
 
409
 
 
410
        if (status)
 
411
            return status;
 
412
 
 
413
        if (new_surf &&
 
414
            cairo_surface_get_type (new_surf) != CAIRO_SURFACE_TYPE_QUARTZ)
 
415
        {
 
416
            ND((stderr, "got a non-quartz surface, format=%d width=%u height=%u type=%d\n", cairo_surface_get_type (pat_surf), rect.width, rect.height, cairo_surface_get_type (new_surf)));
 
417
            cairo_surface_destroy (new_surf);
 
418
            return CAIRO_INT_STATUS_UNSUPPORTED;
 
419
        }
 
420
 
 
421
        *quartz_surf = (cairo_quartz_surface_t *) new_surf;
 
422
    } else {
 
423
        /* If it's a quartz surface, we can try to see if it's a CGBitmapContext;
 
424
         * we do this when we call CGBitmapContextCreateImage below.
 
425
         */
 
426
        cairo_surface_reference (pat_surf);
 
427
        *quartz_surf = (cairo_quartz_surface_t*) pat_surf;
 
428
    }
 
429
 
 
430
    return CAIRO_STATUS_SUCCESS;
 
431
}
 
432
 
 
433
/* Generic cairo_pattern -> CGPattern function */
 
434
static void
 
435
SurfacePatternDrawFunc (void *info, CGContextRef context)
 
436
{
 
437
    cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) info;
 
438
    cairo_surface_t *pat_surf = spat->surface;
 
439
    cairo_int_status_t status;
 
440
 
 
441
    cairo_quartz_surface_t *quartz_surf;
 
442
    CGImageRef img;
 
443
    CGRect imageBounds;
 
444
 
 
445
    status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf);
 
446
    if (status)
 
447
        return;
 
448
 
 
449
    img = CGBitmapContextCreateImage (quartz_surf->cgContext);
 
450
    if (!img) {
 
451
        // ... give up.
 
452
        ND((stderr, "CGBitmapContextCreateImage failed\n"));
 
453
        _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
454
        cairo_surface_destroy ((cairo_surface_t*)quartz_surf);
 
455
        return;
 
456
    }
 
457
 
 
458
    /* XXXtodo WHY does this need to be flipped?  Writing this stuff
 
459
     * to disk shows that in both this path and the path above the source image
 
460
     * has an identical orientation, and the destination context at all times has a Y
 
461
     * flip.  So why do we need to flip in this case?
 
462
     */
 
463
    if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
 
464
        CGContextTranslateCTM (context, 0, CGImageGetHeight(img));
 
465
        CGContextScaleCTM (context, 1, -1);
 
466
    }
 
467
 
 
468
    imageBounds.size = CGSizeMake (CGImageGetWidth(img), CGImageGetHeight(img));
 
469
    imageBounds.origin.x = 0;
 
470
    imageBounds.origin.y = 0;
 
471
 
 
472
    CGContextDrawImage (context, imageBounds, img);
 
473
    if (spat->base.extend == CAIRO_EXTEND_REFLECT) {
 
474
        /* draw 3 more copies of the image, flipped. */
 
475
        CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height);
 
476
        CGContextScaleCTM (context, 1, -1);
 
477
        CGContextDrawImage (context, imageBounds, img);
 
478
        CGContextTranslateCTM (context, 2 * imageBounds.size.width, 0);
 
479
        CGContextScaleCTM (context, -1, 1);
 
480
        CGContextDrawImage (context, imageBounds, img);
 
481
        CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height);
 
482
        CGContextScaleCTM (context, 1, -1);
 
483
        CGContextDrawImage (context, imageBounds, img);
 
484
    }
 
485
 
 
486
    CGImageRelease (img);
 
487
 
 
488
    cairo_surface_destroy ((cairo_surface_t*) quartz_surf);
 
489
}
 
490
 
 
491
/* Borrowed from cairo-meta-surface */
 
492
static cairo_status_t
 
493
_init_pattern_with_snapshot (cairo_pattern_t *pattern,
 
494
                             const cairo_pattern_t *other)
 
495
{
 
496
    cairo_status_t status;
 
497
 
 
498
    status = _cairo_pattern_init_copy (pattern, other);
 
499
    if (status)
 
500
        return status;
 
501
 
 
502
    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
 
503
        cairo_surface_pattern_t *surface_pattern =
 
504
            (cairo_surface_pattern_t *) pattern;
 
505
        cairo_surface_t *surface = surface_pattern->surface;
 
506
 
 
507
        surface_pattern->surface = _cairo_surface_snapshot (surface);
 
508
 
 
509
        cairo_surface_destroy (surface);
 
510
 
 
511
        if (surface_pattern->surface->status)
 
512
            return surface_pattern->surface->status;
 
513
    }
 
514
 
 
515
    return CAIRO_STATUS_SUCCESS;
 
516
}
 
517
 
 
518
static cairo_int_status_t
 
519
_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
 
520
                                                         cairo_pattern_t *abspat,
 
521
                                                         CGPatternRef *cgpat)
 
522
{
 
523
    cairo_surface_pattern_t *spat;
 
524
    cairo_surface_t *pat_surf;
 
525
    cairo_rectangle_int_t extents;
 
526
 
 
527
    CGRect pbounds;
 
528
    CGAffineTransform ptransform, stransform;
 
529
    CGPatternCallbacks cb = { 0,
 
530
                              SurfacePatternDrawFunc,
 
531
                              (CGFunctionReleaseInfoCallback) cairo_pattern_destroy };
 
532
    float rw, rh;
 
533
    cairo_status_t status;
 
534
 
 
535
    cairo_pattern_union_t *snap_pattern = NULL;
 
536
    cairo_pattern_t *target_pattern = abspat;
 
537
 
 
538
    cairo_matrix_t m;
 
539
    /* SURFACE is the only type we'll handle here */
 
540
    if (abspat->type != CAIRO_PATTERN_TYPE_SURFACE)
 
541
        return CAIRO_INT_STATUS_UNSUPPORTED;
 
542
 
 
543
    spat = (cairo_surface_pattern_t *) abspat;
 
544
    pat_surf = spat->surface;
 
545
 
 
546
    status = _cairo_surface_get_extents (pat_surf, &extents);
 
547
    if (status)
 
548
        return status;
 
549
 
 
550
    pbounds.origin.x = 0;
 
551
    pbounds.origin.y = 0;
 
552
 
 
553
    // kjs seems to indicate this should work (setting to 0,0 to avoid
 
554
    // tiling); however, the pattern CTM scaling ends up being NaN in
 
555
    // the pattern draw function if either rw or rh are 0.
 
556
    // XXXtodo get pattern drawing working with extend options
 
557
    // XXXtodo/perf optimize CAIRO_EXTEND_NONE to a single DrawImage instead of a pattern
 
558
    if (spat->base.extend == CAIRO_EXTEND_REFLECT) {
 
559
        /* XXX broken; need to emulate by reflecting the image into 4 quadrants
 
560
         * and then tiling that
 
561
         */
 
562
        pbounds.size.width = 2 * extents.width;
 
563
        pbounds.size.height = 2 * extents.height;
 
564
    } else {
 
565
        pbounds.size.width = extents.width;
 
566
        pbounds.size.height = extents.height;
 
567
    }
 
568
    rw = pbounds.size.width;
 
569
    rh = pbounds.size.height;
 
570
 
 
571
    m = spat->base.matrix;
 
572
    cairo_matrix_invert(&m);
 
573
    _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
 
574
 
 
575
    /* The pattern matrix is relative to the bottom left, again; the
 
576
     * incoming cairo pattern matrix is relative to the upper left.
 
577
     * So we take the pattern matrix and the original context matrix,
 
578
     * which gives us the correct base translation/y flip.
 
579
     */
 
580
    ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM);
 
581
 
 
582
#ifdef QUARTZ_DEBUG
 
583
    ND((stderr, "  pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
 
584
    ND((stderr, "  pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
 
585
    CGAffineTransform xform = CGContextGetCTM(dest->cgContext);
 
586
    ND((stderr, "  context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
 
587
#endif
 
588
 
 
589
 
 
590
    /* XXX fixme: only do snapshots if the context is for printing, or get rid of the
 
591
       other block if it doesn't fafect performance */
 
592
    if (1 /* context is for printing */) {
 
593
        snap_pattern = (cairo_pattern_union_t*) malloc(sizeof(cairo_pattern_union_t));
 
594
        target_pattern = (cairo_pattern_t*) snap_pattern;
 
595
        _init_pattern_with_snapshot (target_pattern, abspat);
 
596
    } else {
 
597
        cairo_pattern_reference (abspat);
 
598
        target_pattern = abspat;
 
599
    }
 
600
 
 
601
    *cgpat = CGPatternCreate (target_pattern,
 
602
                             pbounds,
 
603
                             ptransform,
 
604
                             rw, rh,
 
605
                             kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
 
606
                             TRUE,
 
607
                             &cb);
 
608
    return CAIRO_STATUS_SUCCESS;
 
609
}
 
610
 
 
611
typedef enum {
 
612
    DO_SOLID,
 
613
    DO_SHADING,
 
614
    DO_PATTERN,
 
615
    DO_IMAGE,
 
616
    DO_UNSUPPORTED,
 
617
    DO_NOTHING,
 
618
    DO_TILED_IMAGE
 
619
} cairo_quartz_action_t;
 
620
 
 
621
static cairo_quartz_action_t
 
622
_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
 
623
                                   cairo_linear_pattern_t *lpat)
 
624
{
 
625
    cairo_pattern_t *abspat = (cairo_pattern_t *) lpat;
 
626
    cairo_matrix_t mat;
 
627
    double x0, y0;
 
628
    CGPoint start, end;
 
629
    CGFunctionRef gradFunc;
 
630
    CGColorSpaceRef rgb;
 
631
    bool extend = abspat->extend == CAIRO_EXTEND_PAD;
 
632
 
 
633
    /* bandaid for mozilla bug 379321, also visible in the
 
634
     * linear-gradient-reflect test.
 
635
     */
 
636
    if (abspat->extend == CAIRO_EXTEND_REFLECT ||
 
637
        abspat->extend == CAIRO_EXTEND_REPEAT)
 
638
        return DO_UNSUPPORTED;
 
639
 
 
640
    /* We can only do this if we have an identity pattern matrix;
 
641
     * otherwise fall back through to the generic pattern case.
 
642
     * XXXperf we could optimize this by creating a pattern with the shading;
 
643
     * but we'd need to know the extents to do that.
 
644
     * ... but we don't care; we can use the surface extents for it
 
645
     * XXXtodo - implement gradients with non-identity pattern matrices
 
646
     */
 
647
    cairo_pattern_get_matrix (abspat, &mat);
 
648
    if (mat.xx != 1.0 || mat.yy != 1.0 || mat.xy != 0.0 || mat.yx != 0.0)
 
649
        return DO_UNSUPPORTED;
 
650
 
 
651
    if (!lpat->base.n_stops) {
 
652
        CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
 
653
        CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
 
654
        return DO_SOLID;
 
655
    }
 
656
 
 
657
    x0 = mat.x0;
 
658
    y0 = mat.y0;
 
659
    rgb = CGColorSpaceCreateDeviceRGB();
 
660
 
 
661
    start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x) - x0,
 
662
                         _cairo_fixed_to_double (lpat->p1.y) - y0);
 
663
    end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x) - x0,
 
664
                       _cairo_fixed_to_double (lpat->p2.y) - y0);
 
665
 
 
666
    cairo_pattern_reference (abspat);
 
667
    gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat);
 
668
    surface->sourceShading = CGShadingCreateAxial (rgb,
 
669
                                                   start, end,
 
670
                                                   gradFunc,
 
671
                                                   extend, extend);
 
672
    CGColorSpaceRelease(rgb);
 
673
    CGFunctionRelease(gradFunc);
 
674
 
 
675
    return DO_SHADING;
 
676
}
 
677
 
 
678
static cairo_quartz_action_t
 
679
_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
 
680
                                   cairo_radial_pattern_t *rpat)
 
681
{
 
682
    cairo_pattern_t *abspat = (cairo_pattern_t *)rpat;
 
683
    cairo_matrix_t mat;
 
684
    double x0, y0;
 
685
    CGPoint start, end;
 
686
    CGFunctionRef gradFunc;
 
687
    CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
 
688
    bool extend = abspat->extend == CAIRO_EXTEND_PAD;
 
689
 
 
690
    /* bandaid for mozilla bug 379321, also visible in the
 
691
     * linear-gradient-reflect test.
 
692
     */
 
693
    if (abspat->extend == CAIRO_EXTEND_REFLECT ||
 
694
        abspat->extend == CAIRO_EXTEND_REPEAT)
 
695
        return DO_UNSUPPORTED;
 
696
 
 
697
    /* XXXtodo - implement gradients with non-identity pattern matrices
 
698
     */
 
699
    cairo_pattern_get_matrix (abspat, &mat);
 
700
    if (mat.xx != 1.0 || mat.yy != 1.0 || mat.xy != 0.0 || mat.yx != 0.0)
 
701
        return DO_UNSUPPORTED;
 
702
 
 
703
    if (!rpat->base.n_stops) {
 
704
        CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
 
705
        CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
 
706
        return DO_SOLID;
 
707
    }
 
708
 
 
709
    x0 = mat.x0;
 
710
    y0 = mat.y0;
 
711
    rgb = CGColorSpaceCreateDeviceRGB();
 
712
 
 
713
    start = CGPointMake (_cairo_fixed_to_double (rpat->c1.x) - x0,
 
714
                         _cairo_fixed_to_double (rpat->c1.y) - y0);
 
715
    end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x) - x0,
 
716
                       _cairo_fixed_to_double (rpat->c2.y) - y0);
 
717
 
 
718
    cairo_pattern_reference (abspat);
 
719
    gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat);
 
720
    surface->sourceShading = CGShadingCreateRadial (rgb,
 
721
                                                    start,
 
722
                                                    _cairo_fixed_to_double (rpat->r1),
 
723
                                                    end,
 
724
                                                    _cairo_fixed_to_double (rpat->r2),
 
725
                                                    gradFunc,
 
726
                                                    extend, extend);
 
727
    CGColorSpaceRelease(rgb);
 
728
    CGFunctionRelease(gradFunc);
 
729
 
 
730
    return DO_SHADING;
 
731
}
 
732
 
 
733
static cairo_quartz_action_t
 
734
_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 
735
                            cairo_pattern_t *source)
 
736
{
 
737
    assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
 
738
 
 
739
    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
 
740
        cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
 
741
 
 
742
        CGContextSetRGBStrokeColor (surface->cgContext,
 
743
                                    solid->color.red,
 
744
                                    solid->color.green,
 
745
                                    solid->color.blue,
 
746
                                    solid->color.alpha);
 
747
        CGContextSetRGBFillColor (surface->cgContext,
 
748
                                  solid->color.red,
 
749
                                  solid->color.green,
 
750
                                  solid->color.blue,
 
751
                                  solid->color.alpha);
 
752
 
 
753
        return DO_SOLID;
 
754
    } else if (source->type == CAIRO_PATTERN_TYPE_LINEAR)
 
755
    {
 
756
        cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *)source;
 
757
        return _cairo_quartz_setup_linear_source (surface, lpat);
 
758
 
 
759
    } else if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
 
760
        cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *)source;
 
761
        return _cairo_quartz_setup_radial_source (surface, rpat);
 
762
 
 
763
    } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
 
764
               (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImage && source->extend == CAIRO_EXTEND_REPEAT)))
 
765
    {
 
766
            cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) source;
 
767
            cairo_surface_t *pat_surf = spat->surface;
 
768
            cairo_quartz_surface_t *quartz_surf;
 
769
            CGImageRef img;
 
770
            cairo_matrix_t m = spat->base.matrix;
 
771
            cairo_rectangle_int_t extents;
 
772
            cairo_status_t status;
 
773
 
 
774
            status = _cairo_quartz_surface_to_quartz ((cairo_surface_t *) surface, pat_surf, &quartz_surf);
 
775
            if (status)
 
776
                return DO_UNSUPPORTED;
 
777
 
 
778
            surface->sourceImageSurface = (cairo_surface_t *)quartz_surf;
 
779
 
 
780
            if (IS_EMPTY(quartz_surf))
 
781
                return DO_NOTHING;
 
782
 
 
783
            img = CGBitmapContextCreateImage (quartz_surf->cgContext);
 
784
            if (!img)
 
785
                return DO_UNSUPPORTED;
 
786
 
 
787
            surface->sourceImage = img;
 
788
 
 
789
            cairo_matrix_invert(&m);
 
790
            _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceImageTransform);
 
791
 
 
792
            status = _cairo_surface_get_extents (pat_surf, &extents);
 
793
            if (status)
 
794
                return DO_UNSUPPORTED;
 
795
 
 
796
            surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
 
797
 
 
798
            if (source->extend == CAIRO_EXTEND_NONE)
 
799
                return DO_IMAGE;
 
800
            else
 
801
                return DO_TILED_IMAGE;
 
802
    } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 
803
        float patternAlpha = 1.0f;
 
804
        CGColorSpaceRef patternSpace;
 
805
        CGPatternRef pattern;
 
806
        cairo_int_status_t status;
 
807
 
 
808
        status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
 
809
        if (status)
 
810
            return DO_UNSUPPORTED;
 
811
 
 
812
        // Save before we change the pattern, colorspace, etc. so that
 
813
        // we can restore and make sure that quartz releases our
 
814
        // pattern (which may be stack allocated)
 
815
        CGContextSaveGState(surface->cgContext);
 
816
 
 
817
        patternSpace = CGColorSpaceCreatePattern(NULL);
 
818
        CGContextSetFillColorSpace (surface->cgContext, patternSpace);
 
819
        CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
 
820
        CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
 
821
        CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
 
822
        CGColorSpaceRelease (patternSpace);
 
823
 
 
824
        /* Quartz likes to munge the pattern phase (as yet unexplained
 
825
         * why); force it to 0,0 as we've already baked in the correct
 
826
         * pattern translation into the pattern matrix
 
827
         */
 
828
        CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
 
829
 
 
830
        surface->sourcePattern = pattern;
 
831
 
 
832
        return DO_PATTERN;
 
833
    } else {
 
834
        return DO_UNSUPPORTED;
 
835
    }
 
836
 
 
837
    ASSERT_NOT_REACHED;
 
838
}
 
839
 
 
840
static void
 
841
_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
 
842
                                cairo_pattern_t *source)
 
843
{
 
844
    if (surface->sourceImage) {
 
845
        CGImageRelease(surface->sourceImage);
 
846
        surface->sourceImage = NULL;
 
847
 
 
848
        cairo_surface_destroy(surface->sourceImageSurface);
 
849
        surface->sourceImageSurface = NULL;
 
850
    }
 
851
 
 
852
    if (surface->sourceShading) {
 
853
        CGShadingRelease(surface->sourceShading);
 
854
        surface->sourceShading = NULL;
 
855
    }
 
856
 
 
857
    if (surface->sourcePattern) {
 
858
        CGPatternRelease(surface->sourcePattern);
 
859
        // To tear down the pattern and colorspace
 
860
        CGContextRestoreGState(surface->cgContext);
 
861
 
 
862
        surface->sourcePattern = NULL;
 
863
    }
 
864
}
 
865
 
 
866
/*
 
867
 * get source/dest image implementation
 
868
 */
 
869
 
 
870
static void
 
871
ImageDataReleaseFunc(void *info, const void *data, size_t size)
 
872
{
 
873
    if (data != NULL) {
 
874
        free((void *) data);
 
875
    }
 
876
}
 
877
 
 
878
/* Read the image from the surface's front buffer */
 
879
static cairo_int_status_t
 
880
_cairo_quartz_get_image (cairo_quartz_surface_t *surface,
 
881
                          cairo_image_surface_t **image_out,
 
882
                          unsigned char **data_out)
 
883
{
 
884
    unsigned char *imageData;
 
885
    cairo_image_surface_t *isurf;
 
886
 
 
887
    if (IS_EMPTY(surface)) {
 
888
        *image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
 
889
        if (data_out)
 
890
            *data_out = NULL;
 
891
        return CAIRO_STATUS_SUCCESS;
 
892
    }
 
893
 
 
894
    if (CGBitmapContextGetBitsPerPixel(surface->cgContext) != 0) {
 
895
        unsigned int stride;
 
896
        unsigned int bitinfo;
 
897
        unsigned int bpc, bpp;
 
898
        CGColorSpaceRef colorspace;
 
899
        unsigned int color_comps;
 
900
 
 
901
        imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
 
902
#ifdef USE_10_3_WORKAROUNDS
 
903
        bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext);
 
904
#else
 
905
        bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
 
906
#endif
 
907
        stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
 
908
        bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
 
909
        bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
 
910
 
 
911
        // let's hope they don't add YUV under us
 
912
        colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
 
913
        color_comps = CGColorSpaceGetNumberOfComponents(colorspace);
 
914
 
 
915
        // XXX TODO: We can handle all of these by converting to
 
916
        // pixman masks, including non-native-endian masks
 
917
        if (bpc != 8)
 
918
            return CAIRO_INT_STATUS_UNSUPPORTED;
 
919
 
 
920
        if (bpp != 32 && bpp != 8)
 
921
            return CAIRO_INT_STATUS_UNSUPPORTED;
 
922
 
 
923
        if (color_comps != 3 && color_comps != 1)
 
924
            return CAIRO_INT_STATUS_UNSUPPORTED;
 
925
 
 
926
        if (bpp == 32 && color_comps == 3 &&
 
927
            (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
 
928
            (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
 
929
        {
 
930
            isurf = (cairo_image_surface_t *)
 
931
                cairo_image_surface_create_for_data (imageData,
 
932
                                                     CAIRO_FORMAT_ARGB32,
 
933
                                                     surface->extents.width,
 
934
                                                     surface->extents.height,
 
935
                                                     stride);
 
936
        } else if (bpp == 32 && color_comps == 3 &&
 
937
                   (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
 
938
                   (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
 
939
        {
 
940
            isurf = (cairo_image_surface_t *)
 
941
                cairo_image_surface_create_for_data (imageData,
 
942
                                                     CAIRO_FORMAT_RGB24,
 
943
                                                     surface->extents.width,
 
944
                                                     surface->extents.height,
 
945
                                                     stride);
 
946
        } else if (bpp == 8 && color_comps == 1)
 
947
        {
 
948
            isurf = (cairo_image_surface_t *)
 
949
                cairo_image_surface_create_for_data (imageData,
 
950
                                                     CAIRO_FORMAT_A8,
 
951
                                                     surface->extents.width,
 
952
                                                     surface->extents.height,
 
953
                                                     stride);
 
954
        } else {
 
955
            return CAIRO_INT_STATUS_UNSUPPORTED;
 
956
        }
 
957
    } else {
 
958
        return CAIRO_INT_STATUS_UNSUPPORTED;
 
959
    }
 
960
 
 
961
    *image_out = isurf;
 
962
    return CAIRO_STATUS_SUCCESS;
 
963
}
 
964
 
 
965
/*
 
966
 * Cairo surface backend implementations
 
967
 */
 
968
 
 
969
static cairo_status_t
 
970
_cairo_quartz_surface_finish (void *abstract_surface)
 
971
{
 
972
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
973
 
 
974
    ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
 
975
 
 
976
    if (IS_EMPTY(surface))
 
977
        return CAIRO_STATUS_SUCCESS;
 
978
 
 
979
    /* Restore our saved gstate that we use to reset clipping */
 
980
    CGContextRestoreGState (surface->cgContext);
 
981
 
 
982
    CGContextRelease (surface->cgContext);
 
983
 
 
984
    surface->cgContext = NULL;
 
985
 
 
986
    if (surface->imageData) {
 
987
        free (surface->imageData);
 
988
        surface->imageData = NULL;
 
989
    }
 
990
 
 
991
    return CAIRO_STATUS_SUCCESS;
 
992
}
 
993
 
 
994
static cairo_status_t
 
995
_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
 
996
                                             cairo_image_surface_t **image_out,
 
997
                                             void **image_extra)
 
998
{
 
999
    cairo_int_status_t status;
 
1000
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1001
 
 
1002
    //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
 
1003
 
 
1004
    *image_extra = NULL;
 
1005
 
 
1006
    status = _cairo_quartz_get_image (surface, image_out, NULL);
 
1007
    if (status)
 
1008
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1009
 
 
1010
    return CAIRO_STATUS_SUCCESS;
 
1011
}
 
1012
 
 
1013
static void
 
1014
_cairo_quartz_surface_release_source_image (void *abstract_surface,
 
1015
                                             cairo_image_surface_t *image,
 
1016
                                             void *image_extra)
 
1017
{
 
1018
    cairo_surface_destroy ((cairo_surface_t *) image);
 
1019
}
 
1020
 
 
1021
 
 
1022
static cairo_status_t
 
1023
_cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
 
1024
                                          cairo_rectangle_int_t *interest_rect,
 
1025
                                          cairo_image_surface_t **image_out,
 
1026
                                          cairo_rectangle_int_t *image_rect,
 
1027
                                          void **image_extra)
 
1028
{
 
1029
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1030
    cairo_int_status_t status;
 
1031
    unsigned char *data;
 
1032
 
 
1033
    ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
 
1034
 
 
1035
    *image_rect = surface->extents;
 
1036
 
 
1037
    status = _cairo_quartz_get_image (surface, image_out, &data);
 
1038
    if (status)
 
1039
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1040
 
 
1041
    *image_extra = data;
 
1042
 
 
1043
    return CAIRO_STATUS_SUCCESS;
 
1044
}
 
1045
 
 
1046
static void
 
1047
_cairo_quartz_surface_release_dest_image (void *abstract_surface,
 
1048
                                          cairo_rectangle_int_t *interest_rect,
 
1049
                                          cairo_image_surface_t *image,
 
1050
                                          cairo_rectangle_int_t *image_rect,
 
1051
                                          void *image_extra)
 
1052
{
 
1053
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1054
    unsigned char *imageData = (unsigned char *) image_extra;
 
1055
 
 
1056
    //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
 
1057
 
 
1058
    if (IS_EMPTY(surface)) {
 
1059
        cairo_surface_destroy ((cairo_surface_t*) image);
 
1060
        return;
 
1061
    }
 
1062
 
 
1063
    if (!CGBitmapContextGetData (surface->cgContext)) {
 
1064
        CGDataProviderRef dataProvider;
 
1065
        CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
 
1066
        CGImageRef img;
 
1067
 
 
1068
        dataProvider = CGDataProviderCreateWithData (NULL, imageData,
 
1069
                                                     surface->extents.width * surface->extents.height * 4,
 
1070
                                                     ImageDataReleaseFunc);
 
1071
 
 
1072
        img = CGImageCreate (surface->extents.width, surface->extents.height,
 
1073
                             8, 32,
 
1074
                             surface->extents.width * 4,
 
1075
                             rgb,
 
1076
                             kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
 
1077
                             dataProvider,
 
1078
                             NULL,
 
1079
                             false,
 
1080
                             kCGRenderingIntentDefault);
 
1081
        CGColorSpaceRelease (rgb);
 
1082
 
 
1083
        CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
 
1084
 
 
1085
        CGContextDrawImage (surface->cgContext,
 
1086
                            CGRectMake (0, 0, surface->extents.width, surface->extents.height),
 
1087
                            img);
 
1088
 
 
1089
        CGImageRelease (img);
 
1090
        CGDataProviderRelease (dataProvider);
 
1091
 
 
1092
        ND((stderr, "Image for surface %p was recovered from a bitmap\n", surface));
 
1093
    }
 
1094
 
 
1095
    cairo_surface_destroy ((cairo_surface_t *) image);
 
1096
}
 
1097
 
 
1098
static cairo_surface_t *
 
1099
_cairo_quartz_surface_create_similar (void *abstract_surface,
 
1100
                                       cairo_content_t content,
 
1101
                                       int width,
 
1102
                                       int height)
 
1103
{
 
1104
    /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
 
1105
 
 
1106
    cairo_format_t format;
 
1107
 
 
1108
    if (content == CAIRO_CONTENT_COLOR_ALPHA)
 
1109
        format = CAIRO_FORMAT_ARGB32;
 
1110
    else if (content == CAIRO_CONTENT_COLOR)
 
1111
        format = CAIRO_FORMAT_RGB24;
 
1112
    else if (content == CAIRO_CONTENT_ALPHA)
 
1113
        format = CAIRO_FORMAT_A8;
 
1114
    else
 
1115
        return NULL;
 
1116
 
 
1117
    // verify width and height of surface
 
1118
    if (!verify_surface_size(width, height)) {
 
1119
        _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1120
        return NULL;
 
1121
    }
 
1122
 
 
1123
    return cairo_quartz_surface_create (format, width, height);
 
1124
}
 
1125
 
 
1126
static cairo_status_t
 
1127
_cairo_quartz_surface_clone_similar (void *abstract_surface,
 
1128
                                      cairo_surface_t *src,
 
1129
                                      int              src_x,
 
1130
                                      int              src_y,
 
1131
                                      int              width,
 
1132
                                      int              height,
 
1133
                                      cairo_surface_t **clone_out)
 
1134
{
 
1135
    cairo_quartz_surface_t *new_surface = NULL;
 
1136
    cairo_format_t new_format;
 
1137
    CGImageRef quartz_image = NULL;
 
1138
 
 
1139
    *clone_out = NULL;
 
1140
 
 
1141
    // verify width and height of surface
 
1142
    if (!verify_surface_size(width, height)) {
 
1143
        return CAIRO_INT_STATUS_UNSUPPORTED;
 
1144
    }
 
1145
 
 
1146
    if (cairo_surface_get_type(src) == CAIRO_SURFACE_TYPE_QUARTZ) {
 
1147
        cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
 
1148
 
 
1149
        if (IS_EMPTY(qsurf)) {
 
1150
            *clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, qsurf->extents.width, qsurf->extents.height);
 
1151
            return CAIRO_STATUS_SUCCESS;
 
1152
        }
 
1153
 
 
1154
        quartz_image = CGBitmapContextCreateImage (qsurf->cgContext);
 
1155
        new_format = CAIRO_FORMAT_ARGB32;  /* XXX bogus; recover a real format from the image */
 
1156
    } else if (_cairo_surface_is_image (src)) {
 
1157
        cairo_image_surface_t *isurf = (cairo_image_surface_t *) src;
 
1158
        CGDataProviderRef dataProvider;
 
1159
        CGColorSpaceRef cgColorspace;
 
1160
        CGBitmapInfo bitinfo;
 
1161
        int bitsPerComponent, bitsPerPixel;
 
1162
 
 
1163
        if (isurf->width == 0 || isurf->height == 0) {
 
1164
            *clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, isurf->width, isurf->height);
 
1165
            return CAIRO_STATUS_SUCCESS;
 
1166
        }
 
1167
 
 
1168
        if (isurf->format == CAIRO_FORMAT_ARGB32) {
 
1169
            cgColorspace = CGColorSpaceCreateDeviceRGB();
 
1170
            bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
 
1171
            bitsPerComponent = 8;
 
1172
            bitsPerPixel = 32;
 
1173
        } else if (isurf->format == CAIRO_FORMAT_RGB24) {
 
1174
            cgColorspace = CGColorSpaceCreateDeviceRGB();
 
1175
            bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
 
1176
            bitsPerComponent = 8;
 
1177
            bitsPerPixel = 32;
 
1178
        } else if (isurf->format == CAIRO_FORMAT_A8) {
 
1179
            cgColorspace = CGColorSpaceCreateDeviceGray();
 
1180
            bitinfo = kCGImageAlphaNone;
 
1181
            bitsPerComponent = 8;
 
1182
            bitsPerPixel = 8;
 
1183
        } else {
 
1184
            /* SUPPORT A1, maybe */
 
1185
            return CAIRO_INT_STATUS_UNSUPPORTED;
 
1186
        }
 
1187
 
 
1188
        new_format = isurf->format;
 
1189
 
 
1190
        dataProvider = CGDataProviderCreateWithData (NULL,
 
1191
                                                     isurf->data,
 
1192
                                                     isurf->height * isurf->stride,
 
1193
                                                     NULL);
 
1194
 
 
1195
        quartz_image = CGImageCreate (isurf->width, isurf->height,
 
1196
                                      bitsPerComponent,
 
1197
                                      bitsPerPixel,
 
1198
                                      isurf->stride,
 
1199
                                      cgColorspace,
 
1200
                                      bitinfo,
 
1201
                                      dataProvider,
 
1202
                                      NULL,
 
1203
                                      false,
 
1204
                                      kCGRenderingIntentDefault);
 
1205
        CGDataProviderRelease (dataProvider);
 
1206
        CGColorSpaceRelease (cgColorspace);
 
1207
    } else {
 
1208
        return CAIRO_INT_STATUS_UNSUPPORTED;
 
1209
    }
 
1210
 
 
1211
    if (!quartz_image)
 
1212
        return CAIRO_INT_STATUS_UNSUPPORTED;
 
1213
 
 
1214
    new_surface = (cairo_quartz_surface_t *)
 
1215
        cairo_quartz_surface_create (new_format,
 
1216
                                     CGImageGetWidth (quartz_image),
 
1217
                                     CGImageGetHeight (quartz_image));
 
1218
    if (!new_surface || new_surface->base.status) {
 
1219
        CGImageRelease (quartz_image);
 
1220
        return CAIRO_INT_STATUS_UNSUPPORTED;
 
1221
    }
 
1222
 
 
1223
    CGContextSetCompositeOperation (new_surface->cgContext,
 
1224
                                    kPrivateCGCompositeCopy);
 
1225
 
 
1226
    quartz_image_to_png (quartz_image, NULL);
 
1227
 
 
1228
    CGContextDrawImage (new_surface->cgContext,
 
1229
                        CGRectMake (src_x, src_y, width, height),
 
1230
                        quartz_image);
 
1231
    CGImageRelease (quartz_image);
 
1232
 
 
1233
    *clone_out = (cairo_surface_t*) new_surface;
 
1234
 
 
1235
    return CAIRO_STATUS_SUCCESS;
 
1236
}
 
1237
 
 
1238
static cairo_int_status_t
 
1239
_cairo_quartz_surface_get_extents (void *abstract_surface,
 
1240
                                   cairo_rectangle_int_t *extents)
 
1241
{
 
1242
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1243
 
 
1244
    *extents = surface->extents;
 
1245
 
 
1246
    return CAIRO_STATUS_SUCCESS;
 
1247
}
 
1248
 
 
1249
static cairo_int_status_t
 
1250
_cairo_quartz_surface_paint (void *abstract_surface,
 
1251
                              cairo_operator_t op,
 
1252
                              cairo_pattern_t *source)
 
1253
{
 
1254
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1255
    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
 
1256
    cairo_quartz_action_t action;
 
1257
 
 
1258
    ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
 
1259
 
 
1260
    if (IS_EMPTY(surface))
 
1261
        return CAIRO_STATUS_SUCCESS;
 
1262
 
 
1263
    if (op == CAIRO_OPERATOR_DEST)
 
1264
        return CAIRO_STATUS_SUCCESS;
 
1265
 
 
1266
    CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
 
1267
 
 
1268
    action = _cairo_quartz_setup_source (surface, source);
 
1269
 
 
1270
    if (action == DO_SOLID || action == DO_PATTERN) {
 
1271
        CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
 
1272
                                                          surface->extents.y,
 
1273
                                                          surface->extents.width,
 
1274
                                                          surface->extents.height));
 
1275
    } else if (action == DO_SHADING) {
 
1276
        CGContextDrawShading (surface->cgContext, surface->sourceShading);
 
1277
    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
 
1278
        cairo_surface_pattern_t *surface_pattern =
 
1279
            (cairo_surface_pattern_t *) source;
 
1280
        cairo_surface_t *pat_surf = surface_pattern->surface;
 
1281
 
 
1282
        CGContextSaveGState (surface->cgContext);
 
1283
 
 
1284
        if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
 
1285
            CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
 
1286
            CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
 
1287
                                                              surface->extents.y,
 
1288
                                                              surface->extents.width,
 
1289
                                                              surface->extents.height));
 
1290
        }
 
1291
 
 
1292
        CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
 
1293
        if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
 
1294
            CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
 
1295
            CGContextScaleCTM (surface->cgContext, 1, -1);
 
1296
        }
 
1297
 
 
1298
        if (action == DO_IMAGE)
 
1299
            CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1300
        else
 
1301
            CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1302
        CGContextRestoreGState (surface->cgContext);
 
1303
    } else if (action != DO_NOTHING) {
 
1304
        rv = CAIRO_INT_STATUS_UNSUPPORTED;
 
1305
    }
 
1306
 
 
1307
    _cairo_quartz_teardown_source (surface, source);
 
1308
 
 
1309
    ND((stderr, "-- paint\n"));
 
1310
    return rv;
 
1311
}
 
1312
 
 
1313
static cairo_int_status_t
 
1314
_cairo_quartz_surface_fill (void *abstract_surface,
 
1315
                             cairo_operator_t op,
 
1316
                             cairo_pattern_t *source,
 
1317
                             cairo_path_fixed_t *path,
 
1318
                             cairo_fill_rule_t fill_rule,
 
1319
                             double tolerance,
 
1320
                             cairo_antialias_t antialias)
 
1321
{
 
1322
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1323
    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
 
1324
    cairo_quartz_action_t action;
 
1325
    quartz_stroke_t stroke;
 
1326
 
 
1327
    ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
 
1328
 
 
1329
    if (IS_EMPTY(surface))
 
1330
        return CAIRO_STATUS_SUCCESS;
 
1331
 
 
1332
    if (op == CAIRO_OPERATOR_DEST)
 
1333
        return CAIRO_STATUS_SUCCESS;
 
1334
 
 
1335
    CGContextSaveGState (surface->cgContext);
 
1336
 
 
1337
    CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
 
1338
    CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
 
1339
 
 
1340
    action = _cairo_quartz_setup_source (surface, source);
 
1341
 
 
1342
    CGContextBeginPath (surface->cgContext);
 
1343
 
 
1344
    stroke.cgContext = surface->cgContext;
 
1345
    stroke.ctm_inverse = NULL;
 
1346
    rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
 
1347
    if (rv)
 
1348
        goto BAIL;
 
1349
 
 
1350
    if (action == DO_SOLID || action == DO_PATTERN) {
 
1351
        if (fill_rule == CAIRO_FILL_RULE_WINDING)
 
1352
            CGContextFillPath (surface->cgContext);
 
1353
        else
 
1354
            CGContextEOFillPath (surface->cgContext);
 
1355
    } else if (action == DO_SHADING) {
 
1356
 
 
1357
        // we have to clip and then paint the shading; we can't fill
 
1358
        // with the shading
 
1359
        if (fill_rule == CAIRO_FILL_RULE_WINDING)
 
1360
            CGContextClip (surface->cgContext);
 
1361
        else
 
1362
            CGContextEOClip (surface->cgContext);
 
1363
 
 
1364
        CGContextDrawShading (surface->cgContext, surface->sourceShading);
 
1365
    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
 
1366
        cairo_surface_pattern_t *surface_pattern =
 
1367
            (cairo_surface_pattern_t *) source;
 
1368
        cairo_surface_t *pat_surf = surface_pattern->surface;
 
1369
        if (fill_rule == CAIRO_FILL_RULE_WINDING)
 
1370
            CGContextClip (surface->cgContext);
 
1371
        else
 
1372
            CGContextEOClip (surface->cgContext);
 
1373
 
 
1374
        if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
 
1375
            CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
 
1376
            CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
 
1377
                                                              surface->extents.y,
 
1378
                                                              surface->extents.width,
 
1379
                                                              surface->extents.height));
 
1380
        }
 
1381
 
 
1382
        CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
 
1383
        if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
 
1384
            CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
 
1385
            CGContextScaleCTM (surface->cgContext, 1, -1);
 
1386
        }
 
1387
 
 
1388
        if (action == DO_IMAGE)
 
1389
            CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1390
        else
 
1391
            CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1392
    } else if (action != DO_NOTHING) {
 
1393
        rv = CAIRO_INT_STATUS_UNSUPPORTED;
 
1394
    }
 
1395
 
 
1396
  BAIL:
 
1397
    _cairo_quartz_teardown_source (surface, source);
 
1398
 
 
1399
    CGContextRestoreGState (surface->cgContext);
 
1400
 
 
1401
    ND((stderr, "-- fill\n"));
 
1402
    return rv;
 
1403
}
 
1404
 
 
1405
static cairo_int_status_t
 
1406
_cairo_quartz_surface_stroke (void *abstract_surface,
 
1407
                               cairo_operator_t op,
 
1408
                               cairo_pattern_t *source,
 
1409
                               cairo_path_fixed_t *path,
 
1410
                               cairo_stroke_style_t *style,
 
1411
                               cairo_matrix_t *ctm,
 
1412
                               cairo_matrix_t *ctm_inverse,
 
1413
                               double tolerance,
 
1414
                               cairo_antialias_t antialias)
 
1415
{
 
1416
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1417
    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
 
1418
    cairo_quartz_action_t action;
 
1419
    quartz_stroke_t stroke;
 
1420
    CGAffineTransform strokeTransform;
 
1421
 
 
1422
    ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
 
1423
 
 
1424
    if (IS_EMPTY(surface))
 
1425
        return CAIRO_STATUS_SUCCESS;
 
1426
 
 
1427
    if (op == CAIRO_OPERATOR_DEST)
 
1428
        return CAIRO_STATUS_SUCCESS;
 
1429
 
 
1430
    CGContextSaveGState (surface->cgContext);
 
1431
 
 
1432
    // Turning antialiasing off causes misrendering with
 
1433
    // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels)
 
1434
    //CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
 
1435
    CGContextSetLineWidth (surface->cgContext, style->line_width);
 
1436
    CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
 
1437
    CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
 
1438
    CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
 
1439
    _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
 
1440
    CGContextConcatCTM (surface->cgContext, strokeTransform);
 
1441
 
 
1442
    if (style->dash && style->num_dashes) {
 
1443
#define STATIC_DASH 32
 
1444
        float sdash[STATIC_DASH];
 
1445
        float *fdash = sdash;
 
1446
        unsigned int max_dashes = style->num_dashes;
 
1447
        unsigned int k;
 
1448
 
 
1449
        if (style->num_dashes%2)
 
1450
            max_dashes *= 2;
 
1451
        if (max_dashes > STATIC_DASH)
 
1452
            fdash = _cairo_malloc_ab (max_dashes, sizeof (float));
 
1453
        if (fdash == NULL)
 
1454
            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1455
 
 
1456
        for (k = 0; k < max_dashes; k++)
 
1457
            fdash[k] = (float) style->dash[k % style->num_dashes];
 
1458
 
 
1459
        CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
 
1460
        if (fdash != sdash)
 
1461
            free (fdash);
 
1462
    }
 
1463
 
 
1464
    CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
 
1465
 
 
1466
    action = _cairo_quartz_setup_source (surface, source);
 
1467
 
 
1468
    CGContextBeginPath (surface->cgContext);
 
1469
 
 
1470
    stroke.cgContext = surface->cgContext;
 
1471
    stroke.ctm_inverse = ctm_inverse;
 
1472
    rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
 
1473
    if (rv)
 
1474
        goto BAIL;
 
1475
 
 
1476
    if (action == DO_SOLID || action == DO_PATTERN) {
 
1477
        CGContextStrokePath (surface->cgContext);
 
1478
    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
 
1479
        CGContextReplacePathWithStrokedPath (surface->cgContext);
 
1480
        CGContextClip (surface->cgContext);
 
1481
 
 
1482
        if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
 
1483
            CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
 
1484
            CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
 
1485
                                                              surface->extents.y,
 
1486
                                                              surface->extents.width,
 
1487
                                                              surface->extents.height));
 
1488
        }
 
1489
 
 
1490
        CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
 
1491
        if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) {
 
1492
            CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
 
1493
            CGContextScaleCTM (surface->cgContext, 1, -1);
 
1494
        }
 
1495
 
 
1496
        if (action == DO_IMAGE)
 
1497
            CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1498
        else
 
1499
            CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1500
    } else if (action == DO_SHADING) {
 
1501
        CGContextReplacePathWithStrokedPath (surface->cgContext);
 
1502
        CGContextClip (surface->cgContext);
 
1503
 
 
1504
        CGContextDrawShading (surface->cgContext, surface->sourceShading);
 
1505
    } else if (action != DO_NOTHING) {
 
1506
        rv = CAIRO_INT_STATUS_UNSUPPORTED;
 
1507
    }
 
1508
 
 
1509
  BAIL:
 
1510
    _cairo_quartz_teardown_source (surface, source);
 
1511
 
 
1512
    CGContextRestoreGState (surface->cgContext);
 
1513
 
 
1514
    ND((stderr, "-- stroke\n"));
 
1515
    return rv;
 
1516
}
 
1517
 
 
1518
#if CAIRO_HAS_ATSUI_FONT
 
1519
static cairo_int_status_t
 
1520
_cairo_quartz_surface_show_glyphs (void *abstract_surface,
 
1521
                                    cairo_operator_t op,
 
1522
                                    cairo_pattern_t *source,
 
1523
                                    cairo_glyph_t *glyphs,
 
1524
                                    int num_glyphs,
 
1525
                                    cairo_scaled_font_t *scaled_font)
 
1526
{
 
1527
    CGAffineTransform cairoTextTransform, textTransform, ctm;
 
1528
    // XXXtodo/perf: stack storage for glyphs/sizes
 
1529
#define STATIC_BUF_SIZE 64
 
1530
    CGGlyph glyphs_static[STATIC_BUF_SIZE];
 
1531
    CGSize cg_advances_static[STATIC_BUF_SIZE];
 
1532
    CGGlyph *cg_glyphs = &glyphs_static[0];
 
1533
    CGSize *cg_advances = &cg_advances_static[0];
 
1534
 
 
1535
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1536
    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
 
1537
    cairo_quartz_action_t action;
 
1538
    float xprev, yprev;
 
1539
    int i;
 
1540
 
 
1541
    if (IS_EMPTY(surface))
 
1542
        return CAIRO_STATUS_SUCCESS;
 
1543
 
 
1544
    if (num_glyphs <= 0)
 
1545
        return CAIRO_STATUS_SUCCESS;
 
1546
 
 
1547
    if (op == CAIRO_OPERATOR_DEST)
 
1548
        return CAIRO_STATUS_SUCCESS;
 
1549
 
 
1550
    if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_ATSUI)
 
1551
        return CAIRO_INT_STATUS_UNSUPPORTED;
 
1552
 
 
1553
    CGContextSaveGState (surface->cgContext);
 
1554
 
 
1555
    action = _cairo_quartz_setup_source (surface, source);
 
1556
    if (action == DO_SOLID || action == DO_PATTERN) {
 
1557
        CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
 
1558
    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
 
1559
        CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
 
1560
    } else {
 
1561
        if (action != DO_NOTHING)
 
1562
            rv = CAIRO_INT_STATUS_UNSUPPORTED;
 
1563
        goto BAIL;
 
1564
    }
 
1565
 
 
1566
    CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
 
1567
 
 
1568
    /* this doesn't addref */
 
1569
    CGFontRef cgfref = _cairo_atsui_scaled_font_get_cg_font_ref (scaled_font);
 
1570
    CGContextSetFont (surface->cgContext, cgfref);
 
1571
 
 
1572
    /* So this should include the size; I don't know if I need to extract the
 
1573
     * size from this and call CGContextSetFontSize.. will I get crappy hinting
 
1574
     * with this 1.0 size business?  Or will CG just multiply that size into the
 
1575
     * text matrix?
 
1576
     */
 
1577
    //ND((stderr, "show_glyphs: glyph 0 at: %f, %f\n", glyphs[0].x, glyphs[0].y));
 
1578
    cairoTextTransform = CGAffineTransformMake (scaled_font->font_matrix.xx,
 
1579
                                                scaled_font->font_matrix.yx,
 
1580
                                                scaled_font->font_matrix.xy,
 
1581
                                                scaled_font->font_matrix.yy,
 
1582
                                                0., 0.);
 
1583
 
 
1584
    textTransform = CGAffineTransformMakeTranslation (glyphs[0].x, glyphs[0].y);
 
1585
    textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
 
1586
    textTransform = CGAffineTransformConcat (cairoTextTransform, textTransform);
 
1587
 
 
1588
    ctm = CGAffineTransformMake (scaled_font->ctm.xx,
 
1589
                                 -scaled_font->ctm.yx,
 
1590
                                 -scaled_font->ctm.xy,
 
1591
                                 scaled_font->ctm.yy,
 
1592
                                 0., 0.);
 
1593
    textTransform = CGAffineTransformConcat (ctm, textTransform);
 
1594
 
 
1595
    CGContextSetTextMatrix (surface->cgContext, textTransform);
 
1596
    CGContextSetFontSize (surface->cgContext, 1.0);
 
1597
 
 
1598
    if (num_glyphs > STATIC_BUF_SIZE) {
 
1599
        cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
 
1600
        if (cg_glyphs == NULL) {
 
1601
            rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1602
            goto BAIL;
 
1603
        }
 
1604
 
 
1605
        cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize));
 
1606
        if (cg_advances == NULL) {
 
1607
            rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1608
            goto BAIL;
 
1609
        }
 
1610
    }
 
1611
 
 
1612
    xprev = glyphs[0].x;
 
1613
    yprev = glyphs[0].y;
 
1614
 
 
1615
    cg_glyphs[0] = glyphs[0].index;
 
1616
    cg_advances[0].width = 0;
 
1617
    cg_advances[0].height = 0;
 
1618
 
 
1619
    for (i = 1; i < num_glyphs; i++) {
 
1620
        float xf = glyphs[i].x;
 
1621
        float yf = glyphs[i].y;
 
1622
        cg_glyphs[i] = glyphs[i].index;
 
1623
        cg_advances[i-1].width = xf - xprev;
 
1624
        cg_advances[i-1].height = yf - yprev;
 
1625
        xprev = xf;
 
1626
        yprev = yf;
 
1627
    }
 
1628
 
 
1629
#if 0
 
1630
    for (i = 0; i < num_glyphs; i++) {
 
1631
        ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
 
1632
    }
 
1633
#endif
 
1634
 
 
1635
    CGContextShowGlyphsWithAdvances (surface->cgContext,
 
1636
                                     cg_glyphs,
 
1637
                                     cg_advances,
 
1638
                                     num_glyphs);
 
1639
 
 
1640
    if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
 
1641
        if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
 
1642
            CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
 
1643
            CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
 
1644
                                                              surface->extents.y,
 
1645
                                                              surface->extents.width,
 
1646
                                                              surface->extents.height));
 
1647
        }
 
1648
 
 
1649
        CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
 
1650
        if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) {
 
1651
            CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
 
1652
            CGContextScaleCTM (surface->cgContext, 1, -1);
 
1653
        }
 
1654
 
 
1655
        if (action == DO_IMAGE)
 
1656
            CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1657
        else
 
1658
            CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 
1659
    } else if (action == DO_SHADING) {
 
1660
        CGContextDrawShading (surface->cgContext, surface->sourceShading);
 
1661
    }
 
1662
 
 
1663
BAIL:
 
1664
    if (cg_advances != &cg_advances_static[0]) {
 
1665
        free (cg_advances);
 
1666
    }
 
1667
 
 
1668
    if (cg_glyphs != &glyphs_static[0]) {
 
1669
        free (cg_glyphs);
 
1670
    }
 
1671
 
 
1672
    _cairo_quartz_teardown_source (surface, source);
 
1673
 
 
1674
    CGContextRestoreGState (surface->cgContext);
 
1675
 
 
1676
    return rv;
 
1677
}
 
1678
#endif /* CAIRO_HAS_ATSUI_FONT */
 
1679
 
 
1680
static cairo_int_status_t
 
1681
_cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
 
1682
                                         cairo_operator_t op,
 
1683
                                         cairo_pattern_t *source,
 
1684
                                         cairo_surface_pattern_t *mask)
 
1685
{
 
1686
    cairo_rectangle_int_t extents;
 
1687
    cairo_quartz_surface_t *quartz_surf;
 
1688
    CGRect rect;
 
1689
    CGImageRef img;
 
1690
    cairo_surface_t *pat_surf = mask->surface;
 
1691
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
1692
 
 
1693
    status = _cairo_surface_get_extents (pat_surf, &extents);
 
1694
    if (status)
 
1695
        return status;
 
1696
 
 
1697
    status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf);
 
1698
    if (status)
 
1699
        return status;
 
1700
 
 
1701
    // everything would be masked out, so do nothing
 
1702
    if (IS_EMPTY(quartz_surf))
 
1703
        goto BAIL;
 
1704
 
 
1705
    img = CGBitmapContextCreateImage (quartz_surf->cgContext);
 
1706
    if (!img) {
 
1707
        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1708
        goto BAIL;
 
1709
    }
 
1710
 
 
1711
    rect = CGRectMake (-mask->base.matrix.x0, -mask->base.matrix.y0, extents.width, extents.height);
 
1712
    CGContextSaveGState (surface->cgContext);
 
1713
    CGContextClipToMask (surface->cgContext, rect, img);
 
1714
    status = _cairo_quartz_surface_paint (surface, op, source);
 
1715
 
 
1716
    CGContextRestoreGState (surface->cgContext);
 
1717
    CGImageRelease (img);
 
1718
  BAIL:
 
1719
    cairo_surface_destroy ((cairo_surface_t*) quartz_surf);
 
1720
    return status;
 
1721
}
 
1722
 
 
1723
static cairo_int_status_t
 
1724
_cairo_quartz_surface_mask (void *abstract_surface,
 
1725
                             cairo_operator_t op,
 
1726
                             cairo_pattern_t *source,
 
1727
                             cairo_pattern_t *mask)
 
1728
{
 
1729
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1730
    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
 
1731
 
 
1732
    ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
 
1733
 
 
1734
    if (IS_EMPTY(surface))
 
1735
        return CAIRO_STATUS_SUCCESS;
 
1736
 
 
1737
    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
 
1738
        /* This is easy; we just need to paint with the alpha. */
 
1739
        cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
 
1740
 
 
1741
        CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
 
1742
    } else if (CGContextClipToMask &&
 
1743
               mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
 
1744
               mask->extend == CAIRO_EXTEND_NONE) {
 
1745
        return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask);
 
1746
    } else {
 
1747
        /* So, CGContextClipToMask is not present in 10.3.9, so we're
 
1748
         * doomed; if we have imageData, we can do fallback, otherwise
 
1749
         * just pretend success.
 
1750
         */
 
1751
        if (surface->imageData)
 
1752
            return CAIRO_INT_STATUS_UNSUPPORTED;
 
1753
 
 
1754
        return CAIRO_STATUS_SUCCESS;
 
1755
    }
 
1756
 
 
1757
    rv = _cairo_quartz_surface_paint (surface, op, source);
 
1758
 
 
1759
    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
 
1760
        CGContextSetAlpha (surface->cgContext, 1.0);
 
1761
    }
 
1762
 
 
1763
    ND((stderr, "-- mask\n"));
 
1764
 
 
1765
    return rv;
 
1766
}
 
1767
 
 
1768
static cairo_int_status_t
 
1769
_cairo_quartz_surface_intersect_clip_path (void *abstract_surface,
 
1770
                                            cairo_path_fixed_t *path,
 
1771
                                            cairo_fill_rule_t fill_rule,
 
1772
                                            double tolerance,
 
1773
                                            cairo_antialias_t antialias)
 
1774
{
 
1775
    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
1776
    quartz_stroke_t stroke;
 
1777
    cairo_status_t status;
 
1778
 
 
1779
    ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
 
1780
 
 
1781
    if (IS_EMPTY(surface))
 
1782
        return CAIRO_STATUS_SUCCESS;
 
1783
 
 
1784
    if (path == NULL) {
 
1785
        /* If we're being asked to reset the clip, we can only do it
 
1786
         * by restoring the gstate to our previous saved one, and
 
1787
         * saving it again.
 
1788
         *
 
1789
         * Note that this assumes that ALL quartz surface creation
 
1790
         * functions will do a SaveGState first; we do this in create_internal.
 
1791
         */
 
1792
        CGContextRestoreGState (surface->cgContext);
 
1793
        CGContextSaveGState (surface->cgContext);
 
1794
    } else {
 
1795
        CGContextBeginPath (surface->cgContext);
 
1796
        stroke.cgContext = surface->cgContext;
 
1797
        stroke.ctm_inverse = NULL;
 
1798
 
 
1799
        /* path must not be empty. */
 
1800
        CGContextMoveToPoint (surface->cgContext, 0, 0);
 
1801
        status = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
 
1802
        if (status)
 
1803
            return status;
 
1804
 
 
1805
        if (fill_rule == CAIRO_FILL_RULE_WINDING)
 
1806
            CGContextClip (surface->cgContext);
 
1807
        else
 
1808
            CGContextEOClip (surface->cgContext);
 
1809
    }
 
1810
 
 
1811
    ND((stderr, "-- intersect_clip_path\n"));
 
1812
 
 
1813
    return CAIRO_STATUS_SUCCESS;
 
1814
}
 
1815
 
 
1816
// XXXtodo implement show_page; need to figure out how to handle begin/end
 
1817
 
 
1818
static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
 
1819
    CAIRO_SURFACE_TYPE_QUARTZ,
 
1820
    _cairo_quartz_surface_create_similar,
 
1821
    _cairo_quartz_surface_finish,
 
1822
    _cairo_quartz_surface_acquire_source_image,
 
1823
    _cairo_quartz_surface_release_source_image,
 
1824
    _cairo_quartz_surface_acquire_dest_image,
 
1825
    _cairo_quartz_surface_release_dest_image,
 
1826
    _cairo_quartz_surface_clone_similar,
 
1827
    NULL, /* composite */
 
1828
    NULL, /* fill_rectangles */
 
1829
    NULL, /* composite_trapezoids */
 
1830
    NULL, /* copy_page */
 
1831
    NULL, /* show_page */
 
1832
    NULL, /* set_clip_region */
 
1833
    _cairo_quartz_surface_intersect_clip_path,
 
1834
    _cairo_quartz_surface_get_extents,
 
1835
    NULL, /* old_show_glyphs */
 
1836
    NULL, /* get_font_options */
 
1837
    NULL, /* flush */
 
1838
    NULL, /* mark_dirty_rectangle */
 
1839
    NULL, /* scaled_font_fini */
 
1840
    NULL, /* scaled_glyph_fini */
 
1841
 
 
1842
    _cairo_quartz_surface_paint,
 
1843
    _cairo_quartz_surface_mask,
 
1844
    _cairo_quartz_surface_stroke,
 
1845
    _cairo_quartz_surface_fill,
 
1846
#if CAIRO_HAS_ATSUI_FONT
 
1847
    _cairo_quartz_surface_show_glyphs,
 
1848
#else
 
1849
    NULL, /* surface_show_glyphs */
 
1850
#endif /* CAIRO_HAS_ATSUI_FONT */
 
1851
 
 
1852
    NULL, /* snapshot */
 
1853
    NULL, /* is_similar */
 
1854
    NULL, /* reset */
 
1855
    NULL  /* fill_stroke */
 
1856
};
 
1857
 
 
1858
cairo_quartz_surface_t *
 
1859
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
 
1860
                                        cairo_content_t content,
 
1861
                                        unsigned int width,
 
1862
                                        unsigned int height)
 
1863
{
 
1864
    cairo_quartz_surface_t *surface;
 
1865
 
 
1866
    /* Init the base surface */
 
1867
    surface = malloc(sizeof(cairo_quartz_surface_t));
 
1868
    if (surface == NULL)
 
1869
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
1870
 
 
1871
    memset(surface, 0, sizeof(cairo_quartz_surface_t));
 
1872
 
 
1873
    _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend,
 
1874
                        content);
 
1875
 
 
1876
    /* Save our extents */
 
1877
    surface->extents.x = surface->extents.y = 0;
 
1878
    surface->extents.width = width;
 
1879
    surface->extents.height = height;
 
1880
 
 
1881
    if (IS_EMPTY(surface)) {
 
1882
        surface->cgContext = NULL;
 
1883
        surface->cgContextBaseCTM = CGAffineTransformIdentity;
 
1884
        surface->imageData = NULL;
 
1885
        return surface;
 
1886
    }
 
1887
 
 
1888
    /* Save so we can always get back to a known-good CGContext -- this is
 
1889
     * required for proper behaviour of intersect_clip_path(NULL)
 
1890
     */
 
1891
    CGContextSaveGState (cgContext);
 
1892
 
 
1893
    surface->cgContext = cgContext;
 
1894
    surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
 
1895
 
 
1896
    surface->imageData = NULL;
 
1897
 
 
1898
    return surface;
 
1899
}
 
1900
 
 
1901
/**
 
1902
 * cairo_quartz_surface_create_for_cg_context
 
1903
 * @cgContext: the existing CGContext for which to create the surface
 
1904
 * @width: width of the surface, in pixels
 
1905
 * @height: height of the surface, in pixels
 
1906
 *
 
1907
 * Creates a Quartz surface that wraps the given CGContext.  The
 
1908
 * CGContext is assumed to be in the QuickDraw coordinate space (that
 
1909
 * is, with the origin at the upper left and the Y axis increasing
 
1910
 * downward.)  If the CGContext is in the Quartz coordinate space (with
 
1911
 * the origin at the bottom left), then it should be flipped before
 
1912
 * this function is called:
 
1913
 *
 
1914
 * <informalexample><programlisting>
 
1915
 * CGContextTranslateCTM (cgContext, 0.0, height);
 
1916
 * CGContextScaleCTM (cgContext, 1.0, -1.0);
 
1917
 * </programlisting></informalexample>
 
1918
 *
 
1919
 * A very small number of Cairo operations cannot be translated to
 
1920
 * Quartz operations; those operations will fail on this surface.
 
1921
 * If all Cairo operations are required to succeed, consider rendering
 
1922
 * to a surface created by cairo_quartz_surface_create() and then copying
 
1923
 * the result to the CGContext.
 
1924
 *
 
1925
 * Return value: the newly created Cairo surface.
 
1926
 *
 
1927
 * Since: 1.4
 
1928
 **/
 
1929
 
 
1930
cairo_surface_t *
 
1931
cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
 
1932
                                            unsigned int width,
 
1933
                                            unsigned int height)
 
1934
{
 
1935
    cairo_quartz_surface_t *surf;
 
1936
 
 
1937
    CGContextRetain (cgContext);
 
1938
 
 
1939
    surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
 
1940
                                                  width, height);
 
1941
    if (surf->base.status) {
 
1942
        CGContextRelease (cgContext);
 
1943
        // create_internal will have set an error
 
1944
        return surf;
 
1945
    }
 
1946
 
 
1947
    return (cairo_surface_t *) surf;
 
1948
}
 
1949
 
 
1950
/**
 
1951
 * cairo_quartz_surface_create
 
1952
 * @format: format of pixels in the surface to create
 
1953
 * @width: width of the surface, in pixels
 
1954
 * @height: height of the surface, in pixels
 
1955
 *
 
1956
 * Creates a Quartz surface backed by a CGBitmap.  The surface is
 
1957
 * created using the Device RGB (or Device Gray, for A8) color space.
 
1958
 * All Cairo operations, including those that require software
 
1959
 * rendering, will succeed on this surface.
 
1960
 *
 
1961
 * Return value: the newly created surface.
 
1962
 *
 
1963
 * Since: 1.4
 
1964
 **/
 
1965
cairo_surface_t *
 
1966
cairo_quartz_surface_create (cairo_format_t format,
 
1967
                             unsigned int width,
 
1968
                             unsigned int height)
 
1969
{
 
1970
    cairo_quartz_surface_t *surf;
 
1971
    CGContextRef cgc;
 
1972
    CGColorSpaceRef cgColorspace;
 
1973
    CGBitmapInfo bitinfo;
 
1974
    void *imageData;
 
1975
    int stride;
 
1976
    int bitsPerComponent;
 
1977
 
 
1978
    // verify width and height of surface
 
1979
    if (!verify_surface_size(width, height))
 
1980
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
1981
 
 
1982
    if (width == 0 || height == 0) {
 
1983
        return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
 
1984
                                                                         width, height);
 
1985
    }
 
1986
 
 
1987
    if (format == CAIRO_FORMAT_ARGB32) {
 
1988
        cgColorspace = CGColorSpaceCreateDeviceRGB();
 
1989
        stride = width * 4;
 
1990
        bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
 
1991
        bitsPerComponent = 8;
 
1992
    } else if (format == CAIRO_FORMAT_RGB24) {
 
1993
        cgColorspace = CGColorSpaceCreateDeviceRGB();
 
1994
        stride = width * 4;
 
1995
        bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
 
1996
        bitsPerComponent = 8;
 
1997
    } else if (format == CAIRO_FORMAT_A8) {
 
1998
        cgColorspace = CGColorSpaceCreateDeviceGray();
 
1999
        if (width % 4 == 0)
 
2000
            stride = width;
 
2001
        else
 
2002
            stride = (width & ~3) + 4;
 
2003
        bitinfo = kCGImageAlphaNone;
 
2004
        bitsPerComponent = 8;
 
2005
    } else if (format == CAIRO_FORMAT_A1) {
 
2006
        /* I don't think we can usefully support this, as defined by
 
2007
         * cairo_format_t -- these are 1-bit pixels stored in 32-bit
 
2008
         * quantities.
 
2009
         */
 
2010
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
 
2011
    } else {
 
2012
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
 
2013
    }
 
2014
 
 
2015
    imageData = _cairo_malloc_ab (height, stride);
 
2016
    if (!imageData) {
 
2017
        CGColorSpaceRelease (cgColorspace);
 
2018
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
2019
    }
 
2020
    /* zero the memory to match the image surface behaviour */
 
2021
    memset (imageData, 0, height * stride);
 
2022
 
 
2023
    cgc = CGBitmapContextCreate (imageData,
 
2024
                                 width,
 
2025
                                 height,
 
2026
                                 bitsPerComponent,
 
2027
                                 stride,
 
2028
                                 cgColorspace,
 
2029
                                 bitinfo);
 
2030
    CGColorSpaceRelease (cgColorspace);
 
2031
 
 
2032
    if (!cgc) {
 
2033
        free (imageData);
 
2034
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
2035
    }
 
2036
 
 
2037
    /* flip the Y axis */
 
2038
    CGContextTranslateCTM (cgc, 0.0, height);
 
2039
    CGContextScaleCTM (cgc, 1.0, -1.0);
 
2040
 
 
2041
    surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
 
2042
                                                  width, height);
 
2043
    if (surf->base.status) {
 
2044
        CGContextRelease (cgc);
 
2045
        free (imageData);
 
2046
        // create_internal will have set an error
 
2047
        return surf;
 
2048
    }
 
2049
 
 
2050
    surf->imageData = imageData;
 
2051
 
 
2052
    return (cairo_surface_t *) surf;
 
2053
}
 
2054
 
 
2055
/**
 
2056
 * cairo_quartz_surface_get_cg_context
 
2057
 * @surface: the Cairo Quartz surface
 
2058
 *
 
2059
 * Returns the CGContextRef that the given Quartz surface is backed
 
2060
 * by.
 
2061
 *
 
2062
 * Return value: the CGContextRef for the given surface.
 
2063
 *
 
2064
 * Since: 1.4
 
2065
 **/
 
2066
CGContextRef
 
2067
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
 
2068
{
 
2069
    cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
 
2070
 
 
2071
    if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
 
2072
        return NULL;
 
2073
 
 
2074
    return quartz->cgContext;
 
2075
}
 
2076
 
 
2077
 
 
2078
/* Debug stuff */
 
2079
 
 
2080
#ifdef QUARTZ_DEBUG
 
2081
 
 
2082
#include <Movies.h>
 
2083
 
 
2084
void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest)
 
2085
{
 
2086
    Handle  dataRef = NULL;
 
2087
    OSType  dataRefType;
 
2088
    CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII);
 
2089
 
 
2090
    GraphicsExportComponent grex = 0;
 
2091
    unsigned long sizeWritten;
 
2092
 
 
2093
    ComponentResult result;
 
2094
 
 
2095
    // create the data reference
 
2096
    result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle,
 
2097
                                                    0, &dataRef, &dataRefType);
 
2098
 
 
2099
    if (NULL != dataRef && noErr == result) {
 
2100
        // get the PNG exporter
 
2101
        result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG,
 
2102
                                       &grex);
 
2103
 
 
2104
        if (grex) {
 
2105
            // tell the exporter where to find its source image
 
2106
            result = GraphicsExportSetInputCGImage(grex, inImageRef);
 
2107
 
 
2108
            if (noErr == result) {
 
2109
                // tell the exporter where to save the exporter image
 
2110
                result = GraphicsExportSetOutputDataReference(grex, dataRef,
 
2111
                                                              dataRefType);
 
2112
 
 
2113
                if (noErr == result) {
 
2114
                    // write the PNG file
 
2115
                    result = GraphicsExportDoExport(grex, &sizeWritten);
 
2116
                }
 
2117
            }
 
2118
 
 
2119
            // remember to close the component
 
2120
            CloseComponent(grex);
 
2121
        }
 
2122
 
 
2123
        // remember to dispose of the data reference handle
 
2124
        DisposeHandle(dataRef);
 
2125
    }
 
2126
}
 
2127
#endif
 
2128
 
 
2129
void
 
2130
quartz_image_to_png (CGImageRef imgref, char *dest)
 
2131
{
 
2132
#if 0
 
2133
    static int sctr = 0;
 
2134
    char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
 
2135
 
 
2136
    if (dest == NULL) {
 
2137
        fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
 
2138
        sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
 
2139
        sctr++;
 
2140
        dest = sptr;
 
2141
    }
 
2142
 
 
2143
    ExportCGImageToPNGFile(imgref, dest);
 
2144
#endif
 
2145
}
 
2146
 
 
2147
void
 
2148
quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
 
2149
{
 
2150
#if 0
 
2151
    static int sctr = 0;
 
2152
    char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
 
2153
 
 
2154
    if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
 
2155
        fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
 
2156
        return;
 
2157
    }
 
2158
 
 
2159
    if (dest == NULL) {
 
2160
        fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
 
2161
        sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
 
2162
        sctr++;
 
2163
        dest = sptr;
 
2164
    }
 
2165
 
 
2166
    CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
 
2167
    if (imgref == NULL) {
 
2168
        fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
 
2169
        return;
 
2170
    }
 
2171
 
 
2172
    ExportCGImageToPNGFile(imgref, dest);
 
2173
 
 
2174
    CGImageRelease(imgref);
 
2175
#endif
 
2176
}
 
2177