| Differences between
and this patch
- WebCore/ChangeLog +74 lines
Lines 1-3 WebCore/ChangeLog_sec1
1
2010-09-09  Kenneth Russell  <kbr@google.com>
2
3
        Reviewed by NOBODY (OOPS!).
4
5
        Incorporate algorithm for processing paths into GPU-renderable triangle meshes
6
        https://bugs.webkit.org/show_bug.cgi?id=45521
7
8
        Adding an implementation of Loop and Blinn's GPU accelerated path
9
        rendering algorithm from GPU Gems 3. This implementation pays
10
        particular attention to the efficiency of the curve subdivision
11
        phase needed for correct rendering. It utilizes the OpenGL utility
12
        library tessellator for triangulation of the interior of the
13
        shape. The regions handled by Loop and Blinn's algorithm are
14
        handled by the local triangulator previously incorporated.
15
16
        No tests yet; pixel tests will eventually be used to verify this
17
        algorithm and prevent regressions.
18
19
        * platform/graphics/gpu/LoopBlinnPathProcessor.cpp: Added.
20
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::Segment):
21
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::setup):
22
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::kind):
23
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::getPoint):
24
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::next):
25
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::prev):
26
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::setNext):
27
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::setPrev):
28
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::contour):
29
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::subdivide):
30
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::boundingBox):
31
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::numCrossingsForXRay):
32
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::numberOfTriangles):
33
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::getTriangle):
34
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::numberOfInteriorVertices):
35
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::getInteriorVertex):
36
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::markedForSubdivision):
37
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::setMarkedForSubdivision):
38
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::toString):
39
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::computeBoundingBox):
40
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::Contour):
41
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::add):
42
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::subdivide):
43
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::begin):
44
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::end):
45
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::isOrientedCounterClockwise):
46
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::setIsOrientedCounterClockwise):
47
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::boundingBox):
48
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::fillSide):
49
        (WebCore::LoopBlinnPathProcessorImplementation::Contour::setFillSide):
50
        (WebCore::LoopBlinnPathProcessorImplementation::Segment::triangulate):
51
        (WebCore::LoopBlinnPathProcessor::LoopBlinnPathProcessor):
52
        (WebCore::LoopBlinnPathProcessor::~LoopBlinnPathProcessor):
53
        (WebCore::LoopBlinnPathProcessor::process):
54
        (WebCore::LoopBlinnPathProcessor::buildContours):
55
        (WebCore::LoopBlinnPathProcessor::allSegmentsOverlappingY):
56
        (WebCore::valueToString):
57
        (WebCore::LoopBlinnPathProcessor::determineSidesToFill):
58
        (WebCore::LoopBlinnPathProcessor::determineOrientation):
59
        (WebCore::SweepData::SweepEvent::SweepEvent):
60
        (WebCore::SweepData::SweepEvent::setup):
61
        (WebCore::SweepData::SweepEvent::x):
62
        (WebCore::SweepData::SweepEvent::entry):
63
        (WebCore::SweepData::SweepEvent::interval):
64
        (WebCore::SweepData::SweepEvent::operator<):
65
        (WebCore::SweepData::trianglesOverlap):
66
        (WebCore::LoopBlinnPathProcessor::subdivideCurves):
67
        (WebCore::LoopBlinnPathProcessor::conditionallySubdivide):
68
        (WebCore::LoopBlinnPathProcessor::subdivideCurvesSlow):
69
        (WebCore::TessellationState::vertexCallback):
70
        (WebCore::TessellationState::combineCallback):
71
        (WebCore::TessellationState::edgeFlagCallback):
72
        (WebCore::LoopBlinnPathProcessor::tessellateInterior):
73
        * platform/graphics/gpu/LoopBlinnPathProcessor.h: Added.
74
1
2010-09-09  Kent Tamura  <tkent@chromium.org>
75
2010-09-09  Kent Tamura  <tkent@chromium.org>
2
76
3
        Unreviewed, build fix for Qt.
77
        Unreviewed, build fix for Qt.
- WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.cpp +1224 lines
Line 0 WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.cpp_sec1
1
/*
2
 * Copyright (C) 2010 Google Inc. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 *
8
 * 1.  Redistributions of source code must retain the above copyright
9
 *     notice, this list of conditions and the following disclaimer.
10
 * 2.  Redistributions in binary form must reproduce the above copyright
11
 *     notice, this list of conditions and the following disclaimer in the
12
 *     documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
 */
