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