gfx/skia/trunk/src/pathops/SkPathOpsOp.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/pathops/SkPathOpsOp.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,320 @@
     1.4 +/*
     1.5 + * Copyright 2012 Google Inc.
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +#include "SkAddIntersections.h"
    1.11 +#include "SkOpEdgeBuilder.h"
    1.12 +#include "SkPathOpsCommon.h"
    1.13 +#include "SkPathWriter.h"
    1.14 +
    1.15 +// FIXME: this and find chase should be merge together, along with
    1.16 +// other code that walks winding in angles
    1.17 +// OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
    1.18 +// so it isn't duplicated by walkers like this one
    1.19 +static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int& nextStart, int& nextEnd) {
    1.20 +    while (chase.count()) {
    1.21 +        SkOpSpan* span;
    1.22 +        chase.pop(&span);
    1.23 +        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
    1.24 +        SkOpSegment* segment = backPtr.fOther;
    1.25 +        nextStart = backPtr.fOtherIndex;
    1.26 +        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
    1.27 +        int done = 0;
    1.28 +        if (segment->activeAngle(nextStart, &done, &angles)) {
    1.29 +            SkOpAngle* last = angles.end() - 1;
    1.30 +            nextStart = last->start();
    1.31 +            nextEnd = last->end();
    1.32 +   #if TRY_ROTATE
    1.33 +            *chase.insert(0) = span;
    1.34 +   #else
    1.35 +            *chase.append() = span;
    1.36 +   #endif
    1.37 +            return last->segment();
    1.38 +        }
    1.39 +        if (done == angles.count()) {
    1.40 +            continue;
    1.41 +        }
    1.42 +        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
    1.43 +        bool sortable = SkOpSegment::SortAngles(angles, &sorted,
    1.44 +                SkOpSegment::kMayBeUnordered_SortAngleKind);
    1.45 +        int angleCount = sorted.count();
    1.46 +#if DEBUG_SORT
    1.47 +        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, sortable);
    1.48 +#endif
    1.49 +        if (!sortable) {
    1.50 +            continue;
    1.51 +        }
    1.52 +        // find first angle, initialize winding to computed fWindSum
    1.53 +        int firstIndex = -1;
    1.54 +        const SkOpAngle* angle;
    1.55 +        bool foundAngle = true;
    1.56 +        do {
    1.57 +            ++firstIndex;
    1.58 +            if (firstIndex >= angleCount) {
    1.59 +                foundAngle = false;
    1.60 +                break;
    1.61 +            }
    1.62 +            angle = sorted[firstIndex];
    1.63 +            segment = angle->segment();
    1.64 +        } while (segment->windSum(angle) == SK_MinS32);
    1.65 +        if (!foundAngle) {
    1.66 +            continue;
    1.67 +        }
    1.68 +    #if DEBUG_SORT
    1.69 +        segment->debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
    1.70 +    #endif
    1.71 +        int sumMiWinding = segment->updateWindingReverse(angle);
    1.72 +        int sumSuWinding = segment->updateOppWindingReverse(angle);
    1.73 +        if (segment->operand()) {
    1.74 +            SkTSwap<int>(sumMiWinding, sumSuWinding);
    1.75 +        }
    1.76 +        int nextIndex = firstIndex + 1;
    1.77 +        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
    1.78 +        SkOpSegment* first = NULL;
    1.79 +        do {
    1.80 +            SkASSERT(nextIndex != firstIndex);
    1.81 +            if (nextIndex == angleCount) {
    1.82 +                nextIndex = 0;
    1.83 +            }
    1.84 +            angle = sorted[nextIndex];
    1.85 +            segment = angle->segment();
    1.86 +            int start = angle->start();
    1.87 +            int end = angle->end();
    1.88 +            int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
    1.89 +            segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
    1.90 +                    &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
    1.91 +            if (!segment->done(angle)) {
    1.92 +                if (!first) {
    1.93 +                    first = segment;
    1.94 +                    nextStart = start;
    1.95 +                    nextEnd = end;
    1.96 +                }
    1.97 +                (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
    1.98 +                    oppSumWinding, angle);
    1.99 +            }
   1.100 +        } while (++nextIndex != lastIndex);
   1.101 +        if (first) {
   1.102 +       #if TRY_ROTATE
   1.103 +            *chase.insert(0) = span;
   1.104 +       #else
   1.105 +            *chase.append() = span;
   1.106 +       #endif
   1.107 +            return first;
   1.108 +        }
   1.109 +    }
   1.110 +    return NULL;
   1.111 +}
   1.112 +
   1.113 +/*
   1.114 +static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
   1.115 +        bool windingIsOp, PathOp op) {
   1.116 +    bool active = windingIsActive(winding, spanWinding);
   1.117 +    if (!active) {
   1.118 +        return false;
   1.119 +    }
   1.120 +    if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
   1.121 +        switch (op) {
   1.122 +            case kIntersect_Op:
   1.123 +            case kUnion_Op:
   1.124 +                return true;
   1.125 +            case kDifference_Op: {
   1.126 +                int absSpan = abs(spanWinding);
   1.127 +                int absOpp = abs(oppSpanWinding);
   1.128 +                return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
   1.129 +            }
   1.130 +            case kXor_Op:
   1.131 +                return spanWinding != oppSpanWinding;
   1.132 +            default:
   1.133 +                SkASSERT(0);
   1.134 +        }
   1.135 +    }
   1.136 +    bool opActive = oppWinding != 0;
   1.137 +    return gOpLookup[op][opActive][windingIsOp];
   1.138 +}
   1.139 +*/
   1.140 +
   1.141 +static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
   1.142 +        const int xorMask, const int xorOpMask, SkPathWriter* simple) {
   1.143 +    bool firstContour = true;
   1.144 +    bool unsortable = false;
   1.145 +    bool topUnsortable = false;
   1.146 +    SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
   1.147 +    do {
   1.148 +        int index, endIndex;
   1.149 +        bool done;
   1.150 +        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
   1.151 +                &index, &endIndex, &topLeft, &topUnsortable, &done);
   1.152 +        if (!current) {
   1.153 +            if (topUnsortable || !done) {
   1.154 +                topUnsortable = false;
   1.155 +                SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
   1.156 +                topLeft.fX = topLeft.fY = SK_ScalarMin;
   1.157 +                continue;
   1.158 +            }
   1.159 +            break;
   1.160 +        }
   1.161 +        SkTDArray<SkOpSpan*> chaseArray;
   1.162 +        do {
   1.163 +            if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
   1.164 +                do {
   1.165 +                    if (!unsortable && current->done()) {
   1.166 +            #if DEBUG_ACTIVE_SPANS
   1.167 +                        DebugShowActiveSpans(contourList);
   1.168 +            #endif
   1.169 +                        if (simple->isEmpty()) {
   1.170 +                            simple->init();
   1.171 +                        }
   1.172 +                        break;
   1.173 +                    }
   1.174 +                    SkASSERT(unsortable || !current->done());
   1.175 +                    int nextStart = index;
   1.176 +                    int nextEnd = endIndex;
   1.177 +                    SkOpSegment* next = current->findNextOp(&chaseArray, &nextStart, &nextEnd,
   1.178 +                            &unsortable, op, xorMask, xorOpMask);
   1.179 +                    if (!next) {
   1.180 +                        if (!unsortable && simple->hasMove()
   1.181 +                                && current->verb() != SkPath::kLine_Verb
   1.182 +                                && !simple->isClosed()) {
   1.183 +                            current->addCurveTo(index, endIndex, simple, true);
   1.184 +                            SkASSERT(simple->isClosed());
   1.185 +                        }
   1.186 +                        break;
   1.187 +                    }
   1.188 +        #if DEBUG_FLOW
   1.189 +            SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
   1.190 +                    current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
   1.191 +                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
   1.192 +        #endif
   1.193 +                    current->addCurveTo(index, endIndex, simple, true);
   1.194 +                    current = next;
   1.195 +                    index = nextStart;
   1.196 +                    endIndex = nextEnd;
   1.197 +                } while (!simple->isClosed() && (!unsortable
   1.198 +                        || !current->done(SkMin32(index, endIndex))));
   1.199 +                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
   1.200 +                    // FIXME : add to simplify, xor cpaths
   1.201 +                    int min = SkMin32(index, endIndex);
   1.202 +                    if (!unsortable && !simple->isEmpty()) {
   1.203 +                        unsortable = current->checkSmall(min);
   1.204 +                    }
   1.205 +                    SkASSERT(unsortable || simple->isEmpty());
   1.206 +                    if (!current->done(min)) {
   1.207 +                        current->addCurveTo(index, endIndex, simple, true);
   1.208 +                        current->markDoneBinary(min);
   1.209 +                    }
   1.210 +                }
   1.211 +                simple->close();
   1.212 +            } else {
   1.213 +                SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
   1.214 +                if (last && !last->fLoop) {
   1.215 +                    *chaseArray.append() = last;
   1.216 +                }
   1.217 +            }
   1.218 +            current = findChaseOp(chaseArray, index, endIndex);
   1.219 +        #if DEBUG_ACTIVE_SPANS
   1.220 +            DebugShowActiveSpans(contourList);
   1.221 +        #endif
   1.222 +            if (!current) {
   1.223 +                break;
   1.224 +            }
   1.225 +        } while (true);
   1.226 +    } while (true);
   1.227 +    return simple->someAssemblyRequired();
   1.228 +}
   1.229 +
   1.230 +// pretty picture:
   1.231 +// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
   1.232 +static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = {
   1.233 +//                  inside minuend                               outside minuend
   1.234 +//     inside subtrahend     outside subtrahend      inside subtrahend     outside subtrahend
   1.235 +    {{ kDifference_PathOp,    kIntersect_PathOp }, { kUnion_PathOp, kReverseDifference_PathOp }},
   1.236 +    {{ kIntersect_PathOp,    kDifference_PathOp }, { kReverseDifference_PathOp, kUnion_PathOp }},
   1.237 +    {{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp,    kIntersect_PathOp }},
   1.238 +    {{ kXOR_PathOp,                 kXOR_PathOp }, { kXOR_PathOp,                 kXOR_PathOp }},
   1.239 +    {{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp,    kDifference_PathOp }},
   1.240 +};
   1.241 +
   1.242 +static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
   1.243 +    {{ false, false }, { true, false }},  // diff
   1.244 +    {{ false, false }, { false, true }},  // sect
   1.245 +    {{ false, true }, { true, true }},    // union
   1.246 +    {{ false, true }, { true, false }},   // xor
   1.247 +    {{ false, true }, { false, false }},  // rev diff
   1.248 +};
   1.249 +
   1.250 +bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
   1.251 +#if DEBUG_SHOW_TEST_NAME
   1.252 +    char* debugName = DEBUG_FILENAME_STRING;
   1.253 +    if (debugName && debugName[0]) {
   1.254 +        SkPathOpsDebug::BumpTestName(debugName);
   1.255 +        SkPathOpsDebug::ShowPath(one, two, op, debugName);
   1.256 +    }
   1.257 +#endif
   1.258 +    op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
   1.259 +    SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
   1.260 +            ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
   1.261 +    const SkPath* minuend = &one;
   1.262 +    const SkPath* subtrahend = &two;
   1.263 +    if (op == kReverseDifference_PathOp) {
   1.264 +        minuend = &two;
   1.265 +        subtrahend = &one;
   1.266 +        op = kDifference_PathOp;
   1.267 +    }
   1.268 +#if DEBUG_SORT || DEBUG_SWAP_TOP
   1.269 +    SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
   1.270 +#endif
   1.271 +    // turn path into list of segments
   1.272 +    SkTArray<SkOpContour> contours;
   1.273 +    // FIXME: add self-intersecting cubics' T values to segment
   1.274 +    SkOpEdgeBuilder builder(*minuend, contours);
   1.275 +    const int xorMask = builder.xorMask();
   1.276 +    builder.addOperand(*subtrahend);
   1.277 +    if (!builder.finish()) {
   1.278 +        return false;
   1.279 +    }
   1.280 +    result->reset();
   1.281 +    result->setFillType(fillType);
   1.282 +    const int xorOpMask = builder.xorMask();
   1.283 +    SkTArray<SkOpContour*, true> contourList;
   1.284 +    MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
   1.285 +            xorOpMask == kEvenOdd_PathOpsMask);
   1.286 +    SkOpContour** currentPtr = contourList.begin();
   1.287 +    if (!currentPtr) {
   1.288 +        return true;
   1.289 +    }
   1.290 +    SkOpContour** listEnd = contourList.end();
   1.291 +    // find all intersections between segments
   1.292 +    do {
   1.293 +        SkOpContour** nextPtr = currentPtr;
   1.294 +        SkOpContour* current = *currentPtr++;
   1.295 +        if (current->containsCubics()) {
   1.296 +            AddSelfIntersectTs(current);
   1.297 +        }
   1.298 +        SkOpContour* next;
   1.299 +        do {
   1.300 +            next = *nextPtr++;
   1.301 +        } while (AddIntersectTs(current, next) && nextPtr != listEnd);
   1.302 +    } while (currentPtr != listEnd);
   1.303 +    // eat through coincident edges
   1.304 +
   1.305 +    int total = 0;
   1.306 +    int index;
   1.307 +    for (index = 0; index < contourList.count(); ++index) {
   1.308 +        total += contourList[index]->segments().count();
   1.309 +    }
   1.310 +    HandleCoincidence(&contourList, total);
   1.311 +    // construct closed contours
   1.312 +    SkPathWriter wrapper(*result);
   1.313 +    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
   1.314 +    {  // if some edges could not be resolved, assemble remaining fragments
   1.315 +        SkPath temp;
   1.316 +        temp.setFillType(fillType);
   1.317 +        SkPathWriter assembled(temp);
   1.318 +        Assemble(wrapper, &assembled);
   1.319 +        *result = *assembled.nativePath();
   1.320 +        result->setFillType(fillType);
   1.321 +    }
   1.322 +    return true;
   1.323 +}

mercurial