]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkdrawable-quartz.c
Offset coordinates by 0.5 pixels to get consistant results for both image
[~andy/gtk] / gdk / quartz / gdkdrawable-quartz.c
1 /* gdkdrawable-quartz.c
2  *
3  * Copyright (C) 2005, 2006 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
23 #include <cairo/cairo-quartz.h>
24 #include "gdkprivate-quartz.h"
25
26 static gpointer parent_class;
27
28 typedef struct {
29   GdkDrawable *drawable;
30   CGContextRef context;
31 } SurfaceInfo;
32
33 static cairo_user_data_key_t surface_info_key;
34
35 static void
36 surface_info_destroy (void *data)
37 {
38   SurfaceInfo *info = data;
39
40   _gdk_quartz_drawable_release_context (info->drawable, info->context);
41
42   g_free (info);
43 }
44
45 static cairo_surface_t *
46 gdk_quartz_ref_cairo_surface (GdkDrawable *drawable)
47 {
48   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
49   CGContextRef context;
50   int width, height;
51   cairo_surface_t *surface;
52   SurfaceInfo *info;
53
54   if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable) &&
55       GDK_WINDOW_DESTROYED (impl->wrapper))
56     return NULL;
57
58   context = _gdk_quartz_drawable_get_context (drawable, TRUE);
59   if (!context)
60     return NULL;
61
62   gdk_drawable_get_size (drawable, &width, &height);
63
64   surface = cairo_quartz_surface_create (context, TRUE, width, height);
65
66   info = g_new (SurfaceInfo, 1);
67   info->drawable = drawable;
68   info->context = context;
69
70   cairo_surface_set_user_data (surface, &surface_info_key,
71                                info, surface_info_destroy);
72   return surface;
73 }
74
75 static void
76 gdk_quartz_set_colormap (GdkDrawable *drawable,
77                          GdkColormap *colormap)
78 {
79   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
80
81   if (impl->colormap == colormap)
82     return;
83   
84   if (impl->colormap)
85     g_object_unref (impl->colormap);
86   impl->colormap = colormap;
87   if (impl->colormap)
88     g_object_ref (impl->colormap);
89 }
90
91 static GdkColormap*
92 gdk_quartz_get_colormap (GdkDrawable *drawable)
93 {
94   return GDK_DRAWABLE_IMPL_QUARTZ (drawable)->colormap;
95 }
96
97 static GdkScreen*
98 gdk_quartz_get_screen (GdkDrawable *drawable)
99 {
100   return _gdk_screen;
101 }
102
103 static GdkVisual*
104 gdk_quartz_get_visual (GdkDrawable *drawable)
105 {
106   return gdk_drawable_get_visual (GDK_DRAWABLE_IMPL_QUARTZ (drawable)->wrapper);
107 }
108
109 static int
110 gdk_quartz_get_depth (GdkDrawable *drawable)
111 {
112   /* This is a bit bogus but I'm not sure the other way is better */
113
114   return gdk_drawable_get_depth (GDK_DRAWABLE_IMPL_QUARTZ (drawable)->wrapper);
115 }
116
117 static void
118 gdk_quartz_draw_rectangle (GdkDrawable *drawable,
119                           GdkGC       *gc,
120                           gboolean     filled,
121                           gint         x,
122                           gint         y,
123                           gint         width,
124                           gint         height)
125 {
126   CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
127   CGRect rect = CGRectMake (x + 0.5, y + 0.5, width, height);
128
129   if (!context)
130     return;
131
132   _gdk_quartz_update_context_from_gc (context, gc);
133
134   if (filled)
135     {
136       _gdk_quartz_set_context_fill_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
137                                                     _gdk_gc_get_fg_pixel (gc));
138       CGContextFillRect (context, rect);
139     }
140   else
141     {
142       _gdk_quartz_set_context_stroke_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
143                                                       _gdk_gc_get_fg_pixel (gc));
144       CGContextStrokeRect (context, rect);
145     }
146
147   _gdk_quartz_drawable_release_context (drawable, context);
148 }
149
150 static void
151 gdk_quartz_draw_arc (GdkDrawable *drawable,
152                     GdkGC       *gc,
153                     gboolean     filled,
154                     gint         x,
155                     gint         y,
156                     gint         width,
157                     gint         height,
158                     gint         angle1,
159                     gint         angle2)
160 {
161   CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
162   float start_angle, end_angle;
163
164   if (!context)
165     return;
166
167   _gdk_quartz_update_context_from_gc (context, gc);
168
169   CGContextSaveGState (context);
170
171   CGContextTranslateCTM (context, 
172                          x + width / 2 + 0.5, 
173                          y + height / 2 + 0.5);
174   CGContextScaleCTM (context, 1.0, (float)height / width);
175   start_angle = (2 - (angle1 / (180.0 * 64.0))) * G_PI;
176   end_angle = start_angle - (angle2 / (180.0 * 64.0)) * G_PI;
177
178   if (filled)
179     {
180       _gdk_quartz_set_context_fill_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
181                                                     _gdk_gc_get_fg_pixel (gc));
182
183       CGContextMoveToPoint (context, 0, 0);
184       CGContextAddArc (context, 0, 0, width / 2,
185                        start_angle, end_angle,
186                        TRUE);
187       CGContextClosePath (context);
188       CGContextFillPath (context);
189     }
190   else
191     {
192       _gdk_quartz_set_context_stroke_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
193                                                       _gdk_gc_get_fg_pixel (gc));
194       CGContextAddArc (context, 0, 0, width / 2,
195                        start_angle, end_angle,
196                        TRUE);
197       CGContextStrokePath (context);
198     }
199
200   CGContextRestoreGState (context);
201
202   _gdk_quartz_drawable_release_context (drawable, context);
203 }
204
205 static void
206 gdk_quartz_draw_polygon (GdkDrawable *drawable,
207                          GdkGC       *gc,
208                          gboolean     filled,
209                          GdkPoint    *points,
210                          gint         npoints)
211 {
212   CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
213   int i;
214
215   if (!context)
216     return;
217
218   _gdk_quartz_update_context_from_gc (context, gc);
219
220   if (filled)
221     _gdk_quartz_set_context_fill_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
222                                                    _gdk_gc_get_fg_pixel (gc));
223   else
224     _gdk_quartz_set_context_stroke_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
225                                                      _gdk_gc_get_fg_pixel (gc));
226
227   CGContextMoveToPoint (context, points[0].x + 0.5, points[0].y + 0.5);
228   for (i = 1; i < npoints; i++)
229     CGContextAddLineToPoint (context, points[i].x + 0.5, points[i].y + 0.5);
230
231   CGContextClosePath (context);
232
233   if (filled)
234     CGContextFillPath (context);
235   else
236     CGContextStrokePath (context);
237
238   _gdk_quartz_drawable_release_context (drawable, context);
239 }
240
241 static void
242 gdk_quartz_draw_text (GdkDrawable *drawable,
243                       GdkFont     *font,
244                       GdkGC       *gc,
245                       gint         x,
246                       gint         y,
247                       const gchar *text,
248                       gint         text_length)
249 {
250   /* FIXME: Implement */
251 }
252
253 static void
254 gdk_quartz_draw_text_wc (GdkDrawable    *drawable,
255                          GdkFont        *font,
256                          GdkGC          *gc,
257                          gint            x,
258                          gint            y,
259                          const GdkWChar *text,
260                          gint            text_length)
261 {
262   /* FIXME: Implement */
263 }
264
265 static void
266 gdk_quartz_draw_drawable (GdkDrawable *drawable,
267                          GdkGC       *gc,
268                          GdkPixmap   *src,
269                          gint         xsrc,
270                          gint         ysrc,
271                          gint         xdest,
272                          gint         ydest,
273                          gint         width,
274                          gint         height)
275 {
276   int src_depth = gdk_drawable_get_depth (src);
277   int dest_depth = gdk_drawable_get_depth (drawable);
278   GdkDrawableImplQuartz *impl;
279   GdkDrawableImplQuartz *src_impl;
280   
281   impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
282
283   if (GDK_IS_DRAWABLE_IMPL_QUARTZ (src))
284     src_impl = GDK_DRAWABLE_IMPL_QUARTZ (src);
285   else
286     src_impl = GDK_DRAWABLE_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (src)->impl);
287   
288   if (src_depth == 1)
289     {
290       /* FIXME: src depth 1 is not supported yet */
291     }
292   else if (dest_depth != 0 && src_depth == dest_depth)
293     {
294       CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
295
296       if (!context)
297         return;
298
299       _gdk_quartz_update_context_from_gc (context, gc);
300
301       CGContextSetBlendMode (context, kCGBlendModeNormal);
302
303       CGContextClipToRect (context, CGRectMake (xdest, ydest, width, height));
304       CGContextTranslateCTM (context, xdest - xsrc, ydest - ysrc);
305       CGContextDrawImage (context, CGRectMake(0, 0, 
306                                               GDK_PIXMAP_IMPL_QUARTZ (src_impl)->width, 
307                                               GDK_PIXMAP_IMPL_QUARTZ (src_impl)->height), 
308                           GDK_PIXMAP_IMPL_QUARTZ (src_impl)->image);
309
310       _gdk_quartz_drawable_release_context (drawable, context);
311     }
312   else
313     g_warning ("Attempt to draw a drawable with depth %d to a drawable with depth %d",
314                src_depth, dest_depth);
315 }
316
317 static void
318 gdk_quartz_draw_points (GdkDrawable *drawable,
319                        GdkGC       *gc,
320                        GdkPoint    *points,
321                        gint         npoints)
322 {
323   int i;
324
325   /* Just draw 1x1 rectangles */
326   for (i = 0; i < npoints; i++) 
327     {
328       gdk_draw_rectangle (drawable, gc, TRUE, 
329                           points[i].x, points[i].y,
330                           1, 1);
331     }
332 }
333
334 static void
335 gdk_quartz_draw_segments (GdkDrawable    *drawable,
336                          GdkGC          *gc,
337                          GdkSegment     *segs,
338                          gint            nsegs)
339 {
340   CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
341   int i;
342
343   if (!context)
344     return;
345
346   _gdk_quartz_update_context_from_gc (context, gc);
347   _gdk_quartz_set_context_stroke_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
348                                                   _gdk_gc_get_fg_pixel (gc));
349
350   for (i = 0; i < nsegs; i++)
351     {
352       CGContextMoveToPoint (context, segs[i].x1 + 0.5, segs[i].y1 + 0.5);
353       CGContextAddLineToPoint (context, segs[i].x2 + 0.5, segs[i].y2 + 0.5);
354     }
355   
356   CGContextStrokePath (context);
357
358   _gdk_quartz_drawable_release_context (drawable, context);
359 }
360
361 static void
362 gdk_quartz_draw_lines (GdkDrawable *drawable,
363                        GdkGC       *gc,
364                        GdkPoint    *points,
365                        gint         npoints)
366 {
367   CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
368   int i;
369
370   if (!context)
371     return;
372
373   _gdk_quartz_update_context_from_gc (context, gc);
374   _gdk_quartz_set_context_stroke_color_from_pixel (context, gdk_drawable_get_colormap (drawable),
375                                                    _gdk_gc_get_fg_pixel (gc));
376   
377   for (i = 1; i < npoints; i++)
378     {
379       CGContextMoveToPoint (context, points[i - 1].x + 0.5, points[i - 1].y + 0.5);
380       CGContextAddLineToPoint (context, points[i].x + 0.5, points[i].y + 0.5);
381     }
382   
383   CGContextStrokePath (context);
384
385   _gdk_quartz_drawable_release_context (drawable, context);
386 }
387
388 static void
389 gdk_quartz_draw_pixbuf (GdkDrawable     *drawable,
390                        GdkGC           *gc,
391                        GdkPixbuf       *pixbuf,
392                        gint             src_x,
393                        gint             src_y,
394                        gint             dest_x,
395                        gint             dest_y,
396                        gint             width,
397                        gint             height,
398                        GdkRgbDither     dither,
399                        gint             x_dither,
400                        gint             y_dither)
401 {
402   CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
403   CGColorSpaceRef colorspace;
404   CGDataProviderRef data_provider;
405   CGImageRef image;
406   void *data;
407   int rowstride, pixbuf_width, pixbuf_height;
408   gboolean has_alpha;
409
410   if (!context)
411     return;
412
413   pixbuf_width = gdk_pixbuf_get_width (pixbuf);
414   pixbuf_height = gdk_pixbuf_get_height (pixbuf);
415   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
416   has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
417
418   data = gdk_pixbuf_get_pixels (pixbuf);
419
420   colorspace = CGColorSpaceCreateDeviceRGB ();
421   data_provider = CGDataProviderCreateWithData (NULL, data, pixbuf_height * rowstride, NULL);
422
423   image = CGImageCreate (pixbuf_width, pixbuf_height, 8,
424                          has_alpha ? 32 : 24, rowstride, 
425                          colorspace, 
426                          has_alpha ? kCGImageAlphaLast : 0,
427                          data_provider, NULL, FALSE, 
428                          kCGRenderingIntentDefault);
429
430   CGDataProviderRelease (data_provider);
431   CGColorSpaceRelease (colorspace);
432
433   _gdk_quartz_update_context_from_gc (context, gc);
434
435   CGContextSetBlendMode (context, kCGBlendModeNormal);
436
437   CGContextClipToRect (context, CGRectMake (dest_x, dest_y, width, height));
438   CGContextTranslateCTM (context, dest_x - src_x, dest_y - src_y + pixbuf_height);
439   CGContextScaleCTM (context, 1, -1);
440
441   CGContextDrawImage (context, CGRectMake (0, 0, pixbuf_width, pixbuf_height), image);
442   CGImageRelease (image);
443
444   _gdk_quartz_drawable_release_context (drawable, context);
445 }
446
447 static void
448 gdk_quartz_draw_image (GdkDrawable     *drawable,
449                       GdkGC           *gc,
450                       GdkImage        *image,
451                       gint             xsrc,
452                       gint             ysrc,
453                       gint             xdest,
454                       gint             ydest,
455                       gint             width,
456                       gint             height)
457 {
458   CGContextRef context = _gdk_quartz_drawable_get_context (drawable, FALSE);
459   CGColorSpaceRef colorspace;
460   CGDataProviderRef data_provider;
461   CGImageRef cgimage;
462
463   if (!context)
464     return;
465
466   colorspace = CGColorSpaceCreateDeviceRGB ();
467   data_provider = CGDataProviderCreateWithData (NULL, image->mem, image->height * image->bpl, NULL);
468
469   /* FIXME: Make sure that this function draws 32-bit images correctly,
470   * also check endianness wrt kCGImageAlphaNoneSkipFirst */
471   cgimage = CGImageCreate (image->width, image->height, 8,
472                            32, image->bpl,
473                            colorspace,
474                            kCGImageAlphaNoneSkipFirst, 
475                            data_provider, NULL, FALSE, kCGRenderingIntentDefault);
476
477   CGDataProviderRelease (data_provider);
478   CGColorSpaceRelease (colorspace);
479
480   _gdk_quartz_update_context_from_gc (context, gc);
481
482   CGContextSetBlendMode (context, kCGBlendModeNormal);
483
484   CGContextClipToRect (context, CGRectMake (xdest, ydest, width, height));
485   CGContextTranslateCTM (context, xdest - xsrc, ydest - ysrc + image->height);
486   CGContextScaleCTM (context, 1, -1);
487
488   CGContextDrawImage (context, CGRectMake (0, 0, image->width, image->height), cgimage);
489   CGImageRelease (cgimage);
490
491   _gdk_quartz_drawable_release_context (drawable, context);
492 }
493
494 static void
495 gdk_drawable_impl_quartz_finalize (GObject *object)
496 {
497   GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (object);
498
499   if (impl->colormap)
500     g_object_unref (impl->colormap);
501
502   G_OBJECT_CLASS (parent_class)->finalize (object);
503 }
504
505 static void
506 gdk_drawable_impl_quartz_class_init (GdkDrawableImplQuartzClass *klass)
507 {
508   GObjectClass *object_class = G_OBJECT_CLASS (klass);
509   GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
510
511   parent_class = g_type_class_peek_parent (klass);
512
513   object_class->finalize = gdk_drawable_impl_quartz_finalize;
514
515   drawable_class->create_gc = _gdk_quartz_gc_new;
516   drawable_class->draw_rectangle = gdk_quartz_draw_rectangle;
517   drawable_class->draw_arc = gdk_quartz_draw_arc;
518   drawable_class->draw_polygon = gdk_quartz_draw_polygon;
519   drawable_class->draw_text = gdk_quartz_draw_text;
520   drawable_class->draw_text_wc = gdk_quartz_draw_text_wc;
521   drawable_class->draw_drawable = gdk_quartz_draw_drawable;
522   drawable_class->draw_points = gdk_quartz_draw_points;
523   drawable_class->draw_segments = gdk_quartz_draw_segments;
524   drawable_class->draw_lines = gdk_quartz_draw_lines;
525   drawable_class->draw_image = gdk_quartz_draw_image;
526   drawable_class->draw_pixbuf = gdk_quartz_draw_pixbuf;
527
528   drawable_class->ref_cairo_surface = gdk_quartz_ref_cairo_surface;
529
530   drawable_class->set_colormap = gdk_quartz_set_colormap;
531   drawable_class->get_colormap = gdk_quartz_get_colormap;
532
533   drawable_class->get_depth = gdk_quartz_get_depth;
534   drawable_class->get_screen = gdk_quartz_get_screen;
535   drawable_class->get_visual = gdk_quartz_get_visual;
536
537   drawable_class->_copy_to_image = _gdk_quartz_copy_to_image;
538 }
539
540 GType
541 gdk_drawable_impl_quartz_get_type (void)
542 {
543   static GType object_type = 0;
544
545   if (!object_type)
546     {
547       static const GTypeInfo object_info =
548       {
549         sizeof (GdkDrawableImplQuartzClass),
550         (GBaseInitFunc) NULL,
551         (GBaseFinalizeFunc) NULL,
552         (GClassInitFunc) gdk_drawable_impl_quartz_class_init,
553         NULL,           /* class_finalize */
554         NULL,           /* class_data */
555         sizeof (GdkDrawableImplQuartz),
556         0,              /* n_preallocs */
557         (GInstanceInitFunc) NULL,
558       };
559       
560       object_type = g_type_register_static (GDK_TYPE_DRAWABLE,
561                                             "GdkDrawableImplQuartz",
562                                             &object_info, 0);
563     }
564   
565   return object_type;
566 }
567
568 CGContextRef 
569 _gdk_quartz_drawable_get_context (GdkDrawable *drawable, gboolean antialias)
570 {
571   if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable))
572     {
573       GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (drawable);
574       CGContextRef context;
575
576       impl->pool = [[NSAutoreleasePool alloc] init];
577
578       if (![impl->view lockFocusIfCanDraw])
579         {
580           [impl->pool release];
581           return NULL;
582         }
583
584       context = [[NSGraphicsContext currentContext] graphicsPort];
585
586       CGContextSaveGState (context);
587       CGContextSetAllowsAntialiasing (context, antialias);
588
589       return context;
590     }
591   else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable))
592     {
593       GdkPixmapImplQuartz *impl = GDK_PIXMAP_IMPL_QUARTZ (drawable);
594       CGContextRef context;
595
596       context = CGBitmapContextCreate (impl->data,
597                                        CGImageGetWidth (impl->image),
598                                        CGImageGetHeight (impl->image),
599                                        CGImageGetBitsPerComponent (impl->image),
600                                        CGImageGetBytesPerRow (impl->image),
601                                        CGImageGetColorSpace (impl->image),
602                                        CGImageGetBitmapInfo (impl->image));
603       CGContextSetAllowsAntialiasing (context, antialias);
604       
605       return context;
606     }
607
608   g_assert_not_reached ();
609
610   return NULL;
611 }
612
613 void
614 _gdk_quartz_drawable_release_context (GdkDrawable *drawable, CGContextRef context)
615 {
616   if (!context)
617     return;
618
619   if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable))
620     {
621       GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (drawable);
622
623       CGContextRestoreGState (context);
624       CGContextSetAllowsAntialiasing (context, TRUE);
625
626       [[NSGraphicsContext currentContext] flushGraphics];
627       [impl->view unlockFocus];
628       [impl->pool release];
629     }
630   else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable))
631     {
632       CGContextRelease (context);
633     }
634 }