25
26
#include "config.h"
27
28
#include "LoopBlinnPathProcessor.h"
29
30
#include "FloatPoint.h"
31
#include "FloatRect.h"
32
#include "LoopBlinnClassifier.h"
33
#include "LoopBlinnConstants.h"
34
#include "LoopBlinnLocalTriangulator.h"
35
#include "LoopBlinnMathUtils.h"
36
#include "LoopBlinnPathCache.h"
37
#include "LoopBlinnTextureCoords.h"
38
#include "PODArena.h"
39
#include "PODIntervalTree.h"
40
#include "Path.h"
41
#include "thirdparty/glu/internal_glu.h"
42
#include <algorithm>
43
#include <wtf/Assertions.h>
44
45
#if PLATFORM(SKIA)
46
#include "SkGeometry.h"
47
#include "SkPath.h"
48
#include "SkScalar.h"
49
#else
50
// Must port to your platform.
51
#endif
52
53
namespace WebCore {
54
55
using LoopBlinnMathUtils::XRay;
56
using LoopBlinnMathUtils::chopCubicAt;
57
using LoopBlinnMathUtils::numXRayCrossingsForCubic;
58
using LoopBlinnMathUtils::trianglesOverlap;
59
using LoopBlinnMathUtils::xRayCrossesLine;
60
using LoopBlinnPathProcessorImplementation::Contour;
61
using LoopBlinnPathProcessorImplementation::Segment;
62
63
namespace {
64
65
#ifndef NDEBUG
66
inline String valueToString(const FloatRect& arg)
67
{
68
    StringBuilder builder;
69
    builder.append("[FloatRect left=");
70
    builder.append(String::number(arg.left()));
71
    builder.append(" top=");
72
    builder.append(String::number(arg.top()));
73
    builder.append(" right=");
74
    builder.append(String::number(arg.right()));
75
    builder.append(" bottom=");
76
    builder.append(String::number(arg.bottom()));
77
    builder.append("]");
78
    return builder.toString();
79
}
80
#endif
81
82
} // anonymous namespace
83
84
namespace LoopBlinnPathProcessorImplementation {
85
86
//----------------------------------------------------------------------
87
// Segment
88
//
89
90
// Describes a segment of the path: either a cubic or a line segment.
91
// These are stored in a doubly linked list to speed up curve
92
// subdivision, which occurs due to either rendering artifacts in the
93
// loop case or due to overlapping triangles.
94
class Segment : public Noncopyable {
95
public:
96
    enum Kind {
97
        Cubic,
98
        Line
99
    };
100
101
    // No-argument constructor allows construction by the PODArena class.
102
    Segment()
103
         : m_arena(0)
104
         , m_kind(Cubic)
105
         , m_prev(0)
106
         , m_next(0)
107
         , m_contour(0)
108
         , m_triangulator(0)
109
         , m_markedForSubdivision(false)
110
    {
111
    }
112
113
    // Initializer for cubic curve segments.
114
    void setup(PODArena* arena,
115
               Contour* contour,
116
               FloatPoint cp0,
117
               FloatPoint cp1,
118
               FloatPoint cp2,
119
               FloatPoint cp3)
120
    {
121
        m_arena = arena;
122
        m_contour = contour;
123
        m_kind = Cubic;
124
        m_points[0] = cp0;
125
        m_points[1] = cp1;
126
        m_points[2] = cp2;
127
        m_points[3] = cp3;
128
        computeBoundingBox();
129
    }
130
131
    // Initializer for line segments.
132
    void setup(PODArena* arena,
133
               Contour* contour,
134
               FloatPoint p0,
135
               FloatPoint p1)
136
    {
137
        m_arena = arena;
138
        m_contour = contour;
139
        m_kind = Line;
140
        m_points[0] = p0;
141
        m_points[1] = p1;
142
        computeBoundingBox();
143
    }
144
145
    Kind kind() const { return m_kind; }
146
147
    // Returns the i'th control point, 0 <= i < 4.
148
    const FloatPoint& getPoint(int i)
149
    {
150
        ASSERT(i >= 0 && i < 4);
151
        return m_points[i];
152
    }
153
154
    Segment* next() const { return m_next; }
155
    Segment* prev() const { return m_prev; }
156
157
    void setNext(Segment* next) { m_next = next; }
158
    void setPrev(Segment* prev) { m_prev = prev; }
159
160
    // The contour this segment belongs to.
161
    Contour* contour() const { return m_contour; }
162
163
    // Subdivides the current segment at the given parameter value (0 <=
164
    // t <= 1) and replaces it with the two newly created Segments in
165
    // the linked list, if possible. Returns a pointer to the leftmost
166
    // Segment.
167
    Segment* subdivide(float param)
168
    {
169
        FloatPoint dst[7];
170
        chopCubicAt(m_points, dst, param);
171
        Segment* left = m_arena->allocateObject<Segment>();
172
        Segment* right = m_arena->allocateObject<Segment>();
173
        left->setup(m_arena, m_contour, dst[0], dst[1], dst[2], dst[3]);
174
        right->setup(m_arena, m_contour, dst[3], dst[4], dst[5], dst[6]);
175
        left->setNext(right);
176
        right->setPrev(left);
177
        // Try to set up a link between "this->prev()" and "left".
178
        if (prev()) {
179
            left->setPrev(prev());
180
            prev()->setNext(left);
181
        }
182
        // Try to set up a link between "this->next()" and "right".
183
        Segment* n = next();
184
        if (n) {
185
            right->setNext(n);
186
            n->setPrev(right);
187
        }
188
        // Set up a link between "this" and "left"; this is only to
189
        // provide a certain amount of continuity during forward iteration.
190
        setNext(left);
191
        return left;
192
    }
193
194
    // Subdivides the current segment at the halfway point and replaces
195
    // it with the two newly created Segments in the linked list, if
196
    // possible. Returns a pointer to the leftmost Segment.
197
    Segment* subdivide() { return subdivide(0.5f); }
198
199
    const FloatRect& boundingBox() const { return m_boundingBox; }
200
201
    // Computes the number of times a query line starting at the given
202
    // point and extending to x=+infinity crosses this segment. Outgoing
203
    // "ambiguous" argument indicates whether the query intersected an
204
    // endpoint or tangent point of the segment, indicating that another
205
    // query point is preferred.
206
    int numCrossingsForXRay(const XRay& xRay, bool& ambiguous) const
207
    {
208
        if (m_kind == Cubic)
209
            // Should consider caching the monotonic cubics.
210
            return numXRayCrossingsForCubic(xRay, m_points, ambiguous);
211
212
        return xRayCrossesLine(xRay, m_points, ambiguous) ? 1 : 0;
213
    }
214
215
    // Performs a local triangulation of the control points in this
216
    // segment. This operation only makes sense for cubic type segments.
217
    // texCoords may be null when the klm coordinates have not been
218
    // computed yet.
219
    void triangulate(LoopBlinnLocalTriangulator::InsideEdgeComputation computeInsideEdges,
220
                     const LoopBlinnTextureCoords::Result* texCoords);
221
222
    // Returns the number of control point triangles associated with
223
    // this segment.
224
    int numberOfTriangles() const
225
    {
226
        if (!m_triangulator)
227
            return 0;
228
        return m_triangulator->numberOfTriangles();
229
    }
230
231
    // Fetches the given control point triangle for this segment.
232
    LoopBlinnLocalTriangulator::Triangle* getTriangle(int index)
233
    {
234
        ASSERT(m_triangulator);
235
        return m_triangulator->getTriangle(index);
236
    }
237
238
    // Number of vertices along the inside edge of this segment. This
239
    // can be called either for line or cubic type segments.
240
    int numberOfInteriorVertices() const
241
    {
242
        if (m_kind == Cubic) {
243
            if (m_triangulator)
244
                return m_triangulator->numberOfInteriorVertices();
245
246
            return 0;
247
        }
248
249
        return 2;
250
    }
251
252
    // Returns the given interior vertex, 0 <= index < numberOfInteriorVertices().
253
    FloatPoint getInteriorVertex(int index) const
254
    {
255
        ASSERT(index >= 0 && index < numberOfInteriorVertices());
256
        if (m_kind == Cubic) {
257
            FloatPoint res;
258
            if (m_triangulator) {
259
                LoopBlinnLocalTriangulator::Vertex* vertex = m_triangulator->getInteriorVertex(index);
260
                if (vertex)
261
                    res.set(vertex->xyCoordinates().x(), vertex->xyCoordinates().y());
262
            }
263
            return res;
264
        }
265
266
        return m_points[index];
267
    }
268
269
    // State to assist with curve subdivision.
270
    bool markedForSubdivision() const { return m_markedForSubdivision; }
271
    void setMarkedForSubdivision(bool markedForSubdivision) { m_markedForSubdivision = markedForSubdivision; }
272
273
#ifndef NDEBUG
274
    // Suppport for printing Segments.
275
    String toString() const
276
    {
277
        StringBuilder builder;
278
        builder.append("[Segment kind=");
279
        builder.append(kind() == Line ? "line" : "cubic");
280
        builder.append(" boundingBox=");
281
        builder.append(valueToString(boundingBox()));
282
        builder.append(" contour=0x");
283
        builder.append(String::format("%p", contour()));
284
        builder.append(" markedForSubdivision=");
285
        builder.append(markedForSubdivision() ? "true" : "false");
286
        builder.append("]");
287
        return builder.toString();
288
    }
289
#endif
290
291
 private:
292
    // Computes the bounding box of this Segment.
293
    void computeBoundingBox()
294
    {
295
        switch (m_kind) {
296
        case Cubic:
297
            m_boundingBox.fitToPoints(m_points[0], m_points[1], m_points[2], m_points[3]);
298
            break;
299
300
        case Line:
301
            m_boundingBox.fitToPoints(m_points[0], m_points[1]);
302
            break;
303
        }
304
    }
305
306
    PODArena* m_arena;
307
    Kind m_kind;
308
    FloatPoint m_points[4];
309
    Segment* m_prev;
310
    Segment* m_next;
311
    Contour* m_contour;
312
    FloatRect m_boundingBox;
313
    LoopBlinnLocalTriangulator* m_triangulator;
314
    bool m_markedForSubdivision;
315
};
316
317
//----------------------------------------------------------------------
318
// Contour
319
//
320
321
// Describes a closed contour of the path.
322
class Contour : public Noncopyable {
323
public:
324
    Contour()
325
    {
326
        m_first = &m_sentinel;
327
        m_first->setNext(m_first);
328
        m_first->setPrev(m_first);
329
        m_isOrientedCounterClockwise = true;
330
        m_boundingBoxDirty = false;
331
        m_fillSide = LoopBlinnConstants::RightSide;
332
    }
333
334
    void add(Segment* segment)
335
    {
336
        if (m_first == &m_sentinel) {
337
            // First element is the sentinel. Replace it with the incoming
338
            // segment.
339
            segment->setNext(m_first);
340
            segment->setPrev(m_first);
341
            m_first->setNext(segment);
342
            m_first->setPrev(segment);
343
            m_first = segment;
344
        } else {
345
            // m_first->prev() is the sentinel.
346
            Segment* sentinel = m_first->prev();
347
            Segment* last = sentinel->prev();
348
            last->setNext(segment);
349
            segment->setPrev(last);
350
            segment->setNext(sentinel);
351
            sentinel->setPrev(segment);
352
        }
353
        m_boundingBoxDirty = true;
354
    }
355
356
    // Subdivides the given segment at the given parametric value.
357
    // Returns a pointer to the first of the two portions of the
358
    // subdivided segment.
359
    Segment* subdivide(Segment* segment, float param)
360
    {
361
        Segment* left = segment->subdivide(param);
362
        if (m_first == segment)
363
            m_first = left;
364
        return left;
365
    }
366
367
    // Subdivides the given segment at the halfway point. Returns a
368
    // pointer to the first of the two portions of the subdivided
369
    // segment.
370
    Segment* subdivide(Segment* segment)
371
    {
372
        Segment* left = segment->subdivide();
373
        if (m_first == segment)
374
            m_first = left;
375
        return left;
376
    }
377
378
    // Returns the first segment in the contour for iteration.
379
    Segment* begin() const { return m_first; }
380
381
    // Returns the last segment in the contour for iteration. Callers
382
    // should not iterate over this segment. In other words:
383
    //  for (Segment* cur = contour->begin();
384
    //       cur != contour->end();
385
    //       cur = cur->next()) {
386
    //    // .. process cur ...
387
    //  }
388
    Segment* end() const { return m_first->prev(); }
389
390
    bool isOrientedCounterClockwise() const { return m_isOrientedCounterClockwise; }
391
    void setIsOrientedCounterClockwise(bool isOrientedCounterClockwise) { m_isOrientedCounterClockwise = isOrientedCounterClockwise; }
392
393
    const FloatRect& boundingBox() const
394
    {
395
        if (m_boundingBoxDirty) {
396
            bool first = true;
397
            for (Segment* cur = begin(); cur != end(); cur = cur->next()) {
398
                if (first)
399
                    m_boundingBox = cur->boundingBox();
400
                else
401
                    m_boundingBox.unite(cur->boundingBox());
402
                first = false;
403
            }
404
405
            m_boundingBoxDirty = false;
406
        }
407
        return m_boundingBox;
408
    }
409
410
    // Returns which side of this contour is filled.
411
    LoopBlinnConstants::FillSide fillSide() const
412
    {
413
        return m_fillSide;
414
    }
415
416
    void setFillSide(LoopBlinnConstants::FillSide fillSide)
417
    {
418
        m_fillSide = fillSide;
419
    }
420
421
private:
422
    // The start of the segment chain. The segments are kept in a
423
    // circular doubly linked list for rapid access to the beginning and
424
    // end.
425
    Segment* m_first;
426
427
    // The sentinel element at the end of the chain, needed for
428
    // reasonable iteration semantics.
429
    Segment m_sentinel;
430
431
    bool m_isOrientedCounterClockwise;
432
433
    mutable FloatRect m_boundingBox;
434
    mutable bool m_boundingBoxDirty;
435
436
    // Which side of this contour should be filled.
437
    LoopBlinnConstants::FillSide m_fillSide;
438
};
439
440
//----------------------------------------------------------------------
441
// Segment
442
//
443
444
// Definition of Segment::triangulate(), which must come after
445
// declaration of Contour.
446
void Segment::triangulate(LoopBlinnLocalTriangulator::InsideEdgeComputation computeInsideEdges,
447
                          const LoopBlinnTextureCoords::Result* texCoords)
448
{
449
    ASSERT(m_kind == Cubic);
450
    if (!m_triangulator)
451
        m_triangulator = m_arena->allocateObject<LoopBlinnLocalTriangulator>();
452
    m_triangulator->reset();
453
    for (int i = 0; i < 4; i++) {
454
        LoopBlinnLocalTriangulator::Vertex* vertex = m_triangulator->getVertex(i);
455
        if (texCoords) {
456
            vertex->set(getPoint(i).x(),
457
                        getPoint(i).y(),
458
                        texCoords->klmCoordinates[i].x(),
459
                        texCoords->klmCoordinates[i].y(),
460
                        texCoords->klmCoordinates[i].z());
461
        } else {
462
            vertex->set(getPoint(i).x(),
463
                        getPoint(i).y(),
464
                        // No texture coordinates yet
465
                        0, 0, 0);
466
        }
467
    }
468
    m_triangulator->triangulate(computeInsideEdges, contour()->fillSide());
469
}
470
471
} // namespace LoopBlinnPathProcessorImplementation
472
473
//----------------------------------------------------------------------
474
// LoopBlinnPathProcessor
475
//
476
477
LoopBlinnPathProcessor::LoopBlinnPathProcessor()
478
    : m_arena(PODArena::create())
479
#ifndef NDEBUG
480
    , m_verboseLogging(false)
481
#endif
482
{
483
}
484
485
LoopBlinnPathProcessor::LoopBlinnPathProcessor(PassRefPtr<PODArena> arena)
486
    : m_arena(arena)
487
#ifndef NDEBUG
488
    , m_verboseLogging(false)
489
#endif
490
{
491
}
492
493
LoopBlinnPathProcessor::~LoopBlinnPathProcessor()
494
{
495
}
496
497
void LoopBlinnPathProcessor::process(const Path& path, LoopBlinnPathCache& cache)
498
{
499
    buildContours(path);
500
501
    // Run plane-sweep algorithm to determine overlaps of control point
502
    // curves and subdivide curves appropriately.
503
    subdivideCurves();
504
505
    // Determine orientations of countours. Based on orientation and the
506
    // number of curve crossings at a random point on the contour,
507
    // determine whether to fill the left or right side of the contour.
508
    determineSidesToFill();
509
510
    // Classify curves, compute texture coordinates and subdivide as
511
    // necessary to eliminate rendering artifacts. Do the final
512
    // triangulation of the curve segments, determining the path along
513
    // the interior of the shape.
514
    for (Vector<Contour*>::iterator iter = m_contours.begin();
515
         iter != m_contours.end();
516
         iter++) {
517
        Contour* cur = *iter;
518
        for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) {
519
            if (seg->kind() == Segment::Cubic) {
520
                LoopBlinnClassifier::Result classification = LoopBlinnClassifier::classify(seg->getPoint(0),
521
                                                                                           seg->getPoint(1),
522
                                                                                           seg->getPoint(2),
523
                                                                                           seg->getPoint(3));
524
#ifndef NDEBUG
525
                if (m_verboseLogging)
526
                    LOG_ERROR("Classification: %d", (int) classification.curveType);
527
#endif
528
                LoopBlinnTextureCoords::Result texCoords =
529
                    LoopBlinnTextureCoords::compute(classification, cur->fillSide());
530
                if (texCoords.hasRenderingArtifact) {
531
                    // FIXME: there is a problem where the algorithm
532
                    // sometimes fails to converge when splitting at the
533
                    // subdivision parameter value. For the time being,
534
                    // split halfway.
535
                    cur->subdivide(seg);
536
                    // Next iteration will handle the newly subdivided curves
537
                } else {
538
                    if (!texCoords.isLineOrPoint) {
539
                        seg->triangulate(LoopBlinnLocalTriangulator::ComputeInsideEdges, &texCoords);
540
                        for (int i = 0; i < seg->numberOfTriangles(); i++) {
541
                            LoopBlinnLocalTriangulator::Triangle* triangle = seg->getTriangle(i);
542
                            for (int j = 0; j < 3; j++) {
543
                                LoopBlinnLocalTriangulator::Vertex* vert = triangle->getVertex(j);
544
                                cache.addVertex(vert->xyCoordinates().x(),
545
                                                vert->xyCoordinates().y(),
546
                                                vert->klmCoordinates().x(),
547
                                                vert->klmCoordinates().y(),
548
                                                vert->klmCoordinates().z());
549
                            }
550
                        }
551
#ifdef LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES
552
                        // Show the end user the interior edges as well
553
                        for (int i = 1; i < seg->numberOfInteriorVertices(); i++) {
554
                            FloatPoint vert = seg->getInteriorVertex(i);
555
                            // Duplicate previous vertex to be able to draw GL_LINES
556
                            FloatPoint prev = seg->getInteriorVertex(i - 1);
557
                            cache.addInteriorEdgeVertex(prev.x(), prev.y());
558
                            cache.addInteriorEdgeVertex(vert.x(), vert.y());
559
                        }
560
#endif // LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES
561
                    }
562
                }
563
            }
564
        }
565
    }
566
567
    // Run the interior paths through a tessellation algorithm
568
    // supporting multiple contours.
569
    tessellateInterior(cache);
570
}
571
572
void LoopBlinnPathProcessor::buildContours(const Path& path)
573
{
574
    // Clear out the contours
575
    m_contours.clear();
576
#if PLATFORM(SKIA)
577
    SkPath::Iter iter(*path.platformPath(), false);
578
    SkPoint points[4];
579
    SkPath::Verb verb;
580
    Contour* contour = 0;
581
    SkPoint curPoint = { 0 };
582
    SkPoint moveToPoint = { 0 };
583
    do {
584
        verb = iter.next(points);
585
        if (verb != SkPath::kMove_Verb) {
586
            if (!contour) {
587
                contour = m_arena->allocateObject<Contour>();
588
                m_contours.append(contour);
589
            }
590
        }
591
        switch (verb) {
592
        case SkPath::kMove_Verb: {
593
            contour = m_arena->allocateObject<Contour>();
594
            m_contours.append(contour);
595
            curPoint = points[0];
596
            moveToPoint = points[0];
597
#ifndef NDEBUG
598
            if (m_verboseLogging)
599
                LOG_ERROR("MoveTo (%f, %f)", points[0].fX, points[0].fY);
600
#endif
601
            break;
602
        }
603
        case SkPath::kLine_Verb: {
604
            Segment* segment = m_arena->allocateObject<Segment>();
605
            if (iter.isCloseLine()) {
606
                segment->setup(m_arena.get(), contour, curPoint, points[1]);
607
#ifndef NDEBUG
608
                if (m_verboseLogging)
609
                    LOG_ERROR("CloseLineTo (%f, %f), (%f, %f)", curPoint.fX, curPoint.fY, points[1].fX, points[1].fY);
610
#endif
611
                contour->add(segment);
612
                contour = 0;
613
            } else {
614
                segment->setup(m_arena.get(), contour, points[0], points[1]);
615
#ifndef NDEBUG
616
                if (m_verboseLogging)
617
                    LOG_ERROR("LineTo (%f, %f), (%f, %f)", points[0].fX, points[0].fY, points[1].fX, points[1].fY);
618
#endif
619
                contour->add(segment);
620
                curPoint = points[1];
621
            }
622
            break;
623
        }
624
        case SkPath::kQuad_Verb: {
625
            // Need to degree elevate the quadratic into a cubic
626
            SkPoint cubic[4];
627
            SkConvertQuadToCubic(points, cubic);
628
            Segment* segment = m_arena->allocateObject<Segment>();
629
            segment->setup(m_arena.get(), contour,
630
                           cubic[0], cubic[1], cubic[2], cubic[3]);
631
#ifndef NDEBUG
632
            if (m_verboseLogging)
633
                LOG_ERROR("Quad->CubicTo (%f, %f), (%f, %f), (%f, %f), (%f, %f)", cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY);
634
#endif
635
            contour->add(segment);
636
            curPoint = cubic[3];
637
            break;
638
        }
639
        case SkPath::kCubic_Verb: {
640
            Segment* segment = m_arena->allocateObject<Segment>();
641
            segment->setup(m_arena.get(), contour, points[0], points[1], points[2], points[3]);
642
#ifndef NDEBUG
643
            if (m_verboseLogging)
644
                LOG_ERROR("CubicTo (%f, %f), (%f, %f), (%f, %f), (%f, %f)", points[0].fX, points[0].fY, points[1].fX, points[1].fY, points[2].fX, points[2].fY, points[3].fX, points[3].fY);
645
#endif
646
            contour->add(segment);
647
            curPoint = points[3];
648
            break;
649
        }
650
        case SkPath::kClose_Verb: {
651
            Segment* segment = m_arena->allocateObject<Segment>();
652
            segment->setup(m_arena.get(), contour, curPoint, moveToPoint);
653
#ifndef NDEBUG
654
            if (m_verboseLogging)
655
                LOG_ERROR("Close (%f, %f) -> (%f, %f)", curPoint.fX, curPoint.fY, moveToPoint.fX, moveToPoint.fY);
656
#endif
657
            contour->add(segment);
658
            contour = 0;
659
        }
660
        case SkPath::kDone_Verb:
661
            break;
662
        }
663
    } while (verb != SkPath::kDone_Verb);
664
#else // !PLATFORM(SKIA)
665
    // Must port to your platform.
666
    ASSERT_NOT_REACHED();
667
#endif
668
}
669
670
Vector<Segment*> LoopBlinnPathProcessor::allSegmentsOverlappingY(Contour* queryContour, float x, float y)
671
{
672
    Vector<Segment*> res;
673
    for (Vector<Contour*>::iterator iter = m_contours.begin();
674
         iter != m_contours.end();
675
         iter++) {
676
        Contour* cur = *iter;
677
        for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) {
678
            const FloatRect& boundingBox = seg->boundingBox();
679
            if (boundingBox.top() <= y && y <= boundingBox.bottom())
680
                res.append(seg);
681
        }
682
    }
