summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp
blob: b228cbf8b4038d24a24db50b70b45242f39aa82a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"

#if ENABLE(ACCELERATED_2D_CANVAS)

#include "LoopBlinnMathUtils.h"

#include "FloatPoint.h"
#include <algorithm>
#include <wtf/MathExtras.h>

namespace WebCore {
namespace LoopBlinnMathUtils {

namespace {

// Utility functions local to this file.
int orientation(const FloatPoint& p1,
                const FloatPoint& p2,
                const FloatPoint& p3)
{
    float crossProduct = (p2.y() - p1.y()) * (p3.x() - p2.x()) - (p3.y() - p2.y()) * (p2.x() - p1.x());
    return (crossProduct < 0.0f) ? -1 : ((crossProduct > 0.0f) ? 1 : 0);
}

bool edgeEdgeTest(const FloatSize& v0Delta,
                  const FloatPoint& v0,
                  const FloatPoint& u0,
                  const FloatPoint& u1)
{
    // This edge to edge test is based on Franlin Antonio's gem: "Faster
    // Line Segment Intersection", in Graphics Gems III, pp. 199-202.
    float ax = v0Delta.width();
    float ay = v0Delta.height();
    float bx = u0.x() - u1.x();
    float by = u0.y() - u1.y();
    float cx = v0.x() - u0.x();
    float cy = v0.y() - u0.y();
    float f = ay * bx - ax * by;
    float d = by * cx - bx * cy;
    if ((f > 0 && d >= 0 && d <= f) || (f < 0 && d <= 0 && d >= f)) {
        float e = ax * cy - ay * cx;

        // This additional test avoids reporting coincident edges, which
        // is the behavior we want.
        if (approxEqual(e, 0) || approxEqual(f, 0) || approxEqual(e, f))
            return false;

        if (f > 0)
            return e >= 0 && e <= f;

        return e <= 0 && e >= f;
    }
    return false;
}

bool edgeAgainstTriangleEdges(const FloatPoint& v0,
                              const FloatPoint& v1,
                              const FloatPoint& u0,
                              const FloatPoint& u1,
                              const FloatPoint& u2)
{
    FloatSize delta = v1 - v0;
    // Test edge u0, u1 against v0, v1.
    if (edgeEdgeTest(delta, v0, u0, u1))
        return true;
    // Test edge u1, u2 against v0, v1.
    if (edgeEdgeTest(delta, v0, u1, u2))
        return true;
    // Test edge u2, u1 against v0, v1.
    if (edgeEdgeTest(delta, v0, u2, u0))
        return true;
    return false;
}

// A roundoff factor in the cubic classification and texture coordinate
// generation algorithms. It primarily determines the handling of corner
// cases during the classification process. Be careful when adjusting it;
// it has been determined empirically to work well. When changing it, you
// should look in particular at shapes that contain quadratic curves and
// ensure they still look smooth. Once pixel tests are running against this
// algorithm, they should provide sufficient coverage to ensure that
// adjusting the constant won't break anything.
const float Epsilon = 5.0e-4f;

} // anonymous namespace

// Exported routines

float roundToZero(float val)
{
    if (val < Epsilon && val > -Epsilon)
        return 0;
    return val;
}

bool approxEqual(const FloatPoint& v0, const FloatPoint& v1)
{
    return (v0 - v1).diagonalLengthSquared() < Epsilon * Epsilon;
}

bool approxEqual(const FloatPoint3D& v0, const FloatPoint3D& v1)
{
    return (v0 - v1).lengthSquared() < Epsilon * Epsilon;
}

bool approxEqual(float f0, float f1)
{
    return fabsf(f0 - f1) < Epsilon;
}

bool linesIntersect(const FloatPoint& p1,
                    const FloatPoint& q1,
                    const FloatPoint& p2,
                    const FloatPoint& q2)
{
    return (orientation(p1, q1, p2) != orientation(p1, q1, q2)
            && orientation(p2, q2, p1) != orientation(p2, q2, q1));
}

bool pointInTriangle(const FloatPoint& point,
                     const FloatPoint& a,
                     const FloatPoint& b,
                     const FloatPoint& c)
{
    // Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html
    float x0 = c.x() - a.x();
    float y0 = c.y() - a.y();
    float x1 = b.x() - a.x();
    float y1 = b.y() - a.y();
    float x2 = point.x() - a.x();
    float y2 = point.y() - a.y();

    float dot00 = x0 * x0 + y0 * y0;
    float dot01 = x0 * x1 + y0 * y1;
    float dot02 = x0 * x2 + y0 * y2;
    float dot11 = x1 * x1 + y1 * y1;
    float dot12 = x1 * x2 + y1 * y2;
    float denominator = dot00 * dot11 - dot01 * dot01;
    if (!denominator)
        // Triangle is zero-area. Treat query point as not being inside.
        return false;
    // Compute
    float inverseDenominator = 1.0f / denominator;
    float u = (dot11 * dot02 - dot01 * dot12) * inverseDenominator;
    float v = (dot00 * dot12 - dot01 * dot02) * inverseDenominator;

    return (u > 0.0f) && (v > 0.0f) && (u + v < 1.0f);
}

bool trianglesOverlap(const FloatPoint& a1,
                      const FloatPoint& b1,
                      const FloatPoint& c1,
                      const FloatPoint& a2,
                      const FloatPoint& b2,
                      const FloatPoint& c2)
{
    // Derived from coplanar_tri_tri() at
    // http://jgt.akpeters.com/papers/ShenHengTang03/tri_tri.html ,
    // simplified for the 2D case and modified so that overlapping edges
    // do not report overlapping triangles.

    // Test all edges of triangle 1 against the edges of triangle 2.
    if (edgeAgainstTriangleEdges(a1, b1, a2, b2, c2)
        || edgeAgainstTriangleEdges(b1, c1, a2, b2, c2)
        || edgeAgainstTriangleEdges(c1, a1, a2, b2, c2))
        return true;
    // Finally, test if tri1 is totally contained in tri2 or vice versa.
    // The paper above only performs the first two point-in-triangle tests.
    // Because we define that triangles sharing a vertex or edge don't
    // overlap, we must perform additional tests to see whether one
    // triangle is contained in the other.
    if (pointInTriangle(a1, a2, b2, c2)
        || pointInTriangle(a2, a1, b1, c1)
        || pointInTriangle(b1, a2, b2, c2)
        || pointInTriangle(b2, a1, b1, c1)
        || pointInTriangle(c1, a2, b2, c2)
        || pointInTriangle(c2, a1, b1, c1))
        return true;
    return false;
}

namespace {

// Helper routines for public XRay queries below. All of this code
// originated in Skia; see include/core/ and src/core/, SkScalar.h and
// SkGeometry.{cpp,h}.

const float NearlyZeroConstant = (1.0f / (1 << 12));

bool nearlyZero(float x, float tolerance = NearlyZeroConstant)
{
    ASSERT(tolerance > 0.0f);
    return ::fabsf(x) < tolerance;
}

// Linearly interpolate between a and b, based on t.
// If t is 0, return a; if t is 1, return b; else interpolate.
// t must be [0..1].
float interpolate(float a, float b, float t)
{
    ASSERT(t >= 0 && t <= 1);
    return a + (b - a) * t;
}

float evaluateCubic(float controlPoint0, float controlPoint1, float controlPoint2, float controlPoint3, float t)
{
    ASSERT(t >= 0 && t <= 1);

    if (!t)
        return controlPoint0;

    float ab = interpolate(controlPoint0, controlPoint1, t);
    float bc = interpolate(controlPoint1, controlPoint2, t);
    float cd = interpolate(controlPoint2, controlPoint3, t);
    float abc = interpolate(ab, bc, t);
    float bcd = interpolate(bc, cd, t);
    return interpolate(abc, bcd, t);
}

// Evaluates the point on the source cubic specified by t, 0 <= t <= 1.0.
FloatPoint evaluateCubicAt(const FloatPoint cubic[4], float t)
{
    return FloatPoint(evaluateCubic(cubic[0].x(), cubic[1].x(), cubic[2].x(), cubic[3].x(), t),
                      evaluateCubic(cubic[0].y(), cubic[1].y(), cubic[2].y(), cubic[3].y(), t));
}

bool xRayCrossesMonotonicCubic(const XRay& xRay, const FloatPoint cubic[4], bool& ambiguous)
{
    ambiguous = false;

    // Find the minimum and maximum y of the extrema, which are the
    // first and last points since this cubic is monotonic
    float minY = std::min(cubic[0].y(), cubic[3].y());
    float maxY = std::max(cubic[0].y(), cubic[3].y());

    if (xRay.y() == cubic[0].y()
        || xRay.y() < minY
        || xRay.y() > maxY) {
        // The query line definitely does not cross the curve
        ambiguous = (xRay.y() == cubic[0].y());
        return false;
    }

    const bool pointAtExtremum = (xRay.y() == cubic[3].y());

    float minX = std::min(std::min(std::min(cubic[0].x(), cubic[1].x()),
                                   cubic[2].x()),
                          cubic[3].x());
    if (xRay.x() < minX) {
        // The query line definitely crosses the curve
        ambiguous = pointAtExtremum;
        return true;
    }

    float maxX = std::max(std::max(std::max(cubic[0].x(), cubic[1].x()),
                                   cubic[2].x()),
                          cubic[3].x());
    if (xRay.x() > maxX)
        // The query line definitely does not cross the curve
        return false;

    // Do a binary search to find the parameter value which makes y as
    // close as possible to the query point. See whether the query
    // line's origin is to the left of the associated x coordinate.

    // MaxIterations is chosen as the number of mantissa bits for a float,
    // since there's no way we are going to get more precision by
    // iterating more times than that.
    const int MaxIterations = 23;
    FloatPoint evaluatedPoint;
    int iter = 0;
    float upperT;
    float lowerT;
    // Need to invert direction of t parameter if cubic goes up
    // instead of down
    if (cubic[3].y() > cubic[0].y()) {
        upperT = 1;
        lowerT = 0;
    } else {
        upperT = 0;
        lowerT = 1;
    }
    do {
        float t = 0.5f * (upperT + lowerT);
        evaluatedPoint = evaluateCubicAt(cubic, t);
        if (xRay.y() > evaluatedPoint.y())
            lowerT = t;
        else
            upperT = t;
    } while (++iter < MaxIterations && !nearlyZero(evaluatedPoint.y() - xRay.y()));

    // FIXME: once we have more regression tests for this code,
    // determine whether this should be using a fuzzy test.
    if (xRay.x() <= evaluatedPoint.x()) {
        ambiguous = pointAtExtremum;
        return true;
    }
    return false;
}

// Divides the numerator by the denominator safely for the case where
// the result must lie in the range (0..1). Result indicates whether
// the result is valid.
bool safeUnitDivide(float numerator, float denominator, float& ratio)
{
    if (numerator < 0) {
        // Make the "numerator >= denominator" check below work.
        numerator = -numerator;
        denominator = -denominator;
    }
    if (!numerator || !denominator || numerator >= denominator)
        return false;
    float r = numerator / denominator;
    if (isnan(r))
        return false;
    ASSERT(r >= 0 && r < 1);
    if (!r) // catch underflow if numerator <<<< denominator
        return false;
    ratio = r;
    return true;
}

// From Numerical Recipes in C.
//
//   q = -1/2 (b + sign(b) sqrt[b*b - 4*a*c])
//   x1 = q / a
//   x2 = c / q
//
// Returns the number of real roots of the equation [0..2]. Roots are
// returned in sorted order, smaller root first.
int findUnitQuadRoots(float a, float b, float c, float roots[2])
{
    if (!a)
        return safeUnitDivide(-c, b, roots[0]) ? 1 : 0;

    float discriminant = b*b - 4*a*c;
    if (discriminant < 0 || isnan(discriminant)) // complex roots
        return 0;
    discriminant = sqrtf(discriminant);

    float q = (b < 0) ? -(b - discriminant) / 2 : -(b + discriminant) / 2;
    int numberOfRoots = 0;
    if (safeUnitDivide(q, a, roots[numberOfRoots]))
        ++numberOfRoots;
    if (safeUnitDivide(c, q, roots[numberOfRoots]))
        ++numberOfRoots;
    if (numberOfRoots == 2) {
        // Seemingly have two roots. Check for equality and sort.
        if (roots[0] == roots[1])
            return 1;
        if (roots[0] > roots[1])
            std::swap(roots[0], roots[1]);
    }
    return numberOfRoots;
}

// Cubic'(t) = pt^2 + qt + r, where
//   p = 3(-a + 3(b - c) + d)
//   q = 6(a - 2b + c)
//   r = 3(b - a)
// Solve for t, keeping only those that fit between 0 < t < 1.
int findCubicExtrema(float a, float b, float c, float d, float tValues[2])
{
    // Divide p, q, and r by 3 to simplify the equations.
    float p = d - a + 3*(b - c);
    float q = 2*(a - b - b + c);
    float r = b - a;

    return findUnitQuadRoots(p, q, r, tValues);
}

void interpolateCubicCoords(float controlPoint0, float controlPoint1, float controlPoint2, float controlPoint3, float* dst, float t)
{
    float ab = interpolate(controlPoint0, controlPoint1, t);
    float bc = interpolate(controlPoint1, controlPoint2, t);
    float cd = interpolate(controlPoint2, controlPoint3, t);
    float abc = interpolate(ab, bc, t);
    float bcd = interpolate(bc, cd, t);
    float abcd = interpolate(abc, bcd, t);

    dst[0] = controlPoint0;
    dst[2] = ab;
    dst[4] = abc;
    dst[6] = abcd;
    dst[8] = bcd;
    dst[10] = cd;
    dst[12] = controlPoint3;
}

#ifndef NDEBUG
bool isUnitInterval(float x)
{
    return x > 0 && x < 1;
}
#endif

void chopCubicAtTValues(const FloatPoint src[4], FloatPoint dst[], const float tValues[], int roots)
{
#ifndef NDEBUG
    for (int i = 0; i < roots - 1; ++i) {
        ASSERT(isUnitInterval(tValues[i]));
        ASSERT(isUnitInterval(tValues[i+1]));
        ASSERT(tValues[i] < tValues[i+1]);
    }
#endif

    if (!roots) {
        // nothing to chop
        for (int j = 0; j < 4; ++j)
            dst[j] = src[j];
        return;
    }

    float t = tValues[0];
    FloatPoint tmp[4];
    for (int j = 0; j < 4; ++j)
        tmp[j] = src[j];

    for (int i = 0; i < roots; ++i) {
        chopCubicAt(tmp, dst, t);
        if (i == roots - 1)
            break;

        dst += 3;
        // Make tmp contain the remaining cubic (after the first chop).
        for (int j = 0; j < 4; ++j)
            tmp[j] = dst[j];

        // Watch out for the case that the renormalized t isn't in range.
        if (!safeUnitDivide(tValues[i+1] - tValues[i], 1.0f - tValues[i], t)) {
            // If it isn't, just create a degenerate cubic.
            dst[4] = dst[5] = dst[6] = tmp[3];
            break;
        }
    }
}

void flattenDoubleCubicYExtrema(FloatPoint coords[7])
{
    coords[2].setY(coords[3].y());
    coords[4].setY(coords[3].y());
}

int chopCubicAtYExtrema(const FloatPoint src[4], FloatPoint dst[10])
{
    float tValues[2];
    int roots = findCubicExtrema(src[0].y(), src[1].y(), src[2].y(), src[3].y(), tValues);

    chopCubicAtTValues(src, dst, tValues, roots);
    if (roots) {
        // we do some cleanup to ensure our Y extrema are flat
        flattenDoubleCubicYExtrema(&dst[0]);
        if (roots == 2)
            flattenDoubleCubicYExtrema(&dst[3]);
    }
    return roots;
}

} // anonymous namespace

// Public cubic operations.

void chopCubicAt(const FloatPoint src[4], FloatPoint dst[7], float t)
{
    ASSERT(t >= 0 && t <= 1);

    float output[14];
    interpolateCubicCoords(src[0].x(), src[1].x(), src[2].x(), src[3].x(), &output[0], t);
    interpolateCubicCoords(src[0].y(), src[1].y(), src[2].y(), src[3].y(), &output[1], t);
    for (int i = 0; i < 7; i++)
        dst[i].set(output[2 * i], output[2 * i + 1]);
}

// Public XRay queries.

bool xRayCrossesLine(const XRay& xRay, const FloatPoint pts[2], bool& ambiguous)
{
    ambiguous = false;

    // Determine quick discards.
    // Consider query line going exactly through point 0 to not
    // intersect, for symmetry with xRayCrossesMonotonicCubic.
    if (xRay.y() == pts[0].y()) {
        ambiguous = true;
        return false;
    }
    if (xRay.y() < pts[0].y() && xRay.y() < pts[1].y())
        return false;
    if (xRay.y() > pts[0].y() && xRay.y() > pts[1].y())
        return false;
    if (xRay.x() > pts[0].x() && xRay.x() > pts[1].x())
        return false;
    // Determine degenerate cases
    if (nearlyZero(pts[0].y() - pts[1].y()))
        return false;
    if (nearlyZero(pts[0].x() - pts[1].x())) {
        // We've already determined the query point lies within the
        // vertical range of the line segment.
        if (xRay.x() <= pts[0].x()) {
            ambiguous = (xRay.y() == pts[1].y());
            return true;
        }
        return false;
    }
    // Ambiguity check
    if (xRay.y() == pts[1].y()) {
        if (xRay.x() <= pts[1].x()) {
            ambiguous = true;
            return true;
        }
        return false;
    }
    // Full line segment evaluation
    float deltaY = pts[1].y() - pts[0].y();
    float deltaX = pts[1].x() - pts[0].x();
    float slope = deltaY / deltaX;
    float b = pts[0].y() - slope * pts[0].x();
    // Solve for x coordinate at y = xRay.y()
    float x = (xRay.y() - b) / slope;
    return xRay.x() <= x;
}

int numXRayCrossingsForCubic(const XRay& xRay, const FloatPoint cubic[4], bool& ambiguous)
{
    int numCrossings = 0;
    FloatPoint monotonicCubics[10];
    int numMonotonicCubics = 1 + chopCubicAtYExtrema(cubic, monotonicCubics);
    ambiguous = false;
    FloatPoint* monotonicCubicsPointer = &monotonicCubics[0];
    for (int i = 0; i < numMonotonicCubics; ++i) {
        if (xRayCrossesMonotonicCubic(xRay, monotonicCubicsPointer, ambiguous))
            ++numCrossings;
        if (ambiguous)
            return 0;
        monotonicCubicsPointer += 3;
    }
    return numCrossings;
}

/*
 * Based on C code from the article
 * "Testing the Convexity of a Polygon"
 * by Peter Schorn and Frederick Fisher,
 * (schorn@inf.ethz.ch, fred@kpc.com)
 * in "Graphics Gems IV", Academic Press, 1994
 */

static inline int convexCompare(const FloatSize& delta)
{
    return (delta.width() > 0) ? -1 : /* x coord diff, second pt > first pt */
           (delta.width() < 0) ?  1 : /* x coord diff, second pt < first pt */
           (delta.height() > 0) ? -1 : /* x coord same, second pt > first pt */
           (delta.height() < 0) ?  1 : /* x coord same, second pt > first pt */
           0; /* second pt equals first point */
}

static inline float convexCross(const FloatSize& p, const FloatSize& q)
{
    return p.width() * q.height() - p.height() * q.width();
}

static inline bool convexCheckTriple(const FloatSize& dcur, const FloatSize& dprev, int* curDir, int* dirChanges, int* angleSign)
{
    int thisDir = convexCompare(dcur);
    if (thisDir == -*curDir)
        ++*dirChanges;
    *curDir = thisDir;
    float cross = convexCross(dprev, dcur);
    if (cross > 0) {
        if (*angleSign == -1)
            return false;
        *angleSign = 1;
    } else if (cross < 0) {
        if (*angleSign == 1)
            return false;
        *angleSign = -1;
    }
    return true;
}

bool isConvex(const FloatPoint* vertices, int nVertices)
{
    int dirChanges = 0, angleSign = 0;
    FloatPoint second, third;
    FloatSize dprev, dcur;

    /* Get different point, return if less than 3 diff points. */
    if (nVertices < 3)
        return false;
    int i = 1;
    while (true) {
        second = vertices[i++];
        dprev = second - vertices[0];
        if (dprev.width() || dprev.height())
            break;
        /* Check if out of points. Check here to avoid slowing down cases
         * without repeated points.
         */
        if (i >= nVertices)
            return false;
    }
    FloatPoint saveSecond = second;
    int curDir = convexCompare(dprev);        /* Find initial direction */
    while (i < nVertices) {
        /* Get different point, break if no more points */
        third = vertices[i++];
        dcur = third - second;
        if (!dcur.width() && !dcur.height())
            continue;

        /* Check current three points */
        if (!convexCheckTriple(dcur, dprev, &curDir, &dirChanges, &angleSign)) 
            return false;
        second = third;     /* Remember ptr to current point. */
        dprev = dcur;       /* Remember current delta. */
    }

    /* Must check for direction changes from last vertex back to first */
    third = vertices[0];                  /* Prepare for 'ConvexCheckTriple' */
    dcur = third - second;
    if (convexCompare(dcur)) {
        if (!convexCheckTriple(dcur, dprev, &curDir, &dirChanges, &angleSign)) 
            return false;
        second = third;     /* Remember ptr to current point. */
        dprev = dcur;       /* Remember current delta. */
    }

    /* and check for direction changes back to second vertex */
    dcur = saveSecond - second;
    if (!convexCheckTriple(dcur, dprev, &curDir, &dirChanges, &angleSign)) 
        return false;

    /* Decide on polygon type given accumulated status */
    if (dirChanges > 2)
        return false;

    if (angleSign > 0 || angleSign < 0)
        return true;
    return false;
}

} // namespace LoopBlinnMathUtils
} // namespace WebCore

#endif