1 /**************************************************************************
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
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:
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
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.
25 **************************************************************************/
32 #include "pipe/p_compiler.h"
33 #include "util/u_debug.h"
40 static const float flatness
= 0.5;
43 static INLINE
void split_left(struct bezier
*bez
, VGfloat t
, struct bezier
* left
)
48 left
->x2
= bez
->x1
+ t
* (bez
->x2
- bez
->x1
);
49 left
->y2
= bez
->y1
+ t
* (bez
->y2
- bez
->y1
);
51 left
->x3
= bez
->x2
+ t
* (bez
->x3
- bez
->x2
);
52 left
->y3
= bez
->y2
+ t
* (bez
->y3
- bez
->y2
);
54 bez
->x3
= bez
->x3
+ t
* (bez
->x4
- bez
->x3
);
55 bez
->y3
= bez
->y3
+ t
* (bez
->y4
- bez
->y3
);
57 bez
->x2
= left
->x3
+ t
* (bez
->x3
- left
->x3
);
58 bez
->y2
= left
->y3
+ t
* (bez
->y3
- left
->y3
);
60 left
->x3
= left
->x2
+ t
* (left
->x3
- left
->x2
);
61 left
->y3
= left
->y2
+ t
* (left
->y3
- left
->y2
);
63 left
->x4
= bez
->x1
= left
->x3
+ t
* (bez
->x2
- left
->x3
);
64 left
->y4
= bez
->y1
= left
->y3
+ t
* (bez
->y2
- left
->y3
);
67 static INLINE
void split(struct bezier
*bez
,
68 struct bezier
*first_half
,
69 struct bezier
*second_half
)
71 double c
= (bez
->x2
+ bez
->x3
) * 0.5;
72 first_half
->x2
= (bez
->x1
+ bez
->x2
) * 0.5;
73 second_half
->x3
= (bez
->x3
+ bez
->x4
) * 0.5;
74 first_half
->x1
= bez
->x1
;
75 second_half
->x4
= bez
->x4
;
76 first_half
->x3
= (first_half
->x2
+ c
) * 0.5;
77 second_half
->x2
= (second_half
->x3
+ c
) * 0.5;
78 first_half
->x4
= second_half
->x1
=
79 (first_half
->x3
+ second_half
->x2
) * 0.5;
81 c
= (bez
->y2
+ bez
->y3
) / 2;
82 first_half
->y2
= (bez
->y1
+ bez
->y2
) * 0.5;
83 second_half
->y3
= (bez
->y3
+ bez
->y4
) * 0.5;
84 first_half
->y1
= bez
->y1
;
85 second_half
->y4
= bez
->y4
;
86 first_half
->y3
= (first_half
->y2
+ c
) * 0.5;
87 second_half
->y2
= (second_half
->y3
+ c
) * 0.5;
88 first_half
->y4
= second_half
->y1
=
89 (first_half
->y3
+ second_half
->y2
) * 0.5;
92 struct polygon
* bezier_to_polygon(struct bezier
*bez
)
94 struct polygon
*poly
= polygon_create(64);
95 polygon_vertex_append(poly
, bez
->x1
, bez
->y1
);
96 bezier_add_to_polygon(bez
, poly
);
100 void bezier_add_to_polygon(const struct bezier
*bez
,
101 struct polygon
*poly
)
103 struct bezier beziers
[32];
109 while (b
>= beziers
) {
110 double y4y1
= b
->y4
- b
->y1
;
111 double x4x1
= b
->x4
- b
->x1
;
112 double l
= ABS(x4x1
) + ABS(y4y1
);
115 d
= ABS((x4x1
)*(b
->y1
- b
->y2
) - (y4y1
)*(b
->x1
- b
->x2
))
116 + ABS((x4x1
)*(b
->y1
- b
->y3
) - (y4y1
)*(b
->x1
- b
->x3
));
118 d
= ABS(b
->x1
- b
->x2
) + ABS(b
->y1
- b
->y2
) +
119 ABS(b
->x1
- b
->x3
) + ABS(b
->y1
- b
->y3
);
122 if (d
< flatness
*l
|| b
== beziers
+ 31) {
123 /* good enough, we pop it off and add the endpoint */
124 polygon_vertex_append(poly
, b
->x4
, b
->y4
);
127 /* split, second half of the bezier goes lower into the stack */
134 static void add_if_close(struct bezier
*bez
, VGfloat
*length
, VGfloat error
)
136 struct bezier left
, right
; /* bez poly splits */
137 VGfloat len
= 0.0; /* arc length */
138 VGfloat chord
; /* chord length */
140 len
= len
+ line_length(bez
->x1
, bez
->y1
, bez
->x2
, bez
->y2
);
141 len
= len
+ line_length(bez
->x2
, bez
->y2
, bez
->x3
, bez
->y3
);
142 len
= len
+ line_length(bez
->x3
, bez
->y3
, bez
->x4
, bez
->y4
);
144 chord
= line_length(bez
->x1
, bez
->y1
, bez
->x4
, bez
->y4
);
146 if ((len
-chord
) > error
) {
147 split(bez
, &left
, &right
); /* split in two */
148 add_if_close(&left
, length
, error
); /* try left side */
149 add_if_close(&right
, length
, error
); /* try right side */
153 *length
= *length
+ len
;
158 float bezier_length(struct bezier
*bez
, float error
)
160 VGfloat length
= 0.f
;
162 add_if_close(bez
, &length
, error
);
166 void bezier_init(struct bezier
*bez
,
181 debug_printf("bezier in [%f, %f, %f, %f, %f, %f]\n",
182 x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
);
187 static INLINE
void bezier_init2v(struct bezier
*bez
,
207 void bezier_transform(struct bezier
*bez
,
208 struct matrix
*matrix
)
210 assert(matrix_is_affine(matrix
));
211 matrix_map_point(matrix
, bez
->x1
, bez
->y1
, &bez
->x1
, &bez
->y1
);
212 matrix_map_point(matrix
, bez
->x2
, bez
->y2
, &bez
->x2
, &bez
->y2
);
213 matrix_map_point(matrix
, bez
->x3
, bez
->y3
, &bez
->x3
, &bez
->y3
);
214 matrix_map_point(matrix
, bez
->x4
, bez
->y4
, &bez
->x4
, &bez
->y4
);
217 static INLINE
void bezier_point_at(const struct bezier
*bez
, float t
, float *pt
)
228 pt
[0] = a
*bez
->x1
+ b
*bez
->x2
+ c
*bez
->x3
+ d
*bez
->x4
;
229 pt
[1] = a
*bez
->y1
+ b
*bez
->y2
+ c
*bez
->y3
+ d
*bez
->y4
;
232 static INLINE
void bezier_normal_at(const struct bezier
*bez
, float t
, float *norm
)
239 norm
[0] = (bez
->y2
-bez
->y1
) * a
+ (bez
->y3
-bez
->y2
) * b
+ (bez
->y4
-bez
->y3
) * c
;
240 norm
[1] = -(bez
->x2
-bez
->x1
) * a
- (bez
->x3
-bez
->x2
) * b
- (bez
->x4
-bez
->x3
) * c
;
250 static enum shift_result
good_offset(const struct bezier
*b1
,
251 const struct bezier
*b2
,
252 float offset
, float threshold
)
254 const float o2
= offset
*offset
;
255 const float max_dist_line
= threshold
*offset
*offset
;
256 const float max_dist_normal
= threshold
*offset
;
257 const float spacing
= 0.25;
259 for (i
= spacing
; i
< 0.99; i
+= spacing
) {
260 float p1
[2],p2
[2], d
, l
;
262 bezier_point_at(b1
, i
, p1
);
263 bezier_point_at(b2
, i
, p2
);
264 d
= (p1
[0] - p2
[0])*(p1
[0] - p2
[0]) + (p1
[1] - p2
[1])*(p1
[1] - p2
[1]);
265 if (ABS(d
- o2
) > max_dist_line
)
268 bezier_normal_at(b1
, i
, normal
);
269 l
= ABS(normal
[0]) + ABS(normal
[1]);
271 d
= ABS(normal
[0]*(p1
[1] - p2
[1]) - normal
[1]*(p1
[0] - p2
[0]) ) / l
;
272 if (d
> max_dist_normal
)
279 static INLINE
void shift_line_by_normal(float *l
, float offset
)
284 line_normal(l
, norm
);
285 line_normalize(norm
);
287 tx
= (norm
[2] - norm
[0]) * offset
;
288 ty
= (norm
[3] - norm
[1]) * offset
;
289 l
[0] += tx
; l
[1] += ty
;
290 l
[2] += tx
; l
[3] += ty
;
293 static INLINE VGboolean
is_bezier_line(float (*points
)[2], int count
)
295 float dx13
= points
[2][0] - points
[0][0];
296 float dy13
= points
[2][1] - points
[0][1];
298 float dx12
= points
[1][0] - points
[0][0];
299 float dy12
= points
[1][1] - points
[0][1];
301 debug_assert(count
> 2);
304 return floatsEqual(dx12
* dy13
, dx13
* dy12
);
305 } else if (count
== 4) {
306 float dx14
= points
[3][0] - points
[0][0];
307 float dy14
= points
[3][1] - points
[0][1];
309 return (floatsEqual(dx12
* dy13
, dx13
* dy12
) &&
310 floatsEqual(dx12
* dy14
, dx14
* dy12
));
316 static INLINE
void compute_pt_normal(float *pt1
, float *pt2
, float *res
)
320 line
[0] = 0.f
; line
[1] = 0.f
;
321 line
[2] = pt2
[0] - pt1
[0];
322 line
[3] = pt2
[1] - pt1
[1];
323 line_normal(line
, normal
);
324 line_normalize(normal
);
330 static enum shift_result
shift(const struct bezier
*orig
,
331 struct bezier
*shifted
,
332 float offset
, float threshold
)
336 VGboolean p1_p2_equal
= (orig
->x1
== orig
->x2
&& orig
->y1
== orig
->y2
);
337 VGboolean p2_p3_equal
= (orig
->x2
== orig
->x3
&& orig
->y2
== orig
->y3
);
338 VGboolean p3_p4_equal
= (orig
->x3
== orig
->x4
&& orig
->y3
== orig
->y4
);
343 float points_shifted
[4][2];
344 float prev_normal
[2];
346 points
[np
][0] = orig
->x1
;
347 points
[np
][1] = orig
->y1
;
351 points
[np
][0] = orig
->x2
;
352 points
[np
][1] = orig
->y2
;
357 points
[np
][0] = orig
->x3
;
358 points
[np
][1] = orig
->y3
;
363 points
[np
][0] = orig
->x4
;
364 points
[np
][1] = orig
->y4
;
371 /* We need to specialcase lines of 3 or 4 points due to numerical
372 instability in intersection code below */
373 if (np
> 2 && is_bezier_line(points
, np
)) {
374 float l
[4] = { points
[0][0], points
[0][1],
375 points
[np
-1][0], points
[np
-1][1] };
376 float ctrl1
[2], ctrl2
[2];
377 if (floatsEqual(points
[0][0], points
[np
-1][0]) &&
378 floatsEqual(points
[0][1], points
[np
-1][1]))
381 shift_line_by_normal(l
, offset
);
382 line_point_at(l
, 0.33, ctrl1
);
383 line_point_at(l
, 0.66, ctrl2
);
384 bezier_init(shifted
, l
[0], l
[1],
385 ctrl1
[0], ctrl1
[1], ctrl2
[0], ctrl2
[1],
390 bezier_bounds(orig
, bounds
);
391 if (np
== 4 && bounds
[2] < .1*offset
&& bounds
[3] < .1*offset
) {
392 float l
= (orig
->x1
- orig
->x2
)*(orig
->x1
- orig
->x2
) +
393 (orig
->y1
- orig
->y2
)*(orig
->y1
- orig
->y1
) *
394 (orig
->x3
- orig
->x4
)*(orig
->x3
- orig
->x4
) +
395 (orig
->y3
- orig
->y4
)*(orig
->y3
- orig
->y4
);
396 float dot
= (orig
->x1
- orig
->x2
)*(orig
->x3
- orig
->x4
) +
397 (orig
->y1
- orig
->y2
)*(orig
->y3
- orig
->y4
);
398 if (dot
< 0 && dot
*dot
< 0.8*l
)
399 /* the points are close and reverse dirction. Approximate the whole
400 thing by a semi circle */
404 compute_pt_normal(points
[0], points
[1], prev_normal
);
406 points_shifted
[0][0] = points
[0][0] + offset
* prev_normal
[0];
407 points_shifted
[0][1] = points
[0][1] + offset
* prev_normal
[1];
409 for (i
= 1; i
< np
- 1; ++i
) {
410 float normal_sum
[2], r
;
411 float next_normal
[2];
412 compute_pt_normal(points
[i
], points
[i
+ 1], next_normal
);
414 normal_sum
[0] = prev_normal
[0] + next_normal
[0];
415 normal_sum
[1] = prev_normal
[1] + next_normal
[1];
417 r
= 1.0 + prev_normal
[0] * next_normal
[0]
418 + prev_normal
[1] * next_normal
[1];
420 if (floatsEqual(r
+ 1, 1)) {
421 points_shifted
[i
][0] = points
[i
][0] + offset
* prev_normal
[0];
422 points_shifted
[i
][1] = points
[i
][1] + offset
* prev_normal
[1];
424 float k
= offset
/ r
;
425 points_shifted
[i
][0] = points
[i
][0] + k
* normal_sum
[0];
426 points_shifted
[i
][1] = points
[i
][1] + k
* normal_sum
[1];
429 prev_normal
[0] = next_normal
[0];
430 prev_normal
[1] = next_normal
[1];
433 points_shifted
[np
- 1][0] = points
[np
- 1][0] + offset
* prev_normal
[0];
434 points_shifted
[np
- 1][1] = points
[np
- 1][1] + offset
* prev_normal
[1];
436 bezier_init2v(shifted
,
437 points_shifted
[map
[0]], points_shifted
[map
[1]],
438 points_shifted
[map
[2]], points_shifted
[map
[3]]);
440 return good_offset(orig
, shifted
, offset
, threshold
);
443 static VGboolean
make_circle(const struct bezier
*b
, float offset
, struct bezier
*o
)
452 normals
[0][0] = b
->y2
- b
->y1
;
453 normals
[0][1] = b
->x1
- b
->x2
;
454 dist
= sqrt(normals
[0][0]*normals
[0][0] + normals
[0][1]*normals
[0][1]);
455 if (floatsEqual(dist
+ 1, 1.f
))
457 normals
[0][0] /= dist
;
458 normals
[0][1] /= dist
;
460 normals
[2][0] = b
->y4
- b
->y3
;
461 normals
[2][1] = b
->x3
- b
->x4
;
462 dist
= sqrt(normals
[2][0]*normals
[2][0] + normals
[2][1]*normals
[2][1]);
463 if (floatsEqual(dist
+ 1, 1.f
))
465 normals
[2][0] /= dist
;
466 normals
[2][1] /= dist
;
468 normals
[1][0] = b
->x1
- b
->x2
- b
->x3
+ b
->x4
;
469 normals
[1][1] = b
->y1
- b
->y2
- b
->y3
+ b
->y4
;
470 dist
= -1*sqrt(normals
[1][0]*normals
[1][0] + normals
[1][1]*normals
[1][1]);
471 normals
[1][0] /= dist
;
472 normals
[1][1] /= dist
;
474 for (i
= 0; i
< 2; ++i
) {
475 float cos_a
= normals
[i
][0]*normals
[i
+1][0] + normals
[i
][1]*normals
[i
+1][1];
480 angles
[i
] = acos(cos_a
)/M_PI
;
483 if (angles
[0] + angles
[1] > 1.) {
484 /* more than 180 degrees */
485 normals
[1][0] = -normals
[1][0];
486 normals
[1][1] = -normals
[1][1];
487 angles
[0] = 1. - angles
[0];
488 angles
[1] = 1. - angles
[1];
492 circle
[0][0] = b
->x1
+ normals
[0][0]*offset
;
493 circle
[0][1] = b
->y1
+ normals
[0][1]*offset
;
495 circle
[1][0] = 0.5*(b
->x1
+ b
->x4
) + normals
[1][0]*offset
;
496 circle
[1][1] = 0.5*(b
->y1
+ b
->y4
) + normals
[1][1]*offset
;
498 circle
[2][0] = b
->x4
+ normals
[2][0]*offset
;
499 circle
[2][1] = b
->y4
+ normals
[2][1]*offset
;
501 for (i
= 0; i
< 2; ++i
) {
502 float kappa
= 2.*KAPPA
* sign
* offset
* angles
[i
];
504 o
->x1
= circle
[i
][0];
505 o
->y1
= circle
[i
][1];
506 o
->x2
= circle
[i
][0] - normals
[i
][1]*kappa
;
507 o
->y2
= circle
[i
][1] + normals
[i
][0]*kappa
;
508 o
->x3
= circle
[i
+1][0] + normals
[i
+1][1]*kappa
;
509 o
->y3
= circle
[i
+1][1] - normals
[i
+1][0]*kappa
;
510 o
->x4
= circle
[i
+1][0];
511 o
->y4
= circle
[i
+1][1];
518 int bezier_translate_by_normal(struct bezier
*bez
,
519 struct bezier
*curves
,
524 struct bezier beziers
[10];
525 struct bezier
*b
, *o
;
527 /* fixme: this should really be floatsEqual */
528 if (bez
->x1
== bez
->x2
&& bez
->x1
== bez
->x3
&& bez
->x1
== bez
->x4
&&
529 bez
->y1
== bez
->y2
&& bez
->y1
== bez
->y3
&& bez
->y1
== bez
->y4
)
538 while (b
>= beziers
) {
539 int stack_segments
= b
- beziers
+ 1;
540 enum shift_result res
;
541 if ((stack_segments
== 10) || (o
- curves
== max_curves
- stack_segments
)) {
547 res
= shift(b
, o
, normal_len
, threshold
);
548 if (res
== Discard
) {
550 } else if (res
== Ok
) {
554 } else if (res
== Circle
&& max_curves
- (o
- curves
) >= 2) {
555 /* add semi circle */
556 if (make_circle(b
, normal_len
, o
))
566 while (b
>= beziers
) {
567 enum shift_result res
= shift(b
, o
, normal_len
, threshold
);
569 /* if res isn't Ok or Split then *o is undefined */
570 if (res
== Ok
|| res
== Split
)
576 debug_assert(o
- curves
<= max_curves
);
580 void bezier_bounds(const struct bezier
*bez
,
581 float *bounds
/*x/y/width/height*/)
583 float xmin
= bez
->x1
;
584 float xmax
= bez
->x1
;
585 float ymin
= bez
->y1
;
586 float ymax
= bez
->y1
;
590 else if (bez
->x2
> xmax
)
594 else if (bez
->x3
> xmax
)
598 else if (bez
->x4
> xmax
)
603 else if (bez
->y2
> ymax
)
607 else if (bez
->y3
> ymax
)
611 else if (bez
->y4
> ymax
)
614 bounds
[0] = xmin
; /* x */
615 bounds
[1] = ymin
; /* y */
616 bounds
[2] = xmax
- xmin
; /* width */
617 bounds
[3] = ymax
- ymin
; /* height */
620 void bezier_start_tangent(const struct bezier
*bez
,
623 tangent
[0] = bez
->x1
;
624 tangent
[1] = bez
->y1
;
625 tangent
[2] = bez
->x2
;
626 tangent
[3] = bez
->y2
;
628 if (null_line(tangent
)) {
629 tangent
[0] = bez
->x1
;
630 tangent
[1] = bez
->y1
;
631 tangent
[2] = bez
->x3
;
632 tangent
[3] = bez
->y3
;
634 if (null_line(tangent
)) {
635 tangent
[0] = bez
->x1
;
636 tangent
[1] = bez
->y1
;
637 tangent
[2] = bez
->x4
;
638 tangent
[3] = bez
->y4
;
643 static INLINE VGfloat
bezier_t_at_length(struct bezier
*bez
,
647 VGfloat len
= bezier_length(bez
, error
);
649 VGfloat last_bigger
= 1.;
651 if (at_length
> len
|| floatsEqual(at_length
, len
))
654 if (floatIsZero(at_length
))
659 struct bezier right
= *bez
;
662 split_left(&right
, t
, &left
);
663 tmp_len
= bezier_length(&left
, error
);
664 if (ABS(tmp_len
- at_length
) < error
)
667 if (tmp_len
< at_length
) {
668 t
+= (last_bigger
- t
)*.5;
677 void bezier_point_at_length(struct bezier
*bez
,
682 /* ~0.000001 seems to be required to pass G2080x tests */
683 VGfloat t
= bezier_t_at_length(bez
, length
, 0.000001);
684 bezier_point_at(bez
, t
, point
);
685 bezier_normal_at(bez
, t
, normal
);
689 void bezier_point_at_t(struct bezier
*bez
, float t
,
690 float *point
, float *normal
)
692 bezier_point_at(bez
, t
, point
);
693 bezier_normal_at(bez
, t
, normal
);
697 void bezier_exact_bounds(const struct bezier
*bez
,
698 float *bounds
/*x/y/width/height*/)
700 struct polygon
*poly
= polygon_create(64);
701 polygon_vertex_append(poly
, bez
->x1
, bez
->y1
);
702 bezier_add_to_polygon(bez
, poly
);
703 polygon_bounding_rect(poly
, bounds
);
704 polygon_destroy(poly
);