683
    return res;
684
}
685
686
// Uncomment this to debug the orientation computation.
687
// #define GPU_PATH_PROCESSOR_DEBUG_ORIENTATION
688
689
#ifndef NDEBUG
690
// Routines needed to print the types of IntervalNodes we instantiate
691
// in this file.
692
693
inline String valueToString(const float& value)
694
{
695
    return String::number(value);
696
}
697
698
inline String valueToString(void* const& value)
699
{
700
    return String::format("0x%p", value);
701
}
702
#endif
703
704
void LoopBlinnPathProcessor::determineSidesToFill()
705
{
706
    // Loop and Blinn's algorithm can only easily emulate the even/odd
707
    // fill rule, and only for non-intersecting curves. We can determine
708
    // which side of each curve segment to fill based on its
709
    // clockwise/counterclockwise orientation and how many other
710
    // contours surround it.
711
712
    // To optimize the query of all curve segments intersecting a
713
    // horizontal line going to x=+infinity, we build up an interval
714
    // tree whose keys are the y extents of the segments.
715
    PODIntervalTree<float, Segment*> tree(m_arena);
716
    typedef PODIntervalTree<float, Segment*>::IntervalType IntervalType;
717
718
    for (Vector<Contour*>::iterator iter = m_contours.begin();
719
         iter != m_contours.end();
720
         iter++) {
721
        Contour* cur = *iter;
722
        determineOrientation(cur);
723
        for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) {
724
            const FloatRect& boundingBox = seg->boundingBox();
725
            tree.add(tree.createInterval(boundingBox.top(), boundingBox.bottom(), seg));
726
        }
727
    }
