path.c revision 9ea4936a36f5011695a3996c63cfad6b480b3e49
1/************************************************************************** 2 * 3 * Copyright 2009 VMware, Inc. All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 **************************************************************************/ 26 27#include "path.h" 28 29#include "stroker.h" 30#include "polygon.h" 31#include "bezier.h" 32#include "matrix.h" 33#include "vg_context.h" 34#include "util_array.h" 35#include "arc.h" 36#include "path_utils.h" 37#include "paint.h" 38#include "shader.h" 39 40#include "util/u_memory.h" 41 42#include <assert.h> 43 44#define DEBUG_PATH 0 45 46struct path { 47 struct vg_object base; 48 VGbitfield caps; 49 VGboolean dirty; 50 VGboolean dirty_stroke; 51 52 VGPathDatatype datatype; 53 54 VGfloat scale; 55 VGfloat bias; 56 57 VGint num_segments; 58 59 struct array * segments; 60 struct array * control_points; 61 62 struct { 63 struct polygon_array polygon_array; 64 struct matrix matrix; 65 } fill_polys; 66 67 struct { 68 struct path *path; 69 struct matrix matrix; 70 VGfloat stroke_width; 71 VGfloat miter_limit; 72 VGCapStyle cap_style; 73 VGJoinStyle join_style; 74 } stroked; 75}; 76 77 78static INLINE void data_at(void **data, 79 struct path *p, 80 VGint start, VGint count, 81 VGfloat *out) 82{ 83 VGPathDatatype dt = p->datatype; 84 VGint i; 85 VGint end = start + count; 86 VGfloat *itr = out; 87 88 switch(dt) { 89 case VG_PATH_DATATYPE_S_8: { 90 VGbyte **bdata = (VGbyte **)data; 91 for (i = start; i < end; ++i) { 92 *itr = (*bdata)[i]; 93 ++itr; 94 } 95 *bdata += count; 96 } 97 break; 98 case VG_PATH_DATATYPE_S_16: { 99 VGshort **bdata = (VGshort **)data; 100 for (i = start; i < end; ++i) { 101 *itr = (*bdata)[i]; 102 ++itr; 103 } 104 *bdata += count; 105 } 106 break; 107 case VG_PATH_DATATYPE_S_32: { 108 VGint **bdata = (VGint **)data; 109 for (i = start; i < end; ++i) { 110 *itr = (*bdata)[i]; 111 ++itr; 112 } 113 *bdata += count; 114 } 115 break; 116 case VG_PATH_DATATYPE_F: { 117 VGfloat **fdata = (VGfloat **)data; 118 for (i = start; i < end; ++i) { 119 *itr = (*fdata)[i]; 120 ++itr; 121 } 122 *fdata += count; 123 } 124 break; 125 default: 126 debug_assert(!"Unknown path datatype!"); 127 } 128} 129 130 131void vg_float_to_datatype(VGPathDatatype datatype, 132 VGubyte *common_data, 133 const VGfloat *data, 134 VGint num_coords) 135{ 136 VGint i; 137 switch(datatype) { 138 case VG_PATH_DATATYPE_S_8: { 139 for (i = 0; i < num_coords; ++i) { 140 common_data[i] = (VGubyte)data[i]; 141 } 142 } 143 break; 144 case VG_PATH_DATATYPE_S_16: { 145 VGshort *buf = (VGshort*)common_data; 146 for (i = 0; i < num_coords; ++i) { 147 buf[i] = (VGshort)data[i]; 148 } 149 } 150 break; 151 case VG_PATH_DATATYPE_S_32: { 152 VGint *buf = (VGint*)common_data; 153 for (i = 0; i < num_coords; ++i) { 154 buf[i] = (VGint)data[i]; 155 } 156 } 157 break; 158 case VG_PATH_DATATYPE_F: { 159 memcpy(common_data, data, sizeof(VGfloat) * num_coords); 160 } 161 break; 162 default: 163 debug_assert(!"Unknown path datatype!"); 164 } 165} 166 167static void coords_adjust_by_scale_bias(struct path *p, 168 void *pdata, VGint num_coords, 169 VGfloat scale, VGfloat bias, 170 VGPathDatatype datatype) 171{ 172 VGfloat data[8]; 173 void *coords = (VGfloat *)pdata; 174 VGubyte *common_data = (VGubyte *)pdata; 175 VGint size_dst = size_for_datatype(datatype); 176 VGint i; 177 178 for (i = 0; i < num_coords; ++i) { 179 data_at(&coords, p, 0, 1, data); 180 data[0] = data[0] * scale + bias; 181 vg_float_to_datatype(datatype, common_data, data, 1); 182 common_data += size_dst; 183 } 184} 185 186struct path * path_create(VGPathDatatype dt, VGfloat scale, VGfloat bias, 187 VGint segmentCapacityHint, 188 VGint coordCapacityHint, 189 VGbitfield capabilities) 190{ 191 struct path *path = CALLOC_STRUCT(path); 192 193 vg_init_object(&path->base, vg_current_context(), VG_OBJECT_PATH); 194 path->caps = capabilities & VG_PATH_CAPABILITY_ALL; 195 vg_context_add_object(vg_current_context(), VG_OBJECT_PATH, path); 196 197 path->datatype = dt; 198 path->scale = scale; 199 path->bias = bias; 200 201 path->segments = array_create(size_for_datatype(VG_PATH_DATATYPE_S_8)); 202 path->control_points = array_create(size_for_datatype(dt)); 203 204 path->dirty = VG_TRUE; 205 path->dirty_stroke = VG_TRUE; 206 207 return path; 208} 209 210void path_destroy(struct path *p) 211{ 212 vg_context_remove_object(vg_current_context(), VG_OBJECT_PATH, p); 213 214 array_destroy(p->segments); 215 array_destroy(p->control_points); 216 array_destroy(p->fill_polys.polygon_array.array); 217 218 if (p->stroked.path) 219 path_destroy(p->stroked.path); 220 221 FREE(p); 222} 223 224VGbitfield path_capabilities(struct path *p) 225{ 226 return p->caps; 227} 228 229void path_set_capabilities(struct path *p, VGbitfield bf) 230{ 231 p->caps = (bf & VG_PATH_CAPABILITY_ALL); 232} 233 234void path_append_data(struct path *p, 235 VGint numSegments, 236 const VGubyte * pathSegments, 237 const void * pathData) 238{ 239 VGint old_segments = p->num_segments; 240 VGint num_new_coords = num_elements_for_segments(pathSegments, numSegments); 241 array_append_data(p->segments, pathSegments, numSegments); 242 array_append_data(p->control_points, pathData, num_new_coords); 243 244 p->num_segments += numSegments; 245 if (!floatsEqual(p->scale, 1.f) || !floatsEqual(p->bias, 0.f)) { 246 VGubyte *coords = (VGubyte*)p->control_points->data; 247 coords_adjust_by_scale_bias(p, 248 coords + old_segments * p->control_points->datatype_size, 249 num_new_coords, 250 p->scale, p->bias, p->datatype); 251 } 252 p->dirty = VG_TRUE; 253 p->dirty_stroke = VG_TRUE; 254} 255 256VGint path_num_segments(struct path *p) 257{ 258 return p->num_segments; 259} 260 261static INLINE void map_if_relative(VGfloat ox, VGfloat oy, 262 VGboolean relative, 263 VGfloat *x, VGfloat *y) 264{ 265 if (relative) { 266 if (x) 267 *x += ox; 268 if (y) 269 *y += oy; 270 } 271} 272 273static INLINE void close_polygon(struct polygon *current, 274 VGfloat sx, VGfloat sy, 275 VGfloat ox, VGfloat oy, 276 struct matrix *matrix) 277{ 278 if (!floatsEqual(sx, ox) || 279 !floatsEqual(sy, oy)) { 280 VGfloat x0 = sx; 281 VGfloat y0 = sy; 282 matrix_map_point(matrix, x0, y0, &x0, &y0); 283 polygon_vertex_append(current, x0, y0); 284 } 285} 286 287static void convert_path(struct path *p, 288 VGPathDatatype to, 289 void *dst, 290 VGint num_coords) 291{ 292 VGfloat data[8]; 293 void *coords = (VGfloat *)p->control_points->data; 294 VGubyte *common_data = (VGubyte *)dst; 295 VGint size_dst = size_for_datatype(to); 296 VGint i; 297 298 for (i = 0; i < num_coords; ++i) { 299 data_at(&coords, p, 0, 1, data); 300 vg_float_to_datatype(to, common_data, data, 1); 301 common_data += size_dst; 302 } 303} 304 305 306static void polygon_array_calculate_bounds( struct polygon_array *polyarray ) 307{ 308 struct array *polys = polyarray->array; 309 VGfloat min_x, max_x; 310 VGfloat min_y, max_y; 311 VGfloat bounds[4]; 312 unsigned i; 313 314 assert(polys); 315 316 if (!polys->num_elements) { 317 polyarray->min_x = 0.0f; 318 polyarray->min_y = 0.0f; 319 polyarray->max_x = 0.0f; 320 polyarray->max_y = 0.0f; 321 return; 322 } 323 324 polygon_bounding_rect((((struct polygon**)polys->data)[0]), bounds); 325 min_x = bounds[0]; 326 min_y = bounds[1]; 327 max_x = bounds[0] + bounds[2]; 328 max_y = bounds[1] + bounds[3]; 329 for (i = 1; i < polys->num_elements; ++i) { 330 struct polygon *p = (((struct polygon**)polys->data)[i]); 331 polygon_bounding_rect(p, bounds); 332 min_x = MIN2(min_x, bounds[0]); 333 min_y = MIN2(min_y, bounds[1]); 334 max_x = MAX2(max_x, bounds[0] + bounds[2]); 335 max_y = MAX2(max_y, bounds[1] + bounds[3]); 336 } 337 338 polyarray->min_x = min_x; 339 polyarray->min_y = min_y; 340 polyarray->max_x = max_x; 341 polyarray->max_y = max_y; 342} 343 344 345static struct polygon_array * path_get_fill_polygons(struct path *p, struct matrix *matrix) 346{ 347 VGint i; 348 struct polygon *current = 0; 349 VGfloat sx, sy, px, py, ox, oy; 350 VGfloat x0, y0, x1, y1, x2, y2, x3, y3; 351 VGfloat data[8]; 352 void *coords = (VGfloat *)p->control_points->data; 353 struct array *array; 354 355 if (p->fill_polys.polygon_array.array) 356 { 357 if (memcmp( &p->fill_polys.matrix, 358 matrix, 359 sizeof *matrix ) == 0 && p->dirty == VG_FALSE) 360 { 361 return &p->fill_polys.polygon_array; 362 } 363 else { 364 array_destroy( p->fill_polys.polygon_array.array ); 365 p->fill_polys.polygon_array.array = NULL; 366 } 367 } 368 369 array = array_create(sizeof(struct array*)); 370 371 sx = sy = px = py = ox = oy = 0.f; 372 373 if (p->num_segments) 374 current = polygon_create(32); 375 376 for (i = 0; i < p->num_segments; ++i) { 377 VGubyte segment = ((VGubyte*)(p->segments->data))[i]; 378 VGint command = SEGMENT_COMMAND(segment); 379 VGboolean relative = SEGMENT_ABS_REL(segment); 380 381 switch(command) { 382 case VG_CLOSE_PATH: 383 close_polygon(current, sx, sy, ox, oy, matrix); 384 ox = sx; 385 oy = sy; 386 break; 387 case VG_MOVE_TO: 388 if (current && polygon_vertex_count(current) > 0) { 389 /* add polygon */ 390 close_polygon(current, sx, sy, ox, oy, matrix); 391 array_append_data(array, ¤t, 1); 392 current = polygon_create(32); 393 } 394 data_at(&coords, p, 0, 2, data); 395 x0 = data[0]; 396 y0 = data[1]; 397 map_if_relative(ox, oy, relative, &x0, &y0); 398 sx = x0; 399 sy = y0; 400 ox = x0; 401 oy = y0; 402 px = x0; 403 py = y0; 404 matrix_map_point(matrix, x0, y0, &x0, &y0); 405 polygon_vertex_append(current, x0, y0); 406 break; 407 case VG_LINE_TO: 408 data_at(&coords, p, 0, 2, data); 409 x0 = data[0]; 410 y0 = data[1]; 411 map_if_relative(ox, oy, relative, &x0, &y0); 412 ox = x0; 413 oy = y0; 414 px = x0; 415 py = y0; 416 matrix_map_point(matrix, x0, y0, &x0, &y0); 417 polygon_vertex_append(current, x0, y0); 418 break; 419 case VG_HLINE_TO: 420 data_at(&coords, p, 0, 1, data); 421 x0 = data[0]; 422 y0 = oy; 423 map_if_relative(ox, oy, relative, &x0, 0); 424 ox = x0; 425 px = x0; 426 py = y0; 427 matrix_map_point(matrix, x0, y0, &x0, &y0); 428 polygon_vertex_append(current, x0, y0); 429 break; 430 case VG_VLINE_TO: 431 data_at(&coords, p, 0, 1, data); 432 x0 = ox; 433 y0 = data[0]; 434 map_if_relative(ox, oy, relative, 0, &y0); 435 oy = y0; 436 px = x0; 437 py = y0; 438 matrix_map_point(matrix, x0, y0, &x0, &y0); 439 polygon_vertex_append(current, x0, y0); 440 break; 441 case VG_CUBIC_TO: { 442 struct bezier bezier; 443 data_at(&coords, p, 0, 6, data); 444 x0 = ox; 445 y0 = oy; 446 x1 = data[0]; 447 y1 = data[1]; 448 x2 = data[2]; 449 y2 = data[3]; 450 x3 = data[4]; 451 y3 = data[5]; 452 map_if_relative(ox, oy, relative, &x1, &y1); 453 map_if_relative(ox, oy, relative, &x2, &y2); 454 map_if_relative(ox, oy, relative, &x3, &y3); 455 ox = x3; 456 oy = y3; 457 px = x2; 458 py = y2; 459 assert(matrix_is_affine(matrix)); 460 matrix_map_point(matrix, x0, y0, &x0, &y0); 461 matrix_map_point(matrix, x1, y1, &x1, &y1); 462 matrix_map_point(matrix, x2, y2, &x2, &y2); 463 matrix_map_point(matrix, x3, y3, &x3, &y3); 464 bezier_init(&bezier, x0, y0, x1, y1, 465 x2, y2, x3, y3); 466 bezier_add_to_polygon(&bezier, current); 467 } 468 break; 469 case VG_QUAD_TO: { 470 struct bezier bezier; 471 data_at(&coords, p, 0, 4, data); 472 x0 = ox; 473 y0 = oy; 474 x1 = data[0]; 475 y1 = data[1]; 476 x3 = data[2]; 477 y3 = data[3]; 478 map_if_relative(ox, oy, relative, &x1, &y1); 479 map_if_relative(ox, oy, relative, &x3, &y3); 480 px = x1; 481 py = y1; 482 { /* form a cubic out of it */ 483 x2 = (x3 + 2*x1) / 3.f; 484 y2 = (y3 + 2*y1) / 3.f; 485 x1 = (x0 + 2*x1) / 3.f; 486 y1 = (y0 + 2*y1) / 3.f; 487 } 488 ox = x3; 489 oy = y3; 490 assert(matrix_is_affine(matrix)); 491 matrix_map_point(matrix, x0, y0, &x0, &y0); 492 matrix_map_point(matrix, x1, y1, &x1, &y1); 493 matrix_map_point(matrix, x2, y2, &x2, &y2); 494 matrix_map_point(matrix, x3, y3, &x3, &y3); 495 bezier_init(&bezier, x0, y0, x1, y1, 496 x2, y2, x3, y3); 497 bezier_add_to_polygon(&bezier, current); 498 } 499 break; 500 case VG_SQUAD_TO: { 501 struct bezier bezier; 502 data_at(&coords, p, 0, 2, data); 503 x0 = ox; 504 y0 = oy; 505 x1 = 2*ox-px; 506 y1 = 2*oy-py; 507 x3 = data[0]; 508 y3 = data[1]; 509 map_if_relative(ox, oy, relative, &x3, &y3); 510 px = x1; 511 py = y1; 512 { /* form a cubic out of it */ 513 x2 = (x3 + 2*x1) / 3.f; 514 y2 = (y3 + 2*y1) / 3.f; 515 x1 = (x0 + 2*x1) / 3.f; 516 y1 = (y0 + 2*y1) / 3.f; 517 } 518 ox = x3; 519 oy = y3; 520 assert(matrix_is_affine(matrix)); 521 matrix_map_point(matrix, x0, y0, &x0, &y0); 522 matrix_map_point(matrix, x1, y1, &x1, &y1); 523 matrix_map_point(matrix, x2, y2, &x2, &y2); 524 matrix_map_point(matrix, x3, y3, &x3, &y3); 525 bezier_init(&bezier, x0, y0, x1, y1, 526 x2, y2, x3, y3); 527 bezier_add_to_polygon(&bezier, current); 528 } 529 break; 530 case VG_SCUBIC_TO: { 531 struct bezier bezier; 532 data_at(&coords, p, 0, 4, data); 533 x0 = ox; 534 y0 = oy; 535 x1 = 2*ox-px; 536 y1 = 2*oy-py; 537 x2 = data[0]; 538 y2 = data[1]; 539 x3 = data[2]; 540 y3 = data[3]; 541 map_if_relative(ox, oy, relative, &x2, &y2); 542 map_if_relative(ox, oy, relative, &x3, &y3); 543 ox = x3; 544 oy = y3; 545 px = x2; 546 py = y2; 547 assert(matrix_is_affine(matrix)); 548 matrix_map_point(matrix, x0, y0, &x0, &y0); 549 matrix_map_point(matrix, x1, y1, &x1, &y1); 550 matrix_map_point(matrix, x2, y2, &x2, &y2); 551 matrix_map_point(matrix, x3, y3, &x3, &y3); 552 bezier_init(&bezier, x0, y0, x1, y1, 553 x2, y2, x3, y3); 554 bezier_add_to_polygon(&bezier, current); 555 } 556 break; 557 case VG_SCCWARC_TO: 558 case VG_SCWARC_TO: 559 case VG_LCCWARC_TO: 560 case VG_LCWARC_TO: { 561 VGfloat rh, rv, rot; 562 struct arc arc; 563 564 data_at(&coords, p, 0, 5, data); 565 x0 = ox; 566 y0 = oy; 567 rh = data[0]; 568 rv = data[1]; 569 rot = data[2]; 570 x1 = data[3]; 571 y1 = data[4]; 572 map_if_relative(ox, oy, relative, &x1, &y1); 573#if 0 574 debug_printf("------- ARC (%f, %f), (%f, %f) %f, %f, %f\n", 575 x0, y0, x1, y1, rh, rv, rot); 576#endif 577 arc_init(&arc, command, x0, y0, x1, y1, 578 rh, rv, rot); 579 arc_add_to_polygon(&arc, current, 580 matrix); 581 ox = x1; 582 oy = y1; 583 px = x1; 584 py = y1; 585 } 586 break; 587 default: 588 abort(); 589 assert(!"Unknown segment!"); 590 } 591 } 592 if (current) { 593 if (polygon_vertex_count(current) > 0) { 594 close_polygon(current, sx, sy, ox, oy, matrix); 595 array_append_data(array, ¤t, 1); 596 } else 597 polygon_destroy(current); 598 } 599 600 p->fill_polys.polygon_array.array = array; 601 p->fill_polys.matrix = *matrix; 602 603 polygon_array_calculate_bounds( &p->fill_polys.polygon_array ); 604 605 p->dirty = VG_FALSE; 606 607 return &p->fill_polys.polygon_array; 608} 609 610VGbyte path_datatype_size(struct path *p) 611{ 612 return size_for_datatype(p->datatype); 613} 614 615VGPathDatatype path_datatype(struct path *p) 616{ 617 return p->datatype; 618} 619 620VGfloat path_scale(struct path *p) 621{ 622 return p->scale; 623} 624 625VGfloat path_bias(struct path *p) 626{ 627 return p->bias; 628} 629 630VGint path_num_coords(struct path *p) 631{ 632 return num_elements_for_segments((VGubyte*)p->segments->data, 633 p->num_segments); 634} 635 636void path_modify_coords(struct path *p, 637 VGint startIndex, 638 VGint numSegments, 639 const void * pathData) 640{ 641 VGubyte *segments = (VGubyte*)(p->segments->data); 642 VGint count = num_elements_for_segments(&segments[startIndex], numSegments); 643 VGint start_cp = num_elements_for_segments(segments, startIndex); 644 645 array_change_data(p->control_points, pathData, start_cp, count); 646 coords_adjust_by_scale_bias(p, 647 ((VGubyte*)p->control_points->data) + 648 (startIndex * p->control_points->datatype_size), 649 path_num_coords(p), 650 p->scale, p->bias, p->datatype); 651 p->dirty = VG_TRUE; 652 p->dirty_stroke = VG_TRUE; 653} 654 655void path_for_each_segment(struct path *path, 656 path_for_each_cb cb, 657 void *user_data) 658{ 659 VGint i; 660 struct path_for_each_data p; 661 VGfloat data[8]; 662 void *coords = (VGfloat *)path->control_points->data; 663 664 p.coords = data; 665 p.sx = p.sy = p.px = p.py = p.ox = p.oy = 0.f; 666 p.user_data = user_data; 667 668 for (i = 0; i < path->num_segments; ++i) { 669 VGint command; 670 VGboolean relative; 671 672 p.segment = ((VGubyte*)(path->segments->data))[i]; 673 command = SEGMENT_COMMAND(p.segment); 674 relative = SEGMENT_ABS_REL(p.segment); 675 676 switch(command) { 677 case VG_CLOSE_PATH: 678 cb(path, &p); 679 break; 680 case VG_MOVE_TO: 681 data_at(&coords, path, 0, 2, data); 682 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]); 683 cb(path, &p); 684 p.sx = data[0]; 685 p.sy = data[1]; 686 p.ox = data[0]; 687 p.oy = data[1]; 688 p.px = data[0]; 689 p.py = data[1]; 690 break; 691 case VG_LINE_TO: 692 data_at(&coords, path, 0, 2, data); 693 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]); 694 cb(path, &p); 695 p.ox = data[0]; 696 p.oy = data[1]; 697 p.px = data[0]; 698 p.py = data[1]; 699 break; 700 case VG_HLINE_TO: 701 data_at(&coords, path, 0, 1, data); 702 map_if_relative(p.ox, p.oy, relative, &data[0], 0); 703 p.segment = VG_LINE_TO; 704 data[1] = p.oy; 705 cb(path, &p); 706 p.ox = data[0]; 707 p.oy = data[1]; 708 p.px = data[0]; 709 p.py = data[1]; 710 break; 711 case VG_VLINE_TO: 712 data_at(&coords, path, 0, 1, data); 713 map_if_relative(p.ox, p.oy, relative, 0, &data[0]); 714 p.segment = VG_LINE_TO; 715 data[1] = data[0]; 716 data[0] = p.ox; 717 cb(path, &p); 718 p.ox = data[0]; 719 p.oy = data[1]; 720 p.px = data[0]; 721 p.py = data[1]; 722 break; 723 case VG_CUBIC_TO: { 724 data_at(&coords, path, 0, 6, data); 725 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]); 726 map_if_relative(p.ox, p.oy, relative, &data[2], &data[3]); 727 map_if_relative(p.ox, p.oy, relative, &data[4], &data[5]); 728 cb(path, &p); 729 p.px = data[2]; 730 p.py = data[3]; 731 p.ox = data[4]; 732 p.oy = data[5]; 733 } 734 break; 735 case VG_QUAD_TO: { 736 data_at(&coords, path, 0, 4, data); 737 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]); 738 map_if_relative(p.ox, p.oy, relative, &data[2], &data[3]); 739 cb(path, &p); 740 p.px = data[0]; 741 p.py = data[1]; 742 p.ox = data[2]; 743 p.oy = data[3]; 744 } 745 break; 746 case VG_SQUAD_TO: { 747 data_at(&coords, path, 0, 2, data); 748 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]); 749 cb(path, &p); 750 p.px = 2*p.ox-p.px; 751 p.py = 2*p.oy-p.py; 752 p.ox = data[2]; 753 p.oy = data[3]; 754 } 755 break; 756 case VG_SCUBIC_TO: { 757 data_at(&coords, path, 0, 4, data); 758 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]); 759 map_if_relative(p.ox, p.oy, relative, &data[2], &data[3]); 760 cb(path, &p); 761 p.px = data[0]; 762 p.py = data[1]; 763 p.ox = data[2]; 764 p.oy = data[3]; 765 } 766 break; 767 case VG_SCCWARC_TO: 768 case VG_SCWARC_TO: 769 case VG_LCCWARC_TO: 770 case VG_LCWARC_TO: { 771 data_at(&coords, path, 0, 5, data); 772 map_if_relative(p.ox, p.oy, relative, &data[3], &data[4]); 773#if 0 774 debug_printf("------- ARC (%f, %f), (%f, %f) %f, %f, %f\n", 775 p.ox, p.oy, data[3], data[4], data[0], data[1], data[2]); 776#endif 777 cb(path, &p); 778 p.ox = data[3]; 779 p.oy = data[4]; 780 p.px = data[3]; 781 p.py = data[4]; 782 } 783 break; 784 default: 785 abort(); 786 assert(!"Unknown segment!"); 787 } 788 } 789} 790 791struct transform_data { 792 struct array *segments; 793 struct array *coords; 794 795 struct matrix *matrix; 796 797 VGPathDatatype datatype; 798}; 799 800static VGboolean transform_cb(struct path *p, 801 struct path_for_each_data *pd) 802{ 803 struct transform_data *td = (struct transform_data *)pd->user_data; 804 VGint num_coords = num_elements_for_segments(&pd->segment, 1); 805 VGubyte segment = SEGMENT_COMMAND(pd->segment);/* abs bit is 0 */ 806 VGfloat data[8]; 807 VGubyte common_data[sizeof(VGfloat)*8]; 808 809 memcpy(data, pd->coords, sizeof(VGfloat) * num_coords); 810 811 switch(segment) { 812 case VG_CLOSE_PATH: 813 break; 814 case VG_MOVE_TO: 815 matrix_map_point(td->matrix, 816 data[0], data[1], &data[0], &data[1]); 817 break; 818 case VG_LINE_TO: 819 matrix_map_point(td->matrix, 820 data[0], data[1], &data[0], &data[1]); 821 break; 822 case VG_HLINE_TO: 823 case VG_VLINE_TO: 824 assert(0); 825 break; 826 case VG_QUAD_TO: 827 matrix_map_point(td->matrix, 828 data[0], data[1], &data[0], &data[1]); 829 matrix_map_point(td->matrix, 830 data[2], data[3], &data[2], &data[3]); 831 break; 832 case VG_CUBIC_TO: 833 matrix_map_point(td->matrix, 834 data[0], data[1], &data[0], &data[1]); 835 matrix_map_point(td->matrix, 836 data[2], data[3], &data[2], &data[3]); 837 matrix_map_point(td->matrix, 838 data[4], data[5], &data[4], &data[5]); 839 break; 840 case VG_SQUAD_TO: 841 matrix_map_point(td->matrix, 842 data[0], data[1], &data[0], &data[1]); 843 break; 844 case VG_SCUBIC_TO: 845 matrix_map_point(td->matrix, 846 data[0], data[1], &data[0], &data[1]); 847 matrix_map_point(td->matrix, 848 data[2], data[3], &data[2], &data[3]); 849 break; 850 case VG_SCCWARC_TO: 851 case VG_SCWARC_TO: 852 case VG_LCCWARC_TO: 853 case VG_LCWARC_TO: { 854 struct arc arc; 855 struct path *path = path_create(td->datatype, 856 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL); 857 arc_init(&arc, segment, 858 pd->ox, pd->oy, data[3], data[4], 859 data[0], data[1], data[2]); 860 861 arc_to_path(&arc, path, td->matrix); 862 863 num_coords = path_num_coords(path); 864 865 array_append_data(td->segments, path->segments->data, 866 path->num_segments); 867 array_append_data(td->coords, path->control_points->data, 868 num_coords); 869 path_destroy(path); 870 871 return VG_TRUE; 872 } 873 break; 874 default: 875 break; 876 } 877 878 vg_float_to_datatype(td->datatype, common_data, data, num_coords); 879 880 array_append_data(td->segments, &pd->segment, 1); 881 array_append_data(td->coords, common_data, num_coords); 882 return VG_TRUE; 883} 884 885void path_transform(struct path *dst, struct path *src) 886{ 887 struct transform_data data; 888 struct vg_context *ctx = dst->base.ctx; 889 890 data.segments = dst->segments; 891 data.coords = dst->control_points; 892 data.matrix = &ctx->state.vg.path_user_to_surface_matrix; 893 data.datatype = dst->datatype; 894 895 path_for_each_segment(src, transform_cb, (void*)&data); 896 897 dst->num_segments = dst->segments->num_elements; 898 dst->dirty = VG_TRUE; 899 dst->dirty_stroke = VG_TRUE; 900} 901 902void path_append_path(struct path *dst, 903 struct path *src) 904{ 905 VGint num_coords = path_num_coords(src); 906 void *dst_data = malloc(size_for_datatype(dst->datatype) * num_coords); 907 array_append_data(dst->segments, 908 src->segments->data, 909 src->num_segments); 910 convert_path(src, dst->datatype, 911 dst_data, num_coords); 912 array_append_data(dst->control_points, 913 dst_data, 914 num_coords); 915 free(dst_data); 916 917 dst->num_segments += src->num_segments; 918 dst->dirty = VG_TRUE; 919 dst->dirty_stroke = VG_TRUE; 920} 921 922static INLINE VGboolean is_segment_arc(VGubyte segment) 923{ 924 VGubyte scommand = SEGMENT_COMMAND(segment); 925 return (scommand == VG_SCCWARC_TO || 926 scommand == VG_SCWARC_TO || 927 scommand == VG_LCCWARC_TO || 928 scommand == VG_LCWARC_TO); 929} 930 931struct path_iter_data { 932 struct path *path; 933 VGubyte segment; 934 void *coords; 935 VGfloat px, py, ox, oy, sx, sy; 936}; 937static INLINE VGubyte normalize_coords(struct path_iter_data *pd, 938 VGint *num_coords, 939 VGfloat *data) 940{ 941 VGint command = SEGMENT_COMMAND(pd->segment); 942 VGboolean relative = SEGMENT_ABS_REL(pd->segment); 943 944 switch(command) { 945 case VG_CLOSE_PATH: 946 *num_coords = 0; 947 pd->ox = pd->sx; 948 pd->oy = pd->sy; 949 return VG_CLOSE_PATH; 950 break; 951 case VG_MOVE_TO: 952 data_at(&pd->coords, pd->path, 0, 2, data); 953 map_if_relative(pd->ox, pd->oy, relative, &data[0], &data[1]); 954 pd->sx = data[0]; 955 pd->sy = data[1]; 956 pd->ox = data[0]; 957 pd->oy = data[1]; 958 pd->px = data[0]; 959 pd->py = data[1]; 960 *num_coords = 2; 961 return VG_MOVE_TO_ABS; 962 break; 963 case VG_LINE_TO: 964 data_at(&pd->coords, pd->path, 0, 2, data); 965 map_if_relative(pd->ox, pd->oy, relative, &data[0], &data[1]); 966 pd->ox = data[0]; 967 pd->oy = data[1]; 968 pd->px = data[0]; 969 pd->py = data[1]; 970 *num_coords = 2; 971 return VG_LINE_TO_ABS; 972 break; 973 case VG_HLINE_TO: 974 data_at(&pd->coords, pd->path, 0, 1, data); 975 map_if_relative(pd->ox, pd->oy, relative, &data[0], 0); 976 data[1] = pd->oy; 977 pd->ox = data[0]; 978 pd->oy = data[1]; 979 pd->px = data[0]; 980 pd->py = data[1]; 981 *num_coords = 2; 982 return VG_LINE_TO_ABS; 983 break; 984 case VG_VLINE_TO: 985 data_at(&pd->coords, pd->path, 0, 1, data); 986 map_if_relative(pd->ox, pd->oy, relative, 0, &data[0]); 987 data[1] = data[0]; 988 data[0] = pd->ox; 989 pd->ox = data[0]; 990 pd->oy = data[1]; 991 pd->px = data[0]; 992 pd->py = data[1]; 993 *num_coords = 2; 994 return VG_LINE_TO_ABS; 995 break; 996 case VG_CUBIC_TO: { 997 data_at(&pd->coords, pd->path, 0, 6, data); 998 map_if_relative(pd->ox, pd->oy, relative, &data[0], &data[1]); 999 map_if_relative(pd->ox, pd->oy, relative, &data[2], &data[3]); 1000 map_if_relative(pd->ox, pd->oy, relative, &data[4], &data[5]); 1001 pd->px = data[2]; 1002 pd->py = data[3]; 1003 pd->ox = data[4]; 1004 pd->oy = data[5]; 1005 *num_coords = 6; 1006 return VG_CUBIC_TO_ABS; 1007 } 1008 break; 1009 case VG_QUAD_TO: { 1010 VGfloat x0, y0, x1, y1, x2, y2, x3, y3; 1011 data_at(&pd->coords, pd->path, 0, 4, data); 1012 x0 = pd->ox; 1013 y0 = pd->oy; 1014 x1 = data[0]; 1015 y1 = data[1]; 1016 x3 = data[2]; 1017 y3 = data[3]; 1018 map_if_relative(pd->ox, pd->oy, relative, &x1, &y1); 1019 map_if_relative(pd->ox, pd->oy, relative, &x3, &y3); 1020 pd->px = x1; 1021 pd->py = y1; 1022 { /* form a cubic out of it */ 1023 x2 = (x3 + 2*x1) / 3.f; 1024 y2 = (y3 + 2*y1) / 3.f; 1025 x1 = (x0 + 2*x1) / 3.f; 1026 y1 = (y0 + 2*y1) / 3.f; 1027 } 1028 pd->ox = x3; 1029 pd->oy = y3; 1030 data[0] = x1; 1031 data[1] = y1; 1032 data[2] = x2; 1033 data[3] = y2; 1034 data[4] = x3; 1035 data[5] = y3; 1036 *num_coords = 6; 1037 return VG_CUBIC_TO_ABS; 1038 } 1039 break; 1040 case VG_SQUAD_TO: { 1041 VGfloat x0, y0, x1, y1, x2, y2, x3, y3; 1042 data_at(&pd->coords, pd->path, 0, 2, data); 1043 x0 = pd->ox; 1044 y0 = pd->oy; 1045 x1 = 2 * pd->ox - pd->px; 1046 y1 = 2 * pd->oy - pd->py; 1047 x3 = data[0]; 1048 y3 = data[1]; 1049 map_if_relative(pd->ox, pd->oy, relative, &x3, &y3); 1050 pd->px = x1; 1051 pd->py = y1; 1052 { /* form a cubic out of it */ 1053 x2 = (x3 + 2*x1) / 3.f; 1054 y2 = (y3 + 2*y1) / 3.f; 1055 x1 = (x0 + 2*x1) / 3.f; 1056 y1 = (y0 + 2*y1) / 3.f; 1057 } 1058 pd->ox = x3; 1059 pd->oy = y3; 1060 data[0] = x1; 1061 data[1] = y1; 1062 data[2] = x2; 1063 data[3] = y2; 1064 data[4] = x3; 1065 data[5] = y3; 1066 *num_coords = 6; 1067 return VG_CUBIC_TO_ABS; 1068 } 1069 break; 1070 case VG_SCUBIC_TO: { 1071 VGfloat x0, y0, x1, y1, x2, y2, x3, y3; 1072 data_at(&pd->coords, pd->path, 0, 4, data); 1073 x0 = pd->ox; 1074 y0 = pd->oy; 1075 x1 = 2*pd->ox-pd->px; 1076 y1 = 2*pd->oy-pd->py; 1077 x2 = data[0]; 1078 y2 = data[1]; 1079 x3 = data[2]; 1080 y3 = data[3]; 1081 map_if_relative(pd->ox, pd->oy, relative, &x2, &y2); 1082 map_if_relative(pd->ox, pd->oy, relative, &x3, &y3); 1083 pd->ox = x3; 1084 pd->oy = y3; 1085 pd->px = x2; 1086 pd->py = y2; 1087 data[0] = x1; 1088 data[1] = y1; 1089 data[2] = x2; 1090 data[3] = y2; 1091 data[4] = x3; 1092 data[5] = y3; 1093 *num_coords = 6; 1094 return VG_CUBIC_TO_ABS; 1095 } 1096 break; 1097 case VG_SCCWARC_TO: 1098 case VG_SCWARC_TO: 1099 case VG_LCCWARC_TO: 1100 case VG_LCWARC_TO: { 1101 data_at(&pd->coords, pd->path, 0, 5, data); 1102 map_if_relative(pd->ox, pd->oy, relative, &data[3], &data[4]); 1103 pd->ox = data[3]; 1104 pd->oy = data[4]; 1105 pd->px = data[3]; 1106 pd->py = data[4]; 1107 *num_coords = 5; 1108 return command | VG_ABSOLUTE; 1109 } 1110 break; 1111 default: 1112 abort(); 1113 assert(!"Unknown segment!"); 1114 } 1115} 1116 1117static void linearly_interpolate(VGfloat *result, 1118 const VGfloat *start, 1119 const VGfloat *end, 1120 VGfloat amount, 1121 VGint number) 1122{ 1123 VGint i; 1124 for (i = 0; i < number; ++i) { 1125 result[i] = start[i] + (end[i] - start[i]) * amount; 1126 } 1127} 1128 1129VGboolean path_interpolate(struct path *dst, 1130 struct path *start, struct path *end, 1131 VGfloat amount) 1132{ 1133 /* temporary path that we can discard if it will turn 1134 * out that start is not compatible with end */ 1135 struct path *res_path = path_create(dst->datatype, 1136 1.0, 0.0, 1137 0, 0, dst->caps); 1138 VGint i; 1139 VGfloat start_coords[8]; 1140 VGfloat end_coords[8]; 1141 VGfloat results[8]; 1142 VGubyte common_data[sizeof(VGfloat)*8]; 1143 struct path_iter_data start_iter, end_iter; 1144 1145 memset(&start_iter, 0, sizeof(struct path_iter_data)); 1146 memset(&end_iter, 0, sizeof(struct path_iter_data)); 1147 1148 start_iter.path = start; 1149 start_iter.coords = start->control_points->data; 1150 end_iter.path = end; 1151 end_iter.coords = end->control_points->data; 1152 1153 for (i = 0; i < start->num_segments; ++i) { 1154 VGubyte segment; 1155 VGubyte ssegment, esegment; 1156 VGint snum_coords, enum_coords; 1157 start_iter.segment = ((VGubyte*)(start->segments->data))[i]; 1158 end_iter.segment = ((VGubyte*)(end->segments->data))[i]; 1159 1160 ssegment = normalize_coords(&start_iter, &snum_coords, 1161 start_coords); 1162 esegment = normalize_coords(&end_iter, &enum_coords, 1163 end_coords); 1164 1165 if (is_segment_arc(ssegment)) { 1166 if (!is_segment_arc(esegment)) { 1167 path_destroy(res_path); 1168 return VG_FALSE; 1169 } 1170 if (amount > 0.5) 1171 segment = esegment; 1172 else 1173 segment = ssegment; 1174 } else if (is_segment_arc(esegment)) { 1175 path_destroy(res_path); 1176 return VG_FALSE; 1177 } 1178 else if (ssegment != esegment) { 1179 path_destroy(res_path); 1180 return VG_FALSE; 1181 } 1182 else 1183 segment = ssegment; 1184 1185 linearly_interpolate(results, start_coords, end_coords, 1186 amount, snum_coords); 1187 vg_float_to_datatype(dst->datatype, common_data, results, snum_coords); 1188 path_append_data(res_path, 1, &segment, common_data); 1189 } 1190 1191 path_append_path(dst, res_path); 1192 path_destroy(res_path); 1193 1194 dst->dirty = VG_TRUE; 1195 dst->dirty_stroke = VG_TRUE; 1196 1197 return VG_TRUE; 1198} 1199 1200void path_clear(struct path *p, VGbitfield capabilities) 1201{ 1202 path_set_capabilities(p, capabilities); 1203 array_destroy(p->segments); 1204 array_destroy(p->control_points); 1205 p->segments = array_create(size_for_datatype(VG_PATH_DATATYPE_S_8)); 1206 p->control_points = array_create(size_for_datatype(p->datatype)); 1207 p->num_segments = 0; 1208 p->dirty = VG_TRUE; 1209 p->dirty_stroke = VG_TRUE; 1210} 1211 1212struct path * path_create_stroke(struct path *p, 1213 struct matrix *matrix) 1214{ 1215 VGint i; 1216 VGfloat sx, sy, px, py, ox, oy; 1217 VGfloat x0, y0, x1, y1, x2, y2, x3, y3; 1218 VGfloat data[8]; 1219 void *coords = (VGfloat *)p->control_points->data; 1220 int dashed = (p->base.ctx->state.vg.stroke.dash_pattern_num ? 1 : 0); 1221 struct dash_stroker stroker; 1222 struct vg_state *vg_state = &p->base.ctx->state.vg; 1223 1224 if (p->stroked.path) 1225 { 1226 /* ### compare the dash patterns to see if we can cache them. 1227 * for now we simply always bail out if the path is dashed. 1228 */ 1229 if (memcmp( &p->stroked.matrix, 1230 matrix, 1231 sizeof *matrix ) == 0 && 1232 !dashed && !p->dirty_stroke && 1233 floatsEqual(p->stroked.stroke_width, vg_state->stroke.line_width.f) && 1234 floatsEqual(p->stroked.miter_limit, vg_state->stroke.miter_limit.f) && 1235 p->stroked.cap_style == vg_state->stroke.cap_style && 1236 p->stroked.join_style == vg_state->stroke.join_style) 1237 { 1238 return p->stroked.path; 1239 } 1240 else { 1241 path_destroy( p->stroked.path ); 1242 p->stroked.path = NULL; 1243 } 1244 } 1245 1246 1247 sx = sy = px = py = ox = oy = 0.f; 1248 1249 if (dashed) 1250 dash_stroker_init((struct stroker *)&stroker, vg_state); 1251 else 1252 stroker_init((struct stroker *)&stroker, vg_state); 1253 1254 stroker_begin((struct stroker *)&stroker); 1255 1256 for (i = 0; i < p->num_segments; ++i) { 1257 VGubyte segment = ((VGubyte*)(p->segments->data))[i]; 1258 VGint command = SEGMENT_COMMAND(segment); 1259 VGboolean relative = SEGMENT_ABS_REL(segment); 1260 1261 switch(command) { 1262 case VG_CLOSE_PATH: { 1263 VGfloat x0 = sx; 1264 VGfloat y0 = sy; 1265 matrix_map_point(matrix, x0, y0, &x0, &y0); 1266 stroker_line_to((struct stroker *)&stroker, x0, y0); 1267 } 1268 break; 1269 case VG_MOVE_TO: 1270 data_at(&coords, p, 0, 2, data); 1271 x0 = data[0]; 1272 y0 = data[1]; 1273 map_if_relative(ox, oy, relative, &x0, &y0); 1274 sx = x0; 1275 sy = y0; 1276 ox = x0; 1277 oy = y0; 1278 px = x0; 1279 py = y0; 1280 matrix_map_point(matrix, x0, y0, &x0, &y0); 1281 stroker_move_to((struct stroker *)&stroker, x0, y0); 1282 break; 1283 case VG_LINE_TO: 1284 data_at(&coords, p, 0, 2, data); 1285 x0 = data[0]; 1286 y0 = data[1]; 1287 map_if_relative(ox, oy, relative, &x0, &y0); 1288 ox = x0; 1289 oy = y0; 1290 px = x0; 1291 py = y0; 1292 matrix_map_point(matrix, x0, y0, &x0, &y0); 1293 stroker_line_to((struct stroker *)&stroker, x0, y0); 1294 break; 1295 case VG_HLINE_TO: 1296 data_at(&coords, p, 0, 1, data); 1297 x0 = data[0]; 1298 y0 = oy; 1299 map_if_relative(ox, oy, relative, &x0, 0); 1300 ox = x0; 1301 px = x0; 1302 py = y0; 1303 matrix_map_point(matrix, x0, y0, &x0, &y0); 1304 stroker_line_to((struct stroker *)&stroker, x0, y0); 1305 break; 1306 case VG_VLINE_TO: 1307 data_at(&coords, p, 0, 1, data); 1308 x0 = ox; 1309 y0 = data[0]; 1310 map_if_relative(ox, oy, relative, 0, &y0); 1311 oy = y0; 1312 px = x0; 1313 py = y0; 1314 matrix_map_point(matrix, x0, y0, &x0, &y0); 1315 stroker_line_to((struct stroker *)&stroker, x0, y0); 1316 break; 1317 case VG_CUBIC_TO: { 1318 data_at(&coords, p, 0, 6, data); 1319 x0 = ox; 1320 y0 = oy; 1321 x1 = data[0]; 1322 y1 = data[1]; 1323 x2 = data[2]; 1324 y2 = data[3]; 1325 x3 = data[4]; 1326 y3 = data[5]; 1327 map_if_relative(ox, oy, relative, &x1, &y1); 1328 map_if_relative(ox, oy, relative, &x2, &y2); 1329 map_if_relative(ox, oy, relative, &x3, &y3); 1330 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) && 1331 floatsEqual(x1, x2) && floatsEqual(y1, y2) && 1332 floatsEqual(x2, x3) && floatsEqual(y2, y3)) { 1333 /*ignore the empty segment */ 1334 continue; 1335 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) { 1336 /* if dup vertex, emit a line */ 1337 ox = x3; 1338 oy = y3; 1339 matrix_map_point(matrix, x3, y3, &x3, &y3); 1340 stroker_line_to((struct stroker *)&stroker, x3, y3); 1341 continue; 1342 } 1343 ox = x3; 1344 oy = y3; 1345 px = x2; 1346 py = y2; 1347 assert(matrix_is_affine(matrix)); 1348 matrix_map_point(matrix, x0, y0, &x0, &y0); 1349 matrix_map_point(matrix, x1, y1, &x1, &y1); 1350 matrix_map_point(matrix, x2, y2, &x2, &y2); 1351 matrix_map_point(matrix, x3, y3, &x3, &y3); 1352 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3); 1353 } 1354 break; 1355 case VG_QUAD_TO: { 1356 data_at(&coords, p, 0, 4, data); 1357 x0 = ox; 1358 y0 = oy; 1359 x1 = data[0]; 1360 y1 = data[1]; 1361 x3 = data[2]; 1362 y3 = data[3]; 1363 map_if_relative(ox, oy, relative, &x1, &y1); 1364 map_if_relative(ox, oy, relative, &x3, &y3); 1365 px = x1; 1366 py = y1; 1367 { /* form a cubic out of it */ 1368 x2 = (x3 + 2*x1) / 3.f; 1369 y2 = (y3 + 2*y1) / 3.f; 1370 x1 = (x0 + 2*x1) / 3.f; 1371 y1 = (y0 + 2*y1) / 3.f; 1372 } 1373 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) && 1374 floatsEqual(x1, x2) && floatsEqual(y1, y2) && 1375 floatsEqual(x2, x3) && floatsEqual(y2, y3)) { 1376 /*ignore the empty segment */ 1377 continue; 1378 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) { 1379 /* if dup vertex, emit a line */ 1380 ox = x3; 1381 oy = y3; 1382 matrix_map_point(matrix, x3, y3, &x3, &y3); 1383 stroker_line_to((struct stroker *)&stroker, x3, y3); 1384 continue; 1385 } 1386 ox = x3; 1387 oy = y3; 1388 assert(matrix_is_affine(matrix)); 1389 matrix_map_point(matrix, x0, y0, &x0, &y0); 1390 matrix_map_point(matrix, x1, y1, &x1, &y1); 1391 matrix_map_point(matrix, x2, y2, &x2, &y2); 1392 matrix_map_point(matrix, x3, y3, &x3, &y3); 1393 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3); 1394 } 1395 break; 1396 case VG_SQUAD_TO: { 1397 data_at(&coords, p, 0, 2, data); 1398 x0 = ox; 1399 y0 = oy; 1400 x1 = 2*ox-px; 1401 y1 = 2*oy-py; 1402 x3 = data[0]; 1403 y3 = data[1]; 1404 map_if_relative(ox, oy, relative, &x3, &y3); 1405 px = x1; 1406 py = y1; 1407 { /* form a cubic out of it */ 1408 x2 = (x3 + 2*x1) / 3.f; 1409 y2 = (y3 + 2*y1) / 3.f; 1410 x1 = (x0 + 2*x1) / 3.f; 1411 y1 = (y0 + 2*y1) / 3.f; 1412 } 1413 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) && 1414 floatsEqual(x1, x2) && floatsEqual(y1, y2) && 1415 floatsEqual(x2, x3) && floatsEqual(y2, y3)) { 1416 /*ignore the empty segment */ 1417 continue; 1418 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) { 1419 /* if dup vertex, emit a line */ 1420 ox = x3; 1421 oy = y3; 1422 matrix_map_point(matrix, x3, y3, &x3, &y3); 1423 stroker_line_to((struct stroker *)&stroker, x3, y3); 1424 continue; 1425 } 1426 ox = x3; 1427 oy = y3; 1428 assert(matrix_is_affine(matrix)); 1429 matrix_map_point(matrix, x0, y0, &x0, &y0); 1430 matrix_map_point(matrix, x1, y1, &x1, &y1); 1431 matrix_map_point(matrix, x2, y2, &x2, &y2); 1432 matrix_map_point(matrix, x3, y3, &x3, &y3); 1433 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3); 1434 } 1435 break; 1436 case VG_SCUBIC_TO: { 1437 data_at(&coords, p, 0, 4, data); 1438 x0 = ox; 1439 y0 = oy; 1440 x1 = 2*ox-px; 1441 y1 = 2*oy-py; 1442 x2 = data[0]; 1443 y2 = data[1]; 1444 x3 = data[2]; 1445 y3 = data[3]; 1446 map_if_relative(ox, oy, relative, &x2, &y2); 1447 map_if_relative(ox, oy, relative, &x3, &y3); 1448 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) && 1449 floatsEqual(x1, x2) && floatsEqual(y1, y2) && 1450 floatsEqual(x2, x3) && floatsEqual(y2, y3)) { 1451 /*ignore the empty segment */ 1452 continue; 1453 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) { 1454 /* if dup vertex, emit a line */ 1455 ox = x3; 1456 oy = y3; 1457 matrix_map_point(matrix, x3, y3, &x3, &y3); 1458 stroker_line_to((struct stroker *)&stroker, x3, y3); 1459 continue; 1460 } 1461 ox = x3; 1462 oy = y3; 1463 px = x2; 1464 py = y2; 1465 assert(matrix_is_affine(matrix)); 1466 matrix_map_point(matrix, x0, y0, &x0, &y0); 1467 matrix_map_point(matrix, x1, y1, &x1, &y1); 1468 matrix_map_point(matrix, x2, y2, &x2, &y2); 1469 matrix_map_point(matrix, x3, y3, &x3, &y3); 1470 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3); 1471 } 1472 break; 1473 case VG_SCCWARC_TO: 1474 case VG_SCWARC_TO: 1475 case VG_LCCWARC_TO: 1476 case VG_LCWARC_TO: { 1477 VGfloat rh, rv, rot; 1478 struct arc arc; 1479 1480 data_at(&coords, p, 0, 5, data); 1481 x0 = ox; 1482 y0 = oy; 1483 rh = data[0]; 1484 rv = data[1]; 1485 rot = data[2]; 1486 x1 = data[3]; 1487 y1 = data[4]; 1488 map_if_relative(ox, oy, relative, &x1, &y1); 1489 if (floatsEqual(x1, ox) && floatsEqual(y1, oy)) { 1490 /* if dup vertex, emit a line */ 1491 ox = x1; 1492 oy = y1; 1493 matrix_map_point(matrix, x1, y1, &x1, &y1); 1494 stroker_line_to((struct stroker *)&stroker, x1, y1); 1495 continue; 1496 } 1497 arc_init(&arc, command, x0, y0, x1, y1, 1498 rh, rv, rot); 1499 arc_stroke_cb(&arc, (struct stroker *)&stroker, 1500 matrix); 1501 ox = x1; 1502 oy = y1; 1503 px = x1; 1504 py = y1; 1505 } 1506 break; 1507 default: 1508 abort(); 1509 assert(!"Unknown segment!"); 1510 } 1511 } 1512 1513 stroker_end((struct stroker *)&stroker); 1514 1515 if (dashed) 1516 dash_stroker_cleanup((struct dash_stroker *)&stroker); 1517 else 1518 stroker_cleanup((struct stroker *)&stroker); 1519 1520 p->stroked.path = stroker.base.path; 1521 p->stroked.matrix = *matrix; 1522 p->dirty_stroke = VG_FALSE; 1523 p->stroked.stroke_width = vg_state->stroke.line_width.f; 1524 p->stroked.miter_limit = vg_state->stroke.miter_limit.f; 1525 p->stroked.cap_style = vg_state->stroke.cap_style; 1526 p->stroked.join_style = vg_state->stroke.join_style; 1527 1528 return stroker.base.path; 1529} 1530 1531void path_render(struct path *p, VGbitfield paintModes) 1532{ 1533 struct vg_context *ctx = vg_current_context(); 1534 struct matrix *mat = &ctx->state.vg.path_user_to_surface_matrix; 1535 1536 vg_validate_state(ctx); 1537 1538 shader_set_drawing_image(ctx->shader, VG_FALSE); 1539 shader_set_image(ctx->shader, 0); 1540#if 0 1541 fprintf(stderr, "Matrix(11=%f 12=%f 13=%f 21=%f 22=%f 23=%f 31=%f 32=%f 33=%f)\n", 1542 mat->m[0], mat->m[1], mat->m[2], 1543 mat->m[3], mat->m[4], mat->m[5], 1544 mat->m[6], mat->m[7], mat->m[8]); 1545#endif 1546 if (paintModes & VG_FILL_PATH) { 1547 /* First the fill */ 1548 shader_set_paint(ctx->shader, ctx->state.vg.fill_paint); 1549 shader_bind(ctx->shader); 1550 path_fill(p, mat); 1551 } 1552 1553 if (paintModes & VG_STROKE_PATH){ 1554 /* 8.7.5: "line width less than or equal to 0 prevents stroking from 1555 * taking place."*/ 1556 if (ctx->state.vg.stroke.line_width.f <= 0) 1557 return; 1558 shader_set_paint(ctx->shader, ctx->state.vg.stroke_paint); 1559 shader_bind(ctx->shader); 1560 path_stroke(p); 1561 } 1562} 1563 1564void path_fill(struct path *p, struct matrix *mat) 1565{ 1566 struct vg_context *ctx = vg_current_context(); 1567 { 1568 struct polygon_array *polygon_array = path_get_fill_polygons(p, mat); 1569 struct array *polys = polygon_array->array; 1570 1571 if (!polygon_array || !polys || !polys->num_elements) { 1572 return; 1573 } 1574 polygon_array_fill(polygon_array, ctx); 1575 } 1576} 1577 1578void path_stroke(struct path *p) 1579{ 1580 struct vg_context *ctx = vg_current_context(); 1581 struct matrix *mat = &ctx->state.vg.path_user_to_surface_matrix; 1582 VGFillRule old_fill = ctx->state.vg.fill_rule; 1583 struct matrix identity; 1584 struct path *stroke; 1585 1586 matrix_load_identity(&identity); 1587 stroke = path_create_stroke(p, &identity); 1588 if (stroke && !path_is_empty(stroke)) { 1589 ctx->state.vg.fill_rule = VG_NON_ZERO; 1590 1591 path_fill(stroke, mat); 1592 1593 ctx->state.vg.fill_rule = old_fill; 1594 } 1595} 1596 1597void path_move_to(struct path *p, float x, float y) 1598{ 1599 VGubyte segment = VG_MOVE_TO_ABS; 1600 VGubyte common_data[sizeof(VGfloat) * 2]; 1601 VGfloat data[2] = {x, y}; 1602 1603 vg_float_to_datatype(p->datatype, common_data, data, 2); 1604 path_append_data(p, 1, &segment, common_data); 1605} 1606 1607void path_line_to(struct path *p, float x, float y) 1608{ 1609 VGubyte segment = VG_LINE_TO_ABS; 1610 VGubyte common_data[sizeof(VGfloat) * 2]; 1611 VGfloat data[2] = {x, y}; 1612 1613 vg_float_to_datatype(p->datatype, common_data, data, 2); 1614 1615 path_append_data(p, 1, &segment, common_data); 1616} 1617 1618void path_cubic_to(struct path *p, float px1, float py1, 1619 float px2, float py2, 1620 float x, float y) 1621{ 1622 VGubyte segment = VG_CUBIC_TO_ABS; 1623 VGubyte common_data[sizeof(VGfloat) * 6]; 1624 VGfloat data[6]; 1625 1626 data[0] = px1; data[1] = py1; 1627 data[2] = px2; data[3] = py2; 1628 data[4] = x; data[5] = y; 1629 1630 vg_float_to_datatype(p->datatype, common_data, data, 6); 1631 1632 path_append_data(p, 1, &segment, common_data); 1633} 1634 1635static INLINE void line_bounds(VGfloat *line /*x1,y1,x2,y2*/, 1636 VGfloat *bounds) 1637{ 1638 bounds[0] = MIN2(line[0], line[2]); 1639 bounds[1] = MIN2(line[1], line[3]); 1640 bounds[2] = MAX2(line[0], line[2]) - bounds[0]; 1641 bounds[3] = MAX2(line[1], line[3]) - bounds[1]; 1642} 1643 1644static INLINE void unite_bounds(VGfloat *bounds, 1645 VGfloat *el) 1646{ 1647 VGfloat cx1, cy1, cx2, cy2; 1648 VGfloat nx1, ny1, nx2, ny2; 1649 1650 cx1 = bounds[0]; 1651 cy1 = bounds[1]; 1652 cx2 = bounds[0] + bounds[2]; 1653 cy2 = bounds[1] + bounds[3]; 1654 1655 nx1 = el[0]; 1656 ny1 = el[1]; 1657 nx2 = el[0] + el[2]; 1658 ny2 = el[1] + el[3]; 1659 1660 bounds[0] = MIN2(cx1, nx1); 1661 bounds[1] = MIN2(cy1, ny1); 1662 bounds[2] = MAX2(cx2, nx2) - bounds[0]; 1663 bounds[3] = MAX2(cy2, ny2) - bounds[1]; 1664} 1665 1666static INLINE void set_bounds(VGfloat *bounds, 1667 VGfloat *element_bounds, 1668 VGboolean *initialized) 1669{ 1670 if (!(*initialized)) { 1671 memcpy(bounds, element_bounds, 4 * sizeof(VGfloat)); 1672 *initialized = VG_TRUE; 1673 } else 1674 unite_bounds(bounds, element_bounds); 1675} 1676 1677void path_bounding_rect(struct path *p, float *x, float *y, 1678 float *w, float *h) 1679{ 1680 VGint i; 1681 VGfloat coords[8]; 1682 struct path_iter_data iter; 1683 VGint num_coords; 1684 VGfloat bounds[4]; 1685 VGfloat element_bounds[4]; 1686 VGfloat ox, oy; 1687 VGboolean bounds_inited = VG_FALSE; 1688 1689 memset(&iter, 0, sizeof(struct path_iter_data)); 1690 memset(&bounds, 0, sizeof(bounds)); 1691 1692 if (!p->num_segments) { 1693 bounds[2] = -1; 1694 bounds[3] = -1; 1695 } 1696 1697 1698 iter.path = p; 1699 iter.coords = p->control_points->data; 1700 1701 for (i = 0; i < p->num_segments; ++i) { 1702 VGubyte segment; 1703 iter.segment = ((VGubyte*)(p->segments->data))[i]; 1704 1705 ox = iter.ox; 1706 oy = iter.oy; 1707 1708 segment = normalize_coords(&iter, &num_coords, coords); 1709 1710 switch(segment) { 1711 case VG_CLOSE_PATH: 1712 case VG_MOVE_TO_ABS: 1713 break; 1714 case VG_LINE_TO_ABS: { 1715 VGfloat line[4] = {ox, oy, coords[0], coords[1]}; 1716 line_bounds(line, element_bounds); 1717 set_bounds(bounds, element_bounds, &bounds_inited); 1718 } 1719 break; 1720 case VG_CUBIC_TO_ABS: { 1721 struct bezier bezier; 1722 bezier_init(&bezier, ox, oy, 1723 coords[0], coords[1], 1724 coords[2], coords[3], 1725 coords[4], coords[5]); 1726 bezier_exact_bounds(&bezier, element_bounds); 1727 set_bounds(bounds, element_bounds, &bounds_inited); 1728 } 1729 break; 1730 case VG_SCCWARC_TO: 1731 case VG_SCWARC_TO: 1732 case VG_LCCWARC_TO: 1733 case VG_LCWARC_TO: { 1734 struct arc arc; 1735 struct matrix identity; 1736 struct path *path = path_create(VG_PATH_DATATYPE_F, 1737 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL); 1738 1739 matrix_load_identity(&identity); 1740 arc_init(&arc, segment, 1741 ox, oy, coords[3], coords[4], 1742 coords[0], coords[1], coords[2]); 1743 1744 arc_to_path(&arc, path, &identity); 1745 1746 path_bounding_rect(path, element_bounds + 0, element_bounds + 1, 1747 element_bounds + 2, element_bounds + 3); 1748 set_bounds(bounds, element_bounds, &bounds_inited); 1749 } 1750 break; 1751 default: 1752 assert(0); 1753 } 1754 } 1755 1756 *x = bounds[0]; 1757 *y = bounds[1]; 1758 *w = bounds[2]; 1759 *h = bounds[3]; 1760} 1761 1762float path_length(struct path *p, int start_segment, int num_segments) 1763{ 1764 VGint i; 1765 VGfloat coords[8]; 1766 struct path_iter_data iter; 1767 VGint num_coords; 1768 VGfloat length = 0; 1769 VGfloat ox, oy; 1770 VGboolean in_range = VG_FALSE; 1771 1772 memset(&iter, 0, sizeof(struct path_iter_data)); 1773 1774 iter.path = p; 1775 iter.coords = p->control_points->data; 1776 1777 for (i = 0; i < (start_segment + num_segments); ++i) { 1778 VGubyte segment; 1779 1780 iter.segment = ((VGubyte*)(p->segments->data))[i]; 1781 1782 ox = iter.ox; 1783 oy = iter.oy; 1784 1785 segment = normalize_coords(&iter, &num_coords, coords); 1786 1787 in_range = (i >= start_segment) && i <= (start_segment + num_segments); 1788 if (!in_range) 1789 continue; 1790 1791 switch(segment) { 1792 case VG_MOVE_TO_ABS: 1793 break; 1794 case VG_CLOSE_PATH: { 1795 VGfloat line[4] = {ox, oy, iter.sx, iter.sy}; 1796 length += line_lengthv(line); 1797 } 1798 break; 1799 case VG_LINE_TO_ABS: { 1800 VGfloat line[4] = {ox, oy, coords[0], coords[1]}; 1801 length += line_lengthv(line); 1802 } 1803 break; 1804 case VG_CUBIC_TO_ABS: { 1805 struct bezier bezier; 1806 bezier_init(&bezier, ox, oy, 1807 coords[0], coords[1], 1808 coords[2], coords[3], 1809 coords[4], coords[5]); 1810 length += bezier_length(&bezier, BEZIER_DEFAULT_ERROR); 1811 } 1812 break; 1813 case VG_SCCWARC_TO: 1814 case VG_SCWARC_TO: 1815 case VG_LCCWARC_TO: 1816 case VG_LCWARC_TO: { 1817 struct arc arc; 1818 struct matrix identity; 1819 struct path *path = path_create(VG_PATH_DATATYPE_F, 1820 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL); 1821 1822 matrix_load_identity(&identity); 1823 arc_init(&arc, segment, 1824 ox, oy, coords[3], coords[4], 1825 coords[0], coords[1], coords[2]); 1826 1827 arc_to_path(&arc, path, &identity); 1828 1829 length += path_length(path, 0, path_num_segments(path)); 1830 } 1831 break; 1832 default: 1833 assert(0); 1834 } 1835 } 1836 1837 return length; 1838} 1839 1840static INLINE VGboolean point_on_current_segment(VGfloat distance, 1841 VGfloat length, 1842 VGfloat segment_length) 1843{ 1844 return 1845 (((floatIsZero(distance) || distance < 0) && floatIsZero(length)) || 1846 ((distance > length || floatsEqual(distance, length)) && 1847 (floatsEqual(distance, length + segment_length) || 1848 distance < (length + segment_length)))); 1849} 1850 1851static VGboolean path_point_segment(struct path_iter_data iter, 1852 struct path_iter_data prev_iter, 1853 VGfloat coords[8], 1854 VGfloat distance, 1855 VGfloat length, VGfloat *current_length, 1856 VGfloat *point, VGfloat *normal) 1857{ 1858 switch (iter.segment) { 1859 case VG_MOVE_TO_ABS: 1860 break; 1861 case VG_CLOSE_PATH: { 1862 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, iter.sx, iter.sy}; 1863 VGboolean on_current_segment = VG_FALSE; 1864 *current_length = line_lengthv(line); 1865 on_current_segment = point_on_current_segment(distance, 1866 length, 1867 *current_length); 1868 if (on_current_segment) { 1869 VGfloat at = (distance - length) / line_lengthv(line); 1870 line_normal_vector(line, normal); 1871 line_point_at(line, at, point); 1872 return VG_TRUE; 1873 } 1874 } 1875 break; 1876 case VG_LINE_TO_ABS: { 1877 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, coords[0], coords[1]}; 1878 VGboolean on_current_segment = VG_FALSE; 1879 *current_length = line_lengthv(line); 1880 on_current_segment = point_on_current_segment(distance, 1881 length, 1882 *current_length); 1883 if (on_current_segment) { 1884 VGfloat at = (distance - length) / line_lengthv(line); 1885 line_normal_vector(line, normal); 1886 line_point_at(line, at, point); 1887 return VG_TRUE; 1888 } 1889 } 1890 break; 1891 case VG_CUBIC_TO_ABS: { 1892 struct bezier bezier; 1893 bezier_init(&bezier, prev_iter.ox, prev_iter.oy, 1894 coords[0], coords[1], 1895 coords[2], coords[3], 1896 coords[4], coords[5]); 1897 *current_length = bezier_length(&bezier, BEZIER_DEFAULT_ERROR); 1898 if (point_on_current_segment(distance, length, *current_length)) { 1899 bezier_point_at_length(&bezier, distance - length, 1900 point, normal); 1901 return VG_TRUE; 1902 } 1903 } 1904 break; 1905 case VG_SCCWARC_TO: 1906 case VG_SCWARC_TO: 1907 case VG_LCCWARC_TO: 1908 case VG_LCWARC_TO: { 1909 struct arc arc; 1910 struct matrix identity; 1911 struct path *path = path_create(VG_PATH_DATATYPE_F, 1912 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL); 1913 1914 matrix_load_identity(&identity); 1915 arc_init(&arc, iter.segment, 1916 prev_iter.ox, prev_iter.oy, coords[3], coords[4], 1917 coords[0], coords[1], coords[2]); 1918 1919 arc_to_path(&arc, path, &identity); 1920 1921 *current_length = path_length(path, 0, path_num_segments(path)); 1922 if (point_on_current_segment(distance, length, *current_length)) { 1923 path_point(path, 0, path_num_segments(path), 1924 distance - length, point, normal); 1925 return VG_TRUE; 1926 } 1927 } 1928 break; 1929 default: 1930 assert(0); 1931 } 1932 return VG_FALSE; 1933} 1934 1935void path_point(struct path *p, VGint start_segment, VGint num_segments, 1936 VGfloat distance, VGfloat *point, VGfloat *normal) 1937{ 1938 VGint i; 1939 VGfloat coords[8]; 1940 struct path_iter_data iter, prev_iter; 1941 VGint num_coords; 1942 VGfloat length = 0; 1943 VGfloat current_length = 0; 1944 1945 memset(&iter, 0, sizeof(struct path_iter_data)); 1946 memset(&prev_iter, 0, sizeof(struct path_iter_data)); 1947 1948 point[0] = 0; 1949 point[1] = 0; 1950 1951 normal[0] = 0; 1952 normal[1] = -1; 1953 1954 iter.path = p; 1955 iter.coords = p->control_points->data; 1956 if (distance < 0) 1957 distance = 0; 1958 1959 for (i = 0; i < (start_segment + num_segments); ++i) { 1960 VGboolean outside_range = (i < start_segment || 1961 i >= (start_segment + num_segments)); 1962 1963 prev_iter = iter; 1964 1965 iter.segment = ((VGubyte*)(p->segments->data))[i]; 1966 iter.segment = normalize_coords(&iter, &num_coords, coords); 1967 1968 if (outside_range) 1969 continue; 1970 1971 if (path_point_segment(iter, prev_iter, coords, 1972 distance, length, ¤t_length, 1973 point, normal)) 1974 return; 1975 1976 length += current_length; 1977 } 1978 1979 /* 1980 *OpenVG 1.0 - 8.6.11 vgPointAlongPath 1981 * 1982 * If distance is greater than or equal to the path length 1983 *(i.e., the value returned by vgPathLength when called with the same 1984 *startSegment and numSegments parameters), the visual ending point of 1985 *the path is used. 1986 */ 1987 { 1988 switch (iter.segment) { 1989 case VG_MOVE_TO_ABS: 1990 break; 1991 case VG_CLOSE_PATH: { 1992 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, iter.sx, iter.sy}; 1993 line_normal_vector(line, normal); 1994 line_point_at(line, 1.f, point); 1995 } 1996 break; 1997 case VG_LINE_TO_ABS: { 1998 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, coords[0], coords[1]}; 1999 line_normal_vector(line, normal); 2000 line_point_at(line, 1.f, point); 2001 } 2002 break; 2003 case VG_CUBIC_TO_ABS: { 2004 struct bezier bezier; 2005 bezier_init(&bezier, prev_iter.ox, prev_iter.oy, 2006 coords[0], coords[1], 2007 coords[2], coords[3], 2008 coords[4], coords[5]); 2009 bezier_point_at_t(&bezier, 1.f, point, normal); 2010 } 2011 break; 2012 case VG_SCCWARC_TO: 2013 case VG_SCWARC_TO: 2014 case VG_LCCWARC_TO: 2015 case VG_LCWARC_TO: { 2016 struct arc arc; 2017 struct matrix identity; 2018 struct path *path = path_create(VG_PATH_DATATYPE_F, 2019 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL); 2020 2021 matrix_load_identity(&identity); 2022 arc_init(&arc, iter.segment, 2023 prev_iter.ox, prev_iter.oy, coords[3], coords[4], 2024 coords[0], coords[1], coords[2]); 2025 2026 arc_to_path(&arc, path, &identity); 2027 2028 path_point(path, 0, path_num_segments(path), 2029 /* to make sure we're bigger than len * 2 it */ 2030 2 * path_length(path, 0, path_num_segments(path)), 2031 point, normal); 2032 } 2033 break; 2034 default: 2035 assert(0); 2036 } 2037 } 2038} 2039 2040VGboolean path_is_empty(struct path *p) 2041{ 2042 return p->segments->num_elements == 0; 2043} 2044