1 /* gdkdrawable-quartz.c
3 * Copyright (C) 2005-2007 Imendio AB
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.
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.
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.
22 #include <cairo-quartz.h>
23 #include "gdkprivate-quartz.h"
25 static gpointer parent_class;
27 static cairo_user_data_key_t gdk_quartz_cairo_key;
30 GdkDrawable *drawable;
31 CGContextRef cg_context;
32 } GdkQuartzCairoSurfaceData;
35 gdk_quartz_cairo_surface_destroy (void *data)
37 GdkQuartzCairoSurfaceData *surface_data = data;
38 GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (surface_data->drawable);
40 gdk_quartz_drawable_release_context (surface_data->drawable,
41 surface_data->cg_context);
43 impl->cairo_surface = NULL;
45 g_free (surface_data);
48 static cairo_surface_t *
49 gdk_quartz_ref_cairo_surface (GdkDrawable *drawable)
51 GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
53 if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable) &&
54 GDK_WINDOW_DESTROYED (impl->wrapper))
57 if (!impl->cairo_surface)
59 CGContextRef cg_context;
61 GdkQuartzCairoSurfaceData *surface_data;
63 cg_context = gdk_quartz_drawable_get_context (drawable, TRUE);
67 gdk_drawable_get_size (drawable, &width, &height);
69 impl->cairo_surface = cairo_quartz_surface_create_for_cg_context (cg_context, width, height);
71 surface_data = g_new (GdkQuartzCairoSurfaceData, 1);
72 surface_data->drawable = drawable;
73 surface_data->cg_context = cg_context;
75 cairo_surface_set_user_data (impl->cairo_surface, &gdk_quartz_cairo_key,
76 surface_data, gdk_quartz_cairo_surface_destroy);
79 cairo_surface_reference (impl->cairo_surface);
81 return impl->cairo_surface;
85 gdk_quartz_set_colormap (GdkDrawable *drawable,
86 GdkColormap *colormap)
88 GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
90 if (impl->colormap == colormap)
94 g_object_unref (impl->colormap);
95 impl->colormap = colormap;
97 g_object_ref (impl->colormap);
101 gdk_quartz_get_colormap (GdkDrawable *drawable)
103 return GDK_DRAWABLE_IMPL_QUARTZ (drawable)->colormap;
107 gdk_quartz_get_screen (GdkDrawable *drawable)
113 gdk_quartz_get_visual (GdkDrawable *drawable)
115 return gdk_drawable_get_visual (GDK_DRAWABLE_IMPL_QUARTZ (drawable)->wrapper);
119 gdk_quartz_get_depth (GdkDrawable *drawable)
121 /* This is a bit bogus but I'm not sure the other way is better */
123 return gdk_drawable_get_depth (GDK_DRAWABLE_IMPL_QUARTZ (drawable)->wrapper);
127 gdk_quartz_draw_rectangle (GdkDrawable *drawable,
135 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
140 _gdk_quartz_gc_update_cg_context (gc,
144 GDK_QUARTZ_CONTEXT_FILL :
145 GDK_QUARTZ_CONTEXT_STROKE);
149 CGRect rect = CGRectMake (x, y, width, height);
151 CGContextFillRect (context, rect);
155 CGRect rect = CGRectMake (x + 0.5, y + 0.5, width, height);
157 CGContextStrokeRect (context, rect);
160 gdk_quartz_drawable_release_context (drawable, context);
164 gdk_quartz_draw_arc (GdkDrawable *drawable,
174 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
175 float start_angle, end_angle;
176 gboolean clockwise = FALSE;
181 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
183 GDK_QUARTZ_CONTEXT_FILL :
184 GDK_QUARTZ_CONTEXT_STROKE);
186 CGContextSaveGState (context);
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;
191 /* angle2 is relative to angle1 and can be negative, which switches
192 * the drawing direction
197 /* below, flip the coordinate system back to its original y-diretion
198 * so the angles passed to CGContextAddArc() are interpreted as
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
211 CGContextTranslateCTM (context,
214 CGContextScaleCTM (context, 1.0, - (double)height / (double)width);
216 CGContextMoveToPoint (context, 0, 0);
217 CGContextAddArc (context, 0, 0, width / 2.0,
218 start_angle, end_angle,
220 CGContextClosePath (context);
221 CGContextFillPath (context);
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);
230 CGContextAddArc (context, 0, 0, width / 2.0,
231 start_angle, end_angle,
233 CGContextStrokePath (context);
236 CGContextRestoreGState (context);
238 gdk_quartz_drawable_release_context (drawable, context);
242 gdk_quartz_draw_polygon (GdkDrawable *drawable,
248 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
254 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
256 GDK_QUARTZ_CONTEXT_FILL :
257 GDK_QUARTZ_CONTEXT_STROKE);
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);
265 CGContextClosePath (context);
266 CGContextFillPath (context);
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);
274 CGContextClosePath (context);
275 CGContextStrokePath (context);
278 gdk_quartz_drawable_release_context (drawable, context);
282 gdk_quartz_draw_text (GdkDrawable *drawable,
290 /* FIXME: Implement */
294 gdk_quartz_draw_text_wc (GdkDrawable *drawable,
299 const GdkWChar *text,
302 /* FIXME: Implement */
306 gdk_quartz_draw_drawable (GdkDrawable *drawable,
316 int src_depth = gdk_drawable_get_depth (src);
317 int dest_depth = gdk_drawable_get_depth (drawable);
318 GdkDrawableImplQuartz *src_impl;
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))
326 src_impl = GDK_DRAWABLE_IMPL_QUARTZ (GDK_WINDOW_OBJECT (src)->impl);
327 /* FIXME: Implement drawing a window. */
331 g_assert_not_reached ();
335 /* FIXME: src depth 1 is not supported yet */
337 else if (dest_depth != 0 && src_depth == dest_depth)
339 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
344 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
345 GDK_QUARTZ_CONTEXT_STROKE);
347 CGContextClipToRect (context, CGRectMake (xdest, ydest, width, height));
348 CGContextTranslateCTM (context, xdest - xsrc, ydest - ysrc);
349 CGContextDrawImage (context,
351 GDK_PIXMAP_IMPL_QUARTZ (src_impl)->width,
352 GDK_PIXMAP_IMPL_QUARTZ (src_impl)->height),
353 GDK_PIXMAP_IMPL_QUARTZ (src_impl)->image);
355 gdk_quartz_drawable_release_context (drawable, context);
358 g_warning ("Attempt to draw a drawable with depth %d to a drawable with depth %d",
359 src_depth, dest_depth);
363 gdk_quartz_draw_points (GdkDrawable *drawable,
368 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
374 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
375 GDK_QUARTZ_CONTEXT_STROKE |
376 GDK_QUARTZ_CONTEXT_FILL);
378 /* Just draw 1x1 rectangles */
379 for (i = 0; i < npoints; i++)
381 CGRect rect = CGRectMake (points[i].x, points[i].y, 1, 1);
382 CGContextFillRect (context, rect);
385 gdk_quartz_drawable_release_context (drawable, context);
389 gdk_quartz_fix_cap_not_last_line (GdkGCQuartz *private,
400 if (private->cap_style == GDK_CAP_NOT_LAST && private->line_width == 0)
402 /* fix only vertical and horizontal lines for now */
404 if (y1 == y2 && x1 != x2)
406 *xfix = (x1 < x2) ? -1 : 1;
408 else if (x1 == x2 && y1 != y2)
410 *yfix = (y1 < y2) ? -1 : 1;
416 gdk_quartz_draw_segments (GdkDrawable *drawable,
421 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
422 GdkGCQuartz *private;
428 private = GDK_GC_QUARTZ (gc);
430 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
431 GDK_QUARTZ_CONTEXT_STROKE);
433 for (i = 0; i < nsegs; i++)
437 gdk_quartz_fix_cap_not_last_line (private,
438 segs[i].x1, segs[i].y1,
439 segs[i].x2, segs[i].y2,
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);
446 CGContextStrokePath (context);
448 gdk_quartz_drawable_release_context (drawable, context);
452 gdk_quartz_draw_lines (GdkDrawable *drawable,
457 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
458 GdkGCQuartz *private;
465 private = GDK_GC_QUARTZ (gc);
467 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
468 GDK_QUARTZ_CONTEXT_STROKE);
470 CGContextMoveToPoint (context, points[0].x + 0.5, points[0].y + 0.5);
472 for (i = 1; i < npoints - 1; i++)
473 CGContextAddLineToPoint (context, points[i].x + 0.5, points[i].y + 0.5);
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,
480 CGContextAddLineToPoint (context,
481 points[npoints - 1].x + 0.5 + xfix,
482 points[npoints - 1].y + 0.5 + yfix);
484 CGContextStrokePath (context);
486 gdk_quartz_drawable_release_context (drawable, context);
490 gdk_quartz_draw_pixbuf (GdkDrawable *drawable,
503 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
504 CGColorSpaceRef colorspace;
505 CGDataProviderRef data_provider;
508 int rowstride, pixbuf_width, pixbuf_height;
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);
519 data = gdk_pixbuf_get_pixels (pixbuf);
521 colorspace = CGColorSpaceCreateDeviceRGB ();
522 data_provider = CGDataProviderCreateWithData (NULL, data, pixbuf_height * rowstride, NULL);
524 image = CGImageCreate (pixbuf_width, pixbuf_height, 8,
525 has_alpha ? 32 : 24, rowstride,
527 has_alpha ? kCGImageAlphaLast : 0,
528 data_provider, NULL, FALSE,
529 kCGRenderingIntentDefault);
531 CGDataProviderRelease (data_provider);
532 CGColorSpaceRelease (colorspace);
534 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
535 GDK_QUARTZ_CONTEXT_STROKE);
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);
541 CGContextDrawImage (context, CGRectMake (0, 0, pixbuf_width, pixbuf_height), image);
542 CGImageRelease (image);
544 gdk_quartz_drawable_release_context (drawable, context);
548 gdk_quartz_draw_image (GdkDrawable *drawable,
558 CGContextRef context = gdk_quartz_drawable_get_context (drawable, FALSE);
559 CGColorSpaceRef colorspace;
560 CGDataProviderRef data_provider;
566 colorspace = CGColorSpaceCreateDeviceRGB ();
567 data_provider = CGDataProviderCreateWithData (NULL, image->mem, image->height * image->bpl, NULL);
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,
574 kCGImageAlphaNoneSkipFirst,
575 data_provider, NULL, FALSE, kCGRenderingIntentDefault);
577 CGDataProviderRelease (data_provider);
578 CGColorSpaceRelease (colorspace);
580 _gdk_quartz_gc_update_cg_context (gc, drawable, context,
581 GDK_QUARTZ_CONTEXT_STROKE);
583 CGContextClipToRect (context, CGRectMake (xdest, ydest, width, height));
584 CGContextTranslateCTM (context, xdest - xsrc, ydest - ysrc + image->height);
585 CGContextScaleCTM (context, 1, -1);
587 CGContextDrawImage (context, CGRectMake (0, 0, image->width, image->height), cgimage);
588 CGImageRelease (cgimage);
590 gdk_quartz_drawable_release_context (drawable, context);
594 gdk_drawable_impl_quartz_finalize (GObject *object)
596 GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (object);
599 g_object_unref (impl->colormap);
601 G_OBJECT_CLASS (parent_class)->finalize (object);
605 gdk_drawable_impl_quartz_class_init (GdkDrawableImplQuartzClass *klass)
607 GObjectClass *object_class = G_OBJECT_CLASS (klass);
608 GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
610 parent_class = g_type_class_peek_parent (klass);
612 object_class->finalize = gdk_drawable_impl_quartz_finalize;
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;
627 drawable_class->ref_cairo_surface = gdk_quartz_ref_cairo_surface;
629 drawable_class->set_colormap = gdk_quartz_set_colormap;
630 drawable_class->get_colormap = gdk_quartz_get_colormap;
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;
636 drawable_class->_copy_to_image = _gdk_quartz_image_copy_to_image;
640 gdk_drawable_impl_quartz_get_type (void)
642 static GType object_type = 0;
646 static const GTypeInfo object_info =
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),
656 (GInstanceInitFunc) NULL,
659 object_type = g_type_register_static (GDK_TYPE_DRAWABLE,
660 "GdkDrawableImplQuartz",
668 gdk_quartz_drawable_get_context (GdkDrawable *drawable,
671 GdkDrawableImplQuartz *drawable_impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
672 CGContextRef cg_context;
674 if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable) &&
675 GDK_WINDOW_DESTROYED (drawable_impl->wrapper))
678 if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable))
680 GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (drawable);
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.
688 if (window_impl->in_paint_rect_count == 0)
690 if (![window_impl->view lockFocusIfCanDraw])
694 cg_context = [[NSGraphicsContext currentContext] graphicsPort];
695 CGContextSaveGState (cg_context);
696 CGContextSetAllowsAntialiasing (cg_context, antialias);
698 /* We'll emulate the clipping caused by double buffering here */
699 if (window_impl->begin_paint_count != 0)
706 gdk_region_get_rectangles (window_impl->paint_clip_region,
712 cg_rects = g_new (CGRect, n_rects);
714 for (i = 0; i < n_rects; i++)
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;
722 CGContextClipToRects (cg_context, cg_rects, n_rects);
725 if (cg_rects != &rect)
729 else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable))
731 GdkPixmapImplQuartz *impl = GDK_PIXMAP_IMPL_QUARTZ (drawable);
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);
744 g_warning ("Tried to create CGContext for something not a quartz window or pixmap");
752 gdk_quartz_drawable_release_context (GdkDrawable *drawable,
753 CGContextRef cg_context)
755 if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable))
757 GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (drawable);
759 CGContextRestoreGState (cg_context);
760 CGContextSetAllowsAntialiasing (cg_context, TRUE);
762 /* See comment in gdk_quartz_drawable_get_context(). */
763 if (window_impl->in_paint_rect_count == 0)
764 [window_impl->view unlockFocus];
766 else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable))
767 CGContextRelease (cg_context);
771 _gdk_quartz_drawable_finish (GdkDrawable *drawable)
773 GdkDrawableImplQuartz *impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
775 if (impl->cairo_surface)
777 cairo_surface_finish (impl->cairo_surface);
778 cairo_surface_set_user_data (impl->cairo_surface, &gdk_quartz_cairo_key,
780 impl->cairo_surface = NULL;