728
729
    // Now iterate through the contours and pick a random segment (in
730
    // this case we use the first) and a random point on that segment.
731
    // Find all segments from other contours which intersect this one
732
    // and count the number of crossings a horizontal line to
733
    // x=+infinity makes with those contours. This combined with the
734
    // orientation of the curve tells us which side to fill -- again,
735
    // assuming an even/odd fill rule, which is all we can easily
736
    // handle.
737
    for (Vector<Contour*>::iterator iter = m_contours.begin();
738
         iter != m_contours.end();
739
         iter++) {
740
        Contour* cur = *iter;
741
742
        bool ambiguous = true;
743
        int numCrossings = 0;
744
745
        // For each contour, attempt to find a point on the contour which,
746
        // when we cast an XRay, does not intersect the other contours at
747
        // an ambiguous point (the junction between two curves or at a
748
        // tangent point). Ambiguous points make the determination of
749
        // whether this contour is contained within another fragile. Note
750
        // that this loop is only an approximation to the selection of a
751
        // good casting point. We could as well evaluate a segment to
752
        // determine a point upon it.
753
        for (Segment* seg = cur->begin();
754
             ambiguous && seg != cur->end();
755
             seg = seg->next()) {
756
            numCrossings = 0;
757
            // We use a zero-sized vertical interval for the query.
758
            Vector<IntervalType> overlaps = tree.allOverlaps(tree.createInterval(seg->getPoint(0).y(),
759
                                                                                 seg->getPoint(0).y(),
760
                                                                                 0));
761
#if defined(GPU_PATH_PROCESSOR_DEBUG_ORIENTATION) && !defined(NDEBUG)
762
            Vector<Segment*> slowOverlaps = allSegmentsOverlappingY(cur, seg->getPoint(0).x(), seg->getPoint(0).y());
763
            if (overlaps.size() != slowOverlaps.size()) {
764
                LOG_ERROR("For query point (%f, %f) on contour 0x%p:", seg->getPoint(0).x(), seg->getPoint(0).y(), cur);
765
                LOG_ERROR(" overlaps:");
766
                for (size_t i = 0; i < overlaps.size(); i++)
767
                    LOG_ERROR("  %d: %s", i+1, overlaps[i].data()->toString().ascii().data());
768
                LOG_ERROR(" slowOverlaps:");
769
                for (size_t i = 0; i < slowOverlaps.size(); i++)
770
                    LOG_ERROR("  %d: %s", (i+1) slowOverlaps[i]->toString());
771
                LOG_ERROR("Interval tree:");
772
                tree.dump();
773
            }
774
            ASSERT(overlaps.size() == slowOverlaps.size());
775
#endif // defined(GPU_PATH_PROCESSOR_DEBUG_ORIENTATION) && !defined(NDEBUG)
776
            for (Vector<IntervalType>::iterator iter = overlaps.begin();
777
                 iter != overlaps.end();
778
                 iter++) {
779
                const IntervalType& interval = *iter;
780
                Segment* querySegment = interval.data();
781
                // Ignore segments coming from the same contour.
782
                if (querySegment->contour() != cur) {
783
                    // Only perform queries that can affect the computation.
784
                    const FloatRect& boundingBox = querySegment->contour()->boundingBox();
785
                    if (seg->getPoint(0).x() >= boundingBox.left()
786
                        && seg->getPoint(0).x() <= boundingBox.right()) {
787
                        numCrossings += querySegment->numCrossingsForXRay(seg->getPoint(0),
788
                                                                          ambiguous);
789
                        if (ambiguous) {
790
#ifndef NDEBUG
791
                            if (m_verboseLogging) {
792
                                LOG_ERROR("Ambiguous intersection query at point (%f, %f)", seg->getPoint(0).x(), seg->getPoint(0).y());
793
                                LOG_ERROR("Query segment: %s", querySegment->toString().ascii().data());
794
                            }
795
#endif
796
                            break; // Abort iteration over overlaps.
797
                        }
798
                    }
799
                }
800
            }
801
        } // for (Segment* seg = cur->begin(); ...
802
803
        cur->setFillSide((cur->isOrientedCounterClockwise() ^ (numCrossings & 1)) ? LoopBlinnConstants::LeftSide : LoopBlinnConstants::RightSide);
804
    }
