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