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