805
}
806
807
void LoopBlinnPathProcessor::determineOrientation(Contour* contour)
808
{
809
    // Determine signed area of the polygon represented by the points
810
    // along the segments. Consider this an approximation to the true
811
    // orientation of the polygon; it probably won't handle
812
    // self-intersecting curves correctly.
813
    //
814
    // There is also a pretty basic assumption here that the contour is
815
    // closed.
816
    float signedArea = 0;
817
    for (Segment* seg = contour->begin();
818
         seg != contour->end();
819
         seg = seg->next()) {
820
        int limit = (seg->kind() == Segment::Cubic) ? 4 : 2;
821
        for (int i = 1; i < limit; i++) {
822
            const FloatPoint& prevPoint = seg->getPoint(i - 1);
823
            const FloatPoint& point = seg->getPoint(i);
824
            float curArea = prevPoint.x() * point.y() - prevPoint.y() * point.x();
825
#ifndef NDEBUG
826
            if (m_verboseLogging)
827
                LOG_ERROR("Adding to signed area (%f, %f) -> (%f, %f) = %f", prevPoint.x(), prevPoint.y(), point.x(), point.y(), curArea);
828
#endif
829
            signedArea += curArea;
830
        }
831
    }
832
833
    if (signedArea > 0)
834
        contour->setIsOrientedCounterClockwise(true);
835
    else
836
        contour->setIsOrientedCounterClockwise(false);
837
}
838
839
namespace {
840
841
//----------------------------------------------------------------------
842
// Classes and typedefs needed for curve subdivision. These can't be scoped
843
// within the subdivideCurves() method itself, because templates then fail
844
// to instantiate.
845
846
// The user data which is placed in the PODIntervalTree.
847
struct SweepData {
848
    SweepData()
849
        : triangle(0)
850
        , segment(0)
851
    {
852
    }
853
854
    // The triangle this interval is associated with
855
    LoopBlinnLocalTriangulator::Triangle* triangle;
856
    // The segment the triangle is associated with
857
    Segment* segment;
858
};
859
860
typedef PODIntervalTree<float, SweepData*> SweepTree;
861
typedef SweepTree::IntervalType SweepInterval;
862
863
// The entry / exit events which occur at the minimum and maximum x
864
// coordinates of the control point triangles' bounding boxes.
865
//
866
// Note that this class requires its copy constructor and assignment
867
// operator since it needs to be stored in a Vector.
868
class SweepEvent {
869
public:
870
    SweepEvent()
871
        : m_x(0)
872
        , m_entry(false)
873
        , m_interval(0, 0, 0)
874
    {
875
    }
876
877
    // Initializes the SweepEvent.
878
    void setup(float x, bool entry, SweepInterval interval)
879
    {
880
        m_x = x;
881
        m_entry = entry;
882
        m_interval = interval;
883
    }
884
885
    float x() const { return m_x; }
886
    bool entry() const { return m_entry; }
887
    const SweepInterval& interval() const { return m_interval; }
888
889
    bool operator<(const SweepEvent& other) const
890
    {
891
        return m_x < other.m_x;
892
    }
893
894
private:
895
    float m_x;
896
    bool m_entry;
897
    SweepInterval m_interval;
898
};
899
900
bool trianglesOverlap(LoopBlinnLocalTriangulator::Triangle* t0,
901
                      LoopBlinnLocalTriangulator::Triangle* t1)
902
{
903
    return trianglesOverlap(t0->getVertex(0)->xyCoordinates(),
904
                            t0->getVertex(1)->xyCoordinates(),
905
                            t0->getVertex(2)->xyCoordinates(),
906
                            t1->getVertex(0)->xyCoordinates(),
907
                            t1->getVertex(1)->xyCoordinates(),
908
                            t1->getVertex(2)->xyCoordinates());
909
}
910
911
} // anonymous namespace
912
913
void LoopBlinnPathProcessor::subdivideCurves()
914
{
915
    // We need to determine all overlaps of all control point triangles
916
    // (from different segments, not the same segment) and, if any
917
    // exist, subdivide the associated curves.
918
    //
919
    // The plane-sweep algorithm determines all overlaps of a set of
920
    // rectangles in the 2D plane. Our problem maps very well to this
921
    // algorithm and significantly reduces the complexity compared to a
922
    // naive implementation.
923
    //
924
    // Each bounding box of a control point triangle is converted into
925
    // an "entry" event at its smallest X coordinate and an "exit" event
926
    // at its largest X coordinate. Each event has an associated
927
    // one-dimensional interval representing the Y span of the bounding
928
    // box. We sort these events by increasing X coordinate. We then
929
    // iterate through them. For each entry event we add the interval to
930
    // a side interval tree, and query this tree for overlapping
931
    // intervals. Any overlapping interval corresponds to an overlapping
932
    // bounding box. For each exit event we remove the associated
933
    // interval from the interval tree.
934
935
    Vector<Segment*> curSegments;
936
    Vector<Segment*> nextSegments;
937
938
    // Start things off by considering all of the segments
939
    for (Vector<Contour*>::iterator iter = m_contours.begin();
940
         iter != m_contours.end();
941
         iter++) {
942
        Contour* cur = *iter;
943
        for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) {
944
            if (seg->kind() == Segment::Cubic) {
945
                seg->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0);
946
                curSegments.append(seg);
947
            }
948
        }
