1 /******************************************************************************* 2 * Copyright (c) 2000, 2008 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Port to the D programming language: 11 * Frank Benoit <benoit@tionex.de> 12 *******************************************************************************/ 13 module org.eclipse.swt.graphics.Path; 14 15 import org.eclipse.swt.SWT; 16 import org.eclipse.swt.internal.Compatibility; 17 import org.eclipse.swt.internal.cairo.Cairo : Cairo; 18 import org.eclipse.swt.internal.gtk.OS; 19 import org.eclipse.swt.graphics.Resource; 20 import org.eclipse.swt.graphics.Device; 21 import org.eclipse.swt.graphics.Font; 22 import org.eclipse.swt.graphics.GC; 23 import org.eclipse.swt.graphics.GCData; 24 import org.eclipse.swt.graphics.PathData; 25 import java.lang.all; 26 27 version(Tango){ 28 import tango.stdc..string; 29 } else { 30 import core.stdc..string; 31 } 32 33 /** 34 * Instances of this class represent paths through the two-dimensional 35 * coordinate system. Paths do not have to be continuous, and can be 36 * described using lines, rectangles, arcs, cubic or quadratic bezier curves, 37 * glyphs, or other paths. 38 * <p> 39 * Application code must explicitly invoke the <code>Path.dispose()</code> 40 * method to release the operating system resources managed by each instance 41 * when those instances are no longer required. 42 * </p> 43 * <p> 44 * This class requires the operating system's advanced graphics subsystem 45 * which may not be available on some platforms. 46 * </p> 47 * 48 * @see <a href="http://www.eclipse.org/swt/snippets/#path">Path, Pattern snippets</a> 49 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: GraphicsExample</a> 50 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 51 * 52 * @since 3.1 53 */ 54 public class Path : Resource { 55 alias Resource.init_ init_; 56 /** 57 * the OS resource for the Path 58 * (Warning: This field is platform dependent) 59 * <p> 60 * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT 61 * public API. It is marked public only so that it can be shared 62 * within the packages provided by SWT. It is not available on all 63 * platforms and should never be accessed from application code. 64 * </p> 65 */ 66 public org.eclipse.swt.internal.gtk.OS.cairo_t* handle; 67 68 bool moved, closed = true; 69 70 /** 71 * Constructs a new empty Path. 72 * <p> 73 * This operation requires the operating system's advanced 74 * graphics subsystem which may not be available on some 75 * platforms. 76 * </p> 77 * 78 * @param device the device on which to allocate the path 79 * 80 * @exception IllegalArgumentException <ul> 81 * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device</li> 82 * </ul> 83 * @exception SWTException <ul> 84 * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> 85 * </ul> 86 * @exception SWTError <ul> 87 * <li>ERROR_NO_HANDLES if a handle for the path could not be obtained</li> 88 * </ul> 89 * 90 * @see #dispose() 91 */ 92 public this (Device device) { 93 super(device); 94 this.device.checkCairo(); 95 auto surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_ARGB32, 1, 1); 96 if (surface is null) SWT.error(SWT.ERROR_NO_HANDLES); 97 handle = Cairo.cairo_create(surface); 98 Cairo.cairo_surface_destroy(surface); 99 if (handle is null) SWT.error(SWT.ERROR_NO_HANDLES); 100 init_(); 101 } 102 103 /** 104 * Constructs a new Path that is a copy of <code>path</code>. If 105 * <code>flatness</code> is less than or equal to zero, an unflatten 106 * copy of the path is created. Otherwise, it specifies the maximum 107 * error between the path and its flatten copy. Smaller numbers give 108 * better approximation. 109 * <p> 110 * This operation requires the operating system's advanced 111 * graphics subsystem which may not be available on some 112 * platforms. 113 * </p> 114 * 115 * @param device the device on which to allocate the path 116 * @param path the path to make a copy 117 * @param flatness the flatness value 118 * 119 * @exception IllegalArgumentException <ul> 120 * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device</li> 121 * <li>ERROR_NULL_ARGUMENT - if the path is null</li> 122 * <li>ERROR_INVALID_ARGUMENT - if the path has been disposed</li> 123 * </ul> 124 * @exception SWTException <ul> 125 * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> 126 * </ul> 127 * @exception SWTError <ul> 128 * <li>ERROR_NO_HANDLES if a handle for the path could not be obtained</li> 129 * </ul> 130 * 131 * @see #dispose() 132 * @since 3.4 133 */ 134 public this (Device device, Path path, float flatness) { 135 super(device); 136 if (path is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 137 if (path.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 138 auto surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_ARGB32, 1, 1); 139 if (surface is null) SWT.error(SWT.ERROR_NO_HANDLES); 140 handle = Cairo.cairo_create(surface); 141 Cairo.cairo_surface_destroy(surface); 142 if (handle is null) SWT.error(SWT.ERROR_NO_HANDLES); 143 cairo_path_t* copy; 144 flatness = Math.max(0, flatness); 145 if (flatness is 0) { 146 copy = Cairo.cairo_copy_path(path.handle); 147 } else { 148 double tolerance = Cairo.cairo_get_tolerance(path.handle); 149 Cairo.cairo_set_tolerance(path.handle, flatness); 150 copy = Cairo.cairo_copy_path_flat(path.handle); 151 Cairo.cairo_set_tolerance(path.handle, tolerance); 152 } 153 if (copy is null) { 154 Cairo.cairo_destroy(handle); 155 SWT.error(SWT.ERROR_NO_HANDLES); 156 } 157 Cairo.cairo_append_path(handle, copy); 158 Cairo.cairo_path_destroy(copy); 159 init_(); 160 } 161 162 /** 163 * Constructs a new Path with the specifed PathData. 164 * <p> 165 * This operation requires the operating system's advanced 166 * graphics subsystem which may not be available on some 167 * platforms. 168 * </p> 169 * 170 * @param device the device on which to allocate the path 171 * @param data the data for the path 172 * 173 * @exception IllegalArgumentException <ul> 174 * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device</li> 175 * <li>ERROR_NULL_ARGUMENT - if the data is null</li> 176 * </ul> 177 * @exception SWTException <ul> 178 * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> 179 * </ul> 180 * @exception SWTError <ul> 181 * <li>ERROR_NO_HANDLES if a handle for the path could not be obtained</li> 182 * </ul> 183 * 184 * @see #dispose() 185 * @since 3.4 186 */ 187 public this (Device device, PathData data) { 188 this(device); 189 if (data is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 190 init_(data); 191 } 192 193 /** 194 * Adds to the receiver a circular or elliptical arc that lies within 195 * the specified rectangular area. 196 * <p> 197 * The resulting arc begins at <code>startAngle</code> and extends 198 * for <code>arcAngle</code> degrees. 199 * Angles are interpreted such that 0 degrees is at the 3 o'clock 200 * position. A positive value indicates a counter-clockwise rotation 201 * while a negative value indicates a clockwise rotation. 202 * </p><p> 203 * The center of the arc is the center of the rectangle whose origin 204 * is (<code>x</code>, <code>y</code>) and whose size is specified by the 205 * <code>width</code> and <code>height</code> arguments. 206 * </p><p> 207 * The resulting arc covers an area <code>width + 1</code> pixels wide 208 * by <code>height + 1</code> pixels tall. 209 * </p> 210 * 211 * @param x the x coordinate of the upper-left corner of the arc 212 * @param y the y coordinate of the upper-left corner of the arc 213 * @param width the width of the arc 214 * @param height the height of the arc 215 * @param startAngle the beginning angle 216 * @param arcAngle the angular extent of the arc, relative to the start angle 217 * 218 * @exception SWTException <ul> 219 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 220 * </ul> 221 */ 222 public void addArc(float x, float y, float width, float height, float startAngle, float arcAngle) { 223 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 224 moved = true; 225 if (width is height) { 226 float angle = -startAngle * cast(float)Compatibility.PI / 180; 227 if (closed) Cairo.cairo_move_to(handle, (x + width / 2f) + width / 2f * Math.cos(angle), (y + height / 2f) + height / 2f * Math.sin(angle)); 228 if (arcAngle >= 0) { 229 Cairo.cairo_arc_negative(handle, x + width / 2f, y + height / 2f, width / 2f, angle, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180); 230 } else { 231 Cairo.cairo_arc(handle, x + width / 2f, y + height / 2f, width / 2f, angle, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180); 232 } 233 } else { 234 Cairo.cairo_save(handle); 235 Cairo.cairo_translate(handle, x + width / 2f, y + height / 2f); 236 Cairo.cairo_scale(handle, width / 2f, height / 2f); 237 float angle = -startAngle * cast(float)Compatibility.PI / 180; 238 if (closed) Cairo.cairo_move_to(handle, Math.cos(angle), Math.sin(angle)); 239 if (arcAngle >= 0) { 240 Cairo.cairo_arc_negative(handle, 0, 0, 1, angle, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180); 241 } else { 242 Cairo.cairo_arc(handle, 0, 0, 1, angle, -(startAngle + arcAngle) * cast(float)Compatibility.PI / 180); 243 } 244 Cairo.cairo_restore(handle); 245 } 246 closed = false; 247 if (Math.abs(arcAngle) >= 360) close(); 248 } 249 250 /** 251 * Adds to the receiver the path described by the parameter. 252 * 253 * @param path the path to add to the receiver 254 * 255 * @exception IllegalArgumentException <ul> 256 * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> 257 * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> 258 * </ul> 259 * @exception SWTException <ul> 260 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 261 * </ul> 262 */ 263 public void addPath(Path path) { 264 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 265 if (path is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 266 if (path.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 267 moved = false; 268 auto copy = Cairo.cairo_copy_path(path.handle); 269 if (copy is null) SWT.error(SWT.ERROR_NO_HANDLES); 270 Cairo.cairo_append_path(handle, copy); 271 Cairo.cairo_path_destroy(copy); 272 closed = path.closed; 273 } 274 275 /** 276 * Adds to the receiver the rectangle specified by x, y, width and height. 277 * 278 * @param x the x coordinate of the rectangle to add 279 * @param y the y coordinate of the rectangle to add 280 * @param width the width of the rectangle to add 281 * @param height the height of the rectangle to add 282 * 283 * @exception SWTException <ul> 284 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 285 * </ul> 286 */ 287 public void addRectangle(float x, float y, float width, float height) { 288 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 289 moved = false; 290 Cairo.cairo_rectangle(handle, x, y, width, height); 291 closed = true; 292 } 293 294 /** 295 * Adds to the receiver the pattern of glyphs generated by drawing 296 * the given string using the given font starting at the point (x, y). 297 * 298 * @param string the text to use 299 * @param x the x coordinate of the starting point 300 * @param y the y coordinate of the starting point 301 * @param font the font to use 302 * 303 * @exception IllegalArgumentException <ul> 304 * <li>ERROR_NULL_ARGUMENT - if the font is null</li> 305 * <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li> 306 * </ul> 307 * @exception SWTException <ul> 308 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 309 * </ul> 310 */ 311 public void addString(String str, float x, float y, Font font) { 312 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 313 if (font is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 314 if (font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 315 moved = false; 316 GC.addCairoString(handle, str, x, y, font); 317 closed = true; 318 } 319 320 /** 321 * Closes the current sub path by adding to the receiver a line 322 * from the current point of the path back to the starting point 323 * of the sub path. 324 * 325 * @exception SWTException <ul> 326 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 327 * </ul> 328 */ 329 public void close() { 330 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 331 Cairo.cairo_close_path(handle); 332 moved = false; 333 closed = true; 334 } 335 336 /** 337 * Returns <code>true</code> if the specified point is contained by 338 * the receiver and false otherwise. 339 * <p> 340 * If outline is <code>true</code>, the point (x, y) checked for containment in 341 * the receiver's outline. If outline is <code>false</code>, the point is 342 * checked to see if it is contained within the bounds of the (closed) area 343 * covered by the receiver. 344 * 345 * @param x the x coordinate of the point to test for containment 346 * @param y the y coordinate of the point to test for containment 347 * @param gc the GC to use when testing for containment 348 * @param outline controls whether to check the outline or contained area of the path 349 * @return <code>true</code> if the path contains the point and <code>false</code> otherwise 350 * 351 * @exception IllegalArgumentException <ul> 352 * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> 353 * <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li> 354 * </ul> 355 * @exception SWTException <ul> 356 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 357 * </ul> 358 */ 359 public bool contains(float x, float y, GC gc, bool outline) { 360 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 361 if (gc is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 362 if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 363 //TODO - see Windows 364 gc.initCairo(); 365 gc.checkGC(GC.LINE_CAP | GC.LINE_JOIN | GC.LINE_STYLE | GC.LINE_WIDTH); 366 bool result = false; 367 auto cairo = gc.data.cairo; 368 auto copy = Cairo.cairo_copy_path(handle); 369 if (copy is null) SWT.error(SWT.ERROR_NO_HANDLES); 370 Cairo.cairo_append_path(cairo, copy); 371 Cairo.cairo_path_destroy(copy); 372 if (outline) { 373 result = Cairo.cairo_in_stroke(cairo, x, y) !is 0; 374 } else { 375 result = Cairo.cairo_in_fill(cairo, x, y) !is 0; 376 } 377 Cairo.cairo_new_path(cairo); 378 return result; 379 } 380 381 /** 382 * Adds to the receiver a cubic bezier curve based on the parameters. 383 * 384 * @param cx1 the x coordinate of the first control point of the spline 385 * @param cy1 the y coordinate of the first control of the spline 386 * @param cx2 the x coordinate of the second control of the spline 387 * @param cy2 the y coordinate of the second control of the spline 388 * @param x the x coordinate of the end point of the spline 389 * @param y the y coordinate of the end point of the spline 390 * 391 * @exception SWTException <ul> 392 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 393 * </ul> 394 */ 395 public void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) { 396 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 397 if (!moved) { 398 double currentX = 0, currentY = 0; 399 Cairo.cairo_get_current_point(handle, ¤tX, ¤tY); 400 Cairo.cairo_move_to(handle, currentX, currentY); 401 moved = true; 402 } 403 Cairo.cairo_curve_to(handle, cx1, cy1, cx2, cy2, x, y); 404 closed = false; 405 } 406 407 /** 408 * Replaces the first four elements in the parameter with values that 409 * describe the smallest rectangle that will completely contain the 410 * receiver (i.e. the bounding box). 411 * 412 * @param bounds the array to hold the result 413 * 414 * @exception IllegalArgumentException <ul> 415 * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> 416 * <li>ERROR_INVALID_ARGUMENT - if the parameter is too small to hold the bounding box</li> 417 * </ul> 418 * @exception SWTException <ul> 419 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 420 * </ul> 421 */ 422 public void getBounds(float[] bounds) { 423 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 424 if (bounds is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 425 if (bounds.length < 4) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 426 auto copy = Cairo.cairo_copy_path(handle); 427 if (copy is null) SWT.error(SWT.ERROR_NO_HANDLES); 428 cairo_path_t* path = new cairo_path_t(); 429 memmove(path, copy, cairo_path_t.sizeof); 430 double minX = 0, minY = 0, maxX = 0, maxY = 0; 431 if (path.num_data > 0) { 432 minX = minY = double.max; 433 maxX = maxY = -double.max; 434 int i = 0; 435 cairo_path_data_t* data = new cairo_path_data_t(); 436 while (i < path.num_data) { 437 *data = path.data[i]; 438 switch (data.header.type) { 439 case Cairo.CAIRO_PATH_MOVE_TO: 440 minX = Math.min(minX, path.data[i+1].point.x); 441 minY = Math.min(minY, path.data[i+1].point.y); 442 maxX = Math.max(maxX, path.data[i+1].point.x); 443 maxY = Math.max(maxY, path.data[i+1].point.y); 444 break; 445 case Cairo.CAIRO_PATH_LINE_TO: 446 minX = Math.min(minX, path.data[i+1].point.x); 447 minY = Math.min(minY, path.data[i+1].point.y); 448 maxX = Math.max(maxX, path.data[i+1].point.x); 449 maxY = Math.max(maxY, path.data[i+1].point.y); 450 break; 451 case Cairo.CAIRO_PATH_CURVE_TO: 452 minX = Math.min(minX, path.data[i+1].point.x); 453 minY = Math.min(minY, path.data[i+1].point.y); 454 maxX = Math.max(maxX, path.data[i+1].point.x); 455 maxY = Math.max(maxY, path.data[i+1].point.y); 456 minX = Math.min(minX, path.data[i+2].point.x); 457 minY = Math.min(minY, path.data[i+2].point.y); 458 maxX = Math.max(maxX, path.data[i+2].point.x); 459 maxY = Math.max(maxY, path.data[i+2].point.y); 460 minX = Math.min(minX, path.data[i+3].point.x); 461 minY = Math.min(minY, path.data[i+3].point.y); 462 maxX = Math.max(maxX, path.data[i+3].point.x); 463 maxY = Math.max(maxY, path.data[i+3].point.y); 464 break; 465 case Cairo.CAIRO_PATH_CLOSE_PATH: break; 466 default: 467 } 468 i += data.header.length; 469 } 470 } 471 bounds[0] = cast(float)minX; 472 bounds[1] = cast(float)minY; 473 bounds[2] = cast(float)(maxX - minX); 474 bounds[3] = cast(float)(maxY - minY); 475 Cairo.cairo_path_destroy(copy); 476 } 477 478 /** 479 * Replaces the first two elements in the parameter with values that 480 * describe the current point of the path. 481 * 482 * @param point the array to hold the result 483 * 484 * @exception IllegalArgumentException <ul> 485 * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> 486 * <li>ERROR_INVALID_ARGUMENT - if the parameter is too small to hold the end point</li> 487 * </ul> 488 * @exception SWTException <ul> 489 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 490 * </ul> 491 */ 492 public void getCurrentPoint(float[] point) { 493 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 494 if (point is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 495 if (point.length < 2) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 496 double x = 0, y = 0; 497 Cairo.cairo_get_current_point(handle, &x, &y); 498 point[0] = cast(float)x; 499 point[1] = cast(float)y; 500 } 501 502 /** 503 * Returns a device independent representation of the receiver. 504 * 505 * @return the PathData for the receiver 506 * 507 * @exception SWTException <ul> 508 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 509 * </ul> 510 * 511 * @see PathData 512 */ 513 public PathData getPathData() { 514 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 515 auto copy = Cairo.cairo_copy_path(handle); 516 if (copy is null) SWT.error(SWT.ERROR_NO_HANDLES); 517 cairo_path_t* path = new cairo_path_t(); 518 *path = *copy; 519 byte[] types = new byte[path.num_data]; 520 float[] pts = new float[path.num_data * 6]; 521 int typeIndex = 0, ptsIndex = 0; 522 if (path.num_data > 0) { 523 int i = 0; 524 double[] points = new double[6]; 525 cairo_path_data_t* data = new cairo_path_data_t(); 526 while (i < path.num_data) { 527 switch (data.header.type) { 528 case Cairo.CAIRO_PATH_MOVE_TO: 529 types[typeIndex++] = SWT.PATH_MOVE_TO; 530 pts[ptsIndex++] = cast(float)path.data[i+1].point.x; 531 pts[ptsIndex++] = cast(float)path.data[i+1].point.y; 532 break; 533 case Cairo.CAIRO_PATH_LINE_TO: 534 types[typeIndex++] = SWT.PATH_LINE_TO; 535 pts[ptsIndex++] = cast(float)path.data[i+1].point.x; 536 pts[ptsIndex++] = cast(float)path.data[i+1].point.y; 537 break; 538 case Cairo.CAIRO_PATH_CURVE_TO: 539 types[typeIndex++] = SWT.PATH_CUBIC_TO; 540 pts[ptsIndex++] = cast(float)path.data[i+1].point.x; 541 pts[ptsIndex++] = cast(float)path.data[i+1].point.y; 542 pts[ptsIndex++] = cast(float)path.data[i+2].point.x; 543 pts[ptsIndex++] = cast(float)path.data[i+2].point.y; 544 pts[ptsIndex++] = cast(float)path.data[i+3].point.x; 545 pts[ptsIndex++] = cast(float)path.data[i+3].point.y; 546 break; 547 case Cairo.CAIRO_PATH_CLOSE_PATH: 548 types[typeIndex++] = SWT.PATH_CLOSE; 549 break; 550 default: 551 } 552 i += data.header.length; 553 } 554 } 555 if (typeIndex !is types.length) { 556 byte[] newTypes = new byte[typeIndex]; 557 System.arraycopy(types, 0, newTypes, 0, typeIndex); 558 types = newTypes; 559 } 560 if (ptsIndex !is pts.length) { 561 float[] newPts = new float[ptsIndex]; 562 System.arraycopy(pts, 0, newPts, 0, ptsIndex); 563 pts = newPts; 564 } 565 Cairo.cairo_path_destroy(copy); 566 PathData result = new PathData(); 567 result.types = types; 568 result.points = pts; 569 return result; 570 } 571 572 /** 573 * Adds to the receiver a line from the current point to 574 * the point specified by (x, y). 575 * 576 * @param x the x coordinate of the end of the line to add 577 * @param y the y coordinate of the end of the line to add 578 * 579 * @exception SWTException <ul> 580 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 581 * </ul> 582 */ 583 public void lineTo(float x, float y) { 584 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 585 if (!moved) { 586 double currentX = 0, currentY = 0; 587 Cairo.cairo_get_current_point(handle, ¤tX, ¤tY); 588 Cairo.cairo_move_to(handle, currentX, currentY); 589 moved = true; 590 } 591 Cairo.cairo_line_to(handle, x, y); 592 closed = false; 593 } 594 595 /** 596 * Sets the current point of the receiver to the point 597 * specified by (x, y). Note that this starts a new 598 * sub path. 599 * 600 * @param x the x coordinate of the new end point 601 * @param y the y coordinate of the new end point 602 * 603 * @exception SWTException <ul> 604 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 605 * </ul> 606 */ 607 public void moveTo(float x, float y) { 608 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 609 /* 610 * Bug in Cairo. If cairo_move_to() is not called at the 611 * begining of a subpath, the first cairo_line_to() or 612 * cairo_curve_to() segment do not output anything. The fix 613 * is to detect that the app did not call cairo_move_to() 614 * before those calls and call it explicitly. 615 */ 616 moved = true; 617 Cairo.cairo_move_to(handle, x, y); 618 closed = true; 619 } 620 621 /** 622 * Adds to the receiver a quadratic curve based on the parameters. 623 * 624 * @param cx the x coordinate of the control point of the spline 625 * @param cy the y coordinate of the control point of the spline 626 * @param x the x coordinate of the end point of the spline 627 * @param y the y coordinate of the end point of the spline 628 * 629 * @exception SWTException <ul> 630 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 631 * </ul> 632 */ 633 public void quadTo(float cx, float cy, float x, float y) { 634 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); 635 double currentX = 0, currentY = 0; 636 Cairo.cairo_get_current_point(handle, ¤tX, ¤tY); 637 if (!moved) { 638 Cairo.cairo_move_to(handle, currentX, currentY); 639 moved = true; 640 } 641 float x0 = cast(float)currentX; 642 float y0 = cast(float)currentY; 643 float cx1 = x0 + 2 * (cx - x0) / 3; 644 float cy1 = y0 + 2 * (cy - y0) / 3; 645 float cx2 = cx1 + (x - x0) / 3; 646 float cy2 = cy1 + (y - y0) / 3; 647 Cairo.cairo_curve_to(handle, cx1, cy1, cx2, cy2, x, y); 648 closed = false; 649 } 650 651 override 652 void destroy() { 653 Cairo.cairo_destroy(handle); 654 handle = null; 655 } 656 657 void init_(PathData data) { 658 byte[] types = data.types; 659 float[] points = data.points; 660 for (int i = 0, j = 0; i < types.length; i++) { 661 switch (types[i]) { 662 case SWT.PATH_MOVE_TO: 663 moveTo(points[j++], points[j++]); 664 break; 665 case SWT.PATH_LINE_TO: 666 lineTo(points[j++], points[j++]); 667 break; 668 case SWT.PATH_CUBIC_TO: 669 cubicTo(points[j++], points[j++], points[j++], points[j++], points[j++], points[j++]); 670 break; 671 case SWT.PATH_QUAD_TO: 672 quadTo(points[j++], points[j++], points[j++], points[j++]); 673 break; 674 case SWT.PATH_CLOSE: 675 close(); 676 break; 677 default: 678 dispose(); 679 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 680 } 681 } 682 } 683 684 /** 685 * Returns <code>true</code> if the Path has been disposed, 686 * and <code>false</code> otherwise. 687 * <p> 688 * This method gets the dispose state for the Path. 689 * When a Path has been disposed, it is an error to 690 * invoke any other method using the Path. 691 * 692 * @return <code>true</code> when the Path is disposed, and <code>false</code> otherwise 693 */ 694 public override bool isDisposed() { 695 return handle is null; 696 } 697 698 /** 699 * Returns a string containing a concise, human-readable 700 * description of the receiver. 701 * 702 * @return a string representation of the receiver 703 */ 704 public override String toString() { 705 if (isDisposed()) return "Path {*DISPOSED*}"; 706 return Format( "Path {{{}}", handle ); 707 } 708 709 }