3 * Copyright (C) 2005 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.
24 #include "gdkprivate-quartz.h"
26 static gpointer parent_class = NULL;
29 gdk_quartz_gc_get_values (GdkGC *gc,
34 private = GDK_GC_QUARTZ (gc);
36 values->foreground.pixel = _gdk_gc_get_fg_pixel (gc);
37 values->background.pixel = _gdk_gc_get_bg_pixel (gc);
39 values->function = private->function;
41 values->fill = _gdk_gc_get_fill (gc);
42 values->tile = _gdk_gc_get_tile (gc);
43 values->stipple = _gdk_gc_get_stipple (gc);
45 /* The X11 backend always returns a NULL clip_mask. */
46 values->clip_mask = NULL;
48 values->ts_x_origin = gc->ts_x_origin;
49 values->ts_y_origin = gc->ts_y_origin;
50 values->clip_x_origin = gc->clip_x_origin;
51 values->clip_y_origin = gc->clip_y_origin;
53 values->graphics_exposures = private->graphics_exposures;
55 values->line_width = private->line_width;
56 values->line_style = private->line_style;
57 values->cap_style = private->cap_style;
58 values->join_style = private->join_style;
63 data_provider_release (void *info, const void *data, size_t size)
69 create_clip_mask (GdkPixmap *source_pixmap)
71 int width, height, bytes_per_row, bits_per_pixel;
75 CGContextRef cg_context;
76 CGDataProviderRef data_provider;
78 /* We need to flip the clip mask here, because this cannot be done during
79 * the drawing process when this mask will be used to do clipping. We
80 * quickly create a new CGImage, set up a CGContext, draw the source
81 * image while flipping, and done. If this appears too slow in the
82 * future, we would look into doing this by hand on the actual raw
85 source = GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (source_pixmap)->impl)->image;
87 width = CGImageGetWidth (source);
88 height = CGImageGetHeight (source);
89 bytes_per_row = CGImageGetBytesPerRow (source);
90 bits_per_pixel = CGImageGetBitsPerPixel (source);
92 data = g_malloc (height * bytes_per_row);
93 data_provider = CGDataProviderCreateWithData (data, data,
94 height * bytes_per_row,
95 data_provider_release);
97 clip_mask = CGImageCreate (width, height, 8,
100 CGImageGetColorSpace (source),
101 CGImageGetAlphaInfo (source),
102 data_provider, NULL, FALSE,
103 kCGRenderingIntentDefault);
104 CGDataProviderRelease (data_provider);
106 cg_context = CGBitmapContextCreate (data,
108 CGImageGetBitsPerComponent (source),
110 CGImageGetColorSpace (source),
111 CGImageGetBitmapInfo (source));
113 CGContextTranslateCTM (cg_context, 0, height);
114 CGContextScaleCTM (cg_context, 1.0, -1.0);
116 CGContextDrawImage (cg_context,
117 CGRectMake (0, 0, width, height), source);
119 CGContextRelease (cg_context);
125 gdk_quartz_gc_set_values (GdkGC *gc,
127 GdkGCValuesMask mask)
129 GdkGCQuartz *private = GDK_GC_QUARTZ (gc);
131 if (mask & GDK_GC_FUNCTION)
132 private->function = values->function;
134 if (mask & GDK_GC_SUBWINDOW)
135 private->subwindow_mode = values->subwindow_mode;
137 if (mask & GDK_GC_EXPOSURES)
138 private->graphics_exposures = values->graphics_exposures;
140 if (mask & GDK_GC_CLIP_MASK)
142 private->have_clip_region = FALSE;
143 private->have_clip_mask = values->clip_mask != NULL;
144 if (private->clip_mask)
145 CGImageRelease (private->clip_mask);
147 if (values->clip_mask)
148 private->clip_mask = create_clip_mask (values->clip_mask);
150 private->clip_mask = NULL;
153 if (mask & GDK_GC_LINE_WIDTH)
154 private->line_width = values->line_width;
156 if (mask & GDK_GC_LINE_STYLE)
157 private->line_style = values->line_style;
159 if (mask & GDK_GC_CAP_STYLE)
160 private->cap_style = values->cap_style;
162 if (mask & GDK_GC_JOIN_STYLE)
163 private->join_style = values->join_style;
167 gdk_quartz_gc_set_dashes (GdkGC *gc,
172 GdkGCQuartz *private = GDK_GC_QUARTZ (gc);
175 private->dash_count = n;
176 g_free (private->dash_lengths);
177 private->dash_lengths = g_new (CGFloat, n);
178 for (i = 0; i < n; i++)
179 private->dash_lengths[i] = (CGFloat) dash_list[i];
180 private->dash_phase = (CGFloat) dash_offset;
184 gdk_gc_quartz_finalize (GObject *object)
186 GdkGCQuartz *private = GDK_GC_QUARTZ (object);
188 if (private->clip_mask)
189 CGImageRelease (private->clip_mask);
191 G_OBJECT_CLASS (parent_class)->finalize (object);
195 gdk_gc_quartz_class_init (GdkGCQuartzClass *klass)
197 GObjectClass *object_class = G_OBJECT_CLASS (klass);
198 GdkGCClass *gc_class = GDK_GC_CLASS (klass);
200 parent_class = g_type_class_peek_parent (klass);
202 object_class->finalize = gdk_gc_quartz_finalize;
204 gc_class->get_values = gdk_quartz_gc_get_values;
205 gc_class->set_values = gdk_quartz_gc_set_values;
206 gc_class->set_dashes = gdk_quartz_gc_set_dashes;
210 gdk_gc_quartz_init (GdkGCQuartz *gc_quartz)
212 gc_quartz->function = GDK_COPY;
213 gc_quartz->subwindow_mode = GDK_CLIP_BY_CHILDREN;
214 gc_quartz->graphics_exposures = TRUE;
215 gc_quartz->line_width = 0;
216 gc_quartz->line_style = GDK_LINE_SOLID;
217 gc_quartz->cap_style = GDK_CAP_BUTT;
218 gc_quartz->join_style = GDK_JOIN_MITER;
222 _gdk_gc_quartz_get_type (void)
224 static GType object_type = 0;
228 const GTypeInfo object_info =
230 sizeof (GdkGCQuartzClass),
231 (GBaseInitFunc) NULL,
232 (GBaseFinalizeFunc) NULL,
233 (GClassInitFunc) gdk_gc_quartz_class_init,
234 NULL, /* class_finalize */
235 NULL, /* class_data */
236 sizeof (GdkGCQuartz),
238 (GInstanceInitFunc) gdk_gc_quartz_init,
241 object_type = g_type_register_static (GDK_TYPE_GC,
250 _gdk_quartz_gc_new (GdkDrawable *drawable,
252 GdkGCValuesMask values_mask)
256 gc = g_object_new (GDK_TYPE_GC_QUARTZ, NULL);
258 _gdk_gc_init (gc, drawable, values, values_mask);
260 gdk_quartz_gc_set_values (gc, values, values_mask);
266 _gdk_windowing_gc_set_clip_region (GdkGC *gc,
267 const cairo_region_t *region,
268 gboolean reset_origin)
270 GdkGCQuartz *private = GDK_GC_QUARTZ (gc);
272 if ((private->have_clip_region && ! region) || private->have_clip_mask)
274 if (private->clip_mask)
276 CGImageRelease (private->clip_mask);
277 private->clip_mask = NULL;
279 private->have_clip_mask = FALSE;
282 private->have_clip_region = region != NULL;
286 gc->clip_x_origin = 0;
287 gc->clip_y_origin = 0;
292 _gdk_windowing_gc_copy (GdkGC *dst_gc,
295 GdkGCQuartz *dst_quartz_gc = GDK_GC_QUARTZ (dst_gc);
296 GdkGCQuartz *src_quartz_gc = GDK_GC_QUARTZ (src_gc);
298 dst_quartz_gc->function = src_quartz_gc->function;
299 dst_quartz_gc->subwindow_mode = src_quartz_gc->subwindow_mode;
300 dst_quartz_gc->graphics_exposures = src_quartz_gc->graphics_exposures;
302 dst_quartz_gc->have_clip_region = src_quartz_gc->have_clip_region;
303 dst_quartz_gc->have_clip_mask = src_quartz_gc->have_clip_mask;
305 if (dst_quartz_gc->clip_mask)
307 CGImageRelease (dst_quartz_gc->clip_mask);
308 dst_quartz_gc->clip_mask = NULL;
311 if (src_quartz_gc->clip_mask)
312 dst_quartz_gc->clip_mask =
313 CGImageCreateCopy (GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (src_quartz_gc->clip_mask)->impl)->image);
315 dst_quartz_gc->line_width = src_quartz_gc->line_width;
316 dst_quartz_gc->line_style = src_quartz_gc->line_style;
317 dst_quartz_gc->cap_style = src_quartz_gc->cap_style;
318 dst_quartz_gc->join_style = src_quartz_gc->join_style;
320 g_free (dst_quartz_gc->dash_lengths);
321 dst_quartz_gc->dash_lengths = g_memdup (src_quartz_gc->dash_lengths,
322 sizeof (CGFloat) * src_quartz_gc->dash_count);
323 dst_quartz_gc->dash_count = src_quartz_gc->dash_count;
324 dst_quartz_gc->dash_phase = src_quartz_gc->dash_phase;
328 gdk_gc_get_screen (GdkGC *gc)
333 struct PatternCallbackInfo
335 GdkGCQuartz *private_gc;
336 GdkDrawable *drawable;
340 pattern_callback_info_release (void *info)
346 gdk_quartz_draw_tiled_pattern (void *info,
347 CGContextRef context)
349 struct PatternCallbackInfo *pinfo = info;
350 GdkGC *gc = GDK_GC (pinfo->private_gc);
351 CGImageRef pattern_image;
352 size_t width, height;
354 pattern_image = GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (_gdk_gc_get_tile (gc))->impl)->image;
356 width = CGImageGetWidth (pattern_image);
357 height = CGImageGetHeight (pattern_image);
359 CGContextDrawImage (context,
360 CGRectMake (0, 0, width, height),
365 gdk_quartz_draw_stippled_pattern (void *info,
366 CGContextRef context)
368 struct PatternCallbackInfo *pinfo = info;
369 GdkGC *gc = GDK_GC (pinfo->private_gc);
370 CGImageRef pattern_image;
374 pattern_image = GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (_gdk_gc_get_stipple (gc))->impl)->image;
375 rect = CGRectMake (0, 0,
376 CGImageGetWidth (pattern_image),
377 CGImageGetHeight (pattern_image));
379 CGContextClipToMask (context, rect, pattern_image);
380 color = _gdk_quartz_colormap_get_cgcolor_from_pixel (pinfo->drawable,
381 _gdk_gc_get_fg_pixel (gc));
382 CGContextSetFillColorWithColor (context, color);
383 CGColorRelease (color);
385 CGContextFillRect (context, rect);
389 gdk_quartz_draw_opaque_stippled_pattern (void *info,
390 CGContextRef context)
392 struct PatternCallbackInfo *pinfo = info;
393 GdkGC *gc = GDK_GC (pinfo->private_gc);
394 CGImageRef pattern_image;
398 pattern_image = GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (_gdk_gc_get_stipple (gc))->impl)->image;
399 rect = CGRectMake (0, 0,
400 CGImageGetWidth (pattern_image),
401 CGImageGetHeight (pattern_image));
403 color = _gdk_quartz_colormap_get_cgcolor_from_pixel (pinfo->drawable,
404 _gdk_gc_get_bg_pixel (gc));
405 CGContextSetFillColorWithColor (context, color);
406 CGColorRelease (color);
408 CGContextFillRect (context, rect);
410 CGContextClipToMask (context, rect, pattern_image);
411 color = _gdk_quartz_colormap_get_cgcolor_from_pixel (info,
412 _gdk_gc_get_fg_pixel (gc));
413 CGContextSetFillColorWithColor (context, color);
414 CGColorRelease (color);
416 CGContextFillRect (context, rect);
420 _gdk_quartz_gc_update_cg_context (GdkGC *gc,
421 GdkDrawable *drawable,
422 CGContextRef context,
423 GdkQuartzContextValuesMask mask)
425 GdkGCQuartz *private;
429 g_return_if_fail (gc == NULL || GDK_IS_GC (gc));
434 private = GDK_GC_QUARTZ (gc);
436 if (private->have_clip_region)
440 cairo_region_t *region;
443 region = _gdk_gc_get_clip_region (gc);
444 n_rects = cairo_region_num_rectangles (region);
449 cg_rects = g_new (CGRect, n_rects);
451 for (i = 0; i < n_rects; i++)
453 cairo_rectangle_int_t cairo_rect;
454 cairo_region_get_rectangle (region, i, &cairo_rect);
455 cg_rects[i].origin.x = cairo_rect.x + gc->clip_x_origin;
456 cg_rects[i].origin.y = cairo_rect.y + gc->clip_y_origin;
457 cg_rects[i].size.width = cairo_rect.width;
458 cg_rects[i].size.height = cairo_rect.height;
461 CGContextClipToRects (context, cg_rects, n_rects);
463 if (cg_rects != &rect)
466 else if (private->have_clip_mask && private->clip_mask)
468 /* Note: This is 10.4 only. For lower versions, we need to transform the
469 * mask into a region.
471 CGContextClipToMask (context,
472 CGRectMake (gc->clip_x_origin, gc->clip_y_origin,
473 CGImageGetWidth (private->clip_mask),
474 CGImageGetHeight (private->clip_mask)),
478 fg_pixel = _gdk_gc_get_fg_pixel (gc);
479 bg_pixel = _gdk_gc_get_bg_pixel (gc);
482 CGBlendMode blend_mode = kCGBlendModeNormal;
484 switch (private->function)
487 blend_mode = kCGBlendModeNormal;
492 blend_mode = kCGBlendModeExclusion;
493 fg_pixel = 0xffffffff;
494 bg_pixel = 0xffffffff;
499 case GDK_AND_REVERSE:
505 case GDK_COPY_INVERT:
510 blend_mode = kCGBlendModeNormal; /* FIXME */
514 CGContextSetBlendMode (context, blend_mode);
517 /* FIXME: implement subwindow mode */
519 /* FIXME: implement graphics exposures */
521 if (mask & GDK_QUARTZ_CONTEXT_STROKE)
523 CGLineCap line_cap = kCGLineCapButt;
524 CGLineJoin line_join = kCGLineJoinMiter;
527 color = _gdk_quartz_colormap_get_cgcolor_from_pixel (drawable,
529 CGContextSetStrokeColorWithColor (context, color);
530 CGColorRelease (color);
532 CGContextSetLineWidth (context, MAX (G_MINFLOAT, private->line_width));
534 switch (private->line_style)
537 CGContextSetLineDash (context, 0.0, NULL, 0);
540 case GDK_LINE_DOUBLE_DASH:
541 /* FIXME: Implement; for now, fall back to GDK_LINE_ON_OFF_DASH */
543 case GDK_LINE_ON_OFF_DASH:
544 CGContextSetLineDash (context, private->dash_phase,
545 private->dash_lengths, private->dash_count);
549 switch (private->cap_style)
551 case GDK_CAP_NOT_LAST:
552 /* FIXME: Implement; for now, fall back to GDK_CAP_BUTT */
554 line_cap = kCGLineCapButt;
557 line_cap = kCGLineCapRound;
559 case GDK_CAP_PROJECTING:
560 line_cap = kCGLineCapSquare;
564 CGContextSetLineCap (context, line_cap);
566 switch (private->join_style)
569 line_join = kCGLineJoinMiter;
572 line_join = kCGLineJoinRound;
575 line_join = kCGLineJoinBevel;
579 CGContextSetLineJoin (context, line_join);
582 if (mask & GDK_QUARTZ_CONTEXT_FILL)
584 GdkFill fill = _gdk_gc_get_fill (gc);
585 CGColorSpaceRef baseSpace;
586 CGColorSpaceRef patternSpace;
589 if (fill == GDK_SOLID)
593 color = _gdk_quartz_colormap_get_cgcolor_from_pixel (drawable,
595 CGContextSetFillColorWithColor (context, color);
596 CGColorRelease (color);
600 if (!private->ts_pattern)
602 CGImageRef pattern_image = NULL;
603 gfloat width, height;
604 gboolean is_colored = FALSE;
605 CGPatternCallbacks callbacks = { 0, NULL, NULL };
606 struct PatternCallbackInfo *info;
609 info = g_new (struct PatternCallbackInfo, 1);
610 /* Won't ref to avoid circular dependencies */
611 info->drawable = drawable;
612 info->private_gc = private;
614 callbacks.releaseInfo = pattern_callback_info_release;
619 pattern_image = GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (_gdk_gc_get_tile (gc))->impl)->image;
621 callbacks.drawPattern = gdk_quartz_draw_tiled_pattern;
624 pattern_image = GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (_gdk_gc_get_stipple (gc))->impl)->image;
626 callbacks.drawPattern = gdk_quartz_draw_stippled_pattern;
628 case GDK_OPAQUE_STIPPLED:
629 pattern_image = GDK_PIXMAP_IMPL_QUARTZ (GDK_PIXMAP_OBJECT (_gdk_gc_get_stipple (gc))->impl)->image;
631 callbacks.drawPattern = gdk_quartz_draw_opaque_stippled_pattern;
637 width = CGImageGetWidth (pattern_image);
638 height = CGImageGetHeight (pattern_image);
640 phase = CGPointApplyAffineTransform (CGPointMake (gc->ts_x_origin, gc->ts_y_origin), CGContextGetCTM (context));
641 CGContextSetPatternPhase (context, CGSizeMake (phase.x, phase.y));
643 private->ts_pattern = CGPatternCreate (info,
644 CGRectMake (0, 0, width, height),
645 CGAffineTransformIdentity,
647 kCGPatternTilingConstantSpacing,
652 baseSpace = (fill == GDK_STIPPLED) ? CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB) : NULL;
653 patternSpace = CGColorSpaceCreatePattern (baseSpace);
655 CGContextSetFillColorSpace (context, patternSpace);
656 CGColorSpaceRelease (patternSpace);
657 CGColorSpaceRelease (baseSpace);
659 if (fill == GDK_STIPPLED)
662 const CGFloat *components;
664 color = _gdk_quartz_colormap_get_cgcolor_from_pixel (drawable,
666 components = CGColorGetComponents (color);
668 CGContextSetFillPattern (context, private->ts_pattern,
670 CGColorRelease (color);
673 CGContextSetFillPattern (context, private->ts_pattern, &alpha);
677 if (mask & GDK_QUARTZ_CONTEXT_TEXT)
679 /* FIXME: implement text */
682 if (GDK_IS_WINDOW_IMPL_QUARTZ (drawable))
683 private->is_window = TRUE;
685 private->is_window = FALSE;