949
    }
950
951
    // Subdivide curves at most this many times
952
    const int MaxIterations = 5;
953
    Vector<SweepInterval> overlaps;
954
955
    for (int currentIteration = 0; currentIteration < MaxIterations; ++currentIteration) {
956
        if (!curSegments.size())
957
            // Done
958
            break;
959
960
        Vector<SweepEvent> events;
961
        SweepTree tree(m_arena);
962
        for (Vector<Segment*>::iterator iter = curSegments.begin();
963
             iter != curSegments.end();
964
             iter++) {
965
            Segment* seg = *iter;
966
            if (seg->kind() == Segment::Cubic) {
967
                for (int i = 0; i < seg->numberOfTriangles(); i++) {
968
                    LoopBlinnLocalTriangulator::Triangle* triangle = seg->getTriangle(i);
969
                    FloatRect boundingBox;
970
                    boundingBox.fitToPoints(triangle->getVertex(0)->xyCoordinates(),
971
                                     triangle->getVertex(1)->xyCoordinates(),
972
                                     triangle->getVertex(2)->xyCoordinates());
973
                    // Ignore zero-width triangles to avoid issues with
974
                    // coincident entry and exit events for the same triangle
975
                    if (boundingBox.right() > boundingBox.left()) {
976
                        SweepData* data = m_arena->allocateObject<SweepData>();
977
                        data->triangle = triangle;
978
                        data->segment = seg;
979
                        SweepInterval interval = tree.createInterval(boundingBox.top(), boundingBox.bottom(), data);
980
                        // Add entry and exit events
981
                        SweepEvent event;
982
                        event.setup(boundingBox.left(), true, interval);
983
                        events.append(event);
984
                        event.setup(boundingBox.right(), false, interval);
985
                        events.append(event);
986
                    }
987
                }
988
            }
989
        }
990
991
        // Sort events by increasing X coordinate
992
        std::sort(events.begin(), events.end());
993
#ifndef NDEBUG
994
        for (size_t ii = 1; ii < events.size(); ++ii)
995
            ASSERT(events[ii - 1].x() <= events[ii].x());
996
#endif
997
998
        // Now iterate through the events
999
        for (Vector<SweepEvent>::iterator iter = events.begin();
1000
             iter != events.end();
1001
             iter++) {
1002
            SweepEvent event = *iter;
1003
            if (event.entry()) {
1004
                // Add this interval into the tree
1005
                tree.add(event.interval());
1006
                // See whether the associated segment has been subdivided yet
1007
                if (!event.interval().data()->segment->markedForSubdivision()) {
1008
                    // Query the tree
1009
                    overlaps.clear();
1010
                    tree.allOverlaps(event.interval(), overlaps);
1011
                    // Now see exactly which triangles overlap this one
1012
                    for (Vector<SweepInterval>::iterator iter = overlaps.begin();
1013
                         iter != overlaps.end();
1014
                         iter++) {
1015
                        SweepInterval overlap = *iter;
1016
                        // Only pay attention to overlaps from a different Segment
1017
                        if (event.interval().data()->segment != overlap.data()->segment) {
1018
                            // See whether the triangles actually overlap
1019
                            if (trianglesOverlap(event.interval().data()->triangle,
1020
                                                 overlap.data()->triangle)) {
1021
                                // Actually subdivide the segments.
1022
                                // Each one might already have been subdivided.
1023
                                Segment* seg = event.interval().data()->segment;
1024
                                conditionallySubdivide(seg, nextSegments);
1025
                                seg = overlap.data()->segment;
1026
                                conditionallySubdivide(seg, nextSegments);
1027
                            }
1028
                        }
1029
                    }
1030
                }
1031
            } else {
1032
                // Remove this interval from the tree
1033
                tree.remove(event.interval());
1034
            }
1035
        }
1036
1037
        curSegments = nextSegments;
1038
        nextSegments.clear();
1039
    }
