]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkdrawable-quartz.c
7bcda9035ccbf99a61946c6fea5031833e6f9ac8
[~andy/gtk] / gdk / quartz / gdkdrawable-quartz.c
1 /* gdkdrawable-quartz.c
2  *
3  * Copyright (C) 2005-2007 Imendio AB
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include <cairo-quartz.h>
23 #include "gdkprivate-quartz.h"
24
25 static gpointer parent_class;
26
27 static cairo_user_data_key_t gdk_quartz_cairo_key;
28
29 typedef struct {
30   GdkDrawable  *drawable;
31   CGContextRef  cg_context;
32 } GdkQuartzCairoSurfaceData;
33
34 static void
35 gdk_quartz_cairo_surface_destroy (void *data)
36 {
37   GdkQuartzCairoSurfaceData *surface_data = data;
38   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (surface_data->drawable);
39
40   gdk_quartz_drawable_release_context (surface_data->drawable, 
41                                        surface_data->cg_context);
42
43   impl->cairo_surface = NULL;
44
45   g_free (surface_data);
46 }
47
48 static cairo_surface_t *
49 gdk_quartz_ref_cairo_surface (GdkDrawable *drawable)
50 {
51   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
52
53   if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable) &&
54       GDK_WINDOW_DESTROYED (impl->wrapper))
55     return NULL;
56
57   if (!impl->cairo_surface)
58     {
59       CGContextRef cg_context;
60       int width, height;
61       GdkQuartzCairoSurfaceData *surface_data;
62
63       cg_context = gdk_quartz_drawable_get_context (drawable, TRUE);
64       if (!cg_context)
65         return NULL;
66
67       gdk_drawable_get_size (drawable, &width, &height);
68
69       impl->cairo_surface = cairo_quartz_surface_create_for_cg_context (cg_context, width, height);
70
71       surface_data = g_new (GdkQuartzCairoSurfaceData, 1);
72       surface_data->drawable = drawable;
73       surface_data->cg_context = cg_context;
74
75       cairo_surface_set_user_data (impl->cairo_surface, &gdk_quartz_cairo_key,
76                                    surface_data, gdk_quartz_cairo_surface_destroy);
77     }
78   else
79     cairo_surface_reference (impl->cairo_surface);
80
81   return impl->cairo_surface;
82 }
83
84 static void
85 gdk_quartz_set_colormap (GdkDrawable *drawable,
86                          GdkColormap *colormap)
87 {
88   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
89
90   if (impl->colormap == colormap)
91     return;
92   
93   if (impl->colormap)
94     g_object_unref (impl->colormap);
95   impl->colormap = colormap;
96   if (impl->colormap)
97     g_object_ref (impl->colormap);
98 }
99
100 static GdkColormap*
101 gdk_quartz_get_colormap (GdkDrawable *drawable)
102 {
103   return GDK_DRAWABLE_IMPL_QUARTZ (drawable)->colormap;
104 }
105
106 static GdkScreen*
107 gdk_quartz_get_screen (GdkDrawable *drawable)
108 {
109   return _gdk_screen;
110 }
111
112 static GdkVisual*
113 gdk_quartz_get_visual (GdkDrawable *drawable)
114 {
115   return gdk_drawable_get_visual (GDK_DRAWABLE_IMPL_QUARTZ (drawable)->wrapper);
116 }
117
118 static int
119 gdk_quartz_get_depth (GdkDrawable *drawable)
120 {
121   /* This is a bit bogus but I'm not sure the other way is better */
122
123   return gdk_drawable_get_depth (GDK_DRAWABLE_IMPL_QUARTZ (drawable)->wrapper);
124 }
125
126 static void
127 gdk_quartz_draw_rectangle (GdkDrawable *drawable,
128                            GdkGC       *gc,
129                            gboolean     filled,
130                            gint         x,
131                            gint         y,
132                            gint         width,
133                            gint         height)
134 {
135   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
136
137   if (!context)
138     return;
139
140   _gdk_quartz_gc_update_cg_context (gc, 
141                                     drawable,
142                                     context,
143                                     filled ?
144                                     GDK_QUARTZ_CONTEXT_FILL : 
145                                     GDK_QUARTZ_CONTEXT_STROKE);
146
147   if (filled)
148     {
149       CGRect rect = CGRectMake (x, y, width, height);
150
151       CGContextFillRect (context, rect);
152     }
153   else
154     {
155       CGRect rect = CGRectMake (x + 0.5, y + 0.5, width, height);
156
157       CGContextStrokeRect (context, rect);
158     }
159
160   gdk_quartz_drawable_release_context (drawable, context);
161 }
162
163 static void
164 gdk_quartz_draw_arc (GdkDrawable *drawable,
165                      GdkGC       *gc,
166                      gboolean     filled,
167                      gint         x,
168                      gint         y,
169                      gint         width,
170                      gint         height,
171                      gint         angle1,
172                      gint         angle2)
173 {
174   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
175   float start_angle, end_angle;
176   gboolean clockwise = FALSE;
177
178   if (!context)
179     return;
180
181   _gdk_quartz_gc_update_cg_context (gc, drawable, context,
182                                     filled ?
183                                     GDK_QUARTZ_CONTEXT_FILL :
184                                     GDK_QUARTZ_CONTEXT_STROKE);
185
186   CGContextSaveGState (context);
187
188   start_angle = angle1 * 2.0 * G_PI / 360.0 / 64.0;
189   end_angle = start_angle + angle2 * 2.0 * G_PI / 360.0 / 64.0;
190
191   /*  angle2 is relative to angle1 and can be negative, which switches
192    *  the drawing direction
193    */
194   if (angle2 < 0)
195     clockwise = TRUE;
196
197   /*  below, flip the coordinate system back to its original y-diretion
198    *  so the angles passed to CGContextAddArc() are interpreted as
199    *  expected
200    *
201    *  FIXME: the implementation below works only for perfect circles
202    *  (width == height). Any other aspect ratio either scales the
203    *  line width unevenly or scales away the path entirely for very
204    *  small line widths (esp. for line_width == 0, which is a hair
205    *  line on X11 but must be approximated with the thinnest possible
206    *  line on quartz).
207    */
208
209   if (filled)
210     {
211       CGContextTranslateCTM (context,
212                              x + width / 2.0,
213                              y + height / 2.0);
214       CGContextScaleCTM (context, 1.0, - (double)height / (double)width);
215
216       CGContextMoveToPoint (context, 0, 0);
217       CGContextAddArc (context, 0, 0, width / 2.0,
218                        start_angle, end_angle,
219                        clockwise);
220       CGContextClosePath (context);
221       CGContextFillPath (context);
222     }
223   else
224     {
225       CGContextTranslateCTM (context,
226                              x + width / 2.0 + 0.5,
227                              y + height / 2.0 + 0.5);
228       CGContextScaleCTM (context, 1.0, - (double)height / (double)width);
229
230       CGContextAddArc (context, 0, 0, width / 2.0,
231                        start_angle, end_angle,
232                        clockwise);
233       CGContextStrokePath (context);
234     }
235
236   CGContextRestoreGState (context);
237
238   gdk_quartz_drawable_release_context (drawable, context);
239 }
240
241 static void
242 gdk_quartz_draw_polygon (GdkDrawable *drawable,
243                          GdkGC       *gc,
244                          gboolean     filled,
245                          GdkPoint    *points,
246                          gint         npoints)
247 {
248   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
249   int i;
250
251   if (!context)
252     return;
253
254   _gdk_quartz_gc_update_cg_context (gc, drawable, context,
255                                     filled ?
256                                     GDK_QUARTZ_CONTEXT_FILL :
257                                     GDK_QUARTZ_CONTEXT_STROKE);
258
259   if (filled)
260     {
261       CGContextMoveToPoint (context, points[0].x, points[0].y);
262       for (i = 1; i < npoints; i++)
263         CGContextAddLineToPoint (context, points[i].x, points[i].y);
264
265       CGContextClosePath (context);
266       CGContextFillPath (context);
267     }
268   else
269     {
270       CGContextMoveToPoint (context, points[0].x + 0.5, points[0].y + 0.5);
271       for (i = 1; i < npoints; i++)
272         CGContextAddLineToPoint (context, points[i].x + 0.5, points[i].y + 0.5);
273
274       CGContextClosePath (context);
275       CGContextStrokePath (context);
276     }
277
278   gdk_quartz_drawable_release_context (drawable, context);
279 }
280
281 static void
282 gdk_quartz_draw_text (GdkDrawable *drawable,
283                       GdkFont     *font,
284                       GdkGC       *gc,
285                       gint         x,
286                       gint         y,
287                       const gchar *text,
288                       gint         text_length)
289 {
290   /* FIXME: Implement */
291 }
292
293 static void
294 gdk_quartz_draw_text_wc (GdkDrawable    *drawable,
295                          GdkFont        *font,
296                          GdkGC          *gc,
297                          gint            x,
298                          gint            y,
299                          const GdkWChar *text,
300                          gint            text_length)
301 {
302   /* FIXME: Implement */
303 }
304
305 static void
306 gdk_quartz_draw_drawable (GdkDrawable *drawable,
307                           GdkGC       *gc,
308                           GdkPixmap   *src,
309                           gint         xsrc,
310                           gint         ysrc,
311                           gint         xdest,
312                           gint         ydest,
313                           gint         width,
314                           gint         height)
315 {
316   int src_depth = gdk_drawable_get_depth (src);
317   int dest_depth = gdk_drawable_get_depth (drawable);
318   GdkDrawableImplQuartz *src_impl;
319
320   if (GDK_IS_DRAWABLE_IMPL_QUARTZ (src))
321     src_impl = GDK_DRAWABLE_IMPL_QUARTZ (src);
322   else if (GDK_IS_PIXMAP (src))
323     src_impl = GDK_DRAWABLE_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (src)->impl);
324   else if (GDK_IS_WINDOW (src))
325     {
326       src_impl = GDK_DRAWABLE_IMPL_QUARTZ (GDK_WINDOW_OBJECT (src)->impl);
327       /* FIXME: Implement drawing a window. */
328       return;
329     }
330   else
331     g_assert_not_reached ();
332   
333   if (src_depth == 1)
334     {
335       /* FIXME: src depth 1 is not supported yet */
336     }
337   else if (dest_depth != 0 && src_depth == dest_depth)
338     {
339       CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
340
341       if (!context)
342         return;
343
344       _gdk_quartz_gc_update_cg_context (gc, drawable, context,
345                                         GDK_QUARTZ_CONTEXT_STROKE);
346
347       CGContextClipToRect (context, CGRectMake (xdest, ydest, width, height));
348       CGContextTranslateCTM (context, xdest - xsrc, ydest - ysrc);
349       CGContextDrawImage (context, 
350                           CGRectMake(0, 0, 
351                                      GDK_PIXMAP_IMPL_QUARTZ (src_impl)->width, 
352                                      GDK_PIXMAP_IMPL_QUARTZ (src_impl)->height), 
353                           GDK_PIXMAP_IMPL_QUARTZ (src_impl)->image);
354
355       gdk_quartz_drawable_release_context (drawable, context);
356     }
357   else
358     g_warning ("Attempt to draw a drawable with depth %d to a drawable with depth %d",
359                src_depth, dest_depth);
360 }
361
362 static void
363 gdk_quartz_draw_points (GdkDrawable *drawable,
364                         GdkGC       *gc,
365                         GdkPoint    *points,
366                         gint         npoints)
367 {
368   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
369   int i;
370
371   if (!context)
372     return;
373
374   _gdk_quartz_gc_update_cg_context (gc, drawable, context,
375                                     GDK_QUARTZ_CONTEXT_STROKE |
376                                     GDK_QUARTZ_CONTEXT_FILL);
377
378   /* Just draw 1x1 rectangles */
379   for (i = 0; i < npoints; i++) 
380     {
381       CGRect rect = CGRectMake (points[i].x, points[i].y, 1, 1);
382       CGContextFillRect (context, rect);
383     }
384
385   gdk_quartz_drawable_release_context (drawable, context);
386 }
387
388 static inline void
389 gdk_quartz_fix_cap_not_last_line (GdkGCQuartz *private,
390                                   gint         x1,
391                                   gint         y1,
392                                   gint         x2,
393                                   gint         y2,
394                                   gint        *xfix,
395                                   gint        *yfix)
396 {
397   *xfix = 0;
398   *yfix = 0;
399
400   if (private->cap_style == GDK_CAP_NOT_LAST && private->line_width == 0)
401     {
402       /* fix only vertical and horizontal lines for now */
403
404       if (y1 == y2 && x1 != x2)
405         {
406           *xfix = (x1 < x2) ? -1 : 1;
407         }
408       else if (x1 == x2 && y1 != y2)
409         {
410           *yfix = (y1 < y2) ? -1 : 1;
411         }
412     }
413 }
414
415 static void
416 gdk_quartz_draw_segments (GdkDrawable    *drawable,
417                           GdkGC          *gc,
418                           GdkSegment     *segs,
419                           gint            nsegs)
420 {
421   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
422   GdkGCQuartz *private;
423   int i;
424
425   if (!context)
426     return;
427
428   private = GDK_GC_QUARTZ (gc);
429
430   _gdk_quartz_gc_update_cg_context (gc, drawable, context,
431                                     GDK_QUARTZ_CONTEXT_STROKE);
432
433   for (i = 0; i < nsegs; i++)
434     {
435       gint xfix, yfix;
436
437       gdk_quartz_fix_cap_not_last_line (private,
438                                         segs[i].x1, segs[i].y1,
439                                         segs[i].x2, segs[i].y2,
440                                         &xfix, &yfix);
441
442       CGContextMoveToPoint (context, segs[i].x1 + 0.5, segs[i].y1 + 0.5);
443       CGContextAddLineToPoint (context, segs[i].x2 + 0.5 + xfix, segs[i].y2 + 0.5 + yfix);
444     }
445   
446   CGContextStrokePath (context);
447
448   gdk_quartz_drawable_release_context (drawable, context);
449 }
450
451 static void
452 gdk_quartz_draw_lines (GdkDrawable *drawable,
453                        GdkGC       *gc,
454                        GdkPoint    *points,
455                        gint         npoints)
456 {
457   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
458   GdkGCQuartz *private;
459   gint xfix, yfix;
460   gint i;
461
462   if (!context)
463     return;
464
465   private = GDK_GC_QUARTZ (gc);
466
467   _gdk_quartz_gc_update_cg_context (gc, drawable, context,
468                                     GDK_QUARTZ_CONTEXT_STROKE);
469
470   CGContextMoveToPoint (context, points[0].x + 0.5, points[0].y + 0.5);
471
472   for (i = 1; i < npoints - 1; i++)
473     CGContextAddLineToPoint (context, points[i].x + 0.5, points[i].y + 0.5);
474
475   gdk_quartz_fix_cap_not_last_line (private,
476                                     points[npoints - 2].x, points[npoints - 2].y,
477                                     points[npoints - 1].x, points[npoints - 1].y,
478                                     &xfix, &yfix);
479
480   CGContextAddLineToPoint (context,
481                            points[npoints - 1].x + 0.5 + xfix,
482                            points[npoints - 1].y + 0.5 + yfix);
483
484   CGContextStrokePath (context);
485
486   gdk_quartz_drawable_release_context (drawable, context);
487 }
488
489 static void
490 gdk_quartz_draw_pixbuf (GdkDrawable     *drawable,
491                         GdkGC           *gc,
492                         GdkPixbuf       *pixbuf,
493                         gint             src_x,
494                         gint             src_y,
495                         gint             dest_x,
496                         gint             dest_y,
497                         gint             width,
498                         gint             height,
499                         GdkRgbDither     dither,
500                         gint             x_dither,
501                         gint             y_dither)
502 {
503   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
504   CGColorSpaceRef colorspace;
505   CGDataProviderRef data_provider;
506   CGImageRef image;
507   void *data;
508   int rowstride, pixbuf_width, pixbuf_height;
509   gboolean has_alpha;
510
511   if (!context)
512     return;
513
514   pixbuf_width = gdk_pixbuf_get_width (pixbuf);
515   pixbuf_height = gdk_pixbuf_get_height (pixbuf);
516   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
517   has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
518
519   data = gdk_pixbuf_get_pixels (pixbuf);
520
521   colorspace = CGColorSpaceCreateDeviceRGB ();
522   data_provider = CGDataProviderCreateWithData (NULL, data, pixbuf_height * rowstride, NULL);
523
524   image = CGImageCreate (pixbuf_width, pixbuf_height, 8,
525                          has_alpha ? 32 : 24, rowstride, 
526                          colorspace, 
527                          has_alpha ? kCGImageAlphaLast : 0,
528                          data_provider, NULL, FALSE, 
529                          kCGRenderingIntentDefault);
530
531   CGDataProviderRelease (data_provider);
532   CGColorSpaceRelease (colorspace);
533
534   _gdk_quartz_gc_update_cg_context (gc, drawable, context,
535                                     GDK_QUARTZ_CONTEXT_STROKE);
536
537   CGContextClipToRect (context, CGRectMake (dest_x, dest_y, width, height));
538   CGContextTranslateCTM (context, dest_x - src_x, dest_y - src_y + pixbuf_height);
539   CGContextScaleCTM (context, 1, -1);
540
541   CGContextDrawImage (context, CGRectMake (0, 0, pixbuf_width, pixbuf_height), image);
542   CGImageRelease (image);
543
544   gdk_quartz_drawable_release_context (drawable, context);
545 }
546
547 static void
548 gdk_quartz_draw_image (GdkDrawable     *drawable,
549                        GdkGC           *gc,
550                        GdkImage        *image,
551                        gint             xsrc,
552                        gint             ysrc,
553                        gint             xdest,
554                        gint             ydest,
555                        gint             width,
556                        gint             height)
557 {
558   CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
559   CGColorSpaceRef colorspace;
560   CGDataProviderRef data_provider;
561   CGImageRef cgimage;
562
563   if (!context)
564     return;
565
566   colorspace = CGColorSpaceCreateDeviceRGB ();
567   data_provider = CGDataProviderCreateWithData (NULL, image->mem, image->height * image->bpl, NULL);
568
569   /* FIXME: Make sure that this function draws 32-bit images correctly,
570   * also check endianness wrt kCGImageAlphaNoneSkipFirst */
571   cgimage = CGImageCreate (image->width, image->height, 8,
572                            32, image->bpl,
573                            colorspace,
574                            kCGImageAlphaNoneSkipFirst, 
575                            data_provider, NULL, FALSE, kCGRenderingIntentDefault);
576
577   CGDataProviderRelease (data_provider);
578   CGColorSpaceRelease (colorspace);
579
580   _gdk_quartz_gc_update_cg_context (gc, drawable, context,
581                                     GDK_QUARTZ_CONTEXT_STROKE);
582
583   CGContextClipToRect (context, CGRectMake (xdest, ydest, width, height));
584   CGContextTranslateCTM (context, xdest - xsrc, ydest - ysrc + image->height);
585   CGContextScaleCTM (context, 1, -1);
586
587   CGContextDrawImage (context, CGRectMake (0, 0, image->width, image->height), cgimage);
588   CGImageRelease (cgimage);
589
590   gdk_quartz_drawable_release_context (drawable, context);
591 }
592
593 static void
594 gdk_drawable_impl_quartz_finalize (GObject *object)
595 {
596   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (object);
597
598   if (impl->colormap)
599     g_object_unref (impl->colormap);
600
601   G_OBJECT_CLASS (parent_class)->finalize (object);
602 }
603
604 static void
605 gdk_drawable_impl_quartz_class_init (GdkDrawableImplQuartzClass *klass)
606 {
607   GObjectClass *object_class = G_OBJECT_CLASS (klass);
608   GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
609
610   parent_class = g_type_class_peek_parent (klass);
611
612   object_class->finalize = gdk_drawable_impl_quartz_finalize;
613
614   drawable_class->create_gc = _gdk_quartz_gc_new;
615   drawable_class->draw_rectangle = gdk_quartz_draw_rectangle;
616   drawable_class->draw_arc = gdk_quartz_draw_arc;
617   drawable_class->draw_polygon = gdk_quartz_draw_polygon;
618   drawable_class->draw_text = gdk_quartz_draw_text;
619   drawable_class->draw_text_wc = gdk_quartz_draw_text_wc;
620   drawable_class->draw_drawable = gdk_quartz_draw_drawable;
621   drawable_class->draw_points = gdk_quartz_draw_points;
622   drawable_class->draw_segments = gdk_quartz_draw_segments;
623   drawable_class->draw_lines = gdk_quartz_draw_lines;
624   drawable_class->draw_image = gdk_quartz_draw_image;
625   drawable_class->draw_pixbuf = gdk_quartz_draw_pixbuf;
626
627   drawable_class->ref_cairo_surface = gdk_quartz_ref_cairo_surface;
628
629   drawable_class->set_colormap = gdk_quartz_set_colormap;
630   drawable_class->get_colormap = gdk_quartz_get_colormap;
631
632   drawable_class->get_depth = gdk_quartz_get_depth;
633   drawable_class->get_screen = gdk_quartz_get_screen;
634   drawable_class->get_visual = gdk_quartz_get_visual;
635
636   drawable_class->_copy_to_image = _gdk_quartz_image_copy_to_image;
637 }
638
639 GType
640 gdk_drawable_impl_quartz_get_type (void)
641 {
642   static GType object_type = 0;
643
644   if (!object_type)
645     {
646       static const GTypeInfo object_info =
647       {
648         sizeof (GdkDrawableImplQuartzClass),
649         (GBaseInitFunc) NULL,
650         (GBaseFinalizeFunc) NULL,
651         (GClassInitFunc) gdk_drawable_impl_quartz_class_init,
652         NULL,           /* class_finalize */
653         NULL,           /* class_data */
654         sizeof (GdkDrawableImplQuartz),
655         0,              /* n_preallocs */
656         (GInstanceInitFunc) NULL,
657       };
658       
659       object_type = g_type_register_static (GDK_TYPE_DRAWABLE,
660                                             "GdkDrawableImplQuartz",
661                                             &object_info, 0);
662     }
663   
664   return object_type;
665 }
666
667 CGContextRef 
668 gdk_quartz_drawable_get_context (GdkDrawable *drawable,
669                                  gboolean     antialias)
670 {
671   GdkDrawableImplQuartz *drawable_impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
672   CGContextRef           cg_context;
673
674   if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable) &&
675       GDK_WINDOW_DESTROYED (drawable_impl->wrapper))
676     return NULL;
677
678   if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable))
679     {
680       GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (drawable);
681
682       /* Lock focus when not called as part of a drawRect call. This
683        * is needed when called from outside "real" expose events, for
684        * example for synthesized expose events when realizing windows
685        * and for widgets that send fake expose events like the arrow
686        * buttons in spinbuttons.
687        */
688       if (window_impl->in_paint_rect_count == 0)
689         {
690           if (![window_impl->view lockFocusIfCanDraw])
691             return NULL;
692         }
693
694       cg_context = [[NSGraphicsContext currentContext] graphicsPort];
695       CGContextSaveGState (cg_context);
696       CGContextSetAllowsAntialiasing (cg_context, antialias);
697           
698       /* We'll emulate the clipping caused by double buffering here */
699       if (window_impl->begin_paint_count != 0)
700         {
701           CGRect rect;
702           CGRect *cg_rects;
703           GdkRectangle *rects;
704           gint n_rects, i;
705           
706           gdk_region_get_rectangles (window_impl->paint_clip_region,
707                                      &rects, &n_rects);
708           
709           if (n_rects == 1)
710             cg_rects = &rect;
711           else
712             cg_rects = g_new (CGRect, n_rects);
713           
714           for (i = 0; i < n_rects; i++)
715             {
716               cg_rects[i].origin.x = rects[i].x;
717               cg_rects[i].origin.y = rects[i].y;
718               cg_rects[i].size.width = rects[i].width;
719               cg_rects[i].size.height = rects[i].height;
720             }
721           
722           CGContextClipToRects (cg_context, cg_rects, n_rects);
723           
724           g_free (rects);
725           if (cg_rects != &rect)
726             g_free (cg_rects);
727         }
728     }
729   else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable))
730     {
731       GdkPixmapImplQuartz *impl = GDK_PIXMAP_IMPL_QUARTZ (drawable);
732       
733       cg_context = CGBitmapContextCreate (impl->data,
734                                           CGImageGetWidth (impl->image),
735                                           CGImageGetHeight (impl->image),
736                                           CGImageGetBitsPerComponent (impl->image),
737                                           CGImageGetBytesPerRow (impl->image),
738                                           CGImageGetColorSpace (impl->image),
739                                           CGImageGetBitmapInfo (impl->image));
740       CGContextSetAllowsAntialiasing (cg_context, antialias);
741     }
742   else 
743     {
744       g_warning ("Tried to create CGContext for something not a quartz window or pixmap");
745       cg_context = NULL;
746     }
747
748   return cg_context;
749 }
750
751 void
752 gdk_quartz_drawable_release_context (GdkDrawable  *drawable, 
753                                      CGContextRef  cg_context)
754 {
755   if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable))
756     {
757       GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (drawable);
758
759       CGContextRestoreGState (cg_context);
760       CGContextSetAllowsAntialiasing (cg_context, TRUE);
761
762       /* See comment in gdk_quartz_drawable_get_context(). */
763       if (window_impl->in_paint_rect_count == 0)
764         [window_impl->view unlockFocus];
765     }
766   else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable))
767     CGContextRelease (cg_context);
768 }
769
770 void
771 _gdk_quartz_drawable_finish (GdkDrawable *drawable)
772 {
773   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
774
775   if (impl->cairo_surface)
776     {
777       cairo_surface_finish (impl->cairo_surface);
778       cairo_surface_set_user_data (impl->cairo_surface, &gdk_quartz_cairo_key,
779                                    NULL, NULL);
780       impl->cairo_surface = NULL;
781     }
782 }