1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.graphics.GC;
14 
15 import org.eclipse.swt.graphics.Image;
16 
17 
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.SWTException;
20 import org.eclipse.swt.SWTError;
21 import org.eclipse.swt.graphics.Color;
22 import org.eclipse.swt.graphics.Drawable;
23 import org.eclipse.swt.graphics.Resource;
24 import org.eclipse.swt.graphics.Device;
25 import org.eclipse.swt.graphics.Font;
26 import org.eclipse.swt.graphics.FontMetrics;
27 import org.eclipse.swt.graphics.GCData;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.graphics.ImageData;
30 import org.eclipse.swt.graphics.Path;
31 import org.eclipse.swt.graphics.Pattern;
32 import org.eclipse.swt.graphics.Point;
33 import org.eclipse.swt.graphics.RGB;
34 import org.eclipse.swt.graphics.Rectangle;
35 import org.eclipse.swt.graphics.Region;
36 import org.eclipse.swt.graphics.Resource;
37 import org.eclipse.swt.graphics.Transform;
38 import org.eclipse.swt.graphics.LineAttributes;
39 
40 import org.eclipse.swt.internal.gtk.OS;
41 import org.eclipse.swt.internal.cairo.Cairo : Cairo;
42 import org.eclipse.swt.internal.Converter;
43 import org.eclipse.swt.internal.Compatibility;
44 import java.lang.all;
45 
46 version(Tango){
47     import tango.stdc..string;
48 } else {
49     import core.stdc..string;
50 }
51 
52 
53 /**
54  * Class <code>GC</code> is where all of the drawing capabilities that are
55  * supported by SWT are located. Instances are used to draw on either an
56  * <code>Image</code>, a <code>Control</code>, or directly on a <code>Display</code>.
57  * <dl>
58  * <dt><b>Styles:</b></dt>
59  * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
60  * </dl>
61  *
62  * <p>
63  * The SWT drawing coordinate system is the two-dimensional space with the origin
64  * (0,0) at the top left corner of the drawing area and with (x,y) values increasing
65  * to the right and downward respectively.
66  * </p>
67  *
68  * <p>
69  * Application code must explicitly invoke the <code>GC.dispose()</code>
70  * method to release the operating system resources managed by each instance
71  * when those instances are no longer required. This is <em>particularly</em>
72  * important on Windows95 and Windows98 where the operating system has a limited
73  * number of device contexts available.
74  * </p>
75  *
76  * <p>
77  * Note: Only one of LEFT_TO_RIGHT and RIGHT_TO_LEFT may be specified.
78  * </p>
79  *
80  * @see org.eclipse.swt.events.PaintEvent
81  * @see <a href="http://www.eclipse.org/swt/snippets/#gc">GC snippets</a>
82  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: GraphicsExample, PaintExample</a>
83  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
84  */
85 public final class GC : Resource {
86 
87     alias Resource.init_ init_;
88 
89     /**
90      * the handle to the OS device context
91      * (Warning: This field is platform dependent)
92      * <p>
93      * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
94      * public API. It is marked public only so that it can be shared
95      * within the packages provided by SWT. It is not available on all
96      * platforms and should never be accessed from application code.
97      * </p>
98      */
99     public GdkGC* handle;
100 
101     Drawable drawable;
102     GCData data;
103 
104     const static int FOREGROUND = 1 << 0;
105     const static int BACKGROUND = 1 << 1;
106     const static int FONT = 1 << 2;
107     const static int LINE_STYLE = 1 << 3;
108     const static int LINE_CAP = 1 << 4;
109     const static int LINE_JOIN = 1 << 5;
110     const static int LINE_WIDTH = 1 << 6;
111     const static int LINE_MITERLIMIT = 1 << 7;
112     const static int BACKGROUND_BG = 1 << 8;
113     const static int DRAW_OFFSET = 1 << 9;
114     const static int DRAW = FOREGROUND | LINE_WIDTH | LINE_STYLE  | LINE_CAP  | LINE_JOIN | LINE_MITERLIMIT | DRAW_OFFSET;
115     const static int FILL = BACKGROUND;
116 
117     static const float[] LINE_DOT = [1, 1];
118     static const float[] LINE_DASH = [3, 1];
119     static const float[] LINE_DASHDOT = [3, 1, 1, 1];
120     static const float[] LINE_DASHDOTDOT = [3, 1, 1, 1, 1, 1];
121     static const float[] LINE_DOT_ZERO = [3, 3];
122     static const float[] LINE_DASH_ZERO = [18, 6];
123     static const float[] LINE_DASHDOT_ZERO = [9, 6, 3, 6];
124     static const float[] LINE_DASHDOTDOT_ZERO = [9, 3, 3, 3, 3, 3];
125 
126 this() {
127 }
128 
129 /**
130  * Constructs a new instance of this class which has been
131  * configured to draw on the specified drawable. Sets the
132  * foreground color, background color and font in the GC
133  * to match those in the drawable.
134  * <p>
135  * You must dispose the graphics context when it is no longer required.
136  * </p>
137  * @param drawable the drawable to draw on
138  * @exception IllegalArgumentException <ul>
139  *    <li>ERROR_NULL_ARGUMENT - if the drawable is null</li>
140  *    <li>ERROR_NULL_ARGUMENT - if there is no current device</li>
141  *    <li>ERROR_INVALID_ARGUMENT
142  *          - if the drawable is an image that is not a bitmap or an icon
143  *          - if the drawable is an image or printer that is already selected
144  *            into another graphics context</li>
145  * </ul>
146  * @exception SWTError <ul>
147  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li>
148  *    <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li>
149  * </ul>
150  */
151 public this(Drawable drawable) {
152     this(drawable, 0);
153 }
154 
155 /**
156  * Constructs a new instance of this class which has been
157  * configured to draw on the specified drawable. Sets the
158  * foreground color, background color and font in the GC
159  * to match those in the drawable.
160  * <p>
161  * You must dispose the graphics context when it is no longer required.
162  * </p>
163  *
164  * @param drawable the drawable to draw on
165  * @param style the style of GC to construct
166  *
167  * @exception IllegalArgumentException <ul>
168  *    <li>ERROR_NULL_ARGUMENT - if the drawable is null</li>
169  *    <li>ERROR_NULL_ARGUMENT - if there is no current device</li>
170  *    <li>ERROR_INVALID_ARGUMENT
171  *          - if the drawable is an image that is not a bitmap or an icon
172  *          - if the drawable is an image or printer that is already selected
173  *            into another graphics context</li>
174  * </ul>
175  * @exception SWTError <ul>
176  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li>
177  *    <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li>
178  * </ul>
179  *
180  * @since 2.1.2
181  */
182 public this(Drawable drawable, int style) {
183     if (drawable is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
184     GCData data = new GCData();
185     data.style = checkStyle(style);
186     auto gdkGC = drawable.internal_new_GC(data);
187     Device device = data.device;
188     if (device is null) device = Device.getDevice();
189     if (device is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
190     this.device = data.device = device;
191     init_(drawable, data, gdkGC);
192     init_();
193 }
194 
195 static void addCairoString(org.eclipse.swt.internal.gtk.OS.cairo_t* cairo, String str, float x, float y, Font font) {
196     char* buffer = toStringz( str );
197     if (OS.GTK_VERSION >= OS.buildVERSION(2, 8, 0)) {
198         auto layout = OS.pango_cairo_create_layout(cairo);
199         if (layout is null) SWT.error(SWT.ERROR_NO_HANDLES);
200         OS.pango_layout_set_text(layout, buffer, -1);
201         OS.pango_layout_set_font_description(layout, font.handle);
202         double currentX = 0, currentY = 0;
203         Cairo.cairo_get_current_point(cairo, &currentX, &currentY);
204         if (currentX !is x || currentY !is y) {
205             Cairo.cairo_move_to(cairo, x, y);
206         }
207         OS.pango_cairo_layout_path(cairo, layout);
208         OS.g_object_unref(layout);
209     } else {
210         GC.setCairoFont(cairo, font);
211         cairo_font_extents_t* extents = new cairo_font_extents_t();
212         Cairo.cairo_font_extents(cairo, extents);
213         double baseline = y + extents.ascent;
214         Cairo.cairo_move_to(cairo, x, baseline);
215         Cairo.cairo_text_path(cairo, buffer);
216     }
217 }
218 
219 static int checkStyle (int style) {
220     if ((style & SWT.LEFT_TO_RIGHT) !is 0) style &= ~SWT.RIGHT_TO_LEFT;
221     return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
222 }
223 
224 public static GC gtk_new(GdkGC* handle, GCData data) {
225     GC gc = new GC();
226     gc.device = data.device;
227     gc.init_(null, data, handle);
228     return gc;
229 }
230 
231 public static GC gtk_new(Drawable drawable, GCData data) {
232     GC gc = new GC();
233     auto gdkGC = drawable.internal_new_GC(data);
234     gc.device = data.device;
235     gc.init_(drawable, data, gdkGC);
236     return gc;
237 }
238 
239 void checkGC (int mask) {
240     int state = data.state;
241     if ((state & mask) is mask) return;
242     state = (state ^ mask) & mask;
243     data.state |= mask;
244     auto cairo = data.cairo;
245     if (cairo !is null) {
246         if ((state & (BACKGROUND | FOREGROUND)) !is 0) {
247             GdkColor* color;
248             Pattern pattern;
249             if ((state & FOREGROUND) !is 0) {
250                 color = data.foreground;
251                 pattern = data.foregroundPattern;
252                 data.state &= ~BACKGROUND;
253             } else {
254                 color = data.background;
255                 pattern = data.backgroundPattern;
256                 data.state &= ~FOREGROUND;
257             }
258             if  (pattern !is null) {
259                 if ((data.style & SWT.MIRRORED) !is 0 && pattern.surface !is null) {
260                     auto newPattern = Cairo.cairo_pattern_create_for_surface(pattern.surface);
261                     if (newPattern is null) SWT.error(SWT.ERROR_NO_HANDLES);
262                     Cairo.cairo_pattern_set_extend(newPattern, Cairo.CAIRO_EXTEND_REPEAT);
263                     double[6] matrix; matrix[0] = -1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 1; matrix[4] = 0; matrix[5] = 0;
264                     Cairo.cairo_pattern_set_matrix(newPattern, cast(cairo_matrix_t*) matrix.ptr);
265                     Cairo.cairo_set_source(cairo, newPattern);
266                     Cairo.cairo_pattern_destroy(newPattern);
267                 } else {
268                     Cairo.cairo_set_source(cairo, pattern.handle);
269                 }
270             } else {
271                 Cairo.cairo_set_source_rgba(cairo, (color.red & 0xFFFF) / cast(float)0xFFFF, (color.green & 0xFFFF) / cast(float)0xFFFF, (color.blue & 0xFFFF) / cast(float)0xFFFF, data.alpha / cast(float)0xFF);
272             }
273         }
274         if ((state & FONT) !is 0) {
275             if (data.layout !is null) {
276                 Font font = data.font;
277                 OS.pango_layout_set_font_description(data.layout, font.handle);
278             }
279             if (OS.GTK_VERSION < OS.buildVERSION(2, 8, 0)) {
280                 setCairoFont(cairo, data.font);
281             }
282         }
283         if ((state & LINE_CAP) !is 0) {
284             int cap_style = 0;
285             switch (data.lineCap) {
286                 case SWT.CAP_ROUND: cap_style = Cairo.CAIRO_LINE_CAP_ROUND; break;
287                 case SWT.CAP_FLAT: cap_style = Cairo.CAIRO_LINE_CAP_BUTT; break;
288                 case SWT.CAP_SQUARE: cap_style = Cairo.CAIRO_LINE_CAP_SQUARE; break;
289                 default:
290             }
291             Cairo.cairo_set_line_cap(cairo, cap_style);
292         }
293         if ((state & LINE_JOIN) !is 0) {
294             int join_style = 0;
295             switch (data.lineJoin) {
296                 case SWT.JOIN_MITER: join_style = Cairo.CAIRO_LINE_JOIN_MITER; break;
297                 case SWT.JOIN_ROUND:  join_style = Cairo.CAIRO_LINE_JOIN_ROUND; break;
298                 case SWT.JOIN_BEVEL: join_style = Cairo.CAIRO_LINE_JOIN_BEVEL; break;
299                 default:
300             }
301             Cairo.cairo_set_line_join(cairo, join_style);
302         }
303         if ((state & LINE_WIDTH) !is 0) {
304             Cairo.cairo_set_line_width(cairo, data.lineWidth is 0 ? 1 : data.lineWidth);
305             switch (data.lineStyle) {
306                 case SWT.LINE_DOT:
307                 case SWT.LINE_DASH:
308                 case SWT.LINE_DASHDOT:
309                 case SWT.LINE_DASHDOTDOT:
310                     state |= LINE_STYLE;
311                     break;
312                 default:
313                     break;
314             }
315         }
316         if ((state & LINE_STYLE) !is 0) {
317             float dashesOffset = 0;
318             TryConst!(float)[] dashes = null;
319             float width = data.lineWidth;
320             switch (data.lineStyle) {
321                 case SWT.LINE_SOLID: break;
322                 case SWT.LINE_DASH: dashes = width !is 0 ? LINE_DASH : LINE_DASH_ZERO; break;
323                 case SWT.LINE_DOT: dashes = width !is 0 ? LINE_DOT : LINE_DOT_ZERO; break;
324                 case SWT.LINE_DASHDOT: dashes = width !is 0 ? LINE_DASHDOT : LINE_DASHDOT_ZERO; break;
325                 case SWT.LINE_DASHDOTDOT: dashes = width !is 0 ? LINE_DASHDOTDOT : LINE_DASHDOTDOT_ZERO; break;
326                 case SWT.LINE_CUSTOM: dashes = data.lineDashes; break;
327                 default:
328             }
329             if (dashes !is null) {
330                 dashesOffset = data.lineDashesOffset;
331                 double[] cairoDashes = new double[dashes.length];
332                 for (int i = 0; i < cairoDashes.length; i++) {
333                     cairoDashes[i] = width is 0 || data.lineStyle is SWT.LINE_CUSTOM ? dashes[i] : dashes[i] * width;
334                 }
335                 Cairo.cairo_set_dash(cairo, cairoDashes.ptr, cast(int)/*64bit*/cairoDashes.length, dashesOffset);
336             } else {
337                 Cairo.cairo_set_dash(cairo, null, 0, 0);
338             }
339         }
340         if ((state & LINE_MITERLIMIT) !is 0) {
341             Cairo.cairo_set_miter_limit(cairo, data.lineMiterLimit);
342         }
343         if ((state & DRAW_OFFSET) !is 0) {
344             data.cairoXoffset = data.cairoYoffset = 0;
345             double[] matrix = new double[6];
346             Cairo.cairo_get_matrix(cairo,cast(cairo_matrix_t*) matrix.ptr);
347             double dx = 1;
348             double dy = 1;
349             Cairo.cairo_user_to_device_distance(cairo, &dx, &dy);
350             double scaling = dx;
351             if (scaling < 0) scaling = -scaling;
352             double strokeWidth = data.lineWidth * scaling;
353             if (strokeWidth is 0 || (cast(int)strokeWidth % 2) is 1) {
354                 data.cairoXoffset = 0.5 / scaling;
355             }
356             scaling = dy;
357             if (scaling < 0) scaling = -scaling;
358             strokeWidth = data.lineWidth * scaling;
359             if (strokeWidth is 0 || (cast(int)strokeWidth % 2) is 1) {
360                 data.cairoYoffset = 0.5 / scaling;
361             }
362         }
363         return;
364     }
365     if ((state & (BACKGROUND | FOREGROUND)) !is 0) {
366         GdkColor* foreground;
367         if ((state & FOREGROUND) !is 0) {
368             foreground = data.foreground;
369             data.state &= ~BACKGROUND;
370         } else {
371             foreground = data.background;
372             data.state &= ~FOREGROUND;
373         }
374         OS.gdk_gc_set_foreground(handle, foreground);
375     }
376     if ((state & BACKGROUND_BG) !is 0) {
377         GdkColor* background = data.background;
378         OS.gdk_gc_set_background(handle, background);
379     }
380     if ((state & FONT) !is 0) {
381         if (data.layout !is null) {
382             Font font = data.font;
383             OS.pango_layout_set_font_description(data.layout, font.handle);
384         }
385     }
386     if ((state & (LINE_CAP | LINE_JOIN | LINE_STYLE | LINE_WIDTH)) !is 0) {
387         int cap_style = 0;
388         int join_style = 0;
389         int width = cast(int)data.lineWidth;
390         int line_style = 0;
391         TryConst!(float)[] dashes = null;
392         switch (data.lineCap) {
393             case SWT.CAP_ROUND: cap_style = OS.GDK_CAP_ROUND; break;
394             case SWT.CAP_FLAT: cap_style = OS.GDK_CAP_BUTT; break;
395             case SWT.CAP_SQUARE: cap_style = OS.GDK_CAP_PROJECTING; break;
396                 default:
397         }
398         switch (data.lineJoin) {
399             case SWT.JOIN_ROUND: join_style = OS.GDK_JOIN_ROUND; break;
400             case SWT.JOIN_MITER: join_style = OS.GDK_JOIN_MITER; break;
401             case SWT.JOIN_BEVEL: join_style = OS.GDK_JOIN_BEVEL; break;
402                 default:
403         }
404         switch (data.lineStyle) {
405             case SWT.LINE_SOLID: break;
406             case SWT.LINE_DASH: dashes = width !is 0 ? LINE_DASH : LINE_DASH_ZERO; break;
407             case SWT.LINE_DOT: dashes = width !is 0 ? LINE_DOT : LINE_DOT_ZERO; break;
408             case SWT.LINE_DASHDOT: dashes = width !is 0 ? LINE_DASHDOT : LINE_DASHDOT_ZERO; break;
409             case SWT.LINE_DASHDOTDOT: dashes = width !is 0 ? LINE_DASHDOTDOT : LINE_DASHDOTDOT_ZERO; break;
410             case SWT.LINE_CUSTOM: dashes = data.lineDashes; break;
411                 default:
412         }
413         if (dashes !is null) {
414             if ((state & LINE_STYLE) !is 0) {
415                 auto dash_list = new char[dashes.length];
416                 for (int i = 0; i < dash_list.length; i++) {
417                     dash_list[i] = cast(char)(width is 0 || data.lineStyle is SWT.LINE_CUSTOM ? dashes[i] : dashes[i] * width);
418                 }
419                 OS.gdk_gc_set_dashes(handle, 0, dash_list.ptr, cast(int)/*64bit*/dash_list.length);
420             }
421             line_style = OS.GDK_LINE_ON_OFF_DASH;
422         } else {
423             line_style = OS.GDK_LINE_SOLID;
424         }
425         OS.gdk_gc_set_line_attributes(handle, width, line_style, cap_style, join_style);
426     }
427 }
428 
429 GdkRegion* convertRgn(GdkRegion* rgn, double[] matrix) {
430     auto newRgn = OS.gdk_region_new();
431     int nRects;
432     GdkRectangle* rects;
433     OS.gdk_region_get_rectangles(rgn, &rects, &nRects);
434     GdkRectangle* rect;
435     int[8] pointArray;
436     double x = 0, y = 0;
437     for (int i=0; i<nRects; i++) {
438         rect = rects +i;
439         x = rect.x;
440         y = rect.y;
441         Cairo.cairo_matrix_transform_point(cast(cairo_matrix_t*)matrix.ptr, &x, &y);
442         pointArray[0] = cast(int)x;
443         pointArray[1] = cast(int)y;
444         x = rect.x + rect.width;
445         y = rect.y;
446         Cairo.cairo_matrix_transform_point(cast(cairo_matrix_t*)matrix.ptr, &x, &y);
447         pointArray[2] = cast(int)Math.round(x);
448         pointArray[3] = cast(int)y;
449         x = rect.x + rect.width;
450         y = rect.y + rect.height;
451         Cairo.cairo_matrix_transform_point(cast(cairo_matrix_t*)matrix.ptr, &x, &y);
452         pointArray[4] = cast(int)Math.round(x);
453         pointArray[5] = cast(int)Math.round(y);
454         x = rect.x;
455         y = rect.y + rect.height;
456         Cairo.cairo_matrix_transform_point(cast(cairo_matrix_t*)matrix.ptr, &x, &y);
457         pointArray[6] = cast(int)x;
458         pointArray[7] = cast(int)Math.round(y);
459         auto polyRgn = OS.gdk_region_polygon(cast(GdkPoint*)pointArray.ptr, pointArray.length / 2, OS.GDK_EVEN_ODD_RULE);
460         OS.gdk_region_union(newRgn, polyRgn);
461         OS.gdk_region_destroy(polyRgn);
462     }
463     if (rects !is null) OS.g_free(rects);
464     return newRgn;
465 }
466 
467 /**
468  * Copies a rectangular area of the receiver at the specified
469  * position into the image, which must be of type <code>SWT.BITMAP</code>.
470  *
471  * @param image the image to copy into
472  * @param x the x coordinate in the receiver of the area to be copied
473  * @param y the y coordinate in the receiver of the area to be copied
474  *
475  * @exception IllegalArgumentException <ul>
476  *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
477  *    <li>ERROR_INVALID_ARGUMENT - if the image is not a bitmap or has been disposed</li>
478  * </ul>
479  * @exception SWTException <ul>
480  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
481  * </ul>
482  */
483 public void copyArea(Image image, int x, int y) {
484     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
485     if (image is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
486     if (image.type !is SWT.BITMAP || image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
487     Rectangle rect = image.getBounds();
488     auto gdkGC = OS.gdk_gc_new(image.pixmap);
489     if (gdkGC is null) SWT.error(SWT.ERROR_NO_HANDLES);
490     OS.gdk_gc_set_subwindow(gdkGC, OS.GDK_INCLUDE_INFERIORS);
491     OS.gdk_draw_drawable(image.pixmap, gdkGC, data.drawable, x, y, 0, 0, rect.width, rect.height);
492     OS.g_object_unref(gdkGC);
493 }
494 
495 /**
496  * Copies a rectangular area of the receiver at the source
497  * position onto the receiver at the destination position.
498  *
499  * @param srcX the x coordinate in the receiver of the area to be copied
500  * @param srcY the y coordinate in the receiver of the area to be copied
501  * @param width the width of the area to copy
502  * @param height the height of the area to copy
503  * @param destX the x coordinate in the receiver of the area to copy to
504  * @param destY the y coordinate in the receiver of the area to copy to
505  *
506  * @exception SWTException <ul>
507  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
508  * </ul>
509  */
510 public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY) {
511     copyArea(srcX, srcY, width, height, destX, destY, true);
512 }
513 /**
514  * Copies a rectangular area of the receiver at the source
515  * position onto the receiver at the destination position.
516  *
517  * @param srcX the x coordinate in the receiver of the area to be copied
518  * @param srcY the y coordinate in the receiver of the area to be copied
519  * @param width the width of the area to copy
520  * @param height the height of the area to copy
521  * @param destX the x coordinate in the receiver of the area to copy to
522  * @param destY the y coordinate in the receiver of the area to copy to
523  * @param paint if <code>true</code> paint events will be generated for old and obscured areas
524  *
525  * @exception SWTException <ul>
526  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
527  * </ul>
528  *
529  * @since 3.1
530  */
531 public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY, bool paint) {
532     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
533     if (width <= 0 || height <= 0) return;
534     int deltaX = destX - srcX, deltaY = destY - srcY;
535     if (deltaX is 0 && deltaY is 0) return;
536     auto drawable = data.drawable;
537     if (data.image is null && paint) OS.gdk_gc_set_exposures(handle, true);
538     OS.gdk_draw_drawable(drawable, handle, drawable, srcX, srcY, destX, destY, width, height);
539     if ((data.image is null) & paint) {
540         OS.gdk_gc_set_exposures(handle, false);
541         bool disjoint = (destX + width < srcX) || (srcX + width < destX) || (destY + height < srcY) || (srcY + height < destY);
542         GdkRectangle rect;
543         if (disjoint) {
544             rect.x = srcX;
545             rect.y = srcY;
546             rect.width = width;
547             rect.height = height;
548             OS.gdk_window_invalidate_rect (cast(GdkWindow*)drawable, &rect, false);
549 //          OS.gdk_window_clear_area_e(drawable, srcX, srcY, width, height);
550         } else {
551             if (deltaX !is 0) {
552                 int newX = destX - deltaX;
553                 if (deltaX < 0) newX = destX + width;
554                 rect.x = newX;
555                 rect.y = srcY;
556                 rect.width = Math.abs(deltaX);
557                 rect.height = height;
558                 OS.gdk_window_invalidate_rect (cast(GdkWindow*)drawable, &rect, false);
559 //              OS.gdk_window_clear_area_e(drawable, newX, srcY, Math.abs(deltaX), height);
560             }
561             if (deltaY !is 0) {
562                 int newY = destY - deltaY;
563                 if (deltaY < 0) newY = destY + height;
564                 rect.x = srcX;
565                 rect.y = newY;
566                 rect.width = width;
567                 rect.height = Math.abs(deltaY);
568                 OS.gdk_window_invalidate_rect (cast(GdkWindow*)drawable, &rect, false);
569 //              OS.gdk_window_clear_area_e(drawable, srcX, newY, width, Math.abs(deltaY));
570             }
571         }
572     }
573 }
574 
575 void createLayout() {
576     auto context = OS.gdk_pango_context_get();
577     if (context is null) SWT.error(SWT.ERROR_NO_HANDLES);
578     data.context = context;
579     auto layout = OS.pango_layout_new(context);
580     if (layout is null) SWT.error(SWT.ERROR_NO_HANDLES);
581     data.layout = layout;
582     OS.pango_context_set_language(context, OS.gtk_get_default_language());
583     OS.pango_context_set_base_dir(context, (data.style & SWT.MIRRORED) !is 0 ? OS.PANGO_DIRECTION_RTL : OS.PANGO_DIRECTION_LTR);
584     OS.gdk_pango_context_set_colormap(context, OS.gdk_colormap_get_system());
585     if (OS.GTK_VERSION >= OS.buildVERSION(2, 4, 0)) {
586         OS.pango_layout_set_auto_dir(layout, false);
587     }
588 }
589 
590 void disposeLayout() {
591     data.str = null;
592     if (data.context !is null) OS.g_object_unref(data.context);
593     if (data.layout !is null) OS.g_object_unref(data.layout);
594     data.layout = null;
595     data.context = null;
596 }
597 
598 override
599 void destroy() {
600     if (data.disposeCairo) {
601         auto cairo = data.cairo;
602         if (cairo !is null) Cairo.cairo_destroy(cairo);
603         data.cairo = null;
604     }
605 
606     /* Free resources */
607     auto clipRgn = data.clipRgn;
608     if (clipRgn !is null) OS.gdk_region_destroy(clipRgn);
609     Image image = data.image;
610     if (image !is null) {
611         image.memGC = null;
612         if (image.transparentPixel !is -1) image.createMask();
613     }
614 
615     disposeLayout();
616 
617     /* Dispose the GC */
618     if (drawable !is null) {
619         drawable.internal_dispose_GC(handle, data);
620     }
621     data.drawable = null;
622     data.clipRgn = null;
623     drawable = null;
624     handle = null;
625     data.image = null;
626     data.str = null;
627     data = null;
628 }
629 
630 /**
631  * Draws the outline of a circular or elliptical arc
632  * within the specified rectangular area.
633  * <p>
634  * The resulting arc begins at <code>startAngle</code> and extends
635  * for <code>arcAngle</code> degrees, using the current color.
636  * Angles are interpreted such that 0 degrees is at the 3 o'clock
637  * position. A positive value indicates a counter-clockwise rotation
638  * while a negative value indicates a clockwise rotation.
639  * </p><p>
640  * The center of the arc is the center of the rectangle whose origin
641  * is (<code>x</code>, <code>y</code>) and whose size is specified by the
642  * <code>width</code> and <code>height</code> arguments.
643  * </p><p>
644  * The resulting arc covers an area <code>width + 1</code> pixels wide
645  * by <code>height + 1</code> pixels tall.
646  * </p>
647  *
648  * @param x the x coordinate of the upper-left corner of the arc to be drawn
649  * @param y the y coordinate of the upper-left corner of the arc to be drawn
650  * @param width the width of the arc to be drawn
651  * @param height the height of the arc to be drawn
652  * @param startAngle the beginning angle
653  * @param arcAngle the angular extent of the arc, relative to the start angle
654  *
655  * @exception SWTException <ul>
656  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
657  * </ul>
658  */
659 public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
660     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
661     checkGC(DRAW);
662     if (width < 0) {
663         x = x + width;
664         width = -width;
665     }
666     if (height < 0) {
667         y = y + height;
668         height = -height;
669     }
670     if (width is 0 || height is 0 || arcAngle is 0) return;
671     auto cairo = data.cairo;
672     if (cairo !is null) {
673         double xOffset = data.cairoXoffset, yOffset = data.cairoYoffset;
674         if (width is height) {
675             if (arcAngle >= 0) {
676                 Cairo.cairo_arc_negative(cairo, x + xOffset + width / 2f, y + yOffset + height / 2f, width / 2f, -startAngle * cast(float)Compatibility.PI / 180, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
677             } else {
678                 Cairo.cairo_arc(cairo, x + xOffset + width / 2f, y + yOffset + height / 2f, width / 2f, -startAngle * cast(float)Compatibility.PI / 180, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
679             }
680         } else {
681             Cairo.cairo_save(cairo);
682             Cairo.cairo_translate(cairo, x + xOffset + width / 2f, y + yOffset + height / 2f);
683             Cairo.cairo_scale(cairo, width / 2f, height / 2f);
684             if (arcAngle >= 0) {
685                 Cairo.cairo_arc_negative(cairo, 0, 0, 1, -startAngle * cast(float)Compatibility.PI / 180, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
686             } else {
687                 Cairo.cairo_arc(cairo, 0, 0, 1, -startAngle * cast(float)Compatibility.PI / 180, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
688             }
689             Cairo.cairo_restore(cairo);
690         }
691         Cairo.cairo_stroke(cairo);
692         return;
693     }
694     OS.gdk_draw_arc(data.drawable, handle, 0, x, y, width, height, startAngle * 64, arcAngle * 64);
695 }
696 
697 /**
698  * Draws a rectangle, based on the specified arguments, which has
699  * the appearance of the platform's <em>focus rectangle</em> if the
700  * platform supports such a notion, and otherwise draws a simple
701  * rectangle in the receiver's foreground color.
702  *
703  * @param x the x coordinate of the rectangle
704  * @param y the y coordinate of the rectangle
705  * @param width the width of the rectangle
706  * @param height the height of the rectangle
707  *
708  * @exception SWTException <ul>
709  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
710  * </ul>
711  *
712  * @see #drawRectangle(int, int, int, int)
713  */
714 public void drawFocus(int x, int y, int width, int height) {
715     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
716     /*
717     * Feature in GTK.  The function gtk_widget_get_default_style()
718     * can't be used here because gtk_paint_focus() uses GCs, which
719     * are not valid in the default style. The fix is to use a style
720     * from a widget.
721     */
722     auto style = OS.gtk_widget_get_style(data.device.shellHandle);
723     auto cairo = data.cairo;
724     if (cairo !is null) {
725         checkGC(FOREGROUND);
726         int lineWidth;
727         OS.gtk_widget_style_get1(data.device.shellHandle, OS.focus_line_width.ptr, &lineWidth );
728         Cairo.cairo_save(cairo);
729         Cairo.cairo_set_line_width(cairo, lineWidth);
730         double[2] dashes = 1;
731         double dash_offset = -lineWidth / 2f;
732         while (dash_offset < 0) dash_offset += 2;
733         Cairo.cairo_set_dash(cairo, dashes.ptr, dashes.length, dash_offset);
734         Cairo.cairo_rectangle(cairo, x + lineWidth / 2f, y + lineWidth / 2f, width, height);
735         Cairo.cairo_stroke(cairo);
736         Cairo.cairo_restore(cairo);
737         return;
738     }
739     char dummy;
740     OS.gtk_paint_focus(style,cast(GdkWindow *) data.drawable, OS.GTK_STATE_NORMAL, null, data.device.shellHandle, &dummy, x, y, width, height);
741 }
742 
743 /**
744  * Draws the given image in the receiver at the specified
745  * coordinates.
746  *
747  * @param image the image to draw
748  * @param x the x coordinate of where to draw
749  * @param y the y coordinate of where to draw
750  *
751  * @exception IllegalArgumentException <ul>
752  *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
753  *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
754  *    <li>ERROR_INVALID_ARGUMENT - if the given coordinates are outside the bounds of the image</li>
755  * @exception SWTException <ul>
756  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
757  * </ul>
758  * @exception SWTError <ul>
759  *    <li>ERROR_NO_HANDLES - if no handles are available to perform the operation</li>
760  * </ul>
761  */
762 public void drawImage(Image image, int x, int y) {
763     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
764     if (image is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
765     if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
766     drawImage(image, 0, 0, -1, -1, x, y, -1, -1, true);
767 }
768 
769 /**
770  * Copies a rectangular area from the source image into a (potentially
771  * different sized) rectangular area in the receiver. If the source
772  * and destination areas are of differing sizes, then the source
773  * area will be stretched or shrunk to fit the destination area
774  * as it is copied. The copy fails if any part of the source rectangle
775  * lies outside the bounds of the source image, or if any of the width
776  * or height arguments are negative.
777  *
778  * @param image the source image
779  * @param srcX the x coordinate in the source image to copy from
780  * @param srcY the y coordinate in the source image to copy from
781  * @param srcWidth the width in pixels to copy from the source
782  * @param srcHeight the height in pixels to copy from the source
783  * @param destX the x coordinate in the destination to copy to
784  * @param destY the y coordinate in the destination to copy to
785  * @param destWidth the width in pixels of the destination rectangle
786  * @param destHeight the height in pixels of the destination rectangle
787  *
788  * @exception IllegalArgumentException <ul>
789  *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
790  *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
791  *    <li>ERROR_INVALID_ARGUMENT - if any of the width or height arguments are negative.
792  *    <li>ERROR_INVALID_ARGUMENT - if the source rectangle is not contained within the bounds of the source image</li>
793  * </ul>
794  * @exception SWTException <ul>
795  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
796  * </ul>
797  * @exception SWTError <ul>
798  *    <li>ERROR_NO_HANDLES - if no handles are available to perform the operation</li>
799  * </ul>
800  */
801 public void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight) {
802     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
803     if (srcWidth is 0 || srcHeight is 0 || destWidth is 0 || destHeight is 0) return;
804     if (srcX < 0 || srcY < 0 || srcWidth < 0 || srcHeight < 0 || destWidth < 0 || destHeight < 0) {
805         SWT.error (SWT.ERROR_INVALID_ARGUMENT);
806     }
807     if (image is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
808     if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
809     drawImage(image, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, false);
810 }
811 
812 void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple) {
813     int width;
814     int height;
815     OS.gdk_drawable_get_size(srcImage.pixmap, &width, &height);
816     int imgWidth = width;
817     int imgHeight = height;
818     if (simple) {
819         srcWidth = destWidth = imgWidth;
820         srcHeight = destHeight = imgHeight;
821     } else {
822         simple = srcX is 0 && srcY is 0 &&
823             srcWidth is destWidth && destWidth is imgWidth &&
824             srcHeight is destHeight && destHeight is imgHeight;
825         if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) {
826             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
827         }
828     }
829     auto cairo = data.cairo;
830     if (cairo !is null) {
831         if (data.alpha !is 0) {
832             srcImage.createSurface();
833             Cairo.cairo_save(cairo);
834             if ((data.style & SWT.MIRRORED) !is 0) {
835                 Cairo.cairo_scale(cairo, -1f,  1);
836                 Cairo.cairo_translate(cairo, - 2 * destX - destWidth, 0);
837             }
838             Cairo.cairo_rectangle(cairo, destX , destY, destWidth, destHeight);
839             Cairo.cairo_clip(cairo);
840             Cairo.cairo_translate(cairo, destX - srcX, destY - srcY);
841             if (srcWidth !is destWidth || srcHeight !is destHeight) {
842                 Cairo.cairo_scale(cairo, destWidth / cast(float)srcWidth,  destHeight / cast(float)srcHeight);
843             }
844             int filter = Cairo.CAIRO_FILTER_GOOD;
845             switch (data.interpolation) {
846                 case SWT.DEFAULT: filter = Cairo.CAIRO_FILTER_GOOD; break;
847                 case SWT.NONE: filter = Cairo.CAIRO_FILTER_NEAREST; break;
848                 case SWT.LOW: filter = Cairo.CAIRO_FILTER_FAST; break;
849                 case SWT.HIGH: filter = Cairo.CAIRO_FILTER_BEST; break;
850                 default:
851             }
852             auto pattern = Cairo.cairo_pattern_create_for_surface(srcImage.surface);
853             if (pattern is null) SWT.error(SWT.ERROR_NO_HANDLES);
854             if (srcWidth !is destWidth || srcHeight !is destHeight) {
855                 /*
856                 * Bug in Cairo.  When drawing the image streched with an interpolation
857                 * alghorithm, the edges of the image are faded.  This is not a bug, but
858                 * it is not desired.  To avoid the faded edges, it should be possible to
859                 * use cairo_pattern_set_extend() to set the pattern extend to either
860                 * CAIRO_EXTEND_REFLECT or CAIRO_EXTEND_PAD, but these are not implemented
861                 * in some versions of cairo (1.2.x) and have bugs in others (in 1.4.2 it
862                 * draws with black edges).  The fix is to implement CAIRO_EXTEND_REFLECT
863                 * by creating an image that is 3 times bigger than the original, drawing
864                 * the original image in every quadrant (with an appropriate transform) and
865                 * use this image as the pattern.
866                 *
867                 * NOTE: For some reaons, it is necessary to use CAIRO_EXTEND_PAD with
868                 * the image that was created or the edges are still faded.
869                 */
870                 if (Cairo.cairo_version () >= Cairo.CAIRO_VERSION_ENCODE(1, 4, 0)) {
871                     auto surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_ARGB32, imgWidth * 3, imgHeight * 3);
872                     auto cr = Cairo.cairo_create(surface);
873                     Cairo.cairo_set_source_surface(cr, srcImage.surface, imgWidth, imgHeight);
874                     Cairo.cairo_paint(cr);
875                     Cairo.cairo_scale(cr, -1, -1);
876                     Cairo.cairo_set_source_surface(cr, srcImage.surface, -imgWidth, -imgHeight);
877                     Cairo.cairo_paint(cr);
878                     Cairo.cairo_set_source_surface(cr, srcImage.surface, -imgWidth * 3, -imgHeight);
879                     Cairo.cairo_paint(cr);
880                     Cairo.cairo_set_source_surface(cr, srcImage.surface, -imgWidth, -imgHeight * 3);
881                     Cairo.cairo_paint(cr);
882                     Cairo.cairo_set_source_surface(cr, srcImage.surface, -imgWidth * 3, -imgHeight * 3);
883                     Cairo.cairo_paint(cr);
884                     Cairo.cairo_scale(cr, 1, -1);
885                     Cairo.cairo_set_source_surface(cr, srcImage.surface, -imgWidth, imgHeight);
886                     Cairo.cairo_paint(cr);
887                     Cairo.cairo_set_source_surface(cr, srcImage.surface, -imgWidth * 3, imgHeight);
888                     Cairo.cairo_paint(cr);
889                     Cairo.cairo_scale(cr, -1, -1);
890                     Cairo.cairo_set_source_surface(cr, srcImage.surface, imgWidth, -imgHeight);
891                     Cairo.cairo_paint(cr);
892                     Cairo.cairo_set_source_surface(cr, srcImage.surface, imgWidth, -imgHeight * 3);
893                     Cairo.cairo_paint(cr);
894                     Cairo.cairo_destroy(cr);
895                     auto newPattern = Cairo.cairo_pattern_create_for_surface(surface);
896                     Cairo.cairo_surface_destroy(surface);
897                     if (newPattern is null) SWT.error(SWT.ERROR_NO_HANDLES);
898                     Cairo.cairo_pattern_destroy(pattern);
899                     pattern = newPattern;
900                     Cairo.cairo_pattern_set_extend(pattern, Cairo.CAIRO_EXTEND_PAD);
901                     double[] matrix = new double[6];
902                     Cairo.cairo_matrix_init_translate(cast(cairo_matrix_t*)matrix.ptr, imgWidth, imgHeight);
903                     Cairo.cairo_pattern_set_matrix(pattern, cast(cairo_matrix_t*)matrix.ptr);
904                 }
905 //              Cairo.cairo_pattern_set_extend(pattern, Cairo.CAIRO_EXTEND_REFLECT);
906             }
907             Cairo.cairo_pattern_set_filter(pattern, filter);
908             Cairo.cairo_set_source(cairo, pattern);
909             if (data.alpha !is 0xFF) {
910                 Cairo.cairo_paint_with_alpha(cairo, data.alpha / cast(float)0xFF);
911             } else {
912                 Cairo.cairo_paint(cairo);
913             }
914             Cairo.cairo_restore(cairo);
915             Cairo.cairo_pattern_destroy(pattern);
916         }
917         return;
918     }
919     if (srcImage.alpha !is -1 || srcImage.alphaData !is null) {
920         drawImageAlpha(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight);
921     } else if (srcImage.transparentPixel !is -1 || srcImage.mask !is null) {
922         drawImageMask(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight);
923     } else {
924         drawImage(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight);
925     }
926 }
927 void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, int imgWidth, int imgHeight) {
928     if (srcWidth is destWidth && srcHeight is destHeight) {
929         OS.gdk_draw_drawable(data.drawable, handle, srcImage.pixmap, srcX, srcY, destX, destY, destWidth, destHeight);
930     } else {
931         if (device.useXRender) {
932             drawImageXRender(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight, null, -1);
933             return;
934         }
935         auto pixbuf = scale(srcImage.pixmap, srcX, srcY, srcWidth, srcHeight, destWidth, destHeight);
936         if (pixbuf !is null) {
937             OS.gdk_pixbuf_render_to_drawable(pixbuf, data.drawable, handle, 0, 0, destX, destY, destWidth, destHeight, OS.GDK_RGB_DITHER_NORMAL, 0, 0);
938             OS.g_object_unref(pixbuf);
939         }
940     }
941 }
942 void drawImageAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, int imgWidth, int imgHeight) {
943     if (srcImage.alpha is 0) return;
944     if (srcImage.alpha is 255) {
945         drawImage(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight);
946         return;
947     }
948     if (device.useXRender) {
949         drawImageXRender(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight, srcImage.mask, OS.PictStandardA8);
950         return;
951     }
952     auto pixbuf = OS.gdk_pixbuf_new(OS.GDK_COLORSPACE_RGB, true, 8, srcWidth, srcHeight);
953     if (pixbuf is null) return;
954     auto colormap = OS.gdk_colormap_get_system();
955     OS.gdk_pixbuf_get_from_drawable(pixbuf, srcImage.pixmap, colormap, srcX, srcY, 0, 0, srcWidth, srcHeight);
956     int stride = OS.gdk_pixbuf_get_rowstride(pixbuf);
957     auto pixels = OS.gdk_pixbuf_get_pixels(pixbuf);
958     byte[] line = new byte[stride];
959     byte alpha = cast(byte)srcImage.alpha;
960     byte[] alphaData = srcImage.alphaData;
961     for (int y=0; y<srcHeight; y++) {
962         int alphaIndex = (y + srcY) * imgWidth + srcX;
963         OS.memmove(line.ptr, pixels + (y * stride), stride);
964         for (int x=3; x<stride; x+=4) {
965             line[x] = alphaData is null ? alpha : alphaData[alphaIndex++];
966         }
967         OS.memmove(pixels + (y * stride), line.ptr, stride);
968     }
969     if (srcWidth !is destWidth || srcHeight !is destHeight) {
970         auto scaledPixbuf = OS.gdk_pixbuf_scale_simple(pixbuf, destWidth, destHeight, OS.GDK_INTERP_BILINEAR);
971         OS.g_object_unref(pixbuf);
972         if (scaledPixbuf is null) return;
973         pixbuf = scaledPixbuf;
974     }
975     /*
976     * Feature in GTK.  gdk_draw_pixbuf was introduced in GTK+ 2.2.0 and
977     * supports clipping.
978     */
979     if (OS.GTK_VERSION >= OS.buildVERSION (2, 2, 0)) {
980         OS.gdk_draw_pixbuf(data.drawable, handle, pixbuf, 0, 0, destX, destY, destWidth, destHeight, OS.GDK_RGB_DITHER_NORMAL, 0, 0);
981     } else {
982         OS.gdk_pixbuf_render_to_drawable_alpha(pixbuf, data.drawable, 0, 0, destX, destY, destWidth, destHeight, OS.GDK_PIXBUF_ALPHA_BILEVEL, 128, OS.GDK_RGB_DITHER_NORMAL, 0, 0);
983     }
984     OS.g_object_unref(pixbuf);
985 }
986 void drawImageMask(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, int imgWidth, int imgHeight) {
987     auto drawable = data.drawable;
988     auto colorPixmap = srcImage.pixmap;
989     /* Generate the mask if necessary. */
990     if (srcImage.transparentPixel !is -1) srcImage.createMask();
991     auto maskPixmap = srcImage.mask;
992 
993     if (device.useXRender) {
994         drawImageXRender(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight, maskPixmap, OS.PictStandardA1);
995     } else {
996         if (srcWidth !is destWidth || srcHeight !is destHeight) {
997             auto pixbuf = OS.gdk_pixbuf_new(OS.GDK_COLORSPACE_RGB, true, 8, srcWidth, srcHeight);
998             if (pixbuf !is null) {
999                 auto colormap = OS.gdk_colormap_get_system();
1000                 OS.gdk_pixbuf_get_from_drawable(pixbuf, colorPixmap, colormap, srcX, srcY, 0, 0, srcWidth, srcHeight);
1001                 auto maskPixbuf = OS.gdk_pixbuf_new(OS.GDK_COLORSPACE_RGB, false, 8, srcWidth, srcHeight);
1002                 if (maskPixbuf !is null) {
1003                     OS.gdk_pixbuf_get_from_drawable(maskPixbuf, maskPixmap, null, srcX, srcY, 0, 0, srcWidth, srcHeight);
1004                     int stride = OS.gdk_pixbuf_get_rowstride(pixbuf);
1005                     auto pixels = OS.gdk_pixbuf_get_pixels(pixbuf);
1006                     byte[] line = new byte[stride];
1007                     int maskStride = OS.gdk_pixbuf_get_rowstride(maskPixbuf);
1008                     auto maskPixels = OS.gdk_pixbuf_get_pixels(maskPixbuf);
1009                     byte[] maskLine = new byte[maskStride];
1010                     for (int y=0; y<srcHeight; y++) {
1011                         auto offset = pixels + (y * stride);
1012                         OS.memmove(line.ptr, offset, stride);
1013                         auto maskOffset = maskPixels + (y * maskStride);
1014                         OS.memmove(maskLine.ptr, maskOffset, maskStride);
1015                         for (int x=0; x<srcWidth; x++) {
1016                             if (maskLine[x * 3] is 0) {
1017                                 line[x*4+3] = 0;
1018                             }
1019                         }
1020                         OS.memmove(offset, line.ptr, stride);
1021                     }
1022                     OS.g_object_unref(maskPixbuf);
1023                     auto scaledPixbuf = OS.gdk_pixbuf_scale_simple(pixbuf, destWidth, destHeight, OS.GDK_INTERP_BILINEAR);
1024                     if (scaledPixbuf !is null) {
1025                         GdkPixmap * colorBuffer;
1026                         GdkBitmap * maskBuffer;
1027                         OS.gdk_pixbuf_render_pixmap_and_mask(scaledPixbuf, &colorBuffer, &maskBuffer, 128);
1028                         colorPixmap = cast(GdkDrawable *)colorBuffer;
1029                         maskPixmap = cast(GdkDrawable *)maskBuffer;
1030                         OS.g_object_unref(scaledPixbuf);
1031                     }
1032                 }
1033                 OS.g_object_unref(pixbuf);
1034             }
1035             srcX = 0;
1036             srcY = 0;
1037             srcWidth = destWidth;
1038             srcHeight = destHeight;
1039         }
1040 
1041         /* Merge clipping with mask if necessary */
1042         if (data.clipRgn !is null) {
1043             int newWidth =  srcX + srcWidth;
1044             int newHeight = srcY + srcHeight;
1045             int bytesPerLine = (newWidth + 7) / 8;
1046             auto maskData = new char[bytesPerLine * newHeight];
1047             auto mask = cast(GdkDrawable *) OS.gdk_bitmap_create_from_data(null, maskData.ptr, newWidth, newHeight);
1048             if (mask !is null) {
1049                 auto gc = OS.gdk_gc_new(mask);
1050                 OS.gdk_region_offset(data.clipRgn, -destX + srcX, -destY + srcY);
1051                 OS.gdk_gc_set_clip_region(gc, data.clipRgn);
1052                 OS.gdk_region_offset(data.clipRgn, destX - srcX, destY - srcY);
1053                 GdkColor* color = new GdkColor();
1054                 color.pixel = 1;
1055                 OS.gdk_gc_set_foreground(gc, color);
1056                 OS.gdk_draw_rectangle(mask, gc, 1, 0, 0, newWidth, newHeight);
1057                 OS.gdk_gc_set_function(gc, OS.GDK_AND);
1058                 OS.gdk_draw_drawable(mask, gc, maskPixmap, 0, 0, 0, 0, newWidth, newHeight);
1059                 OS.g_object_unref(gc);
1060                 if (maskPixmap !is null && srcImage.mask !is maskPixmap) OS.g_object_unref(maskPixmap);
1061                 maskPixmap = mask;
1062             }
1063         }
1064 
1065         /* Blit cliping the mask */
1066         GdkGCValues* values = new GdkGCValues();
1067         OS.gdk_gc_get_values(handle, values);
1068         OS.gdk_gc_set_clip_mask(handle, cast(GdkBitmap *)maskPixmap);
1069         OS.gdk_gc_set_clip_origin(handle, destX - srcX, destY - srcY);
1070         OS.gdk_draw_drawable(drawable, handle, colorPixmap, srcX, srcY, destX, destY, srcWidth, srcHeight);
1071         OS.gdk_gc_set_values(handle, values, OS.GDK_GC_CLIP_MASK | OS.GDK_GC_CLIP_X_ORIGIN | OS.GDK_GC_CLIP_Y_ORIGIN);
1072         if (data.clipRgn !is null) OS.gdk_gc_set_clip_region(handle, data.clipRgn);
1073     }
1074 
1075     /* Destroy scaled pixmaps */
1076     if (colorPixmap !is null && srcImage.pixmap !is colorPixmap) OS.g_object_unref(colorPixmap);
1077     if (maskPixmap !is null && srcImage.mask !is maskPixmap) OS.g_object_unref(maskPixmap);
1078     /* Destroy the image mask if the there is a GC created on the image */
1079     if (srcImage.transparentPixel !is -1 && srcImage.memGC !is null) srcImage.destroyMask();
1080 }
1081 void drawImageXRender(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, int imgWidth, int imgHeight, GdkDrawable* maskPixmap, int maskType) {
1082     int translateX = 0, translateY = 0;
1083     auto drawable = data.drawable;
1084     if (data.image is null && !data.realDrawable) {
1085         int x, y;
1086         GdkDrawable* real_drawable;
1087         OS.gdk_window_get_internal_paint_info(cast(GdkWindow*)drawable, &real_drawable, &x, &y);
1088         drawable = real_drawable;
1089         translateX = -x;
1090         translateY = -y;
1091     }
1092     auto xDisplay = OS.GDK_DISPLAY();
1093     size_t maskPict = 0;
1094     if (maskPixmap !is null) {
1095         int attribCount = 0;
1096         XRenderPictureAttributes* attrib;
1097         if (srcImage.alpha !is -1) {
1098             attribCount = 1;
1099             attrib = new XRenderPictureAttributes();
1100             attrib.repeat = true;
1101         }
1102         maskPict = OS.XRenderCreatePicture(cast(void*)xDisplay, OS.gdk_x11_drawable_get_xid(maskPixmap), OS.XRenderFindStandardFormat(cast(void*)xDisplay, maskType), attribCount, attrib);
1103         if (maskPict is 0) SWT.error(SWT.ERROR_NO_HANDLES);
1104     }
1105     auto format = OS.XRenderFindVisualFormat(cast(void*)xDisplay, OS.gdk_x11_visual_get_xvisual(OS.gdk_visual_get_system()));
1106     auto destPict = OS.XRenderCreatePicture(cast(void*)xDisplay, OS.gdk_x11_drawable_get_xid(drawable), format, 0, null);
1107     if (destPict is 0) SWT.error(SWT.ERROR_NO_HANDLES);
1108     size_t srcPict = OS.XRenderCreatePicture(xDisplay, OS.gdk_x11_drawable_get_xid(srcImage.pixmap), format, 0, null);
1109     if (srcPict is 0) SWT.error(SWT.ERROR_NO_HANDLES);
1110     if (srcWidth !is destWidth || srcHeight !is destHeight) {
1111         int[] transform = [cast(int)((cast(float)srcWidth / destWidth) * 65536), 0, 0, 0, cast(int)((cast(float)srcHeight / destHeight) * 65536), 0, 0, 0, 65536];
1112         OS.XRenderSetPictureTransform(xDisplay, srcPict, cast(XTransform*)transform.ptr);
1113         if (maskPict !is 0) OS.XRenderSetPictureTransform(xDisplay, maskPict, cast(XTransform*)transform.ptr);
1114         srcX *= destWidth / cast(float)srcWidth;
1115         srcY *= destHeight / cast(float)srcHeight;
1116     }
1117     auto clipping = data.clipRgn;
1118     if (data.damageRgn !is null) {
1119         if (clipping is null) {
1120             clipping = data.damageRgn;
1121         } else {
1122             clipping = OS.gdk_region_new();
1123             OS.gdk_region_union(clipping, data.clipRgn);
1124             OS.gdk_region_intersect(clipping, data.damageRgn);
1125         }
1126     }
1127     if (clipping !is null) {
1128         int nRects;
1129         GdkRectangle* rects;
1130         OS.gdk_region_get_rectangles(clipping, &rects, &nRects);
1131         GdkRectangle* rect;
1132         short[] xRects = new short[nRects * 4];
1133         for (int i=0, j=0; i<nRects; i++, j+=4) {
1134             rect = rects +i;
1135             xRects[j  ] = cast(short)rect.x;
1136             xRects[j+1] = cast(short)rect.y;
1137             xRects[j+2] = cast(short)rect.width;
1138             xRects[j+3] = cast(short)rect.height;
1139         }
1140         OS.XRenderSetPictureClipRectangles(xDisplay, destPict, translateX, translateY,cast(XRectangle*) xRects.ptr, nRects);
1141         if (clipping !is data.clipRgn && clipping !is data.damageRgn) {
1142             OS.gdk_region_destroy(clipping);
1143         }
1144         if (rects !is null) OS.g_free(rects);
1145     }
1146     OS.XRenderComposite(xDisplay, maskPict !is 0 ? OS.PictOpOver : OS.PictOpSrc, srcPict, maskPict, destPict, srcX, srcY, srcX, srcY, destX + translateX, destY + translateY, destWidth, destHeight);
1147     OS.XRenderFreePicture(xDisplay, destPict);
1148     OS.XRenderFreePicture(xDisplay, srcPict);
1149     if (maskPict !is 0) OS.XRenderFreePicture(xDisplay, maskPict);
1150 }
1151 
1152 GdkPixbuf* scale(GdkDrawable* src, int srcX, int srcY, int srcWidth, int srcHeight, int destWidth, int destHeight) {
1153     auto pixbuf = OS.gdk_pixbuf_new(OS.GDK_COLORSPACE_RGB, false, 8, srcWidth, srcHeight);
1154     if (pixbuf is null) return null;
1155     auto colormap = OS.gdk_colormap_get_system();
1156     OS.gdk_pixbuf_get_from_drawable(pixbuf, src, colormap, srcX, srcY, 0, 0, srcWidth, srcHeight);
1157     auto scaledPixbuf = OS.gdk_pixbuf_scale_simple(pixbuf, destWidth, destHeight, OS.GDK_INTERP_BILINEAR);
1158     OS.g_object_unref(pixbuf);
1159     return scaledPixbuf;
1160 }
1161 
1162 /**
1163  * Draws a line, using the foreground color, between the points
1164  * (<code>x1</code>, <code>y1</code>) and (<code>x2</code>, <code>y2</code>).
1165  *
1166  * @param x1 the first point's x coordinate
1167  * @param y1 the first point's y coordinate
1168  * @param x2 the second point's x coordinate
1169  * @param y2 the second point's y coordinate
1170  *
1171  * @exception SWTException <ul>
1172  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1173  * </ul>
1174  */
1175 public void drawLine(int x1, int y1, int x2, int y2) {
1176     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1177     checkGC(DRAW);
1178     auto cairo = data.cairo;
1179     if (cairo !is null) {
1180         double xOffset = data.cairoXoffset, yOffset = data.cairoYoffset;
1181         Cairo.cairo_move_to(cairo, x1 + xOffset, y1 + yOffset);
1182         Cairo.cairo_line_to(cairo, x2 + xOffset, y2 + yOffset);
1183         Cairo.cairo_stroke(cairo);
1184         return;
1185     }
1186     OS.gdk_draw_line (data.drawable, handle, x1, y1, x2, y2);
1187 }
1188 
1189 /**
1190  * Draws the outline of an oval, using the foreground color,
1191  * within the specified rectangular area.
1192  * <p>
1193  * The result is a circle or ellipse that fits within the
1194  * rectangle specified by the <code>x</code>, <code>y</code>,
1195  * <code>width</code>, and <code>height</code> arguments.
1196  * </p><p>
1197  * The oval covers an area that is <code>width + 1</code>
1198  * pixels wide and <code>height + 1</code> pixels tall.
1199  * </p>
1200  *
1201  * @param x the x coordinate of the upper left corner of the oval to be drawn
1202  * @param y the y coordinate of the upper left corner of the oval to be drawn
1203  * @param width the width of the oval to be drawn
1204  * @param height the height of the oval to be drawn
1205  *
1206  * @exception SWTException <ul>
1207  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1208  * </ul>
1209  */
1210 public void drawOval(int x, int y, int width, int height) {
1211     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1212     checkGC(DRAW);
1213     if (width < 0) {
1214         x = x + width;
1215         width = -width;
1216     }
1217     if (height < 0) {
1218         y = y + height;
1219         height = -height;
1220     }
1221     auto cairo = data.cairo;
1222     if (cairo !is null) {
1223         double xOffset = data.cairoXoffset, yOffset = data.cairoYoffset;
1224         if (width is height) {
1225             Cairo.cairo_arc_negative(cairo, x + xOffset + width / 2f, y + yOffset + height / 2f, width / 2f, 0, -2 * cast(float)Compatibility.PI);
1226         } else {
1227             Cairo.cairo_save(cairo);
1228             Cairo.cairo_translate(cairo, x + xOffset + width / 2f, y + yOffset + height / 2f);
1229             Cairo.cairo_scale(cairo, width / 2f, height / 2f);
1230             Cairo.cairo_arc_negative(cairo, 0, 0, 1, 0, -2 * cast(float)Compatibility.PI);
1231             Cairo.cairo_restore(cairo);
1232         }
1233         Cairo.cairo_stroke(cairo);
1234         return;
1235     }
1236     OS.gdk_draw_arc(data.drawable, handle, 0, x, y, width, height, 0, 23040);
1237 }
1238 
1239 /**
1240  * Draws the path described by the parameter.
1241  * <p>
1242  * This operation requires the operating system's advanced
1243  * graphics subsystem which may not be available on some
1244  * platforms.
1245  * </p>
1246  *
1247  * @param path the path to draw
1248  *
1249  * @exception IllegalArgumentException <ul>
1250  *    <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
1251  *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
1252  * </ul>
1253  * @exception SWTException <ul>
1254  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1255  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
1256  * </ul>
1257  *
1258  * @see Path
1259  *
1260  * @since 3.1
1261  */
1262 public void drawPath(Path path) {
1263     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1264     if (path is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1265     if (path.handle is null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1266     initCairo();
1267     checkGC(DRAW);
1268     auto cairo = data.cairo;
1269     Cairo.cairo_save(cairo);
1270     double xOffset = data.cairoXoffset, yOffset = data.cairoYoffset;
1271     Cairo.cairo_translate(cairo, xOffset, yOffset);
1272     auto copy = Cairo.cairo_copy_path(path.handle);
1273     if (copy is null) SWT.error(SWT.ERROR_NO_HANDLES);
1274     Cairo.cairo_append_path(cairo, copy);
1275     Cairo.cairo_path_destroy(copy);
1276     Cairo.cairo_stroke(cairo);
1277     Cairo.cairo_restore(cairo);
1278 }
1279 
1280 /**
1281  * Draws a pixel, using the foreground color, at the specified
1282  * point (<code>x</code>, <code>y</code>).
1283  * <p>
1284  * Note that the receiver's line attributes do not affect this
1285  * operation.
1286  * </p>
1287  *
1288  * @param x the point's x coordinate
1289  * @param y the point's y coordinate
1290  *
1291  * @exception SWTException <ul>
1292  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1293  * </ul>
1294  *
1295  * @since 3.0
1296  */
1297 public void drawPoint (int x, int y) {
1298     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1299     checkGC(DRAW);
1300     auto cairo = data.cairo;
1301     if (cairo !is null) {
1302         Cairo.cairo_rectangle(cairo, x, y, 1, 1);
1303         Cairo.cairo_fill(cairo);
1304         return;
1305     }
1306     OS.gdk_draw_point(data.drawable, handle, x, y);
1307 }
1308 
1309 /**
1310  * Draws the closed polygon which is defined by the specified array
1311  * of integer coordinates, using the receiver's foreground color. The array
1312  * contains alternating x and y values which are considered to represent
1313  * points which are the vertices of the polygon. Lines are drawn between
1314  * each consecutive pair, and between the first pair and last pair in the
1315  * array.
1316  *
1317  * @param pointArray an array of alternating x and y values which are the vertices of the polygon
1318  *
1319  * @exception SWTException <ul>
1320  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1321  * </ul>
1322  */
1323 public void drawPolygon(int[] pointArray) {
1324     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1325     // SWT externsion: allow null array
1326     //if (pointArray is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1327     checkGC(DRAW);
1328     auto cairo = data.cairo;
1329     if (cairo !is null) {
1330         drawPolyline(cairo, pointArray, true);
1331         Cairo.cairo_stroke(cairo);
1332         return;
1333     }
1334     OS.gdk_draw_polygon(data.drawable, handle, 0, cast(GdkPoint*)pointArray.ptr, cast(int)/*64bit*/pointArray.length / 2);
1335 }
1336 
1337 /**
1338  * Draws the polyline which is defined by the specified array
1339  * of integer coordinates, using the receiver's foreground color. The array
1340  * contains alternating x and y values which are considered to represent
1341  * points which are the corners of the polyline. Lines are drawn between
1342  * each consecutive pair, but not between the first pair and last pair in
1343  * the array.
1344  *
1345  * @param pointArray an array of alternating x and y values which are the corners of the polyline
1346  *
1347  * @exception SWTException <ul>
1348  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1349  * </ul>
1350  */
1351 public void drawPolyline(int[] pointArray) {
1352     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1353     // SWT externsion: allow null array
1354     //if (pointArray is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1355     checkGC(DRAW);
1356     auto cairo = data.cairo;
1357     if (cairo !is null) {
1358         drawPolyline(cairo, pointArray, false);
1359         Cairo.cairo_stroke(cairo);
1360         return;
1361     }
1362     OS.gdk_draw_lines(data.drawable, handle, cast(GdkPoint*)pointArray.ptr, cast(int)/*64bit*/pointArray.length / 2);
1363 }
1364 
1365 void drawPolyline(org.eclipse.swt.internal.gtk.OS.cairo_t* cairo, int[] pointArray, bool close) {
1366     auto count = pointArray.length / 2;
1367     if (count is 0) return;
1368     double xOffset = data.cairoXoffset, yOffset = data.cairoYoffset;
1369     Cairo.cairo_move_to(cairo, pointArray[0] + xOffset, pointArray[1] + yOffset);
1370     for (int i = 1, j=2; i < count; i++, j += 2) {
1371         Cairo.cairo_line_to(cairo, pointArray[j] + xOffset, pointArray[j + 1] + yOffset);
1372     }
1373     if (close) Cairo.cairo_close_path(cairo);
1374 }
1375 
1376 /**
1377  * Draws the outline of the rectangle specified by the arguments,
1378  * using the receiver's foreground color. The left and right edges
1379  * of the rectangle are at <code>x</code> and <code>x + width</code>.
1380  * The top and bottom edges are at <code>y</code> and <code>y + height</code>.
1381  *
1382  * @param x the x coordinate of the rectangle to be drawn
1383  * @param y the y coordinate of the rectangle to be drawn
1384  * @param width the width of the rectangle to be drawn
1385  * @param height the height of the rectangle to be drawn
1386  *
1387  * @exception SWTException <ul>
1388  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1389  * </ul>
1390  */
1391 public void drawRectangle(int x, int y, int width, int height) {
1392     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1393     checkGC(DRAW);
1394     if (width < 0) {
1395         x = x + width;
1396         width = -width;
1397     }
1398     if (height < 0) {
1399         y = y + height;
1400         height = -height;
1401     }
1402     auto cairo = data.cairo;
1403     if (cairo !is null) {
1404         double xOffset = data.cairoXoffset, yOffset = data.cairoYoffset;
1405         Cairo.cairo_rectangle(cairo, x + xOffset, y + yOffset, width, height);
1406         Cairo.cairo_stroke(cairo);
1407         return;
1408     }
1409     OS.gdk_draw_rectangle(data.drawable, handle, 0, x, y, width, height);
1410 }
1411 
1412 /**
1413  * Draws the outline of the specified rectangle, using the receiver's
1414  * foreground color. The left and right edges of the rectangle are at
1415  * <code>rect.x</code> and <code>rect.x + rect.width</code>. The top
1416  * and bottom edges are at <code>rect.y</code> and
1417  * <code>rect.y + rect.height</code>.
1418  *
1419  * @param rect the rectangle to draw
1420  *
1421  * @exception IllegalArgumentException <ul>
1422  *    <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li>
1423  * </ul>
1424  * @exception SWTException <ul>
1425  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1426  * </ul>
1427  */
1428 public void drawRectangle(Rectangle rect) {
1429     if (rect is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1430     drawRectangle (rect.x, rect.y, rect.width, rect.height);
1431 }
1432 /**
1433  * Draws the outline of the round-cornered rectangle specified by
1434  * the arguments, using the receiver's foreground color. The left and
1435  * right edges of the rectangle are at <code>x</code> and <code>x + width</code>.
1436  * The top and bottom edges are at <code>y</code> and <code>y + height</code>.
1437  * The <em>roundness</em> of the corners is specified by the
1438  * <code>arcWidth</code> and <code>arcHeight</code> arguments, which
1439  * are respectively the width and height of the ellipse used to draw
1440  * the corners.
1441  *
1442  * @param x the x coordinate of the rectangle to be drawn
1443  * @param y the y coordinate of the rectangle to be drawn
1444  * @param width the width of the rectangle to be drawn
1445  * @param height the height of the rectangle to be drawn
1446  * @param arcWidth the width of the arc
1447  * @param arcHeight the height of the arc
1448  *
1449  * @exception SWTException <ul>
1450  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1451  * </ul>
1452  */
1453 public void drawRoundRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight) {
1454     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1455     checkGC(DRAW);
1456     int nx = x;
1457     int ny = y;
1458     int nw = width;
1459     int nh = height;
1460     int naw = arcWidth;
1461     int nah = arcHeight;
1462     if (nw < 0) {
1463         nw = 0 - nw;
1464         nx = nx - nw;
1465     }
1466     if (nh < 0) {
1467         nh = 0 - nh;
1468         ny = ny -nh;
1469     }
1470     if (naw < 0) naw = 0 - naw;
1471     if (nah < 0) nah = 0 - nah;
1472     auto cairo = data.cairo;
1473     if (cairo !is null) {
1474         double xOffset = data.cairoXoffset, yOffset = data.cairoYoffset;
1475         if (naw is 0 || nah is 0) {
1476             Cairo.cairo_rectangle(cairo, x + xOffset, y + yOffset, width, height);
1477         } else {
1478             float naw2 = naw / 2f;
1479             float nah2 = nah / 2f;
1480             float fw = nw / naw2;
1481             float fh = nh / nah2;
1482             Cairo.cairo_save(cairo);
1483             Cairo.cairo_translate(cairo, nx + xOffset, ny + yOffset);
1484             Cairo.cairo_scale(cairo, naw2, nah2);
1485             Cairo.cairo_move_to(cairo, fw - 1, 0);
1486             Cairo.cairo_arc(cairo, fw - 1, 1, 1, Compatibility.PI + Compatibility.PI/2.0, Compatibility.PI*2.0);
1487             Cairo.cairo_arc(cairo, fw - 1, fh - 1, 1, 0, Compatibility.PI/2.0);
1488             Cairo.cairo_arc(cairo, 1, fh - 1, 1, Compatibility.PI/2, Compatibility.PI);
1489             Cairo.cairo_arc(cairo, 1, 1, 1, Compatibility.PI, 270.0*Compatibility.PI/180.0);
1490             Cairo.cairo_close_path(cairo);
1491             Cairo.cairo_restore(cairo);
1492         }
1493         Cairo.cairo_stroke(cairo);
1494         return;
1495     }
1496     int naw2 = naw / 2;
1497     int nah2 = nah / 2;
1498     auto drawable = data.drawable;
1499     if (nw > naw) {
1500         if (nh > nah) {
1501             OS.gdk_draw_arc(drawable, handle, 0, nx, ny, naw, nah, 5760, 5760);
1502             OS.gdk_draw_line(drawable, handle, nx + naw2, ny, nx + nw - naw2, ny);
1503             OS.gdk_draw_arc(drawable, handle, 0, nx + nw - naw, ny, naw, nah, 0, 5760);
1504             OS.gdk_draw_line(drawable, handle, nx + nw, ny + nah2, nx + nw, ny + nh - nah2);
1505             OS.gdk_draw_arc(drawable, handle, 0, nx + nw - naw, ny + nh - nah, naw, nah, 17280, 5760);
1506             OS.gdk_draw_line(drawable,handle, nx + naw2, ny + nh, nx + nw - naw2, ny + nh);
1507             OS.gdk_draw_arc(drawable, handle, 0, nx, ny + nh - nah, naw, nah, 11520, 5760);
1508             OS.gdk_draw_line(drawable, handle,  nx, ny + nah2, nx, ny + nh - nah2);
1509         } else {
1510             OS.gdk_draw_arc(drawable, handle, 0, nx, ny, naw, nh, 5760, 11520);
1511             OS.gdk_draw_line(drawable, handle, nx + naw2, ny, nx + nw - naw2, ny);
1512             OS.gdk_draw_arc(drawable, handle, 0, nx + nw - naw, ny, naw, nh, 17280, 11520);
1513             OS.gdk_draw_line(drawable,handle, nx + naw2, ny + nh, nx + nw - naw2, ny + nh);
1514         }
1515     } else {
1516         if (nh > nah) {
1517             OS.gdk_draw_arc(drawable, handle, 0, nx, ny, nw, nah, 0, 11520);
1518             OS.gdk_draw_line(drawable, handle, nx + nw, ny + nah2, nx + nw, ny + nh - nah2);
1519             OS.gdk_draw_arc(drawable, handle, 0, nx, ny + nh - nah, nw, nah, 11520, 11520);
1520             OS.gdk_draw_line(drawable,handle, nx, ny + nah2, nx, ny + nh - nah2);
1521         } else {
1522             OS.gdk_draw_arc(drawable, handle, 0, nx, ny, nw, nh, 0, 23040);
1523         }
1524     }
1525 }
1526 
1527 /**
1528  * Draws the given str, using the receiver's current font and
1529  * foreground color. No tab expansion or carriage return processing
1530  * will be performed. The background of the rectangular area where
1531  * the str is being drawn will be filled with the receiver's
1532  * background color.
1533  *
1534  * @param str the str to be drawn
1535  * @param x the x coordinate of the top left corner of the rectangular area where the str is to be drawn
1536  * @param y the y coordinate of the top left corner of the rectangular area where the str is to be drawn
1537  *
1538  * @exception SWTException <ul>
1539  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1540  * </ul>
1541  */
1542 public void drawString (String str, int x, int y) {
1543     drawString(str, x, y, false);
1544 }
1545 /**
1546  * Draws the given str, using the receiver's current font and
1547  * foreground color. No tab expansion or carriage return processing
1548  * will be performed. If <code>isTransparent</code> is <code>true</code>,
1549  * then the background of the rectangular area where the str is being
1550  * drawn will not be modified, otherwise it will be filled with the
1551  * receiver's background color.
1552  *
1553  * @param str the str to be drawn
1554  * @param x the x coordinate of the top left corner of the rectangular area where the str is to be drawn
1555  * @param y the y coordinate of the top left corner of the rectangular area where the str is to be drawn
1556  * @param isTransparent if <code>true</code> the background will be transparent, otherwise it will be opaque
1557  *
1558  * @exception SWTException <ul>
1559  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1560  * </ul>
1561  */
1562 public void drawString(String str, int x, int y, bool isTransparent) {
1563     drawText(str, x, y, isTransparent ? SWT.DRAW_TRANSPARENT : 0);
1564 }
1565 
1566 /**
1567  * Draws the given str, using the receiver's current font and
1568  * foreground color. Tab expansion and carriage return processing
1569  * are performed. The background of the rectangular area where
1570  * the text is being drawn will be filled with the receiver's
1571  * background color.
1572  *
1573  * @param str the str to be drawn
1574  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
1575  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
1576  *
1577  * @exception SWTException <ul>
1578  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1579  * </ul>
1580  */
1581 public void drawText(String str, int x, int y) {
1582     drawText(str, x, y, SWT.DRAW_DELIMITER | SWT.DRAW_TAB);
1583 }
1584 
1585 /**
1586  * Draws the given str, using the receiver's current font and
1587  * foreground color. Tab expansion and carriage return processing
1588  * are performed. If <code>isTransparent</code> is <code>true</code>,
1589  * then the background of the rectangular area where the text is being
1590  * drawn will not be modified, otherwise it will be filled with the
1591  * receiver's background color.
1592  *
1593  * @param str the str to be drawn
1594  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
1595  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
1596  * @param isTransparent if <code>true</code> the background will be transparent, otherwise it will be opaque
1597  *
1598  * @exception SWTException <ul>
1599  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1600  * </ul>
1601  */
1602 public void drawText(String str, int x, int y, bool isTransparent) {
1603     int flags = SWT.DRAW_DELIMITER | SWT.DRAW_TAB;
1604     if (isTransparent) flags |= SWT.DRAW_TRANSPARENT;
1605     drawText(str, x, y, flags);
1606 }
1607 
1608 /**
1609  * Draws the given str, using the receiver's current font and
1610  * foreground color. Tab expansion, line delimiter and mnemonic
1611  * processing are performed according to the specified flags. If
1612  * <code>flags</code> includes <code>DRAW_TRANSPARENT</code>,
1613  * then the background of the rectangular area where the text is being
1614  * drawn will not be modified, otherwise it will be filled with the
1615  * receiver's background color.
1616  * <p>
1617  * The parameter <code>flags</code> may be a combination of:
1618  * <dl>
1619  * <dt><b>DRAW_DELIMITER</b></dt>
1620  * <dd>draw multiple lines</dd>
1621  * <dt><b>DRAW_TAB</b></dt>
1622  * <dd>expand tabs</dd>
1623  * <dt><b>DRAW_MNEMONIC</b></dt>
1624  * <dd>underline the mnemonic character</dd>
1625  * <dt><b>DRAW_TRANSPARENT</b></dt>
1626  * <dd>transparent background</dd>
1627  * </dl>
1628  * </p>
1629  *
1630  * @param str the str to be drawn
1631  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
1632  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
1633  * @param flags the flags specifying how to process the text
1634  *
1635  * @exception SWTException <ul>
1636  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1637  * </ul>
1638  */
1639 public void drawText (String str, int x, int y, int flags) {
1640     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1641     // SWT extension: allow null string
1642     //if (str is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1643     if (str.length is 0) return;
1644     auto cairo = data.cairo;
1645     if (cairo !is null) {
1646         if (OS.GTK_VERSION < OS.buildVERSION(2, 8, 0)) {
1647             //TODO - honor flags
1648             checkGC(FOREGROUND | FONT);
1649             cairo_font_extents_t* extents = new cairo_font_extents_t();
1650             Cairo.cairo_font_extents(cairo, extents);
1651             double baseline = y + extents.ascent;
1652             Cairo.cairo_move_to(cairo, x, baseline);
1653             char* buffer = toStringz( str );
1654             Cairo.cairo_show_text(cairo, buffer);
1655             Cairo.cairo_new_path(cairo);
1656             return;
1657         }
1658     }
1659     setString(str, flags);
1660     if (cairo !is null) {
1661         if ((flags & SWT.DRAW_TRANSPARENT) is 0) {
1662             checkGC(BACKGROUND);
1663             int width, height;
1664             OS.pango_layout_get_size(data.layout, &width, &height);
1665             Cairo.cairo_rectangle(cairo, x, y, OS.PANGO_PIXELS(width), OS.PANGO_PIXELS(height));
1666             Cairo.cairo_fill(cairo);
1667         }
1668         checkGC(FOREGROUND | FONT);
1669         if ((data.style & SWT.MIRRORED) !is 0) {
1670             Cairo.cairo_save(cairo);
1671             int width, height;
1672             OS.pango_layout_get_size(data.layout, &width, &height);
1673             Cairo.cairo_scale(cairo, -1f,  1);
1674             Cairo.cairo_translate(cairo, -2 * x - OS.PANGO_PIXELS(width), 0);
1675         }
1676         Cairo.cairo_move_to(cairo, x, y);
1677         OS.pango_cairo_show_layout(cairo, data.layout);
1678         if ((data.style & SWT.MIRRORED) !is 0) {
1679             Cairo.cairo_restore(cairo);
1680         }
1681         Cairo.cairo_new_path(cairo);
1682         return;
1683     }
1684     checkGC(FOREGROUND | FONT | BACKGROUND_BG);
1685     GdkColor* background = null;
1686     if ((flags & SWT.DRAW_TRANSPARENT) is 0) background = data.background;
1687     if (!data.xorMode) {
1688         OS.gdk_draw_layout_with_colors(data.drawable, handle, x, y, data.layout, null, background);
1689     } else {
1690         auto layout = data.layout;
1691         int w, h;
1692         OS.pango_layout_get_size(layout, &w, &h);
1693         int width = OS.PANGO_PIXELS(w);
1694         int height = OS.PANGO_PIXELS(h);
1695         auto pixmap = OS.gdk_pixmap_new(cast(GdkDrawable*)OS.GDK_ROOT_PARENT(), width, height, -1);
1696         if (pixmap is null) SWT.error(SWT.ERROR_NO_HANDLES);
1697         auto gdkGC = OS.gdk_gc_new(cast(GdkDrawable*)pixmap);
1698         if (gdkGC is null) SWT.error(SWT.ERROR_NO_HANDLES);
1699         GdkColor* black = new GdkColor();
1700         OS.gdk_gc_set_foreground(gdkGC, black);
1701         OS.gdk_draw_rectangle(cast(GdkDrawable*)pixmap, gdkGC, 1, 0, 0, width, height);
1702         OS.gdk_gc_set_foreground(gdkGC, data.foreground);
1703         OS.gdk_draw_layout_with_colors(cast(GdkDrawable*)pixmap, gdkGC, 0, 0, layout, null, background);
1704         OS.g_object_unref(gdkGC);
1705         OS.gdk_draw_drawable(data.drawable, handle, cast(GdkDrawable*)pixmap, 0, 0, x, y, width, height);
1706         OS.g_object_unref(pixmap);
1707     }
1708 }
1709 
1710 /**
1711  * Compares the argument to the receiver, and returns true
1712  * if they represent the <em>same</em> object using a class
1713  * specific comparison.
1714  *
1715  * @param object the object to compare with this object
1716  * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
1717  *
1718  * @see #hashCode
1719  */
1720 public override equals_t opEquals(Object object) {
1721     if (object is this) return true;
1722     if ( auto gc = cast(GC)object){
1723        return handle is gc.handle;
1724     }
1725     return false;
1726 }
1727 
1728 
1729 /**
1730  * Fills the interior of a circular or elliptical arc within
1731  * the specified rectangular area, with the receiver's background
1732  * color.
1733  * <p>
1734  * The resulting arc begins at <code>startAngle</code> and extends
1735  * for <code>arcAngle</code> degrees, using the current color.
1736  * Angles are interpreted such that 0 degrees is at the 3 o'clock
1737  * position. A positive value indicates a counter-clockwise rotation
1738  * while a negative value indicates a clockwise rotation.
1739  * </p><p>
1740  * The center of the arc is the center of the rectangle whose origin
1741  * is (<code>x</code>, <code>y</code>) and whose size is specified by the
1742  * <code>width</code> and <code>height</code> arguments.
1743  * </p><p>
1744  * The resulting arc covers an area <code>width + 1</code> pixels wide
1745  * by <code>height + 1</code> pixels tall.
1746  * </p>
1747  *
1748  * @param x the x coordinate of the upper-left corner of the arc to be filled
1749  * @param y the y coordinate of the upper-left corner of the arc to be filled
1750  * @param width the width of the arc to be filled
1751  * @param height the height of the arc to be filled
1752  * @param startAngle the beginning angle
1753  * @param arcAngle the angular extent of the arc, relative to the start angle
1754  *
1755  * @exception SWTException <ul>
1756  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1757  * </ul>
1758  *
1759  * @see #drawArc
1760  */
1761 public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
1762     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1763     checkGC(FILL);
1764     if (width < 0) {
1765         x = x + width;
1766         width = -width;
1767     }
1768     if (height < 0) {
1769         y = y + height;
1770         height = -height;
1771     }
1772     if (width is 0 || height is 0 || arcAngle is 0) return;
1773     auto cairo = data.cairo;
1774     if (cairo !is null) {
1775         if (width is height) {
1776             if (arcAngle >= 0) {
1777                 Cairo.cairo_arc_negative(cairo, x + width / 2f, y + height / 2f, width / 2f, -startAngle * cast(float)Compatibility.PI / 180,  -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
1778             } else {
1779                 Cairo.cairo_arc(cairo, x + width / 2f, y + height / 2f, width / 2f, -startAngle * cast(float)Compatibility.PI / 180,  -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
1780             }
1781             Cairo.cairo_line_to(cairo, x + width / 2f, y + height / 2f);
1782         } else {
1783             Cairo.cairo_save(cairo);
1784             Cairo.cairo_translate(cairo, x + width / 2f, y + height / 2f);
1785             Cairo.cairo_scale(cairo, width / 2f, height / 2f);
1786             if (arcAngle >= 0) {
1787                 Cairo.cairo_arc_negative(cairo, 0, 0, 1, -startAngle * cast(float)Compatibility.PI / 180,  -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
1788             } else {
1789                 Cairo.cairo_arc(cairo, 0, 0, 1, -startAngle * cast(float)Compatibility.PI / 180,  -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180);
1790             }
1791             Cairo.cairo_line_to(cairo, 0, 0);
1792             Cairo.cairo_restore(cairo);
1793         }
1794         Cairo.cairo_fill(cairo);
1795         return;
1796     }
1797     OS.gdk_draw_arc(data.drawable, handle, 1, x, y, width, height, startAngle * 64, arcAngle * 64);
1798 }
1799 
1800 /**
1801  * Fills the interior of the specified rectangle with a gradient
1802  * sweeping from left to right or top to bottom progressing
1803  * from the receiver's foreground color to its background color.
1804  *
1805  * @param x the x coordinate of the rectangle to be filled
1806  * @param y the y coordinate of the rectangle to be filled
1807  * @param width the width of the rectangle to be filled, may be negative
1808  *        (inverts direction of gradient if horizontal)
1809  * @param height the height of the rectangle to be filled, may be negative
1810  *        (inverts direction of gradient if vertical)
1811  * @param vertical if true sweeps from top to bottom, else
1812  *        sweeps from left to right
1813  *
1814  * @exception SWTException <ul>
1815  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1816  * </ul>
1817  *
1818  * @see #drawRectangle(int, int, int, int)
1819  */
1820 public void fillGradientRectangle(int x, int y, int width, int height, bool vertical) {
1821     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1822     if ((width is 0) || (height is 0)) return;
1823 
1824     /* Rewrite this to use GdkPixbuf */
1825 
1826     RGB backgroundRGB, foregroundRGB;
1827     backgroundRGB = getBackground().getRGB();
1828     foregroundRGB = getForeground().getRGB();
1829 
1830     RGB fromRGB, toRGB;
1831     fromRGB = foregroundRGB;
1832     toRGB   = backgroundRGB;
1833     bool swapColors = false;
1834     if (width < 0) {
1835         x += width; width = -width;
1836         if (! vertical) swapColors = true;
1837     }
1838     if (height < 0) {
1839         y += height; height = -height;
1840         if (vertical) swapColors = true;
1841     }
1842     if (swapColors) {
1843         fromRGB = backgroundRGB;
1844         toRGB   = foregroundRGB;
1845     }
1846     if (fromRGB.opEquals(toRGB)) {
1847         fillRectangle(x, y, width, height);
1848         return;
1849     }
1850     auto cairo = data.cairo;
1851     if (cairo !is null) {
1852         org.eclipse.swt.internal.gtk.OS.cairo_pattern_t* pattern;
1853         if (vertical) {
1854             pattern = Cairo.cairo_pattern_create_linear (0.0, 0.0, 0.0, 1.0);
1855         } else {
1856             pattern = Cairo.cairo_pattern_create_linear (0.0, 0.0, 1.0, 0.0);
1857         }
1858         Cairo.cairo_pattern_add_color_stop_rgba (pattern, 0, fromRGB.red / 255f, fromRGB.green / 255f, fromRGB.blue / 255f, data.alpha / 255f);
1859         Cairo.cairo_pattern_add_color_stop_rgba (pattern, 1, toRGB.red / 255f, toRGB.green / 255f, toRGB.blue / 255f, data.alpha / 255f);
1860         Cairo.cairo_save(cairo);
1861         Cairo.cairo_translate(cairo, x, y);
1862         Cairo.cairo_scale(cairo, width, height);
1863         Cairo.cairo_rectangle(cairo, 0, 0, 1, 1);
1864         Cairo.cairo_set_source(cairo, pattern);
1865         Cairo.cairo_fill(cairo);
1866         Cairo.cairo_restore(cairo);
1867         Cairo.cairo_pattern_destroy(pattern);
1868         return;
1869     }
1870     ImageData.fillGradientRectangle(this, data.device,
1871         x, y, width, height, vertical, fromRGB, toRGB,
1872         8, 8, 8);
1873 }
1874 
1875 /**
1876  * Fills the interior of an oval, within the specified
1877  * rectangular area, with the receiver's background
1878  * color.
1879  *
1880  * @param x the x coordinate of the upper left corner of the oval to be filled
1881  * @param y the y coordinate of the upper left corner of the oval to be filled
1882  * @param width the width of the oval to be filled
1883  * @param height the height of the oval to be filled
1884  *
1885  * @exception SWTException <ul>
1886  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1887  * </ul>
1888  *
1889  * @see #drawOval
1890  */
1891 public void fillOval(int x, int y, int width, int height) {
1892     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1893     checkGC(FILL);
1894     if (width < 0) {
1895         x = x + width;
1896         width = -width;
1897     }
1898     if (height < 0) {
1899         y = y + height;
1900         height = -height;
1901     }
1902     auto cairo = data.cairo;
1903     if (cairo !is null) {
1904         if (width is height) {
1905             Cairo.cairo_arc_negative(cairo, x + width / 2f, y + height / 2f, width / 2f, 0, 2 * cast(float)Compatibility.PI);
1906         } else {
1907             Cairo.cairo_save(cairo);
1908             Cairo.cairo_translate(cairo, x + width / 2f, y + height / 2f);
1909             Cairo.cairo_scale(cairo, width / 2f, height / 2f);
1910             Cairo.cairo_arc_negative(cairo, 0, 0, 1, 0, 2 * cast(float)Compatibility.PI);
1911             Cairo.cairo_restore(cairo);
1912         }
1913         Cairo.cairo_fill(cairo);
1914         return;
1915     }
1916     OS.gdk_draw_arc(data.drawable, handle, 1, x, y, width, height, 0, 23040);
1917 }
1918 
1919 /**
1920  * Fills the path described by the parameter.
1921  * <p>
1922  * This operation requires the operating system's advanced
1923  * graphics subsystem which may not be available on some
1924  * platforms.
1925  * </p>
1926  *
1927  * @param path the path to fill
1928  *
1929  * @exception IllegalArgumentException <ul>
1930  *    <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
1931  *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
1932  * </ul>
1933  * @exception SWTException <ul>
1934  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1935  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
1936  * </ul>
1937  *
1938  * @see Path
1939  *
1940  * @since 3.1
1941  */
1942 public void fillPath (Path path) {
1943     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1944     if (path is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1945     if (path.handle is null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1946     initCairo();
1947     checkGC(FILL);
1948     auto cairo = data.cairo;
1949     auto copy = Cairo.cairo_copy_path(path.handle);
1950     if (copy is null) SWT.error(SWT.ERROR_NO_HANDLES);
1951     Cairo.cairo_append_path(cairo, copy);
1952     Cairo.cairo_path_destroy(copy);
1953     Cairo.cairo_fill(cairo);
1954 }
1955 
1956 /**
1957  * Fills the interior of the closed polygon which is defined by the
1958  * specified array of integer coordinates, using the receiver's
1959  * background color. The array contains alternating x and y values
1960  * which are considered to represent points which are the vertices of
1961  * the polygon. Lines are drawn between each consecutive pair, and
1962  * between the first pair and last pair in the array.
1963  *
1964  * @param pointArray an array of alternating x and y values which are the vertices of the polygon
1965  *
1966  * @exception SWTException <ul>
1967  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1968  * </ul>
1969  *
1970  * @see #drawPolygon
1971  */
1972 public void fillPolygon(int[] pointArray) {
1973     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
1974     // SWT extension: allow null array
1975     //if (pointArray is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1976     checkGC(FILL);
1977     auto cairo = data.cairo;
1978     if (cairo !is null) {
1979         drawPolyline(cairo, pointArray, true);
1980         Cairo.cairo_fill(cairo);
1981         return;
1982     }
1983     OS.gdk_draw_polygon(data.drawable, handle, 1, cast(GdkPoint*)pointArray.ptr, cast(int)/*64bit*/pointArray.length / 2);
1984 }
1985 
1986 /**
1987  * Fills the interior of the rectangle specified by the arguments,
1988  * using the receiver's background color.
1989  *
1990  * @param x the x coordinate of the rectangle to be filled
1991  * @param y the y coordinate of the rectangle to be filled
1992  * @param width the width of the rectangle to be filled
1993  * @param height the height of the rectangle to be filled
1994  *
1995  * @exception SWTException <ul>
1996  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1997  * </ul>
1998  *
1999  * @see #drawRectangle(int, int, int, int)
2000  */
2001 public void fillRectangle(int x, int y, int width, int height) {
2002     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2003     checkGC(FILL);
2004     if (width < 0) {
2005         x = x + width;
2006         width = -width;
2007     }
2008     if (height < 0) {
2009         y = y + height;
2010         height = -height;
2011     }
2012     auto cairo = data.cairo;
2013     if (cairo !is null) {
2014         Cairo.cairo_rectangle(cairo, x, y, width, height);
2015         Cairo.cairo_fill(cairo);
2016         return;
2017     }
2018     OS.gdk_draw_rectangle(data.drawable, handle, 1, x, y, width, height);
2019 }
2020 
2021 /**
2022  * Fills the interior of the specified rectangle, using the receiver's
2023  * background color.
2024  *
2025  * @param rect the rectangle to be filled
2026  *
2027  * @exception IllegalArgumentException <ul>
2028  *    <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li>
2029  * </ul>
2030  * @exception SWTException <ul>
2031  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2032  * </ul>
2033  *
2034  * @see #drawRectangle(int, int, int, int)
2035  */
2036 public void fillRectangle(Rectangle rect) {
2037     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2038     if (rect is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2039     fillRectangle(rect.x, rect.y, rect.width, rect.height);
2040 }
2041 
2042 /**
2043  * Fills the interior of the round-cornered rectangle specified by
2044  * the arguments, using the receiver's background color.
2045  *
2046  * @param x the x coordinate of the rectangle to be filled
2047  * @param y the y coordinate of the rectangle to be filled
2048  * @param width the width of the rectangle to be filled
2049  * @param height the height of the rectangle to be filled
2050  * @param arcWidth the width of the arc
2051  * @param arcHeight the height of the arc
2052  *
2053  * @exception SWTException <ul>
2054  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2055  * </ul>
2056  *
2057  * @see #drawRoundRectangle
2058  */
2059 public void fillRoundRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight) {
2060     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2061     checkGC(FILL);
2062     int nx = x;
2063     int ny = y;
2064     int nw = width;
2065     int nh = height;
2066     int naw = arcWidth;
2067     int nah = arcHeight;
2068     if (nw < 0) {
2069         nw = 0 - nw;
2070         nx = nx - nw;
2071     }
2072     if (nh < 0) {
2073         nh = 0 - nh;
2074         ny = ny -nh;
2075     }
2076     if (naw < 0) naw = 0 - naw;
2077     if (nah < 0) nah = 0 - nah;
2078     auto cairo = data.cairo;
2079     if (cairo !is null) {
2080         if (naw is 0 || nah is 0) {
2081             Cairo.cairo_rectangle(cairo, x, y, width, height);
2082         } else {
2083             float naw2 = naw / 2f;
2084             float nah2 = nah / 2f;
2085             float fw = nw / naw2;
2086             float fh = nh / nah2;
2087             Cairo.cairo_save(cairo);
2088             Cairo.cairo_translate(cairo, nx, ny);
2089             Cairo.cairo_scale(cairo, naw2, nah2);
2090             Cairo.cairo_move_to(cairo, fw - 1, 0);
2091             Cairo.cairo_arc(cairo, fw - 1, 1, 1, Compatibility.PI + Compatibility.PI/2.0, Compatibility.PI*2.0);
2092             Cairo.cairo_arc(cairo, fw - 1, fh - 1, 1, 0, Compatibility.PI/2.0);
2093             Cairo.cairo_arc(cairo, 1, fh - 1, 1, Compatibility.PI/2, Compatibility.PI);
2094             Cairo.cairo_arc(cairo, 1, 1, 1, Compatibility.PI, 270.0*Compatibility.PI/180.0);
2095             Cairo.cairo_close_path(cairo);
2096             Cairo.cairo_restore(cairo);
2097         }
2098         Cairo.cairo_fill(cairo);
2099         return;
2100     }
2101     int naw2 = naw / 2;
2102     int nah2 = nah / 2;
2103     auto drawable = data.drawable;
2104     if (nw > naw) {
2105         if (nh > nah) {
2106             OS.gdk_draw_arc(drawable, handle, 1, nx, ny, naw, nah, 5760, 5760);
2107             OS.gdk_draw_rectangle(drawable, handle, 1, nx + naw2, ny, nw - naw2 * 2, nh);
2108             OS.gdk_draw_arc(drawable, handle, 1, nx + nw - naw, ny, naw, nah, 0, 5760);
2109             OS.gdk_draw_rectangle(drawable, handle, 1, nx, ny + nah2, naw2, nh - nah2 * 2);
2110             OS.gdk_draw_arc(drawable, handle, 1, nx + nw - naw, ny + nh - nah, naw, nah, 17280, 5760);
2111             OS.gdk_draw_rectangle(drawable, handle, 1, nx + nw - naw2, ny + nah2, naw2, nh - nah2 * 2);
2112             OS.gdk_draw_arc(drawable, handle, 1, nx, ny + nh - nah, naw, nah, 11520, 5760);
2113         } else {
2114             OS.gdk_draw_arc(drawable, handle, 1, nx, ny, naw, nh, 5760, 11520);
2115             OS.gdk_draw_rectangle(drawable, handle, 1, nx + naw2, ny, nw - naw2 * 2, nh);
2116             OS.gdk_draw_arc(drawable, handle, 1, nx + nw - naw, ny, naw, nh, 17280, 11520);
2117         }
2118     } else {
2119         if (nh > nah) {
2120             OS.gdk_draw_arc(drawable, handle, 1, nx, ny, nw, nah, 0, 11520);
2121             OS.gdk_draw_rectangle(drawable, handle, 1, nx, ny + nah2, nw, nh - nah2 * 2);
2122             OS.gdk_draw_arc(drawable, handle, 1, nx, ny + nh - nah, nw, nah, 11520, 11520);
2123         } else {
2124             OS.gdk_draw_arc(drawable, handle, 1, nx, ny, nw, nh, 0, 23040);
2125         }
2126     }
2127 }
2128 
2129 int fixMnemonic (char [] buffer) {
2130     int i=0, j=0;
2131     int mnemonic=-1;
2132     while (i < buffer.length) {
2133         if ((buffer [j++] = buffer [i++]) is '&') {
2134             if (i is buffer.length) {continue;}
2135             if (buffer [i] is '&') {i++; continue;}
2136             if (mnemonic is -1) mnemonic = j;
2137             j--;
2138         }
2139     }
2140     while (j < buffer.length) buffer [j++] = 0;
2141     return mnemonic;
2142 }
2143 
2144 /**
2145  * Returns the <em>advance width</em> of the specified character in
2146  * the font which is currently selected into the receiver.
2147  * <p>
2148  * The advance width is defined as the horizontal distance the cursor
2149  * should move after printing the character in the selected font.
2150  * </p>
2151  *
2152  * @param ch the character to measure
2153  * @return the distance in the x direction to move past the character before painting the next
2154  *
2155  * @exception SWTException <ul>
2156  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2157  * </ul>
2158  */
2159 public int getAdvanceWidth(char ch) {
2160     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2161     //BOGUS
2162     return stringExtent([ch]).x;
2163 }
2164 
2165 /**
2166  * Returns <code>true</code> if receiver is using the operating system's
2167  * advanced graphics subsystem.  Otherwise, <code>false</code> is returned
2168  * to indicate that normal graphics are in use.
2169  * <p>
2170  * Advanced graphics may not be installed for the operating system.  In this
2171  * case, <code>false</code> is always returned.  Some operating system have
2172  * only one graphics subsystem.  If this subsystem supports advanced graphics,
2173  * then <code>true</code> is always returned.  If any graphics operation such
2174  * as alpha, antialias, patterns, interpolation, paths, clipping or transformation
2175  * has caused the receiver to switch from regular to advanced graphics mode,
2176  * <code>true</code> is returned.  If the receiver has been explicitly switched
2177  * to advanced mode and this mode is supported, <code>true</code> is returned.
2178  * </p>
2179  *
2180  * @return the advanced value
2181  *
2182  * @exception SWTException <ul>
2183  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2184  * </ul>
2185  *
2186  * @see #setAdvanced
2187  *
2188  * @since 3.1
2189  */
2190 public bool getAdvanced() {
2191     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2192     return data.cairo !is null;
2193 }
2194 
2195 /**
2196  * Returns the receiver's alpha value. The alpha value
2197  * is between 0 (transparent) and 255 (opaque).
2198  *
2199  * @return the alpha value
2200  *
2201  * @exception SWTException <ul>
2202  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2203  * </ul>
2204  *
2205  * @since 3.1
2206  */
2207 public int getAlpha() {
2208     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2209     return data.alpha;
2210 }
2211 
2212 /**
2213  * Returns the receiver's anti-aliasing setting value, which will be
2214  * one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code> or
2215  * <code>SWT.ON</code>. Note that this controls anti-aliasing for all
2216  * <em>non-text drawing</em> operations.
2217  *
2218  * @return the anti-aliasing setting
2219  *
2220  * @exception SWTException <ul>
2221  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2222  * </ul>
2223  *
2224  * @see #getTextAntialias
2225  *
2226  * @since 3.1
2227  */
2228 public int getAntialias() {
2229     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2230     if (data.cairo is null) return SWT.DEFAULT;
2231     ptrdiff_t antialias = Cairo.cairo_get_antialias(data.cairo);
2232     switch (antialias) {
2233         case Cairo.CAIRO_ANTIALIAS_DEFAULT: return SWT.DEFAULT;
2234         case Cairo.CAIRO_ANTIALIAS_NONE: return SWT.OFF;
2235         case Cairo.CAIRO_ANTIALIAS_GRAY:
2236         case Cairo.CAIRO_ANTIALIAS_SUBPIXEL: return SWT.ON;
2237                 default:
2238     }
2239     return SWT.DEFAULT;
2240 }
2241 
2242 /**
2243  * Returns the background color.
2244  *
2245  * @return the receiver's background color
2246  *
2247  * @exception SWTException <ul>
2248  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2249  * </ul>
2250  */
2251 public Color getBackground() {
2252     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2253     return Color.gtk_new(data.device, data.background);
2254 }
2255 
2256 /**
2257  * Returns the background pattern. The default value is
2258  * <code>null</code>.
2259  *
2260  * @return the receiver's background pattern
2261  *
2262  * @exception SWTException <ul>
2263  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2264  * </ul>
2265  *
2266  * @see Pattern
2267  *
2268  * @since 3.1
2269  */
2270 public Pattern getBackgroundPattern() {
2271     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2272     return data.backgroundPattern;
2273 }
2274 
2275 /**
2276  * Returns the width of the specified character in the font
2277  * selected into the receiver.
2278  * <p>
2279  * The width is defined as the space taken up by the actual
2280  * character, not including the leading and tailing whitespace
2281  * or overhang.
2282  * </p>
2283  *
2284  * @param ch the character to measure
2285  * @return the width of the character
2286  *
2287  * @exception SWTException <ul>
2288  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2289  * </ul>
2290  */
2291 public int getCharWidth(char ch) {
2292     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2293     //BOGUS
2294     return stringExtent([ch]).x;
2295 }
2296 
2297 /**
2298  * Returns the bounding rectangle of the receiver's clipping
2299  * region. If no clipping region is set, the return value
2300  * will be a rectangle which covers the entire bounds of the
2301  * object the receiver is drawing on.
2302  *
2303  * @return the bounding rectangle of the clipping region
2304  *
2305  * @exception SWTException <ul>
2306  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2307  * </ul>
2308  */
2309 public Rectangle getClipping() {
2310     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2311     /* Calculate visible bounds in device space */
2312     int x = 0, y = 0, width = 0, height = 0;
2313     if (data.width !is -1 && data.height !is -1) {
2314         width = data.width;
2315         height = data.height;
2316     } else {
2317         int w, h;
2318         OS.gdk_drawable_get_size(data.drawable, &w, &h);
2319         width = w;
2320         height = h;
2321     }
2322     /* Intersect visible bounds with clipping in device space and then convert then to user space */
2323     auto cairo = data.cairo;
2324     auto clipRgn = data.clipRgn;
2325     auto damageRgn = data.damageRgn;
2326     if (clipRgn !is null || damageRgn !is null || cairo !is null) {
2327         auto rgn = OS.gdk_region_new();
2328         GdkRectangle rect;
2329         rect.width = width;
2330         rect.height = height;
2331         OS.gdk_region_union_with_rect(rgn, &rect);
2332         if (damageRgn !is null) {
2333             OS.gdk_region_intersect (rgn, damageRgn);
2334         }
2335         /* Intersect visible bounds with clipping */
2336         if (clipRgn !is null) {
2337             /* Convert clipping to device space if needed */
2338             if (data.clippingTransform !is null) {
2339                 clipRgn = convertRgn(clipRgn, data.clippingTransform);
2340                 OS.gdk_region_intersect(rgn, clipRgn);
2341                 OS.gdk_region_destroy(clipRgn);
2342             } else {
2343                 OS.gdk_region_intersect(rgn, clipRgn);
2344             }
2345         }
2346         /* Convert to user space */
2347         if (cairo !is null) {
2348             double[] matrix = new double[6];
2349             Cairo.cairo_get_matrix(cairo, cast(cairo_matrix_t*)matrix.ptr);
2350             Cairo.cairo_matrix_invert(cast(cairo_matrix_t*)matrix.ptr);
2351             clipRgn = convertRgn(rgn, matrix);
2352             OS.gdk_region_destroy(rgn);
2353             rgn = clipRgn;
2354         }
2355         OS.gdk_region_get_clipbox(rgn, &rect);
2356         OS.gdk_region_destroy(rgn);
2357         x = rect.x;
2358         y = rect.y;
2359         width = rect.width;
2360         height = rect.height;
2361     }
2362     return new Rectangle(x, y, width, height);
2363 }
2364 
2365 /**
2366  * Sets the region managed by the argument to the current
2367  * clipping region of the receiver.
2368  *
2369  * @param region the region to fill with the clipping region
2370  *
2371  * @exception IllegalArgumentException <ul>
2372  *    <li>ERROR_NULL_ARGUMENT - if the region is null</li>
2373  *    <li>ERROR_INVALID_ARGUMENT - if the region is disposed</li>
2374  * </ul>
2375  * @exception SWTException <ul>
2376  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2377  * </ul>
2378  */
2379 public void getClipping(Region region) {
2380     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2381     if (region is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2382     if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2383     auto clipping = region.handle;
2384     OS.gdk_region_subtract(clipping, clipping);
2385     auto cairo = data.cairo;
2386     auto clipRgn = data.clipRgn;
2387     if (clipRgn is null) {
2388         GdkRectangle rect;
2389         if (data.width !is -1 && data.height !is -1) {
2390             rect.width = data.width;
2391             rect.height = data.height;
2392         } else {
2393             int width, height;
2394             OS.gdk_drawable_get_size(data.drawable, &width, &height);
2395             rect.width = width;
2396             rect.height = height;
2397         }
2398         OS.gdk_region_union_with_rect(clipping, &rect);
2399     } else {
2400         /* Convert clipping to device space if needed */
2401         if (data.clippingTransform !is null) {
2402             auto rgn = convertRgn(clipRgn, data.clippingTransform);
2403             OS.gdk_region_union(clipping, rgn);
2404             OS.gdk_region_destroy(rgn);
2405         } else {
2406             OS.gdk_region_union(clipping, clipRgn);
2407         }
2408     }
2409     if (data.damageRgn !is null) {
2410         OS.gdk_region_intersect(clipping, data.damageRgn);
2411     }
2412     /* Convert to user space */
2413     if (cairo !is null) {
2414         double[] matrix = new double[6];
2415         Cairo.cairo_get_matrix(cairo, cast(cairo_matrix_t*)matrix.ptr);
2416         Cairo.cairo_matrix_invert(cast(cairo_matrix_t*)matrix.ptr);
2417         auto rgn = convertRgn(clipping, matrix);
2418         OS.gdk_region_subtract(clipping, clipping);
2419         OS.gdk_region_union(clipping, rgn);
2420         OS.gdk_region_destroy(rgn);
2421     }
2422 }
2423 
2424 /**
2425  * Returns the receiver's fill rule, which will be one of
2426  * <code>SWT.FILL_EVEN_ODD</code> or <code>SWT.FILL_WINDING</code>.
2427  *
2428  * @return the receiver's fill rule
2429  *
2430  * @exception SWTException <ul>
2431  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2432  * </ul>
2433  *
2434  * @since 3.1
2435  */
2436 public int getFillRule() {
2437     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2438     auto cairo = data.cairo;
2439     if (cairo is null) return SWT.FILL_EVEN_ODD;
2440     return Cairo.cairo_get_fill_rule(cairo) is Cairo.CAIRO_FILL_RULE_WINDING ? SWT.FILL_WINDING : SWT.FILL_EVEN_ODD;
2441 }
2442 
2443 /**
2444  * Returns the font currently being used by the receiver
2445  * to draw and measure text.
2446  *
2447  * @return the receiver's font
2448  *
2449  * @exception SWTException <ul>
2450  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2451  * </ul>
2452  */
2453 public Font getFont() {
2454     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2455     return data.font;
2456 }
2457 
2458 /**
2459  * Returns a FontMetrics which contains information
2460  * about the font currently being used by the receiver
2461  * to draw and measure text.
2462  *
2463  * @return font metrics for the receiver's font
2464  *
2465  * @exception SWTException <ul>
2466  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2467  * </ul>
2468  */
2469 public FontMetrics getFontMetrics() {
2470     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2471     if (data.context is null) createLayout();
2472     checkGC(FONT);
2473     Font font = data.font;
2474     auto context = data.context;
2475     auto lang = OS.pango_context_get_language(context);
2476     auto metrics = OS.pango_context_get_metrics(context, font.handle, lang);
2477     FontMetrics fm = new FontMetrics();
2478     fm.ascent = OS.PANGO_PIXELS(OS.pango_font_metrics_get_ascent(metrics));
2479     fm.descent = OS.PANGO_PIXELS(OS.pango_font_metrics_get_descent(metrics));
2480     fm.averageCharWidth = OS.PANGO_PIXELS(OS.pango_font_metrics_get_approximate_char_width(metrics));
2481     fm.height = fm.ascent + fm.descent;
2482     OS.pango_font_metrics_unref(metrics);
2483     return fm;
2484 }
2485 
2486 /**
2487  * Returns the receiver's foreground color.
2488  *
2489  * @return the color used for drawing foreground things
2490  *
2491  * @exception SWTException <ul>
2492  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2493  * </ul>
2494  */
2495 public Color getForeground() {
2496     if (handle is null) SWT.error(SWT.ERROR_WIDGET_DISPOSED);
2497     return Color.gtk_new(data.device, data.foreground);
2498 }
2499 
2500 /**
2501  * Returns the foreground pattern. The default value is
2502  * <code>null</code>.
2503  *
2504  * @return the receiver's foreground pattern
2505  *
2506  * @exception SWTException <ul>
2507  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2508  * </ul>
2509  *
2510  * @see Pattern
2511  *
2512  * @since 3.1
2513  */
2514 public Pattern getForegroundPattern() {
2515     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2516     return data.foregroundPattern;
2517 }
2518 
2519 /**
2520  * Returns the GCData.
2521  * <p>
2522  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
2523  * API for <code>GC</code>. It is marked public only so that it
2524  * can be shared within the packages provided by SWT. It is not
2525  * available on all platforms, and should never be called from
2526  * application code.
2527  * </p>
2528  *
2529  * @return the receiver's GCData
2530  *
2531  * @exception SWTException <ul>
2532  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2533  * </ul>
2534  *
2535  * @see GCData
2536  *
2537  * @since 3.2
2538  */
2539 public GCData getGCData() {
2540     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2541     return data;
2542 }
2543 
2544 /**
2545  * Returns the receiver's interpolation setting, which will be one of
2546  * <code>SWT.DEFAULT</code>, <code>SWT.NONE</code>,
2547  * <code>SWT.LOW</code> or <code>SWT.HIGH</code>.
2548  *
2549  * @return the receiver's interpolation setting
2550  *
2551  * @exception SWTException <ul>
2552  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2553  * </ul>
2554  *
2555  * @since 3.1
2556  */
2557 public int getInterpolation() {
2558     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2559     return data.interpolation;
2560 }
2561 
2562 /**
2563  * Returns the receiver's line attributes.
2564  *
2565  * @return the line attributes used for drawing lines
2566  *
2567  * @exception SWTException <ul>
2568  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2569  * </ul>
2570  *
2571  * @since 3.3
2572  */
2573 public LineAttributes getLineAttributes() {
2574     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2575     float[] dashes = null;
2576     if (data.lineDashes !is null) {
2577         dashes = new float[data.lineDashes.length];
2578         System.arraycopy(data.lineDashes, 0, dashes, 0, dashes.length);
2579     }
2580     return new LineAttributes(data.lineWidth, data.lineCap, data.lineJoin, data.lineStyle, dashes, data.lineDashesOffset, data.lineMiterLimit);
2581 }
2582 
2583 /**
2584  * Returns the receiver's line cap style, which will be one
2585  * of the constants <code>SWT.CAP_FLAT</code>, <code>SWT.CAP_ROUND</code>,
2586  * or <code>SWT.CAP_SQUARE</code>.
2587  *
2588  * @return the cap style used for drawing lines
2589  *
2590  * @exception SWTException <ul>
2591  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2592  * </ul>
2593  *
2594  * @since 3.1
2595  */
2596 public int getLineCap() {
2597     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2598     return data.lineCap;
2599 }
2600 
2601 /**
2602  * Returns the receiver's line dash style. The default value is
2603  * <code>null</code>.
2604  *
2605  * @return the line dash style used for drawing lines
2606  *
2607  * @exception SWTException <ul>
2608  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2609  * </ul>
2610  *
2611  * @since 3.1
2612  */
2613 public int[] getLineDash() {
2614     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2615     if (data.lineDashes is null) return null;
2616     int[] lineDashes = new int[data.lineDashes.length];
2617     for (int i = 0; i < lineDashes.length; i++) {
2618         lineDashes[i] = cast(int)data.lineDashes[i];
2619     }
2620     return lineDashes;
2621 }
2622 
2623 /**
2624  * Returns the receiver's line join style, which will be one
2625  * of the constants <code>SWT.JOIN_MITER</code>, <code>SWT.JOIN_ROUND</code>,
2626  * or <code>SWT.JOIN_BEVEL</code>.
2627  *
2628  * @return the join style used for drawing lines
2629  *
2630  * @exception SWTException <ul>
2631  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2632  * </ul>
2633  *
2634  * @since 3.1
2635  */
2636 public int getLineJoin() {
2637     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2638     return data.lineJoin;
2639 }
2640 
2641 /**
2642  * Returns the receiver's line style, which will be one
2643  * of the constants <code>SWT.LINE_SOLID</code>, <code>SWT.LINE_DASH</code>,
2644  * <code>SWT.LINE_DOT</code>, <code>SWT.LINE_DASHDOT</code> or
2645  * <code>SWT.LINE_DASHDOTDOT</code>.
2646  *
2647  * @return the style used for drawing lines
2648  *
2649  * @exception SWTException <ul>
2650  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2651  * </ul>
2652  */
2653 public int getLineStyle() {
2654     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2655     return data.lineStyle;
2656 }
2657 
2658 /**
2659  * Returns the width that will be used when drawing lines
2660  * for all of the figure drawing operations (that is,
2661  * <code>drawLine</code>, <code>drawRectangle</code>,
2662  * <code>drawPolyline</code>, and so forth.
2663  *
2664  * @return the receiver's line width
2665  *
2666  * @exception SWTException <ul>
2667  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2668  * </ul>
2669  */
2670 public int getLineWidth() {
2671     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2672     return cast(int)data.lineWidth;
2673 }
2674 
2675 /**
2676  * Returns the receiver's style information.
2677  * <p>
2678  * Note that the value which is returned by this method <em>may
2679  * not match</em> the value which was provided to the constructor
2680  * when the receiver was created. This can occur when the underlying
2681  * operating system does not support a particular combination of
2682  * requested styles.
2683  * </p>
2684  *
2685  * @return the style bits
2686  *
2687  * @exception SWTException <ul>
2688  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2689  * </ul>
2690  *
2691  * @since 2.1.2
2692  */
2693 public int getStyle () {
2694     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2695     return data.style;
2696 }
2697 
2698 /**
2699  * Returns the receiver's text drawing anti-aliasing setting value,
2700  * which will be one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code> or
2701  * <code>SWT.ON</code>. Note that this controls anti-aliasing
2702  * <em>only</em> for text drawing operations.
2703  *
2704  * @return the anti-aliasing setting
2705  *
2706  * @exception SWTException <ul>
2707  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2708  * </ul>
2709  *
2710  * @see #getAntialias
2711  *
2712  * @since 3.1
2713  */
2714 public int getTextAntialias() {
2715     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2716     if (data.cairo is null) return SWT.DEFAULT;
2717     ptrdiff_t antialias = Cairo.CAIRO_ANTIALIAS_DEFAULT;
2718     if (OS.GTK_VERSION < OS.buildVERSION(2, 8, 0)) {
2719         auto options = Cairo.cairo_font_options_create();
2720         Cairo.cairo_get_font_options(data.cairo, options);
2721         antialias = Cairo.cairo_font_options_get_antialias(options);
2722         Cairo.cairo_font_options_destroy(options);
2723     } else {
2724         if (data.context !is null) {
2725             auto options = OS.pango_cairo_context_get_font_options(data.context);
2726             if (options !is null) antialias = Cairo.cairo_font_options_get_antialias(options);
2727         }
2728     }
2729     switch (antialias) {
2730         case Cairo.CAIRO_ANTIALIAS_DEFAULT: return SWT.DEFAULT;
2731         case Cairo.CAIRO_ANTIALIAS_NONE: return SWT.OFF;
2732         case Cairo.CAIRO_ANTIALIAS_GRAY:
2733         case Cairo.CAIRO_ANTIALIAS_SUBPIXEL: return SWT.ON;
2734                 default:
2735     }
2736     return SWT.DEFAULT;
2737 }
2738 
2739 /**
2740  * Sets the parameter to the transform that is currently being
2741  * used by the receiver.
2742  *
2743  * @param transform the destination to copy the transform into
2744  *
2745  * @exception IllegalArgumentException <ul>
2746  *    <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
2747  *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
2748  * </ul>
2749  * @exception SWTException <ul>
2750  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2751  * </ul>
2752  *
2753  * @see Transform
2754  *
2755  * @since 3.1
2756  */
2757 public void getTransform(Transform transform) {
2758     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2759     if (transform is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2760     if (transform.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2761     auto cairo = data.cairo;
2762     if (cairo !is null) {
2763         Cairo.cairo_get_matrix(cairo, cast(cairo_matrix_t*)transform.handle.ptr);
2764         double[] identity = identity();
2765         Cairo.cairo_matrix_invert(cast(cairo_matrix_t*)identity.ptr);
2766         Cairo.cairo_matrix_multiply(cast(cairo_matrix_t*)transform.handle.ptr, cast(cairo_matrix_t*)transform.handle.ptr, cast(cairo_matrix_t*)identity.ptr);
2767     } else {
2768         transform.setElements(1, 0, 0, 1, 0, 0);
2769     }
2770 }
2771 
2772 /**
2773  * Returns <code>true</code> if this GC is drawing in the mode
2774  * where the resulting color in the destination is the
2775  * <em>exclusive or</em> of the color values in the source
2776  * and the destination, and <code>false</code> if it is
2777  * drawing in the mode where the destination color is being
2778  * replaced with the source color value.
2779  *
2780  * @return <code>true</code> true if the receiver is in XOR mode, and false otherwise
2781  *
2782  * @exception SWTException <ul>
2783  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2784  * </ul>
2785  */
2786 public bool getXORMode() {
2787     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2788     return data.xorMode;
2789 }
2790 
2791 /**
2792  * Returns an integer hash code for the receiver. Any two
2793  * objects that return <code>true</code> when passed to
2794  * <code>equals</code> must return the same value for this
2795  * method.
2796  *
2797  * @return the receiver's hash
2798  *
2799  * @exception SWTException <ul>
2800  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2801  * </ul>
2802  *
2803  * @see #equals
2804  */
2805 public override hash_t toHash() {
2806     return cast(hash_t)handle;
2807 }
2808 
2809 double[] identity() {
2810     double[] identity = new double[6];
2811     if ((data.style & SWT.MIRRORED) !is 0) {
2812         int w, h;
2813         OS.gdk_drawable_get_size(data.drawable, &w, &h);
2814         Cairo.cairo_matrix_init(cast(cairo_matrix_t*)identity.ptr, -1, 0, 0, 1, w, 0);
2815     } else {
2816         Cairo.cairo_matrix_init_identity(cast(cairo_matrix_t*)identity.ptr);
2817     }
2818     return identity;
2819 }
2820 
2821 void init_(Drawable drawable, GCData data, GdkGC* gdkGC) {
2822     if (data.foreground !is null) data.state &= ~FOREGROUND;
2823     if (data.background !is null) data.state &= ~(BACKGROUND | BACKGROUND_BG);
2824     if (data.font !is null) data.state &= ~FONT;
2825 
2826     Image image = data.image;
2827     if (image !is null) {
2828         image.memGC = this;
2829         /*
2830          * The transparent pixel mask might change when drawing on
2831          * the image.  Destroy it so that it is regenerated when
2832          * necessary.
2833          */
2834         if (image.transparentPixel !is -1) image.destroyMask();
2835     }
2836     this.drawable = drawable;
2837     this.data = data;
2838     handle = gdkGC;
2839     if ((data.style & SWT.MIRRORED) !is 0) {
2840       initCairo();
2841       auto cairo = data.cairo;
2842       Cairo.cairo_set_matrix(cairo, cast(cairo_matrix_t*) identity().ptr);
2843     }
2844 }
2845 
2846 void initCairo() {
2847     data.device.checkCairo();
2848     auto cairo = data.cairo;
2849     if (cairo !is null) return;
2850     auto xDisplay = OS.GDK_DISPLAY();
2851     auto xVisual = OS.gdk_x11_visual_get_xvisual(OS.gdk_visual_get_system());
2852     size_t xDrawable;
2853     int translateX = 0, translateY = 0;
2854     auto drawable = data.drawable;
2855     if (data.image !is null) {
2856         xDrawable = OS.GDK_PIXMAP_XID(drawable);
2857     } else {
2858         if (!data.realDrawable) {
2859             int x, y;
2860             GdkDrawable* real_drawable;
2861             OS.gdk_window_get_internal_paint_info(cast(GdkWindow*)drawable, &real_drawable, &x, &y);
2862             xDrawable = OS.gdk_x11_drawable_get_xid(real_drawable);
2863             translateX = -x;
2864             translateY = -y;
2865         }
2866     }
2867     int w, h;
2868     OS.gdk_drawable_get_size(drawable, &w, &h);
2869     int width = w, height = h;
2870     auto surface = Cairo.cairo_xlib_surface_create(cast(void*)xDisplay, xDrawable, xVisual, width, height);
2871     if (surface is null) SWT.error(SWT.ERROR_NO_HANDLES);
2872     Cairo.cairo_surface_set_device_offset(surface, translateX, translateY);
2873     data.cairo = cairo = Cairo.cairo_create(surface);
2874     Cairo.cairo_surface_destroy(surface);
2875     if (cairo is null) SWT.error(SWT.ERROR_NO_HANDLES);
2876     data.disposeCairo = true;
2877     Cairo.cairo_set_fill_rule(cairo, Cairo.CAIRO_FILL_RULE_EVEN_ODD);
2878     data.state &= ~(BACKGROUND | FOREGROUND | FONT | LINE_WIDTH | LINE_CAP | LINE_JOIN | LINE_STYLE | DRAW_OFFSET);
2879     setCairoClip(cairo, data.clipRgn);
2880 }
2881 
2882 /**
2883  * Returns <code>true</code> if the receiver has a clipping
2884  * region set into it, and <code>false</code> otherwise.
2885  * If this method returns false, the receiver will draw on all
2886  * available space in the destination. If it returns true,
2887  * it will draw only in the area that is covered by the region
2888  * that can be accessed with <code>getClipping(region)</code>.
2889  *
2890  * @return <code>true</code> if the GC has a clipping region, and <code>false</code> otherwise
2891  *
2892  * @exception SWTException <ul>
2893  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2894  * </ul>
2895  */
2896 public bool isClipped() {
2897     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2898     return data.clipRgn !is null;
2899 }
2900 
2901 /**
2902  * Returns <code>true</code> if the GC has been disposed,
2903  * and <code>false</code> otherwise.
2904  * <p>
2905  * This method gets the dispose state for the GC.
2906  * When a GC has been disposed, it is an error to
2907  * invoke any other method using the GC.
2908  *
2909  * @return <code>true</code> when the GC is disposed and <code>false</code> otherwise
2910  */
2911 public override bool isDisposed() {
2912     return handle is null;
2913 }
2914 
2915 bool isIdentity(double[] matrix) {
2916     if (matrix is null) return true;
2917     return matrix[0] is 1 && matrix[1] is 0 && matrix[2] is 0 && matrix[3] is 1 && matrix[4] is 0 && matrix[5] is 0;
2918 }
2919 
2920 /**
2921  * Sets the receiver to always use the operating system's advanced graphics
2922  * subsystem for all graphics operations if the argument is <code>true</code>.
2923  * If the argument is <code>false</code>, the advanced graphics subsystem is
2924  * no longer used, advanced graphics state is cleared and the normal graphics
2925  * subsystem is used from now on.
2926  * <p>
2927  * Normally, the advanced graphics subsystem is invoked automatically when
2928  * any one of the alpha, antialias, patterns, interpolation, paths, clipping
2929  * or transformation operations in the receiver is requested.  When the receiver
2930  * is switched into advanced mode, the advanced graphics subsystem performs both
2931  * advanced and normal graphics operations.  Because the two subsystems are
2932  * different, their output may differ.  Switching to advanced graphics before
2933  * any graphics operations are performed ensures that the output is consistent.
2934  * </p><p>
2935  * Advanced graphics may not be installed for the operating system.  In this
2936  * case, this operation does nothing.  Some operating system have only one
2937  * graphics subsystem, so switching from normal to advanced graphics does
2938  * nothing.  However, switching from advanced to normal graphics will always
2939  * clear the advanced graphics state, even for operating systems that have
2940  * only one graphics subsystem.
2941  * </p>
2942  *
2943  * @param advanced the new advanced graphics state
2944  *
2945  * @exception SWTException <ul>
2946  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2947  * </ul>
2948  *
2949  * @see #setAlpha
2950  * @see #setAntialias
2951  * @see #setBackgroundPattern
2952  * @see #setClipping(Path)
2953  * @see #setForegroundPattern
2954  * @see #setLineAttributes
2955  * @see #setInterpolation
2956  * @see #setTextAntialias
2957  * @see #setTransform
2958  * @see #getAdvanced
2959  *
2960  * @since 3.1
2961  */
2962 public void setAdvanced(bool advanced) {
2963     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
2964     if ((data.style & SWT.MIRRORED) !is 0) {
2965         if (!advanced) {
2966             setAlpha(0xFF);
2967             setAntialias(SWT.DEFAULT);
2968             setBackgroundPattern(null);
2969             setClipping(cast(GdkRegion*)null);
2970             setForegroundPattern(null);
2971             setInterpolation(SWT.DEFAULT);
2972             setTextAntialias(SWT.DEFAULT);
2973             setTransform(null);
2974         }
2975         return;
2976     }
2977     if (advanced && data.cairo !is null) return;
2978     if (advanced) {
2979         try {
2980             initCairo();
2981         } catch (SWTException e) {}
2982     } else {
2983         if (!data.disposeCairo) return;
2984         auto cairo = data.cairo;
2985         if (cairo !is null) Cairo.cairo_destroy(cairo);
2986         data.cairo = null;
2987         data.interpolation = SWT.DEFAULT;
2988         data.backgroundPattern = data.foregroundPattern = null;
2989         data.state = 0;
2990         setClipping(cast(GdkRegion*)null);
2991     }
2992 }
2993 
2994 /**
2995  * Sets the receiver's alpha value which must be
2996  * between 0 (transparent) and 255 (opaque).
2997  * <p>
2998  * This operation requires the operating system's advanced
2999  * graphics subsystem which may not be available on some
3000  * platforms.
3001  * </p>
3002  * @param alpha the alpha value
3003  *
3004  * @exception SWTException <ul>
3005  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3006  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3007  * </ul>
3008  *
3009  * @see #getAdvanced
3010  * @see #setAdvanced
3011  *
3012  * @since 3.1
3013  */
3014 public void setAlpha(int alpha) {
3015     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3016     if (data.cairo is null && (alpha & 0xff) is 0xff) return;
3017     initCairo();
3018     data.alpha = alpha & 0xff;
3019     data.state &= ~(BACKGROUND | FOREGROUND | BACKGROUND_BG);
3020 }
3021 
3022 /**
3023  * Sets the receiver's anti-aliasing value to the parameter,
3024  * which must be one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code>
3025  * or <code>SWT.ON</code>. Note that this controls anti-aliasing for all
3026  * <em>non-text drawing</em> operations.
3027  * <p>
3028  * This operation requires the operating system's advanced
3029  * graphics subsystem which may not be available on some
3030  * platforms.
3031  * </p>
3032  *
3033  * @param antialias the anti-aliasing setting
3034  *
3035  * @exception IllegalArgumentException <ul>
3036  *    <li>ERROR_INVALID_ARGUMENT - if the parameter is not one of <code>SWT.DEFAULT</code>,
3037  *                                 <code>SWT.OFF</code> or <code>SWT.ON</code></li>
3038  * </ul>
3039  * @exception SWTException <ul>
3040  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3041  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3042  * </ul>
3043  *
3044  * @see #getAdvanced
3045  * @see #setAdvanced
3046  * @see #setTextAntialias
3047  *
3048  * @since 3.1
3049  */
3050 public void setAntialias(int antialias) {
3051     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3052     if (data.cairo is null && antialias is SWT.DEFAULT) return;
3053     int mode = 0;
3054     switch (antialias) {
3055         case SWT.DEFAULT: mode = Cairo.CAIRO_ANTIALIAS_DEFAULT; break;
3056         case SWT.OFF: mode = Cairo.CAIRO_ANTIALIAS_NONE; break;
3057         case SWT.ON: mode = Cairo.CAIRO_ANTIALIAS_GRAY;
3058             break;
3059         default:
3060             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3061     }
3062     initCairo();
3063     auto cairo = data.cairo;
3064     Cairo.cairo_set_antialias(cairo, mode);
3065 }
3066 
3067 /**
3068  * Sets the background color. The background color is used
3069  * for fill operations and as the background color when text
3070  * is drawn.
3071  *
3072  * @param color the new background color for the receiver
3073  *
3074  * @exception IllegalArgumentException <ul>
3075  *    <li>ERROR_NULL_ARGUMENT - if the color is null</li>
3076  *    <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li>
3077  * </ul>
3078  * @exception SWTException <ul>
3079  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3080  * </ul>
3081  */
3082 public void setBackground(Color color) {
3083     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3084     if (color is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3085     if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3086     data.background = color.handle;
3087     data.backgroundPattern = null;
3088     data.state &= ~(BACKGROUND | BACKGROUND_BG);
3089 }
3090 
3091 /**
3092  * Sets the background pattern. The default value is <code>null</code>.
3093  * <p>
3094  * This operation requires the operating system's advanced
3095  * graphics subsystem which may not be available on some
3096  * platforms.
3097  * </p>
3098  *
3099  * @param pattern the new background pattern
3100  *
3101  * @exception IllegalArgumentException <ul>
3102  *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
3103  * </ul>
3104  * @exception SWTException <ul>
3105  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3106  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3107  * </ul>
3108  *
3109  * @see Pattern
3110  * @see #getAdvanced
3111  * @see #setAdvanced
3112  *
3113  * @since 3.1
3114  */
3115 public void setBackgroundPattern(Pattern pattern) {
3116     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3117     if (pattern !is null && pattern.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3118     if (data.cairo is null && pattern is null) return;
3119     initCairo();
3120     if (data.backgroundPattern is pattern) return;
3121     data.backgroundPattern = pattern;
3122     data.state &= ~BACKGROUND;
3123 }
3124 
3125 static void setCairoFont(org.eclipse.swt.internal.gtk.OS.cairo_t* cairo, Font font) {
3126     setCairoFont(cairo, font.handle);
3127 }
3128 
3129 static void setCairoFont(org.eclipse.swt.internal.gtk.OS.cairo_t* cairo, PangoFontDescription* font) {
3130     auto family = OS.pango_font_description_get_family(font);
3131     auto len = /*OS.*/strlen(family);
3132     auto buffer = new char[len + 1];
3133     OS.memmove(buffer.ptr, family, len);
3134     //TODO - convert font height from pango to cairo
3135     double height = OS.PANGO_PIXELS(OS.pango_font_description_get_size(font)) * 96 / 72;
3136     int pangoStyle = OS.pango_font_description_get_style(font);
3137     int pangoWeight = OS.pango_font_description_get_weight(font);
3138     int slant = Cairo.CAIRO_FONT_SLANT_NORMAL;
3139     if (pangoStyle is OS.PANGO_STYLE_ITALIC) slant = Cairo.CAIRO_FONT_SLANT_ITALIC;
3140     if (pangoStyle is OS.PANGO_STYLE_OBLIQUE) slant = Cairo.CAIRO_FONT_SLANT_OBLIQUE;
3141     int weight = Cairo.CAIRO_FONT_WEIGHT_NORMAL;
3142     if (pangoWeight is OS.PANGO_WEIGHT_BOLD) weight = Cairo.CAIRO_FONT_WEIGHT_BOLD;
3143     Cairo.cairo_select_font_face(cairo, buffer.ptr, slant, weight);
3144     Cairo.cairo_set_font_size(cairo, height);
3145 }
3146 
3147 static void setCairoClip(org.eclipse.swt.internal.gtk.OS.cairo_t* cairo, GdkRegion* clipRgn) {
3148     Cairo.cairo_reset_clip(cairo);
3149     if (clipRgn is null) return;
3150     if (OS.GTK_VERSION >= OS.buildVERSION(2, 8, 0)) {
3151         OS.gdk_cairo_region(cairo, clipRgn);
3152     } else {
3153         int nRects;
3154         GdkRectangle * rects;
3155         OS.gdk_region_get_rectangles(clipRgn, &rects, &nRects);
3156         //GdkRectangle* rect = new GdkRectangle();
3157         for (int i=0; i<nRects; i++) {
3158             Cairo.cairo_rectangle(cairo, rects[i].x, rects[i].y, rects[i].width, rects[i].height);
3159         }
3160         if (rects !is null) OS.g_free(rects);
3161     }
3162     Cairo.cairo_clip(cairo);
3163 }
3164 
3165 static void setCairoPatternColor( org.eclipse.swt.internal.gtk.OS.cairo_pattern_t* pattern, int offset, Color c, int alpha) {
3166     GdkColor* color = c.handle;
3167     double aa = (alpha & 0xFF) / cast(double)0xFF;
3168     double red = ((color.red & 0xFFFF) / cast(double)0xFFFF);
3169     double green = ((color.green & 0xFFFF) / cast(double)0xFFFF);
3170     double blue = ((color.blue & 0xFFFF) / cast(double)0xFFFF);
3171     Cairo.cairo_pattern_add_color_stop_rgba(pattern, offset, red, green, blue, aa);
3172 }
3173 
3174 void setClipping(GdkRegion* clipRgn) {
3175     auto cairo = data.cairo;
3176     if (clipRgn is null) {
3177         if (data.clipRgn !is null) {
3178             OS.gdk_region_destroy(data.clipRgn);
3179             data.clipRgn = null;
3180         }
3181         if (cairo !is null) {
3182             data.clippingTransform = null;
3183             setCairoClip(cairo, clipRgn);
3184         } else {
3185             auto clipping = data.damageRgn !is null ? data.damageRgn : null;
3186             OS.gdk_gc_set_clip_region(handle, clipping);
3187         }
3188     } else {
3189         if (data.clipRgn is null) data.clipRgn = OS.gdk_region_new();
3190         OS.gdk_region_subtract(data.clipRgn, data.clipRgn);
3191         OS.gdk_region_union(data.clipRgn, clipRgn);
3192         if (cairo !is null) {
3193             if (data.clippingTransform is null) data.clippingTransform = new double[6];
3194             Cairo.cairo_get_matrix(cairo,cast(cairo_matrix_t *) data.clippingTransform.ptr);
3195             setCairoClip(cairo, clipRgn);
3196         } else {
3197             auto clipping = clipRgn;
3198             if (data.damageRgn !is null) {
3199                 clipping = OS.gdk_region_new();
3200                 OS.gdk_region_union(clipping, clipRgn);
3201                 OS.gdk_region_intersect(clipping, data.damageRgn);
3202             }
3203             OS.gdk_gc_set_clip_region(handle, clipping);
3204             if (clipping !is clipRgn) OS.gdk_region_destroy(clipping);
3205         }
3206     }
3207 }
3208 
3209 /**
3210  * Sets the area of the receiver which can be changed
3211  * by drawing operations to the rectangular area specified
3212  * by the arguments.
3213  *
3214  * @param x the x coordinate of the clipping rectangle
3215  * @param y the y coordinate of the clipping rectangle
3216  * @param width the width of the clipping rectangle
3217  * @param height the height of the clipping rectangle
3218  *
3219  * @exception SWTException <ul>
3220  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3221  * </ul>
3222  */
3223 public void setClipping(int x, int y, int width, int height) {
3224     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3225     if (width < 0) {
3226         x = x + width;
3227         width = -width;
3228     }
3229     if (height < 0) {
3230         y = y + height;
3231         height = -height;
3232     }
3233     GdkRectangle rect;
3234     rect.x = x;
3235     rect.y = y;
3236     rect.width = width;
3237     rect.height = height;
3238     auto clipRgn = OS.gdk_region_new();
3239     OS.gdk_region_union_with_rect(clipRgn, &rect);
3240     setClipping(clipRgn);
3241     OS.gdk_region_destroy(clipRgn);
3242 }
3243 
3244 /**
3245  * Sets the area of the receiver which can be changed
3246  * by drawing operations to the path specified
3247  * by the argument.
3248  * <p>
3249  * This operation requires the operating system's advanced
3250  * graphics subsystem which may not be available on some
3251  * platforms.
3252  * </p>
3253  *
3254  * @param path the clipping path.
3255  *
3256  * @exception IllegalArgumentException <ul>
3257  *    <li>ERROR_INVALID_ARGUMENT - if the path has been disposed</li>
3258  * </ul>
3259  * @exception SWTException <ul>
3260  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3261  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3262  * </ul>
3263  *
3264  * @see Path
3265  * @see #getAdvanced
3266  * @see #setAdvanced
3267  *
3268  * @since 3.1
3269  */
3270 public void setClipping(Path path) {
3271     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3272     if (path !is null && path.isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3273     setClipping(cast(GdkRegion*)null);
3274     if (path !is null) {
3275         initCairo();
3276         auto cairo = data.cairo;
3277         auto copy = Cairo.cairo_copy_path(path.handle);
3278         if (copy is null) SWT.error(SWT.ERROR_NO_HANDLES);
3279         Cairo.cairo_append_path(cairo, copy);
3280         Cairo.cairo_path_destroy(copy);
3281         Cairo.cairo_clip(cairo);
3282     }
3283 }
3284 
3285 /**
3286  * Sets the area of the receiver which can be changed
3287  * by drawing operations to the rectangular area specified
3288  * by the argument.  Specifying <code>null</code> for the
3289  * rectangle reverts the receiver's clipping area to its
3290  * original value.
3291  *
3292  * @param rect the clipping rectangle or <code>null</code>
3293  *
3294  * @exception SWTException <ul>
3295  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3296  * </ul>
3297  */
3298 public void setClipping(Rectangle rect) {
3299     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3300     if (rect is null) {
3301         setClipping(cast(GdkRegion*)null);
3302     } else {
3303         setClipping(rect.x, rect.y, rect.width, rect.height);
3304     }
3305 }
3306 /**
3307  * Sets the area of the receiver which can be changed
3308  * by drawing operations to the region specified
3309  * by the argument.  Specifying <code>null</code> for the
3310  * region reverts the receiver's clipping area to its
3311  * original value.
3312  *
3313  * @param region the clipping region or <code>null</code>
3314  *
3315  * @exception IllegalArgumentException <ul>
3316  *    <li>ERROR_INVALID_ARGUMENT - if the region has been disposed</li>
3317  * </ul>
3318  * @exception SWTException <ul>
3319  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3320  * </ul>
3321  */
3322 public void setClipping(Region region) {
3323     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3324     if (region !is null && region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3325     setClipping(region !is null ? region.handle : cast(GdkRegion*)null);
3326 }
3327 
3328 /**
3329  * Sets the font which will be used by the receiver
3330  * to draw and measure text to the argument. If the
3331  * argument is null, then a default font appropriate
3332  * for the platform will be used instead.
3333  *
3334  * @param font the new font for the receiver, or null to indicate a default font
3335  *
3336  * @exception IllegalArgumentException <ul>
3337  *    <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li>
3338  * </ul>
3339  * @exception SWTException <ul>
3340  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3341  * </ul>
3342  */
3343 public void setFont(Font font) {
3344     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3345     if (font !is null && font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3346     data.font = font !is null ? font : data.device.systemFont;
3347     data.state &= ~FONT;
3348     data.stringWidth = data.stringHeight = -1;
3349 }
3350 
3351 /**
3352  * Sets the receiver's fill rule to the parameter, which must be one of
3353  * <code>SWT.FILL_EVEN_ODD</code> or <code>SWT.FILL_WINDING</code>.
3354  *
3355  * @param rule the new fill rule
3356  *
3357  * @exception IllegalArgumentException <ul>
3358  *    <li>ERROR_INVALID_ARGUMENT - if the rule is not one of <code>SWT.FILL_EVEN_ODD</code>
3359  *                                 or <code>SWT.FILL_WINDING</code></li>
3360  * </ul>
3361  * @exception SWTException <ul>
3362  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3363  * </ul>
3364  *
3365  * @since 3.1
3366  */
3367 public void setFillRule(int rule) {
3368     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3369     int cairo_mode = Cairo.CAIRO_FILL_RULE_EVEN_ODD;
3370     switch (rule) {
3371         case SWT.FILL_WINDING:
3372             cairo_mode = Cairo.CAIRO_FILL_RULE_WINDING; break;
3373         case SWT.FILL_EVEN_ODD:
3374             cairo_mode = Cairo.CAIRO_FILL_RULE_EVEN_ODD; break;
3375         default:
3376             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3377     }
3378     //TODO - need fill rule in X, GDK has no API
3379     initCairo();
3380     auto cairo = data.cairo;
3381     if (cairo !is null) {
3382         Cairo.cairo_set_fill_rule(cairo, cairo_mode);
3383     }
3384 }
3385 
3386 /**
3387  * Sets the foreground color. The foreground color is used
3388  * for drawing operations including when text is drawn.
3389  *
3390  * @param color the new foreground color for the receiver
3391  *
3392  * @exception IllegalArgumentException <ul>
3393  *    <li>ERROR_NULL_ARGUMENT - if the color is null</li>
3394  *    <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li>
3395  * </ul>
3396  * @exception SWTException <ul>
3397  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3398  * </ul>
3399  */
3400 public void setForeground(Color color) {
3401     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3402     if (color is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3403     if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3404     data.foreground = color.handle;
3405     data.foregroundPattern = null;
3406     data.state &= ~FOREGROUND;
3407 }
3408 
3409 /**
3410  * Sets the foreground pattern. The default value is <code>null</code>.
3411  * <p>
3412  * This operation requires the operating system's advanced
3413  * graphics subsystem which may not be available on some
3414  * platforms.
3415  * </p>
3416  * @param pattern the new foreground pattern
3417  *
3418  * @exception IllegalArgumentException <ul>
3419  *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
3420  * </ul>
3421  * @exception SWTException <ul>
3422  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3423  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3424  * </ul>
3425  *
3426  * @see Pattern
3427  * @see #getAdvanced
3428  * @see #setAdvanced
3429  *
3430  * @since 3.1
3431  */
3432 public void setForegroundPattern(Pattern pattern) {
3433     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3434     if (pattern !is null && pattern.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3435     if (data.cairo is null && pattern is null) return;
3436     initCairo();
3437     if (data.foregroundPattern is pattern) return;
3438     data.foregroundPattern = pattern;
3439     data.state &= ~FOREGROUND;
3440 }
3441 
3442 /**
3443  * Sets the receiver's interpolation setting to the parameter, which
3444  * must be one of <code>SWT.DEFAULT</code>, <code>SWT.NONE</code>,
3445  * <code>SWT.LOW</code> or <code>SWT.HIGH</code>.
3446  * <p>
3447  * This operation requires the operating system's advanced
3448  * graphics subsystem which may not be available on some
3449  * platforms.
3450  * </p>
3451  *
3452  * @param interpolation the new interpolation setting
3453  *
3454  * @exception IllegalArgumentException <ul>
3455  *    <li>ERROR_INVALID_ARGUMENT - if the rule is not one of <code>SWT.DEFAULT</code>,
3456  *                                 <code>SWT.NONE</code>, <code>SWT.LOW</code> or <code>SWT.HIGH</code>
3457  * </ul>
3458  * @exception SWTException <ul>
3459  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3460  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3461  * </ul>
3462  *
3463  * @see #getAdvanced
3464  * @see #setAdvanced
3465  *
3466  * @since 3.1
3467  */
3468 public void setInterpolation(int interpolation) {
3469     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3470     if (data.cairo is null && interpolation is SWT.DEFAULT) return;
3471     switch (interpolation) {
3472         case SWT.DEFAULT:
3473         case SWT.NONE:
3474         case SWT.LOW:
3475         case SWT.HIGH:
3476             break;
3477         default:
3478             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3479     }
3480     initCairo();
3481     data.interpolation = interpolation;
3482 }
3483 
3484 /**
3485  * Sets the receiver's line attributes.
3486  * <p>
3487  * This operation requires the operating system's advanced
3488  * graphics subsystem which may not be available on some
3489  * platforms.
3490  * </p>
3491  * @param attributes the line attributes
3492  *
3493  * @exception IllegalArgumentException <ul>
3494  *    <li>ERROR_NULL_ARGUMENT - if the attributes is null</li>
3495  *    <li>ERROR_INVALID_ARGUMENT - if any of the line attributes is not valid</li>
3496  * </ul>
3497  * @exception SWTException <ul>
3498  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3499  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3500  * </ul>
3501  *
3502  * @see LineAttributes
3503  * @see #getAdvanced
3504  * @see #setAdvanced
3505  *
3506  * @since 3.3
3507  */
3508 public void setLineAttributes(LineAttributes attributes) {
3509     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3510     if (attributes is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3511     int mask = 0;
3512     float lineWidth = attributes.width;
3513     if (lineWidth !is data.lineWidth) {
3514         mask |= LINE_WIDTH | DRAW_OFFSET;
3515     }
3516     int lineStyle = attributes.style;
3517     if (lineStyle !is data.lineStyle) {
3518         mask |= LINE_STYLE;
3519         switch (lineStyle) {
3520             case SWT.LINE_SOLID:
3521             case SWT.LINE_DASH:
3522             case SWT.LINE_DOT:
3523             case SWT.LINE_DASHDOT:
3524             case SWT.LINE_DASHDOTDOT:
3525                 break;
3526             case SWT.LINE_CUSTOM:
3527                 if (attributes.dash is null) lineStyle = SWT.LINE_SOLID;
3528                 break;
3529             default:
3530                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3531         }
3532     }
3533     int join = attributes.join;
3534     if (join !is data.lineJoin) {
3535         mask |= LINE_JOIN;
3536         switch (join) {
3537             case SWT.CAP_ROUND:
3538             case SWT.CAP_FLAT:
3539             case SWT.CAP_SQUARE:
3540                 break;
3541             default:
3542                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3543         }
3544     }
3545     int cap = attributes.cap;
3546     if (cap !is data.lineCap) {
3547         mask |= LINE_CAP;
3548         switch (cap) {
3549             case SWT.JOIN_MITER:
3550             case SWT.JOIN_ROUND:
3551             case SWT.JOIN_BEVEL:
3552                 break;
3553             default:
3554                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3555         }
3556     }
3557     float[] dashes = attributes.dash;
3558     float[] lineDashes = data.lineDashes;
3559     if (dashes !is null && dashes.length > 0) {
3560         bool changed = lineDashes is null || lineDashes.length !is dashes.length;
3561         for (int i = 0; i < dashes.length; i++) {
3562             float dash = dashes[i];
3563             if (dash <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3564             if (!changed && lineDashes[i] !is dash) changed = true;
3565         }
3566         if (changed) {
3567             float[] newDashes = new float[dashes.length];
3568             System.arraycopy(dashes, 0, newDashes, 0, dashes.length);
3569             dashes = newDashes;
3570             mask |= LINE_STYLE;
3571         } else {
3572             dashes = lineDashes;
3573         }
3574     } else {
3575         if (lineDashes !is null && lineDashes.length > 0) {
3576             mask |= LINE_STYLE;
3577         } else {
3578             dashes = lineDashes;
3579         }
3580     }
3581     float dashOffset = attributes.dashOffset;
3582     if (dashOffset !is data.lineDashesOffset) {
3583         mask |= LINE_STYLE;
3584     }
3585     float miterLimit = attributes.miterLimit;
3586     if (miterLimit !is data.lineMiterLimit) {
3587         mask |= LINE_MITERLIMIT;
3588     }
3589     initCairo();
3590     if (mask is 0) return;
3591     data.lineWidth = lineWidth;
3592     data.lineStyle = lineStyle;
3593     data.lineCap = cap;
3594     data.lineJoin = join;
3595     data.lineDashes = dashes;
3596     data.lineDashesOffset = dashOffset;
3597     data.lineMiterLimit = miterLimit;
3598     data.state &= ~mask;
3599 }
3600 
3601 /**
3602  * Sets the receiver's line cap style to the argument, which must be one
3603  * of the constants <code>SWT.CAP_FLAT</code>, <code>SWT.CAP_ROUND</code>,
3604  * or <code>SWT.CAP_SQUARE</code>.
3605  *
3606  * @param cap the cap style to be used for drawing lines
3607  *
3608  * @exception IllegalArgumentException <ul>
3609  *    <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li>
3610  * </ul>
3611  * @exception SWTException <ul>
3612  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3613  * </ul>
3614  *
3615  * @since 3.1
3616  */
3617 public void setLineCap(int cap) {
3618     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3619     if (data.lineCap is cap) return;
3620     switch (cap) {
3621         case SWT.CAP_ROUND:
3622         case SWT.CAP_FLAT:
3623         case SWT.CAP_SQUARE:
3624             break;
3625         default:
3626             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3627     }
3628     data.lineCap = cap;
3629     data.state &= ~LINE_CAP;
3630 }
3631 
3632 /**
3633  * Sets the receiver's line dash style to the argument. The default
3634  * value is <code>null</code>. If the argument is not <code>null</code>,
3635  * the receiver's line style is set to <code>SWT.LINE_CUSTOM</code>, otherwise
3636  * it is set to <code>SWT.LINE_SOLID</code>.
3637  *
3638  * @param dashes the dash style to be used for drawing lines
3639  *
3640  * @exception IllegalArgumentException <ul>
3641  *    <li>ERROR_INVALID_ARGUMENT - if any of the values in the array is less than or equal 0</li>
3642  * </ul>
3643  * @exception SWTException <ul>
3644  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3645  * </ul>
3646  *
3647  * @since 3.1
3648  */
3649 public void setLineDash(int[] dashes) {
3650     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3651     float[] lineDashes = data.lineDashes;
3652     if (dashes !is null && dashes.length > 0) {
3653         bool changed = data.lineStyle !is SWT.LINE_CUSTOM || lineDashes is null || lineDashes.length !is dashes.length;
3654         for (int i = 0; i < dashes.length; i++) {
3655             int dash = dashes[i];
3656             if (dash <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3657             if (!changed && lineDashes[i] !is dash) changed = true;
3658         }
3659         if (!changed) return;
3660         data.lineDashes = new float[dashes.length];
3661         for (int i = 0; i < dashes.length; i++) {
3662             data.lineDashes[i] = dashes[i];
3663         }
3664         data.lineStyle = SWT.LINE_CUSTOM;
3665     } else {
3666         if (data.lineStyle is SWT.LINE_SOLID && (lineDashes is null || lineDashes.length is 0)) return;
3667         data.lineDashes = null;
3668         data.lineStyle = SWT.LINE_SOLID;
3669     }
3670     data.state &= ~LINE_STYLE;
3671 }
3672 
3673 /**
3674  * Sets the receiver's line join style to the argument, which must be one
3675  * of the constants <code>SWT.JOIN_MITER</code>, <code>SWT.JOIN_ROUND</code>,
3676  * or <code>SWT.JOIN_BEVEL</code>.
3677  *
3678  * @param join the join style to be used for drawing lines
3679  *
3680  * @exception IllegalArgumentException <ul>
3681  *    <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li>
3682  * </ul>
3683  * @exception SWTException <ul>
3684  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3685  * </ul>
3686  *
3687  * @since 3.1
3688  */
3689 public void setLineJoin(int join) {
3690     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3691     if (data.lineJoin is join) return;
3692     switch (join) {
3693         case SWT.JOIN_MITER:
3694         case SWT.JOIN_ROUND:
3695         case SWT.JOIN_BEVEL:
3696             break;
3697         default:
3698             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3699     }
3700     data.lineJoin = join;
3701     data.state &= ~LINE_JOIN;
3702 }
3703 
3704 /**
3705  * Sets the receiver's line style to the argument, which must be one
3706  * of the constants <code>SWT.LINE_SOLID</code>, <code>SWT.LINE_DASH</code>,
3707  * <code>SWT.LINE_DOT</code>, <code>SWT.LINE_DASHDOT</code> or
3708  * <code>SWT.LINE_DASHDOTDOT</code>.
3709  *
3710  * @param lineStyle the style to be used for drawing lines
3711  *
3712  * @exception IllegalArgumentException <ul>
3713  *    <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li>
3714  * </ul>
3715  * @exception SWTException <ul>
3716  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3717  * </ul>
3718  */
3719 public void setLineStyle(int lineStyle) {
3720     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3721     if (data.lineStyle is lineStyle) return;
3722     switch (lineStyle) {
3723         case SWT.LINE_SOLID:
3724         case SWT.LINE_DASH:
3725         case SWT.LINE_DOT:
3726         case SWT.LINE_DASHDOT:
3727         case SWT.LINE_DASHDOTDOT:
3728             break;
3729         case SWT.LINE_CUSTOM:
3730             if (data.lineDashes is null) lineStyle = SWT.LINE_SOLID;
3731             break;
3732         default:
3733             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3734     }
3735     data.lineStyle = lineStyle;
3736     data.state &= ~LINE_STYLE;
3737 }
3738 
3739 /**
3740  * Sets the width that will be used when drawing lines
3741  * for all of the figure drawing operations (that is,
3742  * <code>drawLine</code>, <code>drawRectangle</code>,
3743  * <code>drawPolyline</code>, and so forth.
3744  * <p>
3745  * Note that line width of zero is used as a hint to
3746  * indicate that the fastest possible line drawing
3747  * algorithms should be used. This means that the
3748  * output may be different from line width one.
3749  * </p>
3750  *
3751  * @param lineWidth the width of a line
3752  *
3753  * @exception SWTException <ul>
3754  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3755  * </ul>
3756  */
3757 public void setLineWidth(int lineWidth) {
3758     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3759     if (data.lineWidth is lineWidth) return;
3760     data.lineWidth = lineWidth;
3761     data.state &= ~(LINE_WIDTH | DRAW_OFFSET);
3762 }
3763 
3764 void setString(String str, int flags) {
3765     if (data.layout is null) createLayout();
3766     if (str is data.str && (flags & ~SWT.DRAW_TRANSPARENT) is (data.drawFlags  & ~SWT.DRAW_TRANSPARENT)) {
3767         return;
3768     }
3769     char[] buffer;
3770     size_t mnemonic, len = str.length ;
3771     auto layout = data.layout;
3772     char[] text = str.dup;
3773     if ((flags & SWT.DRAW_MNEMONIC) !is 0 && (mnemonic = fixMnemonic(text)) !is -1) {
3774         char[] text1 = new char[mnemonic - 1];
3775         System.arraycopy(text, 0, text1, 0, text1.length);
3776         char[] buffer1 = text1.dup;
3777         char[] text2 = new char[text.length - mnemonic];
3778         System.arraycopy(text, mnemonic - 1, text2, 0, text2.length);
3779         char[] buffer2 = text2.dup;
3780         buffer = new char[buffer1.length + buffer2.length];
3781         System.arraycopy(buffer1, 0, buffer, 0, buffer1.length);
3782         System.arraycopy(buffer2, 0, buffer, buffer1.length, buffer2.length);
3783         auto attr_list = OS.pango_attr_list_new();
3784         auto attr = OS.pango_attr_underline_new(OS.PANGO_UNDERLINE_LOW);
3785         attr.start_index = cast(int)/*64bit*/buffer1.length;
3786         attr.end_index = cast(int)/*64bit*/buffer1.length + 1;
3787         OS.pango_attr_list_insert(attr_list, attr);
3788         OS.pango_layout_set_attributes(layout, attr_list);
3789         OS.pango_attr_list_unref(attr_list);
3790     } else {
3791         buffer = text.dup;
3792         OS.pango_layout_set_attributes(layout, null);
3793     }
3794     OS.pango_layout_set_text(layout, buffer.ptr, cast(int)/*64bit*/buffer.length);
3795     OS.pango_layout_set_single_paragraph_mode(layout, (flags & SWT.DRAW_DELIMITER) is 0);
3796     OS.pango_layout_set_tabs(layout, (flags & SWT.DRAW_TAB) !is 0 ? null : data.device.emptyTab);
3797     data.str = str;
3798     data.stringWidth = data.stringHeight = -1;
3799     data.drawFlags = flags;
3800 }
3801 
3802 /**
3803  * Sets the receiver's text anti-aliasing value to the parameter,
3804  * which must be one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code>
3805  * or <code>SWT.ON</code>. Note that this controls anti-aliasing only
3806  * for all <em>text drawing</em> operations.
3807  * <p>
3808  * This operation requires the operating system's advanced
3809  * graphics subsystem which may not be available on some
3810  * platforms.
3811  * </p>
3812  *
3813  * @param antialias the anti-aliasing setting
3814  *
3815  * @exception IllegalArgumentException <ul>
3816  *    <li>ERROR_INVALID_ARGUMENT - if the parameter is not one of <code>SWT.DEFAULT</code>,
3817  *                                 <code>SWT.OFF</code> or <code>SWT.ON</code></li>
3818  * </ul>
3819  * @exception SWTException <ul>
3820  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3821  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3822  * </ul>
3823  *
3824  * @see #getAdvanced
3825  * @see #setAdvanced
3826  * @see #setAntialias
3827  *
3828  * @since 3.1
3829  */
3830 public void setTextAntialias(int antialias) {
3831     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3832     if (data.cairo is null && antialias is SWT.DEFAULT) return;
3833     int mode = 0;
3834     switch (antialias) {
3835         case SWT.DEFAULT: mode = Cairo.CAIRO_ANTIALIAS_DEFAULT; break;
3836         case SWT.OFF: mode = Cairo.CAIRO_ANTIALIAS_NONE; break;
3837         case SWT.ON: mode = Cairo.CAIRO_ANTIALIAS_GRAY;
3838             break;
3839         default:
3840             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3841     }
3842     initCairo();
3843     auto options = Cairo.cairo_font_options_create();
3844     Cairo.cairo_font_options_set_antialias(options, mode);
3845     if (OS.GTK_VERSION < OS.buildVERSION(2, 8, 0)) {
3846         Cairo.cairo_set_font_options(data.cairo, options);
3847     } else {
3848         if (data.context is null) createLayout();
3849         OS.pango_cairo_context_set_font_options(data.context, options);
3850     }
3851     Cairo.cairo_font_options_destroy(options);
3852 }
3853 
3854 /**
3855  * Sets the transform that is currently being used by the receiver. If
3856  * the argument is <code>null</code>, the current transform is set to
3857  * the identity transform.
3858  * <p>
3859  * This operation requires the operating system's advanced
3860  * graphics subsystem which may not be available on some
3861  * platforms.
3862  * </p>
3863  *
3864  * @param transform the transform to set
3865  *
3866  * @exception IllegalArgumentException <ul>
3867  *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
3868  * </ul>
3869  * @exception SWTException <ul>
3870  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3871  *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
3872  * </ul>
3873  *
3874  * @see Transform
3875  * @see #getAdvanced
3876  * @see #setAdvanced
3877  *
3878  * @since 3.1
3879  */
3880 public void setTransform(Transform transform) {
3881     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3882     if (transform !is null && transform.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3883     if (data.cairo is null && transform is null) return;
3884     initCairo();
3885     auto cairo = data.cairo;
3886     double[] identity = identity();
3887     if (transform !is null) {
3888         Cairo.cairo_matrix_multiply(cast(cairo_matrix_t*)identity.ptr, cast(cairo_matrix_t*)transform.handle.ptr, cast(cairo_matrix_t*)identity.ptr);
3889     }
3890     Cairo.cairo_set_matrix(cairo, cast(cairo_matrix_t*)identity.ptr);
3891     data.state &= ~DRAW_OFFSET;
3892 }
3893 
3894 /**
3895  * If the argument is <code>true</code>, puts the receiver
3896  * in a drawing mode where the resulting color in the destination
3897  * is the <em>exclusive or</em> of the color values in the source
3898  * and the destination, and if the argument is <code>false</code>,
3899  * puts the receiver in a drawing mode where the destination color
3900  * is replaced with the source color value.
3901  * <p>
3902  * Note that this mode in fundamentally unsupportable on certain
3903  * platforms, notably Carbon (Mac OS X). Clients that want their
3904  * code to run on all platforms need to avoid this method.
3905  * </p>
3906  *
3907  * @param xor if <code>true</code>, then <em>xor</em> mode is used, otherwise <em>source copy</em> mode is used
3908  *
3909  * @exception SWTException <ul>
3910  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3911  * </ul>
3912  *
3913  * @deprecated this functionality is not supported on some platforms
3914  */
3915 public void setXORMode(bool xor) {
3916     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3917     OS.gdk_gc_set_function(handle, xor ? OS.GDK_XOR : OS.GDK_COPY);
3918     data.xorMode = xor;
3919 }
3920 
3921 /**
3922  * Returns the extent of the given str. No tab
3923  * expansion or carriage return processing will be performed.
3924  * <p>
3925  * The <em>extent</em> of a str is the width and height of
3926  * the rectangular area it would cover if drawn in a particular
3927  * font (in this case, the current font in the receiver).
3928  * </p>
3929  *
3930  * @param str the str to measure
3931  * @return a point containing the extent of the str
3932  *
3933  * @exception SWTException <ul>
3934  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3935  * </ul>
3936  */
3937 public Point stringExtent(String str) {
3938     return textExtent(str, 0);
3939 }
3940 
3941 /**
3942  * Returns the extent of the given str. Tab expansion and
3943  * carriage return processing are performed.
3944  * <p>
3945  * The <em>extent</em> of a str is the width and height of
3946  * the rectangular area it would cover if drawn in a particular
3947  * font (in this case, the current font in the receiver).
3948  * </p>
3949  *
3950  * @param str the str to measure
3951  * @return a point containing the extent of the str
3952  *
3953  * @exception SWTException <ul>
3954  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3955  * </ul>
3956  */
3957 public Point textExtent(String str) {
3958     return textExtent(str, SWT.DRAW_DELIMITER | SWT.DRAW_TAB);
3959 }
3960 
3961 /**
3962  * Returns the extent of the given str. Tab expansion, line
3963  * delimiter and mnemonic processing are performed according to
3964  * the specified flags, which can be a combination of:
3965  * <dl>
3966  * <dt><b>DRAW_DELIMITER</b></dt>
3967  * <dd>draw multiple lines</dd>
3968  * <dt><b>DRAW_TAB</b></dt>
3969  * <dd>expand tabs</dd>
3970  * <dt><b>DRAW_MNEMONIC</b></dt>
3971  * <dd>underline the mnemonic character</dd>
3972  * <dt><b>DRAW_TRANSPARENT</b></dt>
3973  * <dd>transparent background</dd>
3974  * </dl>
3975  * <p>
3976  * The <em>extent</em> of a str is the width and height of
3977  * the rectangular area it would cover if drawn in a particular
3978  * font (in this case, the current font in the receiver).
3979  * </p>
3980  *
3981  * @param str the str to measure
3982  * @param flags the flags specifying how to process the text
3983  * @return a point containing the extent of the str
3984  *
3985  * @exception SWTException <ul>
3986  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3987  * </ul>
3988  */
3989 public Point textExtent(String str, int flags) {
3990     if (handle is null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
3991     //DWT_CHANGE: allow null for string
3992     //if (str is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3993     auto cairo = data.cairo;
3994     if (cairo !is null) {
3995         if (OS.GTK_VERSION < OS.buildVERSION(2, 8, 0)) {
3996             //TODO - honor flags
3997             checkGC(FONT);
3998             char* buffer = toStringz(str);
3999             cairo_font_extents_t* font_extents = new cairo_font_extents_t();
4000             Cairo.cairo_font_extents(cairo, font_extents);
4001             cairo_text_extents_t* extents = new cairo_text_extents_t();
4002             Cairo.cairo_text_extents(cairo, buffer, extents);
4003             return new Point(cast(int)extents.width, cast(int)font_extents.height);
4004         }
4005     }
4006     setString(str, flags);
4007     checkGC(FONT);
4008     if (data.stringWidth !is -1) return new Point(data.stringWidth, data.stringHeight);
4009     int width, height;
4010     OS.pango_layout_get_size(data.layout, &width, &height);
4011     return new Point(data.stringWidth = OS.PANGO_PIXELS(width), data.stringHeight = OS.PANGO_PIXELS(height));
4012 }
4013 
4014 /**
4015  * Returns a str containing a concise, human-readable
4016  * description of the receiver.
4017  *
4018  * @return a str representation of the receiver
4019  */
4020 public override String toString () {
4021     if (isDisposed()) return "GC {*DISPOSED*}";
4022     return Format( "GC {{{}}", handle );
4023 }
4024 
4025 }
4026 
4027