1040
}
1041
1042
void LoopBlinnPathProcessor::conditionallySubdivide(Segment* seg, Vector<Segment*>& nextSegments)
1043
{
1044
    if (!seg->markedForSubdivision()) {
1045
        seg->setMarkedForSubdivision(true);
1046
        Segment* next = seg->contour()->subdivide(seg);
1047
        // Triangulate the newly subdivided segments.
1048
        next->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0);
1049
        next->next()->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0);
1050
        // Add them for the next iteration.
1051
        nextSegments.append(next);
1052
        nextSegments.append(next->next());
1053
    }
1054
}
1055
1056
#ifndef NDEBUG
1057
void LoopBlinnPathProcessor::subdivideCurvesSlow()
1058
{
1059
    // Alternate, significantly slower algorithm for curve subdivision
1060
    // for use in debugging.
1061
    Vector<Segment*> curSegments;
1062
    Vector<Segment*> nextSegments;
1063
1064
    // Start things off by considering all of the segments
1065
    for (Vector<Contour*>::iterator iter = m_contours.begin();
1066
         iter != m_contours.end();
1067
         iter++) {
1068
        Contour* cur = *iter;
1069
        for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) {
1070
            if (seg->kind() == Segment::Cubic) {
1071
                seg->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0);
1072
                curSegments.append(seg);
1073
            }
1074
        }
1075
    }
1076
1077
    // Subdivide curves at most this many times
1078
    const int MaxIterations = 5;
1079
1080
    for (int currentIteration = 0; currentIteration < MaxIterations; ++currentIteration) {
1081
        if (!curSegments.size())
1082
            // Done
1083
            break;
1084
1085
        for (Vector<Segment*>::iterator iter = curSegments.begin();
1086
             iter != curSegments.end();
1087
             iter++) {
1088
            Segment* seg = *iter;
1089
            if (seg->kind() == Segment::Cubic) {
1090
                for (Vector<Segment*>::iterator iter2 = curSegments.begin();
1091
                     iter2 != curSegments.end();
1092
                     iter2++) {
1093
                    Segment* seg2 = *iter2;
1094
                    if (seg2->kind() == Segment::Cubic && seg != seg2) {
1095
                        for (int i = 0; i < seg->numberOfTriangles(); i++) {
1096
                            LoopBlinnLocalTriangulator::Triangle* triangle = seg->getTriangle(i);
1097
                            for (int j = 0; j < seg2->numberOfTriangles(); j++) {
1098
                                LoopBlinnLocalTriangulator::Triangle* triangle2 = seg2->getTriangle(j);
1099
                                if (trianglesOverlap(triangle, triangle2)) {
1100
                                    conditionallySubdivide(seg, nextSegments);
1101
                                    conditionallySubdivide(seg2, nextSegments);
1102
                                }
1103
                            }
1104
                        }
1105
                    }
1106
                }
1107
            }
1108
        }
1109
1110
        curSegments = nextSegments;
1111
        nextSegments.clear();
1112
    }
1113
}
1114
#endif
1115
1116
namespace {
1117
1118
//----------------------------------------------------------------------
1119
// Structures and callbacks for tessellation of the interior region of
1120
// the contours.
1121
1122
// The user data for the GLU tessellator.
1123
struct TessellationState {
1124
    TessellationState(LoopBlinnPathCache& inputCache)
1125
        : cache(inputCache) { }
1126
1127
    LoopBlinnPathCache& cache;
1128
    Vector<void*> allocatedPointers;
1129
};
1130
1131
static void vertexCallback(void* vertexData, void* data)
1132
{
1133
    TessellationState* state = static_cast<TessellationState*>(data);
1134
    GLdouble* location = static_cast<GLdouble*>(vertexData);
1135
    state->cache.addInteriorVertex(static_cast<float>(location[0]),
1136
                                   static_cast<float>(location[1]));
1137
}
1138
1139
static void combineCallback(GLdouble coords[3], void* vertexData[4],
1140
                            GLfloat weight[4], void** outData,
1141
                            void* polygonData)
1142
{
1143
    TessellationState* state = static_cast<TessellationState*>(polygonData);
1144
    GLdouble* outVertex = static_cast<GLdouble*>(malloc(3 * sizeof(GLdouble)));
1145
    state->allocatedPointers.append(outVertex);
1146
    outVertex[0] = coords[0];
1147
    outVertex[1] = coords[1];
1148
    outVertex[2] = coords[2];
1149
    *outData = outVertex;
1150
}
1151
1152
static void edgeFlagCallback(GLboolean flag)
1153
{
1154
    // No-op just to prevent triangle strips and fans from being passed to us.
1155
}
1156
1157
} // anonymous namespace
1158
1159
void LoopBlinnPathProcessor::tessellateInterior(LoopBlinnPathCache& cache)
1160
{
1161
    // Because the GLU tessellator requires its input in
1162
    // double-precision format, we need to make a separate copy of the
1163
    // data.
1164
    Vector<GLdouble> vertexData;
1165
    Vector<size_t> contourEndings;
1166
    // For avoiding adding coincident vertices.
1167
    float curX = 0, curY = 0;
1168
    for (Vector<Contour*>::iterator iter = m_contours.begin();
1169
         iter != m_contours.end();
1170
         iter++) {
1171
        Contour* cur = *iter;
1172
        bool first = true;
1173
        for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) {
1174
            int numberOfInteriorVertices = seg->numberOfInteriorVertices();
1175
            for (int i = 0; i < numberOfInteriorVertices - 1; i++) {
1176
                FloatPoint point = seg->getInteriorVertex(i);
1177
                if (first) {
1178
                    first = false;
1179
                    vertexData.append(point.x());
1180
                    vertexData.append(point.y());
1181
                    vertexData.append(0);
1182
                    curX = point.x();
1183
                    curY = point.y();
1184
                } else if (point.x() != curX || point.y() != curY)  {
1185
                    vertexData.append(point.x());
1186
                    vertexData.append(point.y());
1187
                    vertexData.append(0);
1188
                    curX = point.x();
1189
                    curY = point.y();
1190
                }
1191
            }
1192
        }
1193
        contourEndings.append(vertexData.size());
1194
    }
1195
    // Now that we have all of the vertex data in a stable location in
1196
    // memory, call the tessellator.
1197
    GLUtesselator* tess = internal_gluNewTess();
1198
    TessellationState state(cache);
1199
    internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA,
1200
                             reinterpret_cast<GLvoid (*)()>(vertexCallback));
1201
    internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA,
1202
                             reinterpret_cast<GLvoid (*)()>(combineCallback));
1203
    internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG,
1204
                             reinterpret_cast<GLvoid (*)()>(edgeFlagCallback));
1205
    internal_gluTessBeginPolygon(tess, &state);
1206
    internal_gluTessBeginContour(tess);
1207
    GLdouble* base = vertexData.data();
1208
    int contourIndex = 0;
1209
    for (size_t i = 0; i < vertexData.size(); i += 3) {
1210
        if (i == contourEndings[contourIndex]) {
1211
            internal_gluTessEndContour(tess);
1212
            internal_gluTessBeginContour(tess);
1213
            ++contourIndex;
1214
        }
1215
        internal_gluTessVertex(tess, &base[i], &base[i]);
1216
    }
1217
    internal_gluTessEndContour(tess);
1218
    internal_gluTessEndPolygon(tess);
1219
    for (size_t i = 0; i < state.allocatedPointers.size(); i++)
1220
        free(state.allocatedPointers[i]);
1221
    internal_gluDeleteTess(tess);
1222
}
1223
1224
} // namespace WebCore
- WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.h +126 lines
Line 0 WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.h_sec1
1
/*
2
 * Copyright (C) 2010 Google Inc. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 *
8
 * 1.  Redistributions of source code must retain the above copyright
9
 *     notice, this list of conditions and the following disclaimer.
10
 * 2.  Redistributions in binary form must reproduce the above copyright
11
 *     notice, this list of conditions and the following disclaimer in the
12
 *     documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
 */
25
26
// The main entry point for Loop and Blinn's GPU accelerated curve
27
// rendering algorithm.
28
29
#ifndef LoopBlinnPathProcessor_h
30
#define LoopBlinnPathProcessor_h
31
32
#include <wtf/Noncopyable.h>
33
#include <wtf/PassRefPtr.h>
34
#include <wtf/RefPtr.h>
35
#include <wtf/Vector.h>
36
37
namespace WebCore {
38
39
// We use a namespace for classes which are simply implementation
40
// details of the algorithm but which we need to reference from the
41
// class definition.
42
namespace LoopBlinnPathProcessorImplementation {
43
44
class Contour;
45
class Segment;
46
47
} // namespace LoopBlinnPathProcessorImplementation
48
49
class Path;
50
class LoopBlinnPathCache;
51
class PODArena;
52
53
// The LoopBlinnPathProcessor turns a Path (assumed to contain one or
54
// more closed regions) into a set of exterior and interior triangles,
55
// stored in the LoopBlinnPathCache. The exterior triangles have
56
// associated 3D texture coordinates which are used to evaluate the
57
// curve's inside/outside function on a per-pixel basis. The interior
58
// triangles are filled with 100% opacity.
59
//
60
// Note that the fill style and management of multiple layers are
61
// separate concerns, handled at a higher level with shaders and
62
// polygon offsets.
63
class LoopBlinnPathProcessor {
64
public:
65
    LoopBlinnPathProcessor();
66
    explicit LoopBlinnPathProcessor(PassRefPtr<PODArena> arena);
67
    ~LoopBlinnPathProcessor();
68
69
    // Transforms the given path into a triangle mesh for rendering
70
    // using Loop and Blinn's shader, placing the result into the given
71
    // LoopBlinnPathCache.
72
    void process(const Path& path, LoopBlinnPathCache& cache);
73
74
#ifndef NDEBUG
75
    // Enables or disables verbose logging in debug mode.
76
    void setVerboseLogging(bool onOrOff);
77
#endif
78
79
private:
80
    // Builds a list of contours for the given path.
81
    void buildContours(const Path& path);
82
83
    // Determines whether the left or right side of each contour should
84
    // be filled.
85
    void determineSidesToFill();
86
87
    // Determines whether the given (closed) contour is oriented
88
    // clockwise or counterclockwise.
89
    void determineOrientation(LoopBlinnPathProcessorImplementation::Contour* contour);
90
91
    // Subdivides the curves so that there are no overlaps of the
92
    // triangles associated with the curves' control points.
93
    void subdivideCurves();
94
95
    // Helper function used during curve subdivision.
96
    void conditionallySubdivide(LoopBlinnPathProcessorImplementation::Segment* seg,
97
                                Vector<LoopBlinnPathProcessorImplementation::Segment*>& nextSegments);
98
99
    // Tessellates the interior regions of the contours.
100
    void tessellateInterior(LoopBlinnPathCache& cache);
101
102
    // For debugging the orientation computation. Returns all of the
103
    // segments overlapping the given Y coordinate.
104
    Vector<LoopBlinnPathProcessorImplementation::Segment*> allSegmentsOverlappingY(LoopBlinnPathProcessorImplementation::Contour* queryContour, float x, float y);
105
106
#ifndef NDEBUG
107
    // For debugging the curve subdivision algorithm. Subdivides the
108
    // curves using an alternate, slow (O(n^3)) algorithm.
109
    void subdivideCurvesSlow();
110
#endif
111
112
    // PODArena from which to allocate temporary objects.
113
    RefPtr<PODArena> m_arena;
114
115
    // The contours described by the path.
116
    Vector<LoopBlinnPathProcessorImplementation::Contour*> m_contours;
117
118
#ifndef NDEBUG
119
    // Whether or not to perform verbose logging in debug mode.
120
    bool m_verboseLogging;
121
#endif
122
};
123
124
} // namespace WebCore
125
126
#endif // LoopBlinnPathProcessor_h

Return to Bug 45521