Merge latest Skia into master (20 commits)

https://skia.googlesource.com/skia.git/+log/1e18aec..5be6c95

Test: Presubmit checks will test this change.
Change-Id: I015c44e57cc363bc0509e5a0d662edd8304ff434
diff --git a/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp b/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp
index 6050a5c..95152fa 100644
--- a/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp
+++ b/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp
@@ -1323,10 +1323,10 @@
                                       GrSamplerParams::FilterMode::kNone_FilterMode);
         sk_sp<GrTexture> permutationsTexture(
             GrRefCachedBitmapTexture(args.fContext, paintingData->getImprovedPermutationsBitmap(),
-                                     textureParams));
+                                     textureParams, nullptr));
         sk_sp<GrTexture> gradientTexture(
             GrRefCachedBitmapTexture(args.fContext, paintingData->getGradientBitmap(),
-                                     textureParams));
+                                     textureParams, nullptr));
         return GrImprovedPerlinNoiseEffect::Make(fNumOctaves, fSeed, paintingData,
                                                  permutationsTexture.get(),
                                                  gradientTexture.get(), m);
@@ -1350,10 +1350,10 @@
 
     sk_sp<GrTexture> permutationsTexture(
         GrRefCachedBitmapTexture(args.fContext, paintingData->getPermutationsBitmap(),
-                                 GrSamplerParams::ClampNoFilter()));
+                                 GrSamplerParams::ClampNoFilter(), nullptr));
     sk_sp<GrTexture> noiseTexture(
         GrRefCachedBitmapTexture(args.fContext, paintingData->getNoiseBitmap(),
-                                 GrSamplerParams::ClampNoFilter()));
+                                 GrSamplerParams::ClampNoFilter(), nullptr));
 
     if ((permutationsTexture) && (noiseTexture)) {
         sk_sp<GrFragmentProcessor> inner(
diff --git a/gm/all_bitmap_configs.cpp b/gm/all_bitmap_configs.cpp
index 0d9c2c5..e954fc3 100644
--- a/gm/all_bitmap_configs.cpp
+++ b/gm/all_bitmap_configs.cpp
@@ -260,37 +260,27 @@
             *pm.writable_addr32(x, y) = make_pixel(x, y, alphaType);
         }
     }
-    bm->setImmutable();
 }
 
-DEF_SIMPLE_GM(all_variants_8888, canvas, 4 * SCALE + 30, 4 * SCALE + 30) {
+DEF_SIMPLE_GM(all_variants_8888, canvas, 4 * SCALE + 30, 2 * SCALE + 10) {
     sk_tool_utils::draw_checkerboard(canvas, SK_ColorLTGRAY, SK_ColorWHITE, 8);
 
     sk_sp<SkColorSpace> colorSpaces[] {
         SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named),
         nullptr,
     };
-
-    SkPaint rectPaint;
-    rectPaint.setColor(0x8000FF00);
-    SkRect r = { 0, 0, SCALE, SCALE };
-
     for (auto colorSpace : colorSpaces) {
         canvas->save();
-        // unpremul is expected to *not* draw anything
         for (auto alphaType : {kPremul_SkAlphaType, kUnpremul_SkAlphaType}) {
             canvas->save();
             for (auto colorType : {kRGBA_8888_SkColorType, kBGRA_8888_SkColorType}) {
                 SkBitmap bm;
                 make_color_test_bitmap_variant(colorType, alphaType, colorSpace, &bm);
-                canvas->drawRect(r, rectPaint);
                 canvas->drawBitmap(bm, 0.0f, 0.0f);
-                canvas->drawRect(r.makeOffset(0, SCALE + 10), rectPaint);
-                canvas->drawImage(SkImage::MakeFromBitmap(bm), 0, SCALE + 10, nullptr);
                 canvas->translate(SCALE + 10, 0.0f);
             }
             canvas->restore();
-            canvas->translate(0.0f, (SCALE + 10) * 2);
+            canvas->translate(0.0f, SCALE + 10);
         }
         canvas->restore();
         canvas->translate(2 * (SCALE + 10), 0.0f);
diff --git a/gm/deferredtextureimage.cpp b/gm/deferredtextureimage.cpp
index 1043c1b..ed1717d 100644
--- a/gm/deferredtextureimage.cpp
+++ b/gm/deferredtextureimage.cpp
@@ -85,7 +85,7 @@
     SkPaint paint;
     paint.setFilterQuality(params->fQuality);
 
-    int mipLevelCount = SkMipMap::ComputeLevelCount(512, 512);
+    int mipLevelCount = SkMipMap::ComputeLevelCount(image->width(), image->height());
     size_t requiredMemoryInBytes = image->getDeferredTextureImageData(
         *proxy, params, 1, nullptr, canvas->imageInfo().colorSpace());
     if (requiredMemoryInBytes == 0) {
@@ -107,13 +107,14 @@
     canvas->translate(10.f, offsetHeight);
     canvas->drawImage(uploadedImage, 0, 0, &paint);
     canvas->restore();
-    offsetHeight += 512 + 10;
+    offsetHeight += image->height() + 10;
     // handle generated mipmap levels
     for (int i = 0; i < mipLevelCount; i++) {
-        SkISize mipSize = SkMipMap::ComputeLevelSize(512, 512, i);
+        SkISize mipSize = SkMipMap::ComputeLevelSize(image->width(), image->height(), i);
         canvas->save();
         canvas->translate(10.f, offsetHeight);
-        canvas->scale(mipSize.width() / 512.f, mipSize.height() / 512.f);
+        canvas->scale(mipSize.width() / static_cast<float>(image->width()),
+                      mipSize.height() / static_cast<float>(image->height()));
         canvas->drawImage(uploadedImage, 0, 0, &paint);
         canvas->restore();
         offsetHeight += mipSize.height() + 10;
@@ -123,16 +124,17 @@
     offsetHeight = 10;
     // handle base mipmap level
     canvas->save();
-    canvas->translate(512.f + 20.f, offsetHeight);
+    canvas->translate(image->width() + 20.f, offsetHeight);
     canvas->drawImage(image, 0, 0, &paint);
     canvas->restore();
-    offsetHeight += 512 + 10;
+    offsetHeight += image->height() + 10;
     // handle generated mipmap levels
     for (int i = 0; i < mipLevelCount; i++) {
-        SkISize mipSize = SkMipMap::ComputeLevelSize(512, 512, i);
+        SkISize mipSize = SkMipMap::ComputeLevelSize(image->width(), image->height(), i);
         canvas->save();
-        canvas->translate(512.f + 20.f, offsetHeight);
-        canvas->scale(mipSize.width() / 512.f, mipSize.height() / 512.f);
+        canvas->translate(image->width() + 20.f, offsetHeight);
+        canvas->scale(mipSize.width() / static_cast<float>(image->width()),
+                      mipSize.height() / static_cast<float>(image->height()));
         canvas->drawImage(image, 0, 0, &paint);
         canvas->restore();
         offsetHeight += mipSize.height() + 10;
@@ -182,4 +184,29 @@
     DrawDeferredTextureImageData(canvas, &params);
 }
 
+DEF_SIMPLE_GM(deferred_texture_image_medium_encoded_indexed, canvas, 128 + 128 + 30, 340) {
+    sk_sp<SkImage> encodedImage = GetResourceAsImage("color_wheel.gif");
+    if (!encodedImage) {
+        SkDebugf("\nCould not load resource.\n");
+        return;
+    }
+
+    auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
+                                                           kMedium_SkFilterQuality, 0);
+    DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), &params);
+}
+
+DEF_SIMPLE_GM(deferred_texture_image_medium_decoded_indexed, canvas, 128 + 128 + 30, 340) {
+    SkBitmap bitmap;
+    if (!GetResourceAsBitmap("color_wheel.gif", &bitmap)) {
+        SkDebugf("\nCould not decode resource.\n");
+        return;
+    }
+    sk_sp<SkImage> decodedImage = SkImage::MakeFromBitmap(bitmap);
+
+    auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
+                                                           kMedium_SkFilterQuality, 0);
+    DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), &params);
+}
+
 #endif
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index 1fdff21..5394f6a 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -347,7 +347,8 @@
         sk_sp<SkColorSpace> texColorSpace;
         sk_sp<GrTexture> texture(
             cache->lockAsTexture(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
-                                 canvas->imageInfo().colorSpace(), &texColorSpace, nullptr));
+                                 canvas->imageInfo().colorSpace(), &texColorSpace,
+                                 nullptr, nullptr));
         if (!texture) {
             // show placeholder if we have no texture
             SkPaint paint;
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index 8520d28..372ff5d 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -97,10 +97,7 @@
         } else {
             vm.reset();
         }
-        SkMatrix tm;
-        tm = vm;
-        tm.postIDiv(2*S, 2*S);
-        paint.addColorTextureProcessor(texture, nullptr, tm);
+        paint.addColorTextureProcessor(texture, nullptr, vm);
 
         renderTargetContext->drawRect(clip, GrPaint(paint), GrAA::kNo, vm,
                                       SkRect::MakeWH(2 * S, 2 * S));
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index 32042aa..0bebd8c 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -83,17 +83,16 @@
         }
 
         sk_sp<GrTexture> texture(
-            GrRefCachedBitmapTexture(context, fBmp, GrSamplerParams::ClampNoFilter()));
+            GrRefCachedBitmapTexture(context, fBmp, GrSamplerParams::ClampNoFilter(), nullptr));
         if (!texture) {
             return;
         }
 
         SkTArray<SkMatrix> textureMatrices;
-        textureMatrices.push_back().setIDiv(texture->width(), texture->height());
-        textureMatrices.push_back() = textureMatrices[0];
-        textureMatrices.back().postScale(1.5f, 0.85f);
-        textureMatrices.push_back() = textureMatrices[0];
-        textureMatrices.back().preRotate(45.f, texture->width() / 2.f, texture->height() / 2.f);
+        textureMatrices.push_back() = SkMatrix::I();
+        textureMatrices.push_back() = SkMatrix::MakeScale(1.5f, 0.85f);
+        textureMatrices.push_back();
+        textureMatrices.back().setRotate(45.f, texture->width() / 2.f, texture->height() / 2.f);
 
         const SkIRect texelDomains[] = {
             fBmp.bounds(),
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index 8d78f13..3244463 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -82,11 +82,11 @@
 
         sk_sp<GrTexture> texture[3];
         texture[0].reset(
-            GrRefCachedBitmapTexture(context, fBmp[0], GrSamplerParams::ClampBilerp()));
+            GrRefCachedBitmapTexture(context, fBmp[0], GrSamplerParams::ClampBilerp(), nullptr));
         texture[1].reset(
-            GrRefCachedBitmapTexture(context, fBmp[1], GrSamplerParams::ClampBilerp()));
+            GrRefCachedBitmapTexture(context, fBmp[1], GrSamplerParams::ClampBilerp(), nullptr));
         texture[2].reset(
-            GrRefCachedBitmapTexture(context, fBmp[2], GrSamplerParams::ClampBilerp()));
+            GrRefCachedBitmapTexture(context, fBmp[2], GrSamplerParams::ClampBilerp(), nullptr));
 
         if (!texture[0] || !texture[1] || !texture[2]) {
             return;
@@ -204,11 +204,11 @@
 
         sk_sp<GrTexture> texture[3];
         texture[0].reset(
-            GrRefCachedBitmapTexture(context, fBmp[0], GrSamplerParams::ClampBilerp()));
+            GrRefCachedBitmapTexture(context, fBmp[0], GrSamplerParams::ClampBilerp(), nullptr));
         texture[1].reset(
-            GrRefCachedBitmapTexture(context, fBmp[1], GrSamplerParams::ClampBilerp()));
+            GrRefCachedBitmapTexture(context, fBmp[1], GrSamplerParams::ClampBilerp(), nullptr));
         texture[2].reset(
-            GrRefCachedBitmapTexture(context, fBmp[1], GrSamplerParams::ClampBilerp()));
+            GrRefCachedBitmapTexture(context, fBmp[1], GrSamplerParams::ClampBilerp(), nullptr));
 
         if (!texture[0] || !texture[1] || !texture[2]) {
             return;
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index 2dea479..4a8c286 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -40,8 +40,8 @@
      *  All pixels have their color components stored without any regard to the
      *  alpha. e.g. this is the default configuration for PNG images.
      *
-     *  This alpha-type is ONLY supported for readPixels/writePixels operations,
-     *  and is not supported for drawing.
+     *  This alpha-type is ONLY supported for input images. Rendering cannot
+     *  generate this on output.
      */
     kUnpremul_SkAlphaType,
 
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index 4bb887e..2ac3749 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -382,14 +382,6 @@
      */
     bool asLuminanceColor(SkColor*) const;
 
-#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    /**
-     *  If the shader is a custom shader which has data the caller might want, call this function
-     *  to get that data.
-     */
-    virtual bool asACustomShader(void** /* customData */) const { return false; }
-#endif
-
     //////////////////////////////////////////////////////////////////////////
     //  Methods to create combinations or variants of shaders
 
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 6664db9..7e8d4e8 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -36,7 +36,6 @@
 class GrResourceEntry;
 class GrResourceCache;
 class GrResourceProvider;
-class GrTestTarget;
 class GrTextBlobCache;
 class GrTextContext;
 class GrSamplerParams;
@@ -341,9 +340,6 @@
     const GrResourceProvider* resourceProvider() const { return fResourceProvider; }
     GrResourceCache* getResourceCache() { return fResourceCache; }
 
-    // Called by tests that draw directly to the context via GrRenderTargetContext
-    void getTestTarget(GrTestTarget*, sk_sp<GrRenderTargetContext>);
-
     /** Reset GPU stats */
     void resetGpuStats() const ;
 
@@ -371,7 +367,7 @@
     /** Get pointer to atlas texture for given mask format. Note that this wraps an
         actively mutating texture in an SkImage. This could yield unexpected results
         if it gets cached or used more generally. */
-    sk_sp<SkImage> getFontAtlasImage(GrMaskFormat format);
+    sk_sp<SkImage> getFontAtlasImage_ForTesting(GrMaskFormat format);
 
     GrAuditTrail* getAuditTrail() { return &fAuditTrail; }
 
diff --git a/include/gpu/GrCoordTransform.h b/include/gpu/GrCoordTransform.h
index 1f1cac7..5c16c89 100644
--- a/include/gpu/GrCoordTransform.h
+++ b/include/gpu/GrCoordTransform.h
@@ -20,7 +20,11 @@
  */
 class GrCoordTransform : SkNoncopyable {
 public:
-    GrCoordTransform() { SkDEBUGCODE(fInProcessor = false); }
+    GrCoordTransform()
+        : fTexture(nullptr)
+        , fNormalize(false) {
+        SkDEBUGCODE(fInProcessor = false);
+    }
 
     /**
      * Create a transformation that maps [0, 1] to a texture's boundaries. The precision is inferred
@@ -30,7 +34,7 @@
     GrCoordTransform(const GrTexture* texture, GrSamplerParams::FilterMode filter) {
         SkASSERT(texture);
         SkDEBUGCODE(fInProcessor = false);
-        this->reset(texture, filter);
+        this->reset(SkMatrix::I(), texture, filter);
     }
 
     /**
@@ -52,18 +56,23 @@
         this->reset(m, precision);
     }
 
-    void reset(const GrTexture* texture, GrSamplerParams::FilterMode filter) {
-        SkASSERT(!fInProcessor);
-        SkASSERT(texture);
-        this->reset(MakeDivByTextureWHMatrix(texture), texture, filter);
-    }
+    void reset(const SkMatrix&, const GrTexture*, GrSamplerParams::FilterMode filter,
+               bool normalize = true);
 
-    void reset(const SkMatrix&, const GrTexture*, GrSamplerParams::FilterMode filter);
-    void reset(const SkMatrix& m, GrSLPrecision precision = kDefault_GrSLPrecision);
+    void reset(const SkMatrix& m, GrSLPrecision precision = kDefault_GrSLPrecision) {
+        SkASSERT(!fInProcessor);
+        fMatrix = m;
+        fTexture = nullptr;
+        fNormalize = false;
+        fReverseY = false;
+        fPrecision = precision;
+    }
 
     GrCoordTransform& operator= (const GrCoordTransform& that) {
         SkASSERT(!fInProcessor);
         fMatrix = that.fMatrix;
+        fTexture = that.fTexture;
+        fNormalize = that.fNormalize;
         fReverseY = that.fReverseY;
         fPrecision = that.fPrecision;
         return *this;
@@ -78,29 +87,38 @@
         return &fMatrix;
     }
 
-    bool operator==(const GrCoordTransform& that) const {
-        return fMatrix.cheapEqualTo(that.fMatrix) &&
-               fReverseY == that.fReverseY &&
-               fPrecision == that.fPrecision;
+    bool hasSameEffectAs(const GrCoordTransform& that) const {
+        if (fNormalize != that.fNormalize ||
+            fReverseY != that.fReverseY ||
+            fPrecision != that.fPrecision ||
+            !fMatrix.cheapEqualTo(that.fMatrix)) {
+            return false;
+        }
+
+        if (fNormalize) {
+            SkASSERT(fTexture && that.fTexture);
+            return fTexture->width() == that.fTexture->width() &&
+                   fTexture->height() == that.fTexture->height();
+        }
+
+        return true;
     }
 
-    bool operator!=(const GrCoordTransform& that) const { return !(*this == that); }
-
     const SkMatrix& getMatrix() const { return fMatrix; }
+    const GrTexture* texture() const { return fTexture; }
+    bool normalize() const { return fNormalize; }
     bool reverseY() const { return fReverseY; }
     GrSLPrecision precision() const { return fPrecision; }
 
-    /** Useful for effects that want to insert a texture matrix that is implied by the texture
-        dimensions */
-    static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) {
-        SkASSERT(texture);
-        SkMatrix mat;
-        (void)mat.setIDiv(texture->width(), texture->height());
-        return mat;
-    }
-
 private:
+    // The textures' effect is to optionally normalize the final matrix, so a blind
+    // equality check could be misleading
+    bool operator==(const GrCoordTransform& that) const;
+    bool operator!=(const GrCoordTransform& that) const;
+
     SkMatrix                fMatrix;
+    const GrTexture*        fTexture;
+    bool                    fNormalize;
     bool                    fReverseY;
     GrSLPrecision           fPrecision;
     typedef SkNoncopyable INHERITED;
diff --git a/include/gpu/GrRenderTargetContext.h b/include/gpu/GrRenderTargetContext.h
index e595a4f..aab1050 100644
--- a/include/gpu/GrRenderTargetContext.h
+++ b/include/gpu/GrRenderTargetContext.h
@@ -391,7 +391,6 @@
 
     friend class GrDrawingManager; // for ctor
     friend class GrRenderTargetContextPriv;
-    friend class GrTestTarget;  // for access to getOpList
     friend class GrSWMaskHelper;                 // for access to addDrawOp
 
     // All the path renderers currently make their own ops
diff --git a/include/gpu/GrShaderCaps.h b/include/gpu/GrShaderCaps.h
index e1f5747..918138c 100644
--- a/include/gpu/GrShaderCaps.h
+++ b/include/gpu/GrShaderCaps.h
@@ -164,13 +164,16 @@
 
     bool usesPrecisionModifiers() const { return fUsesPrecisionModifiers; }
 
-    // Returns whether we can use the glsl funciton any() in our shader code.
+    // Returns whether we can use the glsl function any() in our shader code.
     bool canUseAnyFunctionInShader() const { return fCanUseAnyFunctionInShader; }
 
     bool canUseMinAndAbsTogether() const { return fCanUseMinAndAbsTogether; }
 
     bool mustForceNegatedAtanParamToFloat() const { return fMustForceNegatedAtanParamToFloat; }
 
+    // Returns whether a device incorrectly implements atan(y,x) as atan(y/x)
+    bool atan2ImplementedAsAtanYOverX() const { return fAtan2ImplementedAsAtanYOverX; }
+
     bool requiresLocalOutputColorForFBFetch() const { return fRequiresLocalOutputColorForFBFetch; }
 
     // Returns the string of an extension that must be enabled in the shader to support
@@ -299,6 +302,7 @@
     // Used for specific driver bug work arounds
     bool fCanUseMinAndAbsTogether : 1;
     bool fMustForceNegatedAtanParamToFloat : 1;
+    bool fAtan2ImplementedAsAtanYOverX : 1;
     bool fRequiresLocalOutputColorForFBFetch : 1;
 
     PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][kGrSLPrecisionCount];
diff --git a/include/gpu/GrXferProcessor.h b/include/gpu/GrXferProcessor.h
index 17cd2c7..353c682 100644
--- a/include/gpu/GrXferProcessor.h
+++ b/include/gpu/GrXferProcessor.h
@@ -307,6 +307,16 @@
 class GrXPFactory {
 public:
     typedef GrXferProcessor::DstTexture DstTexture;
+
+    /** Describes known properties of a draw's color input to the GrXferProcessor. */
+    enum class ColorType { kUnknown, kOpaqueConstant, kConstant, kOpaque };
+
+    /**
+     * Indicates whether a draw's coverage input to the GrXferProcessor is solid, single channel
+     * or LCD (four channel coverage).
+     */
+    enum class CoverageType { kNone, kSingleChannel, kLCD };
+
     GrXferProcessor* createXferProcessor(const GrPipelineAnalysis&,
                                          bool hasMixedSamples,
                                          const DstTexture*,
@@ -329,24 +339,33 @@
     virtual void getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
                                           InvariantBlendedColor*) const = 0;
 
-    bool willNeedDstTexture(const GrCaps& caps, const GrPipelineAnalysis&) const;
+    bool willNeedDstTexture(const GrCaps& caps, const GrPipelineAnalysis& analysis) const;
 
 protected:
     constexpr GrXPFactory() {}
 
+    static bool ColorTypeIsOpaque(ColorType type) {
+        return ColorType::kOpaqueConstant == type || ColorType::kOpaque == type;
+    }
+
+    static bool ColorTypeIsConstant(ColorType type) {
+        return ColorType::kOpaqueConstant == type || ColorType::kConstant == type;
+    }
+
 private:
     virtual GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
                                                    const GrPipelineAnalysis&,
                                                    bool hasMixedSamples,
                                                    const DstTexture*) const = 0;
 
-    bool willReadDstColor(const GrCaps&, const GrPipelineAnalysis&) const;
+    bool willReadDstColor(const GrCaps& caps, const GrPipelineAnalysis& analysis) const;
 
     /**
      *  Returns true if the XP generated by this factory will explicitly read dst in the fragment
-     *  shader.
+     *  shader. This will not be called for draws that read from PLS since the dst color is always
+     *  available in such draws.
      */
-    virtual bool onWillReadDstColor(const GrCaps&, const GrPipelineAnalysis&) const = 0;
+    virtual bool willReadDstColor(const GrCaps&, ColorType, CoverageType) const = 0;
 };
 #if defined(__GNUC__) || defined(__clang)
 #pragma GCC diagnostic pop
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index e2f09e5..3f72fa1 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -77,11 +77,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 /** Returns a texture representing the bitmap that is compatible with the GrSamplerParams. The
-    texture is inserted into the cache (unless the bitmap is marked volatile) and can be
-    retrieved again via this function. */
-GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrSamplerParams&);
+ *  texture is inserted into the cache (unless the bitmap is marked volatile) and can be
+ *  retrieved again via this function.
+ *  The 'scaleAdjust' in/out parameter will be updated to hold any rescaling that needs to be
+ *  performed on the absolute texture coordinates (e.g., if the texture is resized out to
+ *  the next power of two). It can be null if the caller is sure the bitmap won't be resized.
+ */
+GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&,
+                                    const GrSamplerParams&, SkScalar scaleAdjust[2]);
 
-sk_sp<GrTexture> GrMakeCachedBitmapTexture(GrContext*, const SkBitmap&, const GrSamplerParams&);
+sk_sp<GrTexture> GrMakeCachedBitmapTexture(GrContext*, const SkBitmap&,
+                                           const GrSamplerParams&, SkScalar scaleAdjust[2]);
 
 // TODO: Move SkImageInfo2GrPixelConfig to SkGrPriv.h (requires cleanup to SkWindow its subclasses).
 GrPixelConfig SkImageInfo2GrPixelConfig(const SkImageInfo& info, const GrCaps& caps);
diff --git a/include/gpu/effects/GrCoverageSetOpXP.h b/include/gpu/effects/GrCoverageSetOpXP.h
index ca71abc..385cad8 100644
--- a/include/gpu/effects/GrCoverageSetOpXP.h
+++ b/include/gpu/effects/GrCoverageSetOpXP.h
@@ -40,9 +40,7 @@
                                            bool hasMixedSamples,
                                            const DstTexture*) const override;
 
-    bool onWillReadDstColor(const GrCaps&, const GrPipelineAnalysis&) const override {
-        return false;
-    }
+    bool willReadDstColor(const GrCaps&, ColorType, CoverageType) const override { return false; }
 
     GR_DECLARE_XP_FACTORY_TEST;
 
diff --git a/include/gpu/effects/GrPorterDuffXferProcessor.h b/include/gpu/effects/GrPorterDuffXferProcessor.h
index ca14275..028cac6 100644
--- a/include/gpu/effects/GrPorterDuffXferProcessor.h
+++ b/include/gpu/effects/GrPorterDuffXferProcessor.h
@@ -63,7 +63,7 @@
                                            bool hasMixedSamples,
                                            const DstTexture*) const override;
 
-    bool onWillReadDstColor(const GrCaps&, const GrPipelineAnalysis&) const override;
+    bool willReadDstColor(const GrCaps&, ColorType, CoverageType) const override;
 
     GR_DECLARE_XP_FACTORY_TEST;
     static void TestGetXPOutputTypes(const GrXferProcessor*, int* outPrimary, int* outSecondary);
diff --git a/infra/bots/gen_tasks.go b/infra/bots/gen_tasks.go
index 61b0900..89a6fe2 100644
--- a/infra/bots/gen_tasks.go
+++ b/infra/bots/gen_tasks.go
@@ -230,9 +230,9 @@
 		"Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Debug-Android",
 		"Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Release-Android",
 		"Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android",
-		// "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
+		"Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
 		"Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android",
-		// "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
+		"Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
 		"Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android",
 		"Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android",
 		"Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android",
diff --git a/infra/bots/tasks.json b/infra/bots/tasks.json
index de6e96d..e81a5d6 100644
--- a/infra/bots/tasks.json
+++ b/infra/bots/tasks.json
@@ -1142,12 +1142,24 @@
         "Upload-Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android"
       ]
     },
+    "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan"
+      ]
+    },
     "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android": {
       "priority": 0.8,
       "tasks": [
         "Upload-Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android"
       ]
     },
+    "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan"
+      ]
+    },
     "Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android": {
       "priority": 0.8,
       "tasks": [
@@ -9524,6 +9536,55 @@
       "isolate": "test_skia.isolate",
       "priority": 0.8
     },
+    "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:19"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:33"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:3"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86-Debug-Android_Vulkan"
+      ],
+      "dimensions": [
+        "device_os:N2G10B",
+        "device_type:fugu",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "swarm_test",
+        "repository=<(REPO)",
+        "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
+        "mastername=fake-master",
+        "buildnumber=2",
+        "slavename=fake-buildslave",
+        "nobuildbot=True",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia.isolate",
+      "priority": 0.8
+    },
     "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android": {
       "cipd_packages": [
         {
@@ -9573,6 +9634,55 @@
       "isolate": "test_skia.isolate",
       "priority": 0.8
     },
+    "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:19"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:33"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:3"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86-Release-Android_Vulkan"
+      ],
+      "dimensions": [
+        "device_os:N2G10B",
+        "device_type:fugu",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "swarm_test",
+        "repository=<(REPO)",
+        "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
+        "mastername=fake-master",
+        "buildnumber=2",
+        "slavename=fake-buildslave",
+        "nobuildbot=True",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia.isolate",
+      "priority": 0.8
+    },
     "Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android": {
       "cipd_packages": [
         {
@@ -15147,6 +15257,35 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
+    "Upload-Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan": {
+      "dependencies": [
+        "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
+        "mastername=fake-master",
+        "buildnumber=2",
+        "slavename=fake-buildslave",
+        "nobuildbot=True",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
     "Upload-Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android": {
       "dependencies": [
         "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android"
@@ -15176,6 +15315,35 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
+    "Upload-Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan": {
+      "dependencies": [
+        "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
+        "mastername=fake-master",
+        "buildnumber=2",
+        "slavename=fake-buildslave",
+        "nobuildbot=True",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
     "Upload-Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android": {
       "dependencies": [
         "Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android"
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg
index bf850ca..20c8529 100644
--- a/infra/config/recipes.cfg
+++ b/infra/config/recipes.cfg
@@ -23,5 +23,5 @@
   project_id: "skia-recipes"
   url: "https://skia.googlesource.com/skia-recipes.git"
   branch: "master"
-  revision: "7ab5cc6255f6bae0ce71241a26c509f557e9983b"
+  revision: "e2e91bd2def317153737460ec00b4266ff03e4ab"
 }
diff --git a/samplecode/SampleAnimatedText.cpp b/samplecode/SampleAnimatedText.cpp
index cc672ff..2b047c2 100644
--- a/samplecode/SampleAnimatedText.cpp
+++ b/samplecode/SampleAnimatedText.cpp
@@ -103,7 +103,8 @@
 #if SK_SUPPORT_GPU
         GrContext* grContext = canvas->getGrContext();
         if (grContext) {
-            sk_sp<SkImage> image = grContext->getFontAtlasImage(GrMaskFormat::kA8_GrMaskFormat);
+            sk_sp<SkImage> image =
+                        grContext->getFontAtlasImage_ForTesting(GrMaskFormat::kA8_GrMaskFormat);
             canvas->drawImageRect(image,
                                   SkRect::MakeXYWH(512.0f, 10.0f, 512.0f, 512.0f), &paint);
         }
diff --git a/src/core/SkArenaAlloc.cpp b/src/core/SkArenaAlloc.cpp
index 1d359d1..5ac08dc 100644
--- a/src/core/SkArenaAlloc.cpp
+++ b/src/core/SkArenaAlloc.cpp
@@ -39,10 +39,6 @@
     return nullptr;
 }
 
-struct Skipper {
-    char* operator()(char* objEnd, uint32_t size) { return objEnd - size; }
-};
-
 SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize)
     : fDtorCursor {block}
     , fCursor     {block}
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 367a348..0375ab7 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -53,12 +53,6 @@
 
 #define RETURN_ON_NULL(ptr)     do { if (nullptr == (ptr)) return; } while (0)
 
-#define RETURN_ON_UNPREMUL_IMAGE(image) \
-    do { if (as_IB(image)->onAlphaType() == kUnpremul_SkAlphaType) return; } while (0)
-
-#define RETURN_ON_UNPREMUL_BITMAP(bitmap) \
-    do { if (bitmap.alphaType() == kUnpremul_SkAlphaType) return; } while (0)
-
 /*
  *  Return true if the drawing this rect would hit every pixels in the canvas.
  *
@@ -1915,14 +1909,12 @@
 
 void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
     RETURN_ON_NULL(image);
-    RETURN_ON_UNPREMUL_IMAGE(image);
     this->onDrawImage(image, x, y, paint);
 }
 
 void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
                              const SkPaint* paint, SrcRectConstraint constraint) {
     RETURN_ON_NULL(image);
-    RETURN_ON_UNPREMUL_IMAGE(image);
     if (dst.isEmpty() || src.isEmpty()) {
         return;
     }
@@ -1932,14 +1924,12 @@
 void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
                              const SkPaint* paint, SrcRectConstraint constraint) {
     RETURN_ON_NULL(image);
-    RETURN_ON_UNPREMUL_IMAGE(image);
     this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
 }
 
 void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
                              SrcRectConstraint constraint) {
     RETURN_ON_NULL(image);
-    RETURN_ON_UNPREMUL_IMAGE(image);
     this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
                         constraint);
 }
@@ -1947,7 +1937,6 @@
 void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
                              const SkPaint* paint) {
     RETURN_ON_NULL(image);
-    RETURN_ON_UNPREMUL_IMAGE(image);
     if (dst.isEmpty()) {
         return;
     }
@@ -1961,7 +1950,6 @@
 void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
                                 const SkPaint* paint) {
     RETURN_ON_NULL(image);
-    RETURN_ON_UNPREMUL_IMAGE(image);
     if (dst.isEmpty()) {
         return;
     }
@@ -1984,7 +1972,6 @@
     if (bitmap.drawsNothing()) {
         return;
     }
-    RETURN_ON_UNPREMUL_BITMAP(bitmap);
     this->onDrawBitmap(bitmap, dx, dy, paint);
 }
 
@@ -1993,7 +1980,6 @@
     if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
         return;
     }
-    RETURN_ON_UNPREMUL_BITMAP(bitmap);
     this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
 }
 
@@ -2013,7 +1999,6 @@
     if (bitmap.drawsNothing() || dst.isEmpty()) {
         return;
     }
-    RETURN_ON_UNPREMUL_BITMAP(bitmap);
     if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
         this->onDrawBitmapNine(bitmap, center, dst, paint);
     } else {
@@ -2026,7 +2011,6 @@
     if (bitmap.drawsNothing() || dst.isEmpty()) {
         return;
     }
-    RETURN_ON_UNPREMUL_BITMAP(bitmap);
 
     SkIRect bounds;
     Lattice latticePlusBounds = lattice;
@@ -2046,10 +2030,10 @@
                          const SkColor colors[], int count, SkBlendMode mode,
                          const SkRect* cull, const SkPaint* paint) {
     RETURN_ON_NULL(atlas);
-    RETURN_ON_UNPREMUL_IMAGE(atlas);
     if (count <= 0) {
         return;
     }
+    SkASSERT(atlas);
     SkASSERT(tex);
     this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
 }
diff --git a/src/core/SkGlyph.h b/src/core/SkGlyph.h
index 73f8704..06c1d76 100644
--- a/src/core/SkGlyph.h
+++ b/src/core/SkGlyph.h
@@ -8,10 +8,12 @@
 #ifndef SkGlyph_DEFINED
 #define SkGlyph_DEFINED
 
+#include "SkArenaAlloc.h"
 #include "SkChecksum.h"
-#include "SkTypes.h"
 #include "SkFixed.h"
 #include "SkMask.h"
+#include "SkTypes.h"
+
 
 class SkPath;
 class SkGlyphCache;
@@ -161,13 +163,17 @@
         fForceBW        = 0;
     }
 
+    static size_t BitsToBytes(size_t bits) {
+        return (bits + 7) >> 3;
+    }
+
     /**
      *  Compute the rowbytes for the specified width and mask-format.
      */
     static unsigned ComputeRowBytes(unsigned width, SkMask::Format format) {
         unsigned rb = width;
         if (SkMask::kBW_Format == format) {
-            rb = (rb + 7) >> 3;
+            rb = BitsToBytes(rb);
         } else if (SkMask::kARGB32_Format == format) {
             rb <<= 2;
         } else if (SkMask::kLCD16_Format == format) {
@@ -178,6 +184,26 @@
         return rb;
     }
 
+    size_t allocImage(SkArenaAlloc* alloc) {
+        size_t allocSize;
+        if (SkMask::kBW_Format == fMaskFormat) {
+            allocSize = BitsToBytes(fWidth) * fHeight;
+            fImage = alloc->makeArrayDefault<char>(allocSize);
+        } else if (SkMask::kARGB32_Format == fMaskFormat) {
+            allocSize = fWidth * fHeight;
+            fImage = alloc->makeArrayDefault<uint32_t>(fWidth * fHeight);
+            allocSize *= sizeof(uint32_t);
+        } else if (SkMask::kLCD16_Format == fMaskFormat) {
+            allocSize = SkAlign2(fWidth) * fHeight;
+            fImage = alloc->makeArrayDefault<uint16_t>(allocSize);
+            allocSize *= sizeof(uint16_t);
+        } else {
+            allocSize = SkAlign4(fWidth) * fHeight;
+            fImage = alloc->makeArrayDefault<char>(allocSize);
+        }
+        return allocSize;
+    }
+
     unsigned rowBytes() const {
         return ComputeRowBytes(fWidth, (SkMask::Format)fMaskFormat);
     }
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
index c1abcd1..e20343d 100644
--- a/src/core/SkGlyphCache.cpp
+++ b/src/core/SkGlyphCache.cpp
@@ -33,15 +33,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// so we don't grow our arrays a lot
-#define kMinGlyphCount      16
-#define kMinGlyphImageSize  (16*2)
-#define kMinAllocAmount     ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
-
 SkGlyphCache::SkGlyphCache(const SkDescriptor* desc, std::unique_ptr<SkScalerContext> ctx)
     : fDesc(desc->copy())
-    , fScalerContext(std::move(ctx))
-    , fGlyphAlloc(kMinAllocAmount) {
+    , fScalerContext(std::move(ctx)) {
     SkASSERT(desc);
     SkASSERT(fScalerContext);
 
@@ -189,9 +183,7 @@
 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
     if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
         if (nullptr == glyph.fImage) {
-            size_t  size = glyph.computeImageSize();
-            const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
-                                        SkChunkAlloc::kReturnNil_AllocFailType);
+            size_t  size = const_cast<SkGlyph&>(glyph).allocImage(&fAlloc);
             // check that alloc() actually succeeded
             if (glyph.fImage) {
                 fScalerContext->getImage(glyph);
@@ -209,8 +201,7 @@
 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
     if (glyph.fWidth) {
         if (glyph.fPathData == nullptr) {
-            SkGlyph::PathData* pathData =
-                    (SkGlyph::PathData* ) fGlyphAlloc.allocThrow(sizeof(SkGlyph::PathData));
+            SkGlyph::PathData* pathData = fAlloc.make<SkGlyph::PathData>();
             const_cast<SkGlyph&>(glyph).fPathData = pathData;
             pathData->fIntercept = nullptr;
             SkPath* path = pathData->fPath = new SkPath;
@@ -330,8 +321,7 @@
         return;
     }
 
-    SkGlyph::Intercept* intercept =
-            (SkGlyph::Intercept* ) fGlyphAlloc.allocThrow(sizeof(SkGlyph::Intercept));
+    SkGlyph::Intercept* intercept = fAlloc.make<SkGlyph::Intercept>();
     intercept->fNext = glyph->fPathData->fIntercept;
     intercept->fBounds[0] = bounds[0];
     intercept->fBounds[1] = bounds[1];
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
index 8cb5337..ab1c9ba 100644
--- a/src/core/SkGlyphCache.h
+++ b/src/core/SkGlyphCache.h
@@ -7,6 +7,7 @@
 #ifndef SkGlyphCache_DEFINED
 #define SkGlyphCache_DEFINED
 
+#include "SkArenaAlloc.h"
 #include "SkBitmap.h"
 #include "SkChunkAlloc.h"
 #include "SkDescriptor.h"
@@ -235,7 +236,14 @@
     // Map from a combined GlyphID and sub-pixel position to a SkGlyph.
     SkTHashTable<SkGlyph, SkPackedGlyphID, SkGlyph::HashTraits> fGlyphMap;
 
-    SkChunkAlloc           fGlyphAlloc;
+    // so we don't grow our arrays a lot
+    static constexpr size_t       kMinGlyphCount = 16;
+    static constexpr size_t       kMinGlyphImageSize = (16*2);
+    static constexpr size_t       kMinAllocAmount
+        = ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount);
+
+    char                   storage[kMinAllocAmount];
+    SkArenaAlloc           fAlloc {storage};
 
     std::unique_ptr<CharGlyphRec[]> fPackedUnicharIDToPackedGlyphID;
 
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index b3b523a..6ac5abe 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -262,10 +262,6 @@
     for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
         GrPaint paint;
         paint.setGammaCorrect(dstRenderTargetContext->isGammaCorrect());
-        // TODO: this matrix relies on the final instantiated size of the texture. This
-        // will have to be deferred for TextureProxys
-        SkMatrix matrix;
-        matrix.setIDiv(srcTexture->width(), srcTexture->height());
         SkIRect dstRect(srcRect);
         if (srcBounds && i == 1) {
             SkRect domain = SkRect::Make(*srcBounds);
@@ -274,7 +270,7 @@
             sk_sp<GrFragmentProcessor> fp(GrTextureDomainEffect::Make(
                                                         srcTexture.get(),
                                                         nullptr,
-                                                        matrix,
+                                                        SkMatrix::I(),
                                                         domain,
                                                         GrTextureDomain::kDecal_Mode,
                                                         GrSamplerParams::kBilerp_FilterMode));
@@ -283,7 +279,7 @@
             srcOffset.set(0, 0);
         } else {
             GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
-            paint.addColorTextureProcessor(srcTexture.get(), nullptr, matrix, params);
+            paint.addColorTextureProcessor(srcTexture.get(), nullptr, SkMatrix::I(), params);
         }
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
         shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY);
@@ -369,12 +365,7 @@
             return nullptr;
         }
 
-        // TODO: this matrix relies on the final instantiated size of the texture. This
-        // will have to be deferred for TextureProxys
-        SkMatrix matrix;
-        matrix.setIDiv(tex->width(), tex->height());
-
-        paint.addColorTextureProcessor(tex.get(), nullptr, matrix, params);
+        paint.addColorTextureProcessor(tex.get(), nullptr, SkMatrix::I(), params);
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
         SkIRect dstRect(srcRect);
diff --git a/src/core/SkImageCacherator.cpp b/src/core/SkImageCacherator.cpp
index ed36d66..1556683 100644
--- a/src/core/SkImageCacherator.cpp
+++ b/src/core/SkImageCacherator.cpp
@@ -245,11 +245,9 @@
         return false;
     }
 
-    const uint32_t pixelOpsFlags = 0;
     if (!tex->readPixels(fInfo.colorSpace(), 0, 0, bitmap->width(), bitmap->height(),
                          SkImageInfo2GrPixelConfig(cacheInfo, *tex->getContext()->caps()),
-                         cacheInfo.colorSpace(), bitmap->getPixels(), bitmap->rowBytes(),
-                         pixelOpsFlags)) {
+                         cacheInfo.colorSpace(), bitmap->getPixels(), bitmap->rowBytes())) {
         bitmap->reset();
         return false;
     }
@@ -608,13 +606,16 @@
 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrSamplerParams& params,
                                             SkColorSpace* dstColorSpace,
                                             sk_sp<SkColorSpace>* texColorSpace,
-                                            const SkImage* client, SkImage::CachingHint chint) {
+                                            const SkImage* client,
+                                            SkScalar scaleAdjust[2],
+                                            SkImage::CachingHint chint) {
     if (!ctx) {
         return nullptr;
     }
 
     return GrImageTextureMaker(ctx, this, client, chint).refTextureForParams(params, dstColorSpace,
-                                                                             texColorSpace);
+                                                                             texColorSpace,
+                                                                             scaleAdjust);
 }
 
 #else
@@ -622,7 +623,8 @@
 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrSamplerParams&,
                                             SkColorSpace* dstColorSpace,
                                             sk_sp<SkColorSpace>* texColorSpace,
-                                            const SkImage* client, SkImage::CachingHint) {
+                                            const SkImage* client,
+                                            SkScalar scaleAdjust[2], SkImage::CachingHint) {
     return nullptr;
 }
 
diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h
index 0e37efa..c5c3e97 100644
--- a/src/core/SkImageCacherator.h
+++ b/src/core/SkImageCacherator.h
@@ -59,9 +59,15 @@
      *  added to the cache on its behalf.
      *
      *  The caller is responsible for calling texture->unref() when they are done.
+     *
+     *  The scaleAdjust in/out parameter will return any scale adjustment that needs
+     *  to be applied to the absolute texture coordinates in the case where the image
+     *  was resized to meet the sampling requirements (e.g., resized out to the next power of 2).
+     *  It can be null if the caller knows resizing will not be required.
      */
     GrTexture* lockAsTexture(GrContext*, const GrSamplerParams&, SkColorSpace* dstColorSpace,
                              sk_sp<SkColorSpace>* texColorSpace, const SkImage* client,
+                             SkScalar scaleAdjust[2],
                              SkImage::CachingHint = SkImage::kAllow_CachingHint);
 
     /**
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index a05ae0a..390ebe0 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1038,7 +1038,7 @@
 
 const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
     SkMatrix::Identity_pts, SkMatrix::Trans_pts,
-    SkMatrix::Scale_pts,   SkMatrix::Scale_pts,
+    SkMatrix::Scale_pts,    SkMatrix::Scale_pts,
     SkMatrix::Affine_vpts,  SkMatrix::Affine_vpts,
     SkMatrix::Affine_vpts,  SkMatrix::Affine_vpts,
     // repeat the persp proc 8 times
diff --git a/src/core/SkPM4fPriv.h b/src/core/SkPM4fPriv.h
index a6232be..a08f158 100644
--- a/src/core/SkPM4fPriv.h
+++ b/src/core/SkPM4fPriv.h
@@ -135,22 +135,29 @@
     return append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst);
 }
 
-static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) {
-    SkColor4f color4f;
-    if (dst) {
-        // sRGB gamma, sRGB gamut.
-        color4f = SkColor4f::FromColor(color);
+static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
+    SkColor4f color4f = c;
+    if (src && dst) {
         void* color4f_ptr = &color4f;
 
         float scratch_matrix_3x4[12];
 
         SkRasterPipeline p;
         p.append(SkRasterPipeline::constant_color, color4f_ptr);
-        append_gamut_transform(&p, scratch_matrix_3x4,
-                               SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named).get(), dst);
+        append_gamut_transform(&p, scratch_matrix_3x4, src, dst);
         p.append(SkRasterPipeline::store_f32, &color4f_ptr);
 
         p.run(0,0,1);
+    }
+    return color4f;
+}
+
+static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) {
+    SkColor4f color4f;
+    if (dst) {
+        // sRGB gamma, sRGB gamut.
+        color4f = to_colorspace(SkColor4f::FromColor(color),
+                                SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named).get(), dst);
     } else {
         // Linear gamma, dst gamut.
         swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f);
diff --git a/src/core/SkRasterPipeline.h b/src/core/SkRasterPipeline.h
index ac1e3b9..558eead 100644
--- a/src/core/SkRasterPipeline.h
+++ b/src/core/SkRasterPipeline.h
@@ -93,7 +93,8 @@
     M(bilinear_nx) M(bilinear_px) M(bilinear_ny) M(bilinear_py)  \
     M(bicubic_n3x) M(bicubic_n1x) M(bicubic_p1x) M(bicubic_p3x)  \
     M(bicubic_n3y) M(bicubic_n1y) M(bicubic_p1y) M(bicubic_p3y)  \
-    M(save_xy) M(accumulate)
+    M(save_xy) M(accumulate)                                     \
+    M(linear_gradient_2stops)
 
 class SkRasterPipeline {
 public:
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 4f48f78..8b2801e 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -103,7 +103,7 @@
     }
 
     sk_sp<GrTexture> resultTex(
-        GrRefCachedBitmapTexture(context, bmp, GrSamplerParams::ClampNoFilter()));
+        GrRefCachedBitmapTexture(context, bmp, GrSamplerParams::ClampNoFilter(), nullptr));
     if (!resultTex) {
         return nullptr;
     }
@@ -241,8 +241,8 @@
 #if SK_SUPPORT_GPU
     sk_sp<GrTexture> onAsTextureRef(GrContext* context) const override {
         if (context) {
-            return sk_ref_sp(
-                GrRefCachedBitmapTexture(context, fBitmap, GrSamplerParams::ClampNoFilter()));
+            return sk_ref_sp(GrRefCachedBitmapTexture(context, fBitmap,
+                                                      GrSamplerParams::ClampNoFilter(), nullptr));
         }
 
         return nullptr;
@@ -251,7 +251,7 @@
     sk_sp<GrTextureProxy> onAsTextureProxy(GrContext* context) const override {
         if (context) {
             sk_sp<GrTexture> tex(sk_ref_sp(GrRefCachedBitmapTexture(
-                context, fBitmap, GrSamplerParams::ClampNoFilter())));
+                context, fBitmap, GrSamplerParams::ClampNoFilter(), nullptr)));
             sk_sp<GrSurfaceProxy> sProxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
             return sk_ref_sp(sProxy->asTextureProxy());
         }
diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.cpp b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
index afafe08..a259607 100644
--- a/src/effects/GrAlphaThresholdFragmentProcessor.cpp
+++ b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
@@ -32,12 +32,6 @@
                                                                 bounds));
 }
 
-static SkMatrix make_div_and_translate_matrix(GrTexture* texture, int x, int y) {
-    SkMatrix matrix = GrCoordTransform::MakeDivByTextureWHMatrix(texture);
-    matrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y));
-    return matrix;
-}
-
 GrAlphaThresholdFragmentProcessor::GrAlphaThresholdFragmentProcessor(
                                                            GrTexture* texture,
                                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
@@ -47,11 +41,11 @@
                                                            const SkIRect& bounds)
     : fInnerThreshold(innerThreshold)
     , fOuterThreshold(outerThreshold)
-    , fImageCoordTransform(GrCoordTransform::MakeDivByTextureWHMatrix(texture), texture,
-                           GrSamplerParams::kNone_FilterMode)
+    , fImageCoordTransform(SkMatrix::I(), texture, GrSamplerParams::kNone_FilterMode)
     , fImageTextureSampler(texture)
     , fColorSpaceXform(std::move(colorSpaceXform))
-    , fMaskCoordTransform(make_div_and_translate_matrix(maskTexture, -bounds.x(), -bounds.y()),
+    , fMaskCoordTransform(SkMatrix::MakeTrans(SkIntToScalar(-bounds.x()),
+                                              SkIntToScalar(-bounds.y())),
                           maskTexture,
                           GrSamplerParams::kNone_FilterMode)
     , fMaskTextureSampler(maskTexture) {
diff --git a/src/effects/SkArithmeticImageFilter.cpp b/src/effects/SkArithmeticImageFilter.cpp
index 3e4d48f..d33a9dc 100644
--- a/src/effects/SkArithmeticImageFilter.cpp
+++ b/src/effects/SkArithmeticImageFilter.cpp
@@ -348,10 +348,8 @@
     sk_sp<GrFragmentProcessor> bgFP;
 
     if (backgroundTex) {
-        SkMatrix backgroundMatrix;
-        backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
-        backgroundMatrix.preTranslate(-SkIntToScalar(backgroundOffset.fX),
-                                      -SkIntToScalar(backgroundOffset.fY));
+        SkMatrix backgroundMatrix = SkMatrix::MakeTrans(-SkIntToScalar(backgroundOffset.fX),
+                                                        -SkIntToScalar(backgroundOffset.fY));
         sk_sp<GrColorSpaceXform> bgXform =
                 GrColorSpaceXform::Make(background->getColorSpace(), outputProperties.colorSpace());
         bgFP = GrTextureDomainEffect::Make(
@@ -364,10 +362,8 @@
     }
 
     if (foregroundTex) {
-        SkMatrix foregroundMatrix;
-        foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height());
-        foregroundMatrix.preTranslate(-SkIntToScalar(foregroundOffset.fX),
-                                      -SkIntToScalar(foregroundOffset.fY));
+        SkMatrix foregroundMatrix = SkMatrix::MakeTrans(-SkIntToScalar(foregroundOffset.fX),
+                                                        -SkIntToScalar(foregroundOffset.fY));
         sk_sp<GrColorSpaceXform> fgXform =
                 GrColorSpaceXform::Make(foreground->getColorSpace(), outputProperties.colorSpace());
         sk_sp<GrFragmentProcessor> foregroundFP;
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index aff0a32..b53fc41 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -1516,10 +1516,9 @@
 
     if (!isNormalBlur) {
         GrPaint paint;
-        SkMatrix matrix;
-        matrix.setIDiv(src->width(), src->height());
         // Blend pathTexture over blurTexture.
-        paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(src, nullptr, matrix));
+        paint.addCoverageFragmentProcessor(
+                                        GrSimpleTextureEffect::Make(src, nullptr, SkMatrix::I()));
         if (kInner_SkBlurStyle == fBlurStyle) {
             // inner:  dst = dst * src
             paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 28483bd..0d39403 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -336,9 +336,8 @@
             return nullptr;
         }
 
-        SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displTexture.get());
-        offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displOffset.fX),
-                                  SkIntToScalar(colorOffset.fY - displOffset.fY));
+        SkMatrix offsetMatrix = SkMatrix::MakeTrans(SkIntToScalar(colorOffset.fX - displOffset.fX),
+                                                    SkIntToScalar(colorOffset.fY - displOffset.fY));
         SkColorSpace* colorSpace = ctx.outputProperties().colorSpace();
         sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(color->getColorSpace(),
                                                                            colorSpace);
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 53c85bd..da4dcb8 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -1702,7 +1702,7 @@
                                    const SkMatrix& matrix,
                                    BoundaryMode boundaryMode,
                                    const SkIRect* srcBounds)
-    : INHERITED(texture, nullptr, GrCoordTransform::MakeDivByTextureWHMatrix(texture))
+    : INHERITED(texture, nullptr, SkMatrix::I())
     , fLight(light)
     , fSurfaceScale(surfaceScale)
     , fFilterMatrix(matrix)
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index 2b04532..c8b32ae 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -71,8 +71,7 @@
                       float yInvZoom,
                       float xInvInset,
                       float yInvInset)
-        : INHERITED(texture, std::move(colorSpaceXform),
-                    GrCoordTransform::MakeDivByTextureWHMatrix(texture))
+        : INHERITED(texture, std::move(colorSpaceXform), SkMatrix::I())
         , fBounds(bounds)
         , fXOffset(xOffset)
         , fYOffset(yOffset)
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp
index dc3d9a6..90a3531 100644
--- a/src/effects/SkPerlinNoiseShader.cpp
+++ b/src/effects/SkPerlinNoiseShader.cpp
@@ -515,7 +515,6 @@
     bool stitchTiles() const { return fStitchTiles; }
     const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
     int numOctaves() const { return fNumOctaves; }
-    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
 
 private:
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
@@ -546,6 +545,7 @@
                         GrTexture* permutationsTexture, GrTexture* noiseTexture,
                         const SkMatrix& matrix)
       : fType(type)
+      , fCoordTransform(matrix)
       , fNumOctaves(numOctaves)
       , fStitchTiles(stitchTiles)
       , fPermutationsSampler(permutationsTexture)
@@ -554,7 +554,6 @@
         this->initClassID<GrPerlinNoiseEffect>();
         this->addTextureSampler(&fPermutationsSampler);
         this->addTextureSampler(&fNoiseSampler);
-        fCoordTransform.reset(matrix);
         this->addCoordTransform(&fCoordTransform);
     }
 
@@ -926,10 +925,10 @@
             new PaintingData(fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, matrix);
     sk_sp<GrTexture> permutationsTexture(
         GrRefCachedBitmapTexture(args.fContext, paintingData->getPermutationsBitmap(),
-                                 GrSamplerParams::ClampNoFilter()));
+                                 GrSamplerParams::ClampNoFilter(), nullptr));
     sk_sp<GrTexture> noiseTexture(
         GrRefCachedBitmapTexture(args.fContext, paintingData->getNoiseBitmap(),
-                                 GrSamplerParams::ClampNoFilter()));
+                                 GrSamplerParams::ClampNoFilter(), nullptr));
 
     SkMatrix m = *args.fViewMatrix;
     m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1);
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 9c74634..6934a3e 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -474,7 +474,7 @@
     if (-1 == row) {
         atlas = nullptr;
         texture.reset(
-            GrRefCachedBitmapTexture(context, bitmap, GrSamplerParams::ClampNoFilter()));
+            GrRefCachedBitmapTexture(context, bitmap, GrSamplerParams::ClampNoFilter(), nullptr));
     } else {
         texture.reset(SkRef(atlas->getTexture()));
     }
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index 7f91df3..c52eeba 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -249,10 +249,8 @@
     sk_sp<GrFragmentProcessor> bgFP;
 
     if (backgroundTex) {
-        SkMatrix backgroundMatrix;
-        backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
-        backgroundMatrix.preTranslate(-SkIntToScalar(backgroundOffset.fX),
-                                      -SkIntToScalar(backgroundOffset.fY));
+        SkMatrix backgroundMatrix = SkMatrix::MakeTrans(-SkIntToScalar(backgroundOffset.fX),
+                                                        -SkIntToScalar(backgroundOffset.fY));
         sk_sp<GrColorSpaceXform> bgXform = GrColorSpaceXform::Make(background->getColorSpace(),
                                                                    outputProperties.colorSpace());
         bgFP = GrTextureDomainEffect::Make(
@@ -266,10 +264,8 @@
     }
 
     if (foregroundTex) {
-        SkMatrix foregroundMatrix;
-        foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height());
-        foregroundMatrix.preTranslate(-SkIntToScalar(foregroundOffset.fX),
-                                      -SkIntToScalar(foregroundOffset.fY));
+        SkMatrix foregroundMatrix = SkMatrix::MakeTrans(-SkIntToScalar(foregroundOffset.fX),
+                                                        -SkIntToScalar(foregroundOffset.fY));
         sk_sp<GrColorSpaceXform> fgXform = GrColorSpaceXform::Make(foreground->getColorSpace(),
                                                                    outputProperties.colorSpace());
         sk_sp<GrFragmentProcessor> foregroundFP;
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index ca64cb7..b1d3e13 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -1659,14 +1659,18 @@
             fRow = fAtlas->lockRow(bitmap);
             if (-1 != fRow) {
                 fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
-                fCoordTransform.reset(*args.fMatrix, fAtlas->getTexture(), params.filterMode());
+                // This is 1/2 places where auto-normalization is disabled
+                fCoordTransform.reset(*args.fMatrix, fAtlas->getTexture(),
+                                      params.filterMode(), false);
                 fTextureSampler.reset(fAtlas->getTexture(), params);
             } else {
-                sk_sp<GrTexture> texture(GrRefCachedBitmapTexture(args.fContext, bitmap, params));
+                sk_sp<GrTexture> texture(GrRefCachedBitmapTexture(args.fContext, bitmap,
+                                                                  params, nullptr));
                 if (!texture) {
                     return;
                 }
-                fCoordTransform.reset(*args.fMatrix, texture.get(), params.filterMode());
+                // This is 2/2 places where auto-normalization is disabled
+                fCoordTransform.reset(*args.fMatrix, texture.get(), params.filterMode(), false);
                 fTextureSampler.reset(texture.get(), params);
                 fYCoord = SK_ScalarHalf;
             }
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
index 4bb640d..aab1ac7 100644
--- a/src/effects/gradients/SkLinearGradient.cpp
+++ b/src/effects/gradients/SkLinearGradient.cpp
@@ -86,6 +86,73 @@
         : CheckedCreateContext<  LinearGradientContext>(storage, *this, rec);
 }
 
+// For now, only a 2-stop raster pipeline specialization.
+//
+// Stages:
+//
+//   * matrix (map dst -> grad space)
+//   * clamp/repeat/mirror (tiling)
+//   * linear_gradient_2stops (lerp c0/c1)
+//   * optional premul
+//
+bool SkLinearGradient::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
+                                      const SkMatrix& ctm, const SkPaint& paint) const {
+    if (fColorCount > 2) {
+        return false;
+    }
+
+    // Local matrix not supported currently.  Remove once we have a generic RP wrapper.
+    if (!getLocalMatrix().isIdentity()) {
+        return false;
+    }
+
+    SkASSERT(fColorCount == 2);
+    SkASSERT(fOrigPos == nullptr || (fOrigPos[0] == 0 && fOrigPos[1] == 1));
+
+    SkMatrix dstToPts;
+    if (!ctm.invert(&dstToPts)) {
+        return false;
+    }
+
+    const auto dstToUnit = SkMatrix::Concat(fPtsToUnit, dstToPts);
+
+    auto* m = alloc->makeArrayDefault<float>(9);
+    if (dstToUnit.asAffine(m)) {
+        // TODO: mapping y is not needed; split the matrix stages to save some math?
+        p->append(SkRasterPipeline::matrix_2x3, m);
+    } else {
+        dstToUnit.get9(m);
+        p->append(SkRasterPipeline::matrix_perspective, m);
+    }
+
+    // TODO: clamp/repeat/mirror const 1f stages?
+    auto* limit = alloc->make<float>(1.0f);
+
+    switch (fTileMode) {
+        case kClamp_TileMode:  p->append(SkRasterPipeline:: clamp_x, limit); break;
+        case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit); break;
+        case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit); break;
+    }
+
+    const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
+    const SkColor4f c0 = to_colorspace(fOrigColors4f[0], fColorSpace.get(), cs),
+                    c1 = to_colorspace(fOrigColors4f[1], fColorSpace.get(), cs);
+    const SkPM4f  pmc0 = premulGrad ? c0.premul() : SkPM4f::From4f(Sk4f::Load(&c0)),
+                  pmc1 = premulGrad ? c1.premul() : SkPM4f::From4f(Sk4f::Load(&c1));
+
+    auto* c0_and_dc = alloc->makeArrayDefault<SkPM4f>(2);
+    c0_and_dc[0] = pmc0;
+    c0_and_dc[1] = SkPM4f::From4f(pmc1.to4f() - pmc0.to4f());
+
+    p->append(SkRasterPipeline::linear_gradient_2stops, c0_and_dc);
+
+    if (!premulGrad && !this->colorsAreOpaque()) {
+        p->append(SkRasterPipeline::premul);
+    }
+
+    return true;
+}
+
 // This swizzles SkColor into the same component order as SkPMColor, but does not actually
 // "pre" multiply the color components.
 //
diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h
index 7a85b88..acf9c21 100644
--- a/src/effects/gradients/SkLinearGradient.h
+++ b/src/effects/gradients/SkLinearGradient.h
@@ -69,6 +69,9 @@
     size_t onContextSize(const ContextRec&) const override;
     Context* onCreateContext(const ContextRec&, void* storage) const override;
 
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+                        const SkMatrix&, const SkPaint&) const override;
+
 private:
     class LinearGradient4fContext;
 
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
index 1608ae2..64778c3 100644
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -210,8 +210,18 @@
     SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
     SkString t;
     // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
-    t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)",
-             coords2D.c_str(), coords2D.c_str());
+    if (args.fShaderCaps->atan2ImplementedAsAtanYOverX()) {
+        // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
+        // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in
+        // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device
+        // handle the undefined behavior of the second paramenter being 0 instead of doing the
+        // divide ourselves and using atan instead.
+        t.printf("(2.0 * atan(- %s.y, length(%s) - %s.x) * 0.1591549430918 + 0.5)",
+                 coords2D.c_str(), coords2D.c_str(), coords2D.c_str());
+    } else {
+        t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)",
+                 coords2D.c_str(), coords2D.c_str());
+    }
     this->emitColor(args.fFragBuilder,
                     args.fUniformHandler,
                     args.fShaderCaps,
diff --git a/src/gpu/GrBlurUtils.cpp b/src/gpu/GrBlurUtils.cpp
index 23a3c6c..7a8a252 100644
--- a/src/gpu/GrBlurUtils.cpp
+++ b/src/gpu/GrBlurUtils.cpp
@@ -9,6 +9,7 @@
 #include "GrRenderTargetContext.h"
 #include "GrCaps.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrFixedClip.h"
 #include "GrRenderTargetContextPriv.h"
 #include "effects/GrSimpleTextureEffect.h"
@@ -46,10 +47,8 @@
         return false;
     }
 
-    SkMatrix matrix;
-    matrix.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
-    // TODO: this divide relies on the instantiated texture's size!
-    matrix.postIDiv(maskTex->width(), maskTex->height());
+    SkMatrix matrix = SkMatrix::MakeTrans(-SkIntToScalar(maskRect.fLeft),
+                                          -SkIntToScalar(maskRect.fTop));
     matrix.preConcat(viewMatrix);
     paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(maskTex, nullptr, matrix));
 
@@ -91,25 +90,21 @@
     desc.fHeight = dstM.fBounds.height();
     desc.fConfig = kAlpha_8_GrPixelConfig;
 
-    sk_sp<GrSurfaceProxy> proxy(GrSurfaceProxy::MakeDeferred(*context->caps(), desc,
-                                                             SkBackingFit::kApprox,
-                                                             SkBudgeted::kYes));
-    if (!proxy || !proxy->asTextureProxy()) {
+    sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext(
+                                                        desc,
+                                                        SkBackingFit::kApprox,
+                                                        SkBudgeted::kYes);
+    if (!sContext) {
         return false;
     }
 
-    // This is a bit goofy but, until writePixels is moved to GrSurfaceContext, we're stuck
-    // instantiating here to do the writePixels
-    GrTexture* texture = proxy->asTextureProxy()->instantiate(context->textureProvider());
-    if (!texture) {
+    SkImageInfo ii = SkImageInfo::MakeA8(desc.fWidth, desc.fHeight);
+    if (!sContext->writePixels(ii, dstM.fImage, dstM.fRowBytes, 0, 0)) {
         return false;
     }
 
-    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                         dstM.fImage, dstM.fRowBytes);
-
     return draw_mask(renderTargetContext, context->textureProvider(), clipData, viewMatrix,
-                     dstM.fBounds, std::move(paint), sk_ref_sp(proxy->asTextureProxy()));
+                     dstM.fBounds, std::move(paint), sk_ref_sp(sContext->asDeferredTexture()));
 }
 
 // Create a mask of 'devPath' and place the result in 'mask'.
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 5543dff..1322e25 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -308,10 +308,8 @@
     SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
     if (tempTexture) {
         sk_sp<GrFragmentProcessor> fp;
-        SkMatrix textureMatrix;
-        textureMatrix.setIDiv(tempTexture->width(), tempTexture->height());
         if (applyPremulToSrc) {
-            fp = this->createUPMToPMEffect(tempTexture.get(), tempDrawInfo.fSwizzle, textureMatrix);
+            fp = this->createUPMToPMEffect(tempTexture.get(), tempDrawInfo.fSwizzle, SkMatrix::I());
             // If premultiplying was the only reason for the draw, fall back to a straight write.
             if (!fp) {
                 if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference) {
@@ -325,7 +323,7 @@
             if (!fp) {
                 fp = GrConfigConversionEffect::Make(tempTexture.get(), tempDrawInfo.fSwizzle,
                                                     GrConfigConversionEffect::kNone_PMConversion,
-                                                    textureMatrix);
+                                                    SkMatrix::I());
                 if (!fp) {
                     return false;
                 }
@@ -463,9 +461,7 @@
                                                            tempDrawInfo.fTempSurfaceDesc.fSampleCnt,
                                                            tempDrawInfo.fTempSurfaceDesc.fOrigin);
         if (tempRTC) {
-            SkMatrix textureMatrix;
-            textureMatrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
-            textureMatrix.postIDiv(src->width(), src->height());
+            SkMatrix textureMatrix = SkMatrix::MakeTrans(SkIntToScalar(left), SkIntToScalar(top));
             sk_sp<GrFragmentProcessor> fp;
             if (unpremul) {
                 fp = this->createPMToUPMEffect(src->asTexture(), tempDrawInfo.fSwizzle,
diff --git a/src/gpu/GrCoordTransform.cpp b/src/gpu/GrCoordTransform.cpp
index 63d91a8..4afd0ef 100644
--- a/src/gpu/GrCoordTransform.cpp
+++ b/src/gpu/GrCoordTransform.cpp
@@ -11,11 +11,13 @@
 #include "GrGpu.h"
 
 void GrCoordTransform::reset(const SkMatrix& m, const GrTexture* texture,
-                             GrSamplerParams::FilterMode filter) {
+                             GrSamplerParams::FilterMode filter, bool normalize) {
     SkASSERT(texture);
     SkASSERT(!fInProcessor);
 
     fMatrix = m;
+    fTexture = texture;
+    fNormalize = normalize;
     fReverseY = kBottomLeft_GrSurfaceOrigin == texture->origin();
 
     // Always start at kDefault. Then if precisions differ we see if the precision needs to be
@@ -52,9 +54,3 @@
     }
 }
 
-void GrCoordTransform::reset(const SkMatrix& m, GrSLPrecision precision) {
-    SkASSERT(!fInProcessor);
-    fMatrix = m;
-    fReverseY = false;
-    fPrecision = precision;
-}
diff --git a/src/gpu/GrFragmentProcessor.cpp b/src/gpu/GrFragmentProcessor.cpp
index eb6a35c..0518966 100644
--- a/src/gpu/GrFragmentProcessor.cpp
+++ b/src/gpu/GrFragmentProcessor.cpp
@@ -92,7 +92,7 @@
     }
     int count = this->numCoordTransforms();
     for (int i = 0; i < count; ++i) {
-        if (this->coordTransform(i) != that.coordTransform(i)) {
+        if (!this->coordTransform(i).hasSameEffectAs(that.coordTransform(i))) {
             return false;
         }
     }
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 3538b80..4880f6c 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -58,12 +58,16 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool GrGpu::makeCopyForTextureParams(int width, int height, const GrSamplerParams& textureParams,
-                                     GrTextureProducer::CopyParams* copyParams) const {
+                                     GrTextureProducer::CopyParams* copyParams,
+                                     SkScalar scaleAdjust[2]) const {
     const GrCaps& caps = *this->caps();
     if (textureParams.isTiled() && !caps.npotTextureTileSupport() &&
         (!SkIsPow2(width) || !SkIsPow2(height))) {
+        SkASSERT(scaleAdjust);
         copyParams->fWidth = GrNextPow2(width);
         copyParams->fHeight = GrNextPow2(height);
+        scaleAdjust[0] = ((SkScalar) copyParams->fWidth) / width;
+        scaleAdjust[1] = ((SkScalar) copyParams->fHeight) / height;
         switch (textureParams.filterMode()) {
             case GrSamplerParams::kNone_FilterMode:
                 copyParams->fFilter = GrSamplerParams::kNone_FilterMode;
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 39735d2..0aed3d2 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -473,19 +473,20 @@
     // GrSamplerParams. This variation is called when the caller will create a new texture using the
     // texture provider from a non-texture src (cpu-backed image, ...).
     bool makeCopyForTextureParams(int width, int height, const GrSamplerParams&,
-                                 GrTextureProducer::CopyParams*) const;
+                                 GrTextureProducer::CopyParams*, SkScalar scaleAdjust[2]) const;
 
     // Like the above but this variation should be called when the caller is not creating the
     // original texture but rather was handed the original texture. It adds additional checks
     // relevant to original textures that were created external to Skia via
     // GrTextureProvider::wrap methods.
     bool makeCopyForTextureParams(GrTexture* texture, const GrSamplerParams& params,
-                                  GrTextureProducer::CopyParams* copyParams) const {
+                                  GrTextureProducer::CopyParams* copyParams,
+                                  SkScalar scaleAdjust[2]) const {
         if (this->makeCopyForTextureParams(texture->width(), texture->height(), params,
-                                           copyParams)) {
+                                           copyParams, scaleAdjust)) {
             return true;
         }
-        return this->onMakeCopyForTextureParams(texture, params, copyParams);
+        return this->onMakeCopyForTextureParams(texture, params, copyParams, scaleAdjust);
     }
 
     // This is only to be used in GL-specific tests.
@@ -549,7 +550,8 @@
     virtual gr_instanced::InstancedRendering* onCreateInstancedRendering() = 0;
 
     virtual bool onMakeCopyForTextureParams(GrTexture* texture, const GrSamplerParams&,
-                                            GrTextureProducer::CopyParams*) const { return false; }
+                                            GrTextureProducer::CopyParams*,
+                                            SkScalar scaleAdjust[2]) const { return false; }
 
     virtual bool onGetReadPixelsInfo(GrSurface* srcSurface, int readWidth, int readHeight,
                                      size_t rowBytes, GrPixelConfig readConfig, DrawPreference*,
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 240f9f9..d881888 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -111,15 +111,11 @@
         return nullptr;
     }
 
-    // TODO: can skip this step when writePixels is moved
-    GrTexture* tex = sContext->asDeferredTexture()->instantiate(context->textureProvider());
-    if (!tex) {
+    SkImageInfo ii = SkImageInfo::MakeA8(desc.fWidth, desc.fHeight);
+    if (!sContext->writePixels(ii, fPixels.addr(), fPixels.rowBytes(), 0, 0)) {
         return nullptr;
     }
 
-    tex->writePixels(0, 0, fPixels.width(), fPixels.height(), kAlpha_8_GrPixelConfig,
-                     fPixels.addr(), fPixels.rowBytes());
-
     return sk_ref_sp(sContext->asDeferredTexture());
 }
 
@@ -176,10 +172,8 @@
     // We use device coords to compute the texture coordinates. We take the device coords and apply
     // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
     // matrix to normalized coords.
-    SkMatrix maskMatrix;
-    maskMatrix.setIDiv(texture->width(), texture->height());
-    maskMatrix.preTranslate(SkIntToScalar(-textureOriginInDeviceSpace.fX),
-                            SkIntToScalar(-textureOriginInDeviceSpace.fY));
+    SkMatrix maskMatrix = SkMatrix::MakeTrans(SkIntToScalar(-textureOriginInDeviceSpace.fX),
+                                              SkIntToScalar(-textureOriginInDeviceSpace.fY));
     maskMatrix.preConcat(viewMatrix);
     std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeNonAAFill(paint.getColor(), SkMatrix::I(),
                                                                   dstRect, nullptr, &invert);
diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp
index f9db968..54cd3e5 100644
--- a/src/gpu/GrShaderCaps.cpp
+++ b/src/gpu/GrShaderCaps.cpp
@@ -56,6 +56,7 @@
     fCanUseAnyFunctionInShader = true;
     fCanUseMinAndAbsTogether = true;
     fMustForceNegatedAtanParamToFloat = false;
+    fAtan2ImplementedAsAtanYOverX = false;
     fRequiresLocalOutputColorForFBFetch = false;
     fFlatInterpolationSupport = false;
     fNoPerspectiveInterpolationSupport = false;
diff --git a/src/gpu/GrTextureAdjuster.cpp b/src/gpu/GrTextureAdjuster.cpp
index 7142ab9..2fa5241 100644
--- a/src/gpu/GrTextureAdjuster.cpp
+++ b/src/gpu/GrTextureAdjuster.cpp
@@ -64,7 +64,8 @@
 }
 
 GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrSamplerParams& params,
-                                                      SkIPoint* outOffset) {
+                                                      SkIPoint* outOffset,
+                                                      SkScalar scaleAdjust[2]) {
     GrTexture* texture = this->originalTexture();
     GrContext* context = texture->getContext();
     CopyParams copyParams;
@@ -81,7 +82,8 @@
         copyParams.fWidth = contentArea->width();
         copyParams.fHeight = contentArea->height();
         copyParams.fFilter = GrSamplerParams::kBilerp_FilterMode;
-    } else if (!context->getGpu()->makeCopyForTextureParams(texture, params, &copyParams)) {
+    } else if (!context->getGpu()->makeCopyForTextureParams(texture, params, &copyParams,
+                                                            scaleAdjust)) {
         if (outOffset) {
             if (contentArea) {
                 outOffset->set(contentArea->fLeft, contentArea->fRight);
@@ -124,7 +126,8 @@
     if (filterOrNullForBicubic) {
         params.setFilterMode(*filterOrNullForBicubic);
     }
-    sk_sp<GrTexture> texture(this->refTextureSafeForParams(params, nullptr));
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
+    sk_sp<GrTexture> texture(this->refTextureSafeForParams(params, nullptr, scaleAdjust));
     if (!texture) {
         return nullptr;
     }
@@ -132,6 +135,7 @@
     // content.
     if (texture.get() != this->originalTexture()) {
         contentArea = nullptr;
+        textureMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
     }
 
     DomainMode domainMode =
@@ -156,7 +160,6 @@
     }
     SkASSERT(kNoDomain_DomainMode == domainMode ||
              (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
-    textureMatrix.postIDiv(texture->width(), texture->height());
     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace,
                                                                        dstColorSpace);
     return CreateFragmentProcessorForDomainAndFilter(texture.get(), std::move(colorSpaceXform),
diff --git a/src/gpu/GrTextureAdjuster.h b/src/gpu/GrTextureAdjuster.h
index e437c80..1738d60 100644
--- a/src/gpu/GrTextureAdjuster.h
+++ b/src/gpu/GrTextureAdjuster.h
@@ -23,7 +23,8 @@
         outOffset will be the top-left corner of the subset if a copy is not made. Otherwise,
         the copy will be tight to the contents and outOffset will be (0, 0). If the copy's size
         does not match subset's dimensions then the contents are scaled to fit the copy.*/
-    GrTexture* refTextureSafeForParams(const GrSamplerParams&, SkIPoint* outOffset);
+    GrTexture* refTextureSafeForParams(const GrSamplerParams&, SkIPoint* outOffset,
+                                       SkScalar scaleAdjust[2]);
 
     sk_sp<GrFragmentProcessor> createFragmentProcessor(
                                 const SkMatrix& textureMatrix,
diff --git a/src/gpu/GrTextureMaker.cpp b/src/gpu/GrTextureMaker.cpp
index 37272be..e68703a 100644
--- a/src/gpu/GrTextureMaker.cpp
+++ b/src/gpu/GrTextureMaker.cpp
@@ -12,7 +12,8 @@
 
 GrTexture* GrTextureMaker::refTextureForParams(const GrSamplerParams& params,
                                                SkColorSpace* dstColorSpace,
-                                               sk_sp<SkColorSpace>* texColorSpace) {
+                                               sk_sp<SkColorSpace>* texColorSpace,
+                                               SkScalar scaleAdjust[2]) {
     CopyParams copyParams;
     bool willBeMipped = params.filterMode() == GrSamplerParams::kMipMap_FilterMode;
 
@@ -25,7 +26,7 @@
     }
 
     if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params,
-                                                      &copyParams)) {
+                                                      &copyParams, scaleAdjust)) {
         return this->refOriginalTexture(willBeMipped, dstColorSpace);
     }
     GrUniqueKey copyKey;
@@ -77,22 +78,24 @@
         params.reset(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
     }
     sk_sp<SkColorSpace> texColorSpace;
-    sk_sp<GrTexture> texture(this->refTextureForParams(params, dstColorSpace, &texColorSpace));
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
+    sk_sp<GrTexture> texture(this->refTextureForParams(params, dstColorSpace, &texColorSpace,
+                                                       scaleAdjust));
     if (!texture) {
         return nullptr;
     }
+    SkMatrix adjustedMatrix = textureMatrix;
+    adjustedMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
     SkRect domain;
     DomainMode domainMode =
         DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
                             texture->width(), texture->height(),
                             nullptr, fmForDetermineDomain, &domain);
     SkASSERT(kTightCopy_DomainMode != domainMode);
-    SkMatrix normalizedTextureMatrix = textureMatrix;
-    normalizedTextureMatrix.postIDiv(texture->width(), texture->height());
     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
                                                                        dstColorSpace);
     return CreateFragmentProcessorForDomainAndFilter(texture.get(), std::move(colorSpaceXform),
-                                                     normalizedTextureMatrix, domainMode, domain,
+                                                     adjustedMatrix, domainMode, domain,
                                                      filterOrNullForBicubic);
 }
 
diff --git a/src/gpu/GrTextureMaker.h b/src/gpu/GrTextureMaker.h
index 0ea5f6d..767d4ff 100644
--- a/src/gpu/GrTextureMaker.h
+++ b/src/gpu/GrTextureMaker.h
@@ -19,10 +19,12 @@
     /**
      *  Returns a texture that is safe for use with the params. If the size of the returned texture
      *  does not match width()/height() then the contents of the original must be scaled to fit
-     *  the texture. Places the color space of the texture in (*texColorSpace).
+     *  the texture. Additionally, the 'scaleAdjust' must be applied to the texture matrix
+     *  in order to correct the absolute texture coordinates.
+     *  Places the color space of the texture in (*texColorSpace).
      */
     GrTexture* refTextureForParams(const GrSamplerParams&, SkColorSpace* dstColorSpace,
-                                   sk_sp<SkColorSpace>* texColorSpace);
+                                   sk_sp<SkColorSpace>* texColorSpace, SkScalar scaleAdjust[2]);
 
     sk_sp<GrFragmentProcessor> createFragmentProcessor(
                                 const SkMatrix& textureMatrix,
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index 0adae8c..496dbb2 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -52,16 +52,9 @@
 
     SkRect localRect;
     if (subset) {
-        SkScalar sx = SK_Scalar1 / inputTexture->width();
-        SkScalar sy = SK_Scalar1 / inputTexture->height();
-
         localRect = SkRect::Make(*subset);
-        localRect.fLeft *= sx;
-        localRect.fTop *= sy;
-        localRect.fRight *= sx;
-        localRect.fBottom *= sy;
     } else {
-        localRect = SkRect::MakeWH(1.f, 1.f);
+        localRect = SkRect::MakeWH(inputTexture->width(), inputTexture->height());
     }
 
     SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
diff --git a/src/gpu/GrTextureToYUVPlanes.cpp b/src/gpu/GrTextureToYUVPlanes.cpp
index b9252fa..132b680 100644
--- a/src/gpu/GrTextureToYUVPlanes.cpp
+++ b/src/gpu/GrTextureToYUVPlanes.cpp
@@ -22,8 +22,8 @@
 static bool convert_texture(GrTexture* src, GrRenderTargetContext* dst, int dstW, int dstH,
                             SkYUVColorSpace colorSpace, MakeFPProc proc) {
 
-    SkScalar xScale = SkIntToScalar(src->width()) / dstW / src->width();
-    SkScalar yScale = SkIntToScalar(src->height()) / dstH / src->height();
+    SkScalar xScale = SkIntToScalar(src->width()) / dstW;
+    SkScalar yScale = SkIntToScalar(src->height()) / dstH;
     GrSamplerParams::FilterMode filter;
     if (dstW == src->width() && dstW == src->height()) {
         filter = GrSamplerParams::kNone_FilterMode;
diff --git a/src/gpu/GrXferProcessor.cpp b/src/gpu/GrXferProcessor.cpp
index 7de0d77..d88259d 100644
--- a/src/gpu/GrXferProcessor.cpp
+++ b/src/gpu/GrXferProcessor.cpp
@@ -180,6 +180,40 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+using ColorType = GrXPFactory::ColorType;
+using CoverageType = GrXPFactory::CoverageType;
+
+ColorType analysis_color_type(const GrPipelineAnalysis& analysis) {
+    if (analysis.fColorPOI.validFlags() == kRGBA_GrColorComponentFlags) {
+        return GrColorIsOpaque(analysis.fColorPOI.color()) ? ColorType::kOpaqueConstant
+                                                           : ColorType::kConstant;
+    }
+    if ((analysis.fColorPOI.validFlags() & kA_GrColorComponentFlag) &&
+        GrColorIsOpaque(analysis.fColorPOI.color())) {
+        return ColorType::kOpaque;
+    }
+    return ColorType::kUnknown;
+}
+
+CoverageType analysis_coverage_type(const GrPipelineAnalysis& analysis) {
+    if (analysis.fCoveragePOI.isSolidWhite()) {
+        return CoverageType::kNone;
+    }
+    if (analysis.fCoveragePOI.isLCDCoverage()) {
+        return CoverageType::kLCD;
+    }
+    return CoverageType::kSingleChannel;
+}
+
+bool GrXPFactory::willReadDstColor(const GrCaps& caps, const GrPipelineAnalysis& analysis) const {
+    if (analysis.fUsesPLSDstRead) {
+        return true;
+    }
+    ColorType colorType = analysis_color_type(analysis);
+    CoverageType coverageType = analysis_coverage_type(analysis);
+    return this->willReadDstColor(caps, colorType, coverageType);
+}
+
 GrXferProcessor* GrXPFactory::createXferProcessor(const GrPipelineAnalysis& analysis,
                                                   bool hasMixedSamples,
                                                   const DstTexture* dstTexture,
@@ -200,9 +234,6 @@
 }
 
 bool GrXPFactory::willNeedDstTexture(const GrCaps& caps, const GrPipelineAnalysis& analysis) const {
-    return (this->willReadDstColor(caps, analysis) && !caps.shaderCaps()->dstReadInShaderSupport());
-}
-
-bool GrXPFactory::willReadDstColor(const GrCaps& caps, const GrPipelineAnalysis& analysis) const {
-    return analysis.fUsesPLSDstRead || this->onWillReadDstColor(caps, analysis);
+    return !analysis.fUsesPLSDstRead && !caps.shaderCaps()->dstReadInShaderSupport() &&
+           this->willReadDstColor(caps, analysis);
 }
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index 1330cfe..2f75d92 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -146,7 +146,7 @@
 
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth,
-            yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
+                                     yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
 
     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
 
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 1dec0f6..85eb671 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1041,20 +1041,18 @@
     SkASSERT(bitmap.width() <= fContext->caps()->maxTileSize() &&
              bitmap.height() <= fContext->caps()->maxTileSize());
 
-    sk_sp<GrTexture> texture = GrMakeCachedBitmapTexture(fContext.get(), bitmap, params);
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
+    sk_sp<GrTexture> texture = GrMakeCachedBitmapTexture(fContext.get(), bitmap,
+                                                         params, scaleAdjust);
     if (nullptr == texture) {
         return;
     }
     sk_sp<GrColorSpaceXform> colorSpaceXform =
         GrColorSpaceXform::Make(bitmap.colorSpace(), fRenderTargetContext->getColorSpace());
 
-    SkScalar iw = 1.f / texture->width();
-    SkScalar ih = 1.f / texture->height();
-
-    SkMatrix texMatrix;
     // Compute a matrix that maps the rect we will draw to the src rect.
-    texMatrix.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
-    texMatrix.postScale(iw, ih);
+    SkMatrix texMatrix = SkMatrix::MakeRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
+    texMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
 
     // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
     // the rest from the SkPaint.
@@ -1122,8 +1120,8 @@
         }
 
         // draw sprite neither filters nor tiles.
-        texture.reset(
-            GrRefCachedBitmapTexture(fContext.get(), bitmap, GrSamplerParams::ClampNoFilter()));
+        texture.reset(GrRefCachedBitmapTexture(fContext.get(), bitmap,
+                                               GrSamplerParams::ClampNoFilter(), nullptr));
         if (!texture) {
             return;
         }
@@ -1200,10 +1198,7 @@
             SkMatrix::I(),
             SkRect::Make(SkIRect::MakeXYWH(
                     left + offset.fX, top + offset.fY, subset.width(), subset.height())),
-            SkRect::MakeXYWH(SkIntToScalar(subset.fLeft) / texture->width(),
-                             SkIntToScalar(subset.fTop) / texture->height(),
-                             SkIntToScalar(subset.width()) / texture->width(),
-                             SkIntToScalar(subset.height()) / texture->height()));
+            SkRect::Make(subset));
 }
 
 void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
@@ -1291,8 +1286,8 @@
         return nullptr;
     }
 
-    sk_sp<GrTexture> texture =
-        GrMakeCachedBitmapTexture(fContext.get(), bitmap, GrSamplerParams::ClampNoFilter());
+    sk_sp<GrTexture> texture = GrMakeCachedBitmapTexture(fContext.get(), bitmap,
+                                                         GrSamplerParams::ClampNoFilter(), nullptr);
     if (!texture) {
         return nullptr;
     }
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 50c7cc2..f7949e2 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -279,16 +279,17 @@
 }
 
 GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap,
-                                    const GrSamplerParams& params) {
+                                    const GrSamplerParams& params, SkScalar scaleAdjust[2]) {
     // Caller doesn't care about the texture's color space (they can always get it from the bitmap)
-    return GrBitmapTextureMaker(ctx, bitmap).refTextureForParams(params, nullptr, nullptr);
+    return GrBitmapTextureMaker(ctx, bitmap).refTextureForParams(params, nullptr,
+                                                                 nullptr, scaleAdjust);
 }
 
 sk_sp<GrTexture> GrMakeCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap,
-                                           const GrSamplerParams& params) {
+                                           const GrSamplerParams& params, SkScalar scaleAdjust[2]) {
     // Caller doesn't care about the texture's color space (they can always get it from the bitmap)
     GrTexture* tex = GrBitmapTextureMaker(ctx, bitmap).refTextureForParams(params, nullptr,
-                                                                           nullptr);
+                                                                           nullptr, scaleAdjust);
     return sk_sp<GrTexture>(tex);
 }
 
diff --git a/src/gpu/effects/Gr1DKernelEffect.h b/src/gpu/effects/Gr1DKernelEffect.h
index d7402e8..29e0e5d 100644
--- a/src/gpu/effects/Gr1DKernelEffect.h
+++ b/src/gpu/effects/Gr1DKernelEffect.h
@@ -31,7 +31,7 @@
     Gr1DKernelEffect(GrTexture* texture,
                      Direction direction,
                      int radius)
-        : INHERITED(texture, nullptr, GrCoordTransform::MakeDivByTextureWHMatrix(texture))
+        : INHERITED(texture, nullptr, SkMatrix::I())
         , fDirection(direction)
         , fRadius(radius) {}
 
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index 50a2a5d..07d1c53 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -181,8 +181,7 @@
     static const SkShader::TileMode kClampClamp[] =
         { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode };
     return GrBicubicEffect::Make(d->fTextures[texIdx], colorSpaceXform,
-                                 GrCoordTransform::MakeDivByTextureWHMatrix(d->fTextures[texIdx]),
-                                 kClampClamp);
+                                 SkMatrix::I(), kClampClamp);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 8405fcb..a11be2b 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -185,6 +185,9 @@
         }
     }
 
+    const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
+                                             kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
     sk_sp<GrRenderTargetContext> readRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
                                                                           kSize, kSize,
                                                                           kConfig, nullptr));
@@ -216,7 +219,7 @@
         *upmToPMRule = kConversionRules[i][1];
 
         static const SkRect kDstRect = SkRect::MakeIWH(kSize, kSize);
-        static const SkRect kSrcRect = SkRect::MakeIWH(1, 1);
+        static const SkRect kSrcRect = SkRect::MakeIWH(kSize, kSize);
         // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
         // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
         // We then verify that two reads produced the same values.
@@ -240,7 +243,9 @@
         readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kDstRect,
                                 kSrcRect);
 
-        readRTC->asTexture()->readPixels(0, 0, kSize, kSize, kConfig, firstRead);
+        if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
+            continue;
+        }
 
         paint2.addColorFragmentProcessor(std::move(upmToPM));
         paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -254,7 +259,9 @@
         readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kDstRect,
                                 kSrcRect);
 
-        readRTC->asTexture()->readPixels(0, 0, kSize, kSize, kConfig, secondRead);
+        if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
+            continue;
+        }
 
         failed = false;
         for (int y = 0; y < kSize && !failed; ++y) {
diff --git a/src/gpu/effects/GrCustomXfermode.cpp b/src/gpu/effects/GrCustomXfermode.cpp
index 070fa2f..f8af7db 100644
--- a/src/gpu/effects/GrCustomXfermode.cpp
+++ b/src/gpu/effects/GrCustomXfermode.cpp
@@ -54,15 +54,16 @@
 }
 
 static bool can_use_hw_blend_equation(GrBlendEquation equation,
-                                      const GrPipelineAnalysis& analysis,
+                                      bool usePLSRead,
+                                      bool isLCDCoverage,
                                       const GrCaps& caps) {
     if (!caps.advancedBlendEquationSupport()) {
         return false;
     }
-    if (analysis.fUsesPLSDstRead) {
+    if (usePLSRead) {
         return false;
     }
-    if (analysis.fCoveragePOI.isLCDCoverage()) {
+    if (isLCDCoverage) {
         return false; // LCD coverage must be applied after the blend equation.
     }
     if (caps.canUseAdvancedBlendEquation(equation)) {
@@ -340,8 +341,7 @@
                                            bool hasMixedSamples,
                                            const DstTexture*) const override;
 
-    bool onWillReadDstColor(const GrCaps&, const GrPipelineAnalysis&) const override;
-
+    bool willReadDstColor(const GrCaps&, ColorType, CoverageType) const override;
 
     GR_DECLARE_XP_FACTORY_TEST;
 
@@ -359,16 +359,20 @@
                                                         bool hasMixedSamples,
                                                         const DstTexture* dstTexture) const {
     SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
-    if (can_use_hw_blend_equation(fHWBlendEquation, analysis, caps)) {
+    if (can_use_hw_blend_equation(fHWBlendEquation, analysis.fUsesPLSDstRead,
+                                  analysis.fCoveragePOI.isLCDCoverage(), caps)) {
         SkASSERT(!dstTexture || !dstTexture->texture());
         return new CustomXP(fMode, fHWBlendEquation);
     }
     return new CustomXP(dstTexture, hasMixedSamples, fMode);
 }
 
-bool CustomXPFactory::onWillReadDstColor(const GrCaps& caps,
-                                         const GrPipelineAnalysis& analysis) const {
-    return !can_use_hw_blend_equation(fHWBlendEquation, analysis, caps);
+bool CustomXPFactory::willReadDstColor(const GrCaps& caps, ColorType colorType,
+                                       CoverageType coverageType) const {
+    // This should not be called if we're using PLS dst read.
+    static constexpr bool kUsesPLSRead = false;
+    return !can_use_hw_blend_equation(fHWBlendEquation, kUsesPLSRead,
+                                      CoverageType::kLCD == coverageType, caps);
 }
 
 void CustomXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
diff --git a/src/gpu/effects/GrDisableColorXP.h b/src/gpu/effects/GrDisableColorXP.h
index 9f56ae5..f027e3c 100644
--- a/src/gpu/effects/GrDisableColorXP.h
+++ b/src/gpu/effects/GrDisableColorXP.h
@@ -32,15 +32,13 @@
 private:
     constexpr GrDisableColorXPFactory() {}
 
+    bool willReadDstColor(const GrCaps&, ColorType, CoverageType) const override { return false; }
+
     GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
                                            const GrPipelineAnalysis&,
                                            bool hasMixedSamples,
                                            const DstTexture* dstTexture) const override;
 
-    bool onWillReadDstColor(const GrCaps&, const GrPipelineAnalysis&) const override {
-        return false;
-    }
-
     GR_DECLARE_XP_FACTORY_TEST;
 
     typedef GrXPFactory INHERITED;
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
index 8b98d0b..01fc6ce 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.cpp
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
@@ -156,7 +156,7 @@
                                                      const SkIPoint& kernelOffset,
                                                      GrTextureDomain::Mode tileMode,
                                                      bool convolveAlpha)
-  : INHERITED(texture, nullptr, GrCoordTransform::MakeDivByTextureWHMatrix(texture)),
+  : INHERITED(texture, nullptr, SkMatrix::I()),
     fKernelSize(kernelSize),
     fGain(SkScalarToFloat(gain)),
     fBias(SkScalarToFloat(bias) / 255.0f),
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
index 7f40906..59710eb 100644
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
@@ -320,21 +320,17 @@
     /* screen */     COEFF_FORMULA(   kOne_GrBlendCoeff,    kISC_GrBlendCoeff),
 };
 
-static BlendFormula get_blend_formula(const GrProcOptInfo& colorPOI,
-                                      const GrProcOptInfo& coveragePOI,
+static BlendFormula get_blend_formula(bool isOpaque,
+                                      bool hasCoverage,
                                       bool hasMixedSamples,
                                       SkBlendMode xfermode) {
     SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
-    SkASSERT(!coveragePOI.isLCDCoverage());
-
-    bool conflatesCoverage = !coveragePOI.isSolidWhite() || hasMixedSamples;
-    return gBlendTable[colorPOI.isOpaque()][conflatesCoverage][(int)xfermode];
+    bool conflatesCoverage = hasCoverage || hasMixedSamples;
+    return gBlendTable[isOpaque][conflatesCoverage][(int)xfermode];
 }
 
-static BlendFormula get_lcd_blend_formula(const GrProcOptInfo& coveragePOI,
-                                          SkBlendMode xfermode) {
+static BlendFormula get_lcd_blend_formula(SkBlendMode xfermode) {
     SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
-    SkASSERT(coveragePOI.isLCDCoverage());
 
     return gLCDBlendTable[(int)xfermode];
 }
@@ -759,9 +755,10 @@
             SkASSERT(!dstTexture || !dstTexture->texture());
             return PDLCDXferProcessor::Create(fBlendMode, analysis.fColorPOI);
         }
-        blendFormula = get_lcd_blend_formula(analysis.fCoveragePOI, fBlendMode);
+        blendFormula = get_lcd_blend_formula(fBlendMode);
     } else {
-        blendFormula = get_blend_formula(analysis.fColorPOI, analysis.fCoveragePOI, hasMixedSamples,
+        blendFormula = get_blend_formula(analysis.fColorPOI.isOpaque(),
+                                         !analysis.fCoveragePOI.isSolidWhite(), hasMixedSamples,
                                          fBlendMode);
     }
 
@@ -804,8 +801,8 @@
     }
 }
 
-bool GrPorterDuffXPFactory::onWillReadDstColor(const GrCaps& caps,
-                                               const GrPipelineAnalysis& analysis) const {
+bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, ColorType colorType,
+                                             CoverageType coverageType) const {
     if (caps.shaderCaps()->dualSourceBlendingSupport()) {
         return false;
     }
@@ -813,20 +810,20 @@
     // When we have four channel coverage we always need to read the dst in order to correctly
     // blend. The one exception is when we are using srcover mode and we know the input color into
     // the XP.
-    if (analysis.fCoveragePOI.isLCDCoverage()) {
-        if (SkBlendMode::kSrcOver == fBlendMode &&
-            kRGBA_GrColorComponentFlags == analysis.fColorPOI.validFlags() &&
+    if (CoverageType::kLCD == coverageType) {
+        if (SkBlendMode::kSrcOver == fBlendMode && ColorTypeIsConstant(colorType) &&
             !caps.shaderCaps()->dstReadInShaderSupport()) {
             return false;
         }
-        return get_lcd_blend_formula(analysis.fCoveragePOI, fBlendMode).hasSecondaryOutput();
+        return get_lcd_blend_formula(fBlendMode).hasSecondaryOutput();
     }
 
     // We fallback on the shader XP when the blend formula would use dual source blending but we
     // don't have support for it.
     static const bool kHasMixedSamples = false;
     SkASSERT(!caps.usesMixedSamples()); // We never use mixed samples without dual source blending.
-    auto formula = get_blend_formula(analysis.fColorPOI, analysis.fCoveragePOI, kHasMixedSamples,
+    auto formula = get_blend_formula(ColorTypeIsOpaque(colorType),
+                                     CoverageType::kSingleChannel == coverageType, kHasMixedSamples,
                                      fBlendMode);
     return formula.hasSecondaryOutput();
 }
@@ -893,7 +890,7 @@
     }
 
     BlendFormula blendFormula;
-    blendFormula = get_lcd_blend_formula(analysis.fCoveragePOI, SkBlendMode::kSrcOver);
+    blendFormula = get_lcd_blend_formula(SkBlendMode::kSrcOver);
     if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) {
         return new ShaderPDXferProcessor(dstTexture, hasMixedSamples, SkBlendMode::kSrcOver);
     }
@@ -917,15 +914,17 @@
             !caps.shaderCaps()->dstReadInShaderSupport()) {
             return false;
         }
-        auto formula = get_lcd_blend_formula(analysis.fCoveragePOI, SkBlendMode::kSrcOver);
+        auto formula = get_lcd_blend_formula(SkBlendMode::kSrcOver);
         return formula.hasSecondaryOutput();
     }
 
     // We fallback on the shader XP when the blend formula would use dual source blending but we
     // don't have support for it.
     static const bool kHasMixedSamples = false;
+    bool isOpaque = analysis.fColorPOI.isOpaque();
+    bool hasCoverage = !analysis.fCoveragePOI.isSolidWhite();
     SkASSERT(!caps.usesMixedSamples()); // We never use mixed samples without dual source blending.
-    auto formula = get_blend_formula(analysis.fColorPOI, analysis.fCoveragePOI, kHasMixedSamples,
-                                     SkBlendMode::kSrcOver);
+    auto formula =
+            get_blend_formula(isOpaque, hasCoverage, kHasMixedSamples, SkBlendMode::kSrcOver);
     return formula.hasSecondaryOutput();
 }
diff --git a/src/gpu/effects/GrYUVEffect.cpp b/src/gpu/effects/GrYUVEffect.cpp
index 6d1fac2..41bab18 100644
--- a/src/gpu/effects/GrYUVEffect.cpp
+++ b/src/gpu/effects/GrYUVEffect.cpp
@@ -66,18 +66,17 @@
                                            GrTexture* vTexture, const SkISize sizes[3],
                                            SkYUVColorSpace colorSpace, bool nv12) {
         SkScalar w[3], h[3];
-        w[0] = SkIntToScalar(sizes[0].fWidth)  / SkIntToScalar(yTexture->width());
-        h[0] = SkIntToScalar(sizes[0].fHeight) / SkIntToScalar(yTexture->height());
-        w[1] = SkIntToScalar(sizes[1].fWidth)  / SkIntToScalar(uTexture->width());
-        h[1] = SkIntToScalar(sizes[1].fHeight) / SkIntToScalar(uTexture->height());
-        w[2] = SkIntToScalar(sizes[2].fWidth)  / SkIntToScalar(vTexture->width());
-        h[2] = SkIntToScalar(sizes[2].fHeight) / SkIntToScalar(vTexture->height());
-        SkMatrix yuvMatrix[3];
-        yuvMatrix[0] = GrCoordTransform::MakeDivByTextureWHMatrix(yTexture);
-        yuvMatrix[1] = yuvMatrix[0];
-        yuvMatrix[1].preScale(w[1] / w[0], h[1] / h[0]);
-        yuvMatrix[2] = yuvMatrix[0];
-        yuvMatrix[2].preScale(w[2] / w[0], h[2] / h[0]);
+        w[0] = SkIntToScalar(sizes[0].fWidth);
+        h[0] = SkIntToScalar(sizes[0].fHeight);
+        w[1] = SkIntToScalar(sizes[1].fWidth);
+        h[1] = SkIntToScalar(sizes[1].fHeight);
+        w[2] = SkIntToScalar(sizes[2].fWidth);
+        h[2] = SkIntToScalar(sizes[2].fHeight);
+        const SkMatrix yuvMatrix[3] = {
+            SkMatrix::I(),
+            SkMatrix::MakeScale(w[1] / w[0], h[1] / h[0]),
+            SkMatrix::MakeScale(w[2] / w[0], h[2] / h[0])
+        };
         GrSamplerParams::FilterMode uvFilterMode =
             ((sizes[1].fWidth  != sizes[0].fWidth) ||
              (sizes[1].fHeight != sizes[0].fHeight) ||
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 0e5d856..b9b87a1 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -4706,7 +4706,8 @@
 }
 
 bool GrGLGpu::onMakeCopyForTextureParams(GrTexture* texture, const GrSamplerParams& textureParams,
-                                         GrTextureProducer::CopyParams* copyParams) const {
+                                         GrTextureProducer::CopyParams* copyParams,
+                                         SkScalar scaleAdjust[2]) const {
     if (textureParams.isTiled() ||
         GrSamplerParams::kMipMap_FilterMode == textureParams.filterMode()) {
         GrGLTexture* glTexture = static_cast<GrGLTexture*>(texture);
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index c9c3fc4..fdc2ebb 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -187,7 +187,8 @@
                            const SkTArray<GrMipLevel>& texels);
 
     bool onMakeCopyForTextureParams(GrTexture*, const GrSamplerParams&,
-                                    GrTextureProducer::CopyParams*) const override;
+                                    GrTextureProducer::CopyParams*,
+                                    SkScalar scaleAdjust[2]) const override;
 
     // Checks whether glReadPixels can be called to get pixel values in readConfig from the
     // render target.
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
index 24f21ff..f39fff2 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
@@ -16,6 +16,11 @@
                                                       const GrCoordTransform& coordTransform) {
     SkMatrix combined;
     combined.setConcat(coordTransform.getMatrix(), localMatrix);
+    if (coordTransform.normalize()) {
+        SkASSERT(coordTransform.texture());
+        combined.postIDiv(coordTransform.texture()->width(), coordTransform.texture()->height());
+    }
+
     if (coordTransform.reverseY()) {
         // combined.postScale(1,-1);
         // combined.postTranslate(0,1);
diff --git a/src/gpu/text/GrAtlasGlyphCache.cpp b/src/gpu/text/GrAtlasGlyphCache.cpp
index 5b71a07..315b3c0 100644
--- a/src/gpu/text/GrAtlasGlyphCache.cpp
+++ b/src/gpu/text/GrAtlasGlyphCache.cpp
@@ -119,8 +119,11 @@
   * @param filename      Full path to desired file
   */
 static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, const char* filename) {
+
+    SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(),
+                                       kRGBA_8888_SkColorType, kPremul_SkAlphaType);
     SkBitmap bm;
-    if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(sProxy->width(), sProxy->height()))) {
+    if (!bm.tryAllocPixels(ii)) {
         return false;
     }
 
@@ -131,14 +134,7 @@
         return false;
     }
 
-    // TODO: remove this instantiation when readPixels is on SurfaceContext
-    GrTexture* tex = sContext->asDeferredTexture()->instantiate(context->textureProvider());
-    if (!tex) {
-        return false;
-    }
-
-    bool result = tex->readPixels(0, 0, sProxy->width(), sProxy->height(), kSkia8888_GrPixelConfig,
-                                  bm.getPixels(), bm.rowBytes());
+    bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), 0, 0);
     if (!result) {
         SkDebugf("------ failed to read pixels for %s\n", filename);
         return false;
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index f614ae3..383d3ca 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -19,6 +19,7 @@
     fMustDoCopiesFromOrigin = false;
     fSupportsCopiesAsDraws = false;
     fMustSubmitCommandsBeforeCopyOp = false;
+    fMustSleepOnTearDown  = false;
 
     /**************************************************************************
     * GrDrawTargetCaps fields
@@ -79,6 +80,16 @@
         fMustSubmitCommandsBeforeCopyOp = true;
     }
 
+#if defined(SK_BUILD_FOR_WIN)
+    if (kNvidia_VkVendor == properties.vendorID) {
+        fMustSleepOnTearDown = true;
+    }
+#elif defined(SK_BUILD_FOR_ANDROID)
+    if (kImagination_VkVendor == properties.vendorID) {
+        fMustSleepOnTearDown = true;
+    }
+#endif
+
     this->applyOptionsOverrides(contextOptions);
     fShaderCaps->applyOptionsOverrides(contextOptions);
 }
@@ -163,6 +174,10 @@
         }
     }
 
+    if (kImagination_VkVendor == properties.vendorID) {
+        shaderCaps->fAtan2ImplementedAsAtanYOverX = true;
+    }
+
     // Vulkan is based off ES 3.0 so the following should all be supported
     shaderCaps->fUsesPrecisionModifiers = true;
     shaderCaps->fFlatInterpolationSupport = true;
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index b390a9b..6d030f6 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -76,6 +76,10 @@
         return fMustSubmitCommandsBeforeCopyOp;
     }
 
+    bool mustSleepOnTearDown() const {
+        return fMustSleepOnTearDown;
+    }
+
     /**
      * Returns both a supported and most prefered stencil format to use in draws.
      */
@@ -87,6 +91,7 @@
     enum VkVendor {
         kQualcomm_VkVendor = 20803,
         kNvidia_VkVendor = 4318,
+        kImagination_VkVendor = 4112,
     };
 
     void init(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface,
@@ -136,6 +141,11 @@
     // as draws.
     bool fMustSubmitCommandsBeforeCopyOp;
 
+    // Sometimes calls to QueueWaitIdle return before actually signalling the fences
+    // on the command buffers even though they have completed. This causes an assert to fire when
+    // destroying the command buffers. Therefore we add a sleep to make sure the fence signals.
+    bool fMustSleepOnTearDown;
+
     typedef GrCaps INHERITED;
 };
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 853e68c..b94a379 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -168,12 +168,13 @@
     // destroying the command buffers. Currently this ony seems to happen on windows, so we add a
     // sleep to make sure the fence signals.
 #ifdef SK_DEBUG
+    if (this->vkCaps().mustSleepOnTearDown()) {
 #if defined(SK_BUILD_FOR_WIN)
-    Sleep(10); // In milliseconds
+        Sleep(10); // In milliseconds
 #else
-    // Uncomment if above bug happens on non windows build.
-    // sleep(1);        // In seconds
+        sleep(1);  // In seconds
 #endif
+    }
 #endif
 
 #ifdef SK_DEBUG
diff --git a/src/image/SkImageShader.cpp b/src/image/SkImageShader.cpp
index 5cb1c24..b1fcb0c 100644
--- a/src/image/SkImageShader.cpp
+++ b/src/image/SkImageShader.cpp
@@ -103,10 +103,6 @@
 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
                                     const SkMatrix* localMatrix,
                                     SkTBlitterAllocator* allocator) {
-    if (image && as_IB(image)->onAlphaType() == kUnpremul_SkAlphaType) {
-        return nullptr;
-    }
-
     SkShader* shader;
     if (!image || bitmap_is_too_big(image->width(), image->height())) {
         if (nullptr == allocator) {
@@ -148,8 +144,6 @@
 #include "effects/GrSimpleTextureEffect.h"
 
 sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& args) const {
-    SkMatrix matrix;
-    matrix.setIDiv(fImage->width(), fImage->height());
 
     SkMatrix lmInverse;
     if (!this->getLocalMatrix().invert(&lmInverse)) {
@@ -162,7 +156,6 @@
         }
         lmInverse.postConcat(inv);
     }
-    matrix.preConcat(lmInverse);
 
     SkShader::TileMode tm[] = { fTileModeX, fTileModeY };
 
@@ -176,20 +169,23 @@
                                     &doBicubic);
     GrSamplerParams params(tm, textureFilterMode);
     sk_sp<SkColorSpace> texColorSpace;
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
     sk_sp<GrTexture> texture(as_IB(fImage)->asTextureRef(args.fContext, params, args.fDstColorSpace,
-                                                         &texColorSpace));
+                                                         &texColorSpace, scaleAdjust));
     if (!texture) {
         return nullptr;
     }
 
+    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
+
     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
                                                                        args.fDstColorSpace);
     sk_sp<GrFragmentProcessor> inner;
     if (doBicubic) {
-        inner = GrBicubicEffect::Make(texture.get(), std::move(colorSpaceXform), matrix, tm);
+        inner = GrBicubicEffect::Make(texture.get(), std::move(colorSpaceXform), lmInverse, tm);
     } else {
         inner = GrSimpleTextureEffect::Make(texture.get(), std::move(colorSpaceXform),
-                                            matrix, params);
+                                            lmInverse, params);
     }
 
     if (GrPixelConfigIsAlphaOnly(texture->config())) {
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index e839a05..e2a4e40 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -56,7 +56,7 @@
 
     // Caller must call unref when they are done.
     virtual GrTexture* asTextureRef(GrContext*, const GrSamplerParams&, SkColorSpace*,
-                                    sk_sp<SkColorSpace>*) const = 0;
+                                    sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const = 0;
 
     virtual sk_sp<SkImage> onMakeSubset(const SkIRect&) const = 0;
 
diff --git a/src/image/SkImage_Generator.cpp b/src/image/SkImage_Generator.cpp
index 45f04e4..da9d61a 100644
--- a/src/image/SkImage_Generator.cpp
+++ b/src/image/SkImage_Generator.cpp
@@ -32,7 +32,7 @@
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
     bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
     GrTexture* asTextureRef(GrContext*, const GrSamplerParams&, SkColorSpace*,
-                            sk_sp<SkColorSpace>*) const override;
+                            sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const override;
     bool onIsLazyGenerated() const override { return true; }
 
 private:
@@ -79,8 +79,9 @@
 
 GrTexture* SkImage_Generator::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
                                            SkColorSpace* dstColorSpace,
-                                           sk_sp<SkColorSpace>* texColorSpace) const {
-    return fCache.lockAsTexture(ctx, params, dstColorSpace, texColorSpace, this);
+                                           sk_sp<SkColorSpace>* texColorSpace,
+                                           SkScalar scaleAdjust[2]) const {
+    return fCache.lockAsTexture(ctx, params, dstColorSpace, texColorSpace, this, scaleAdjust);
 }
 
 sk_sp<SkImage> SkImage_Generator::onMakeSubset(const SkIRect& subset) const {
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 02f53e7..66ee514 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -97,13 +97,14 @@
 
 GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
                                      SkColorSpace* dstColorSpace,
-                                     sk_sp<SkColorSpace>* texColorSpace) const {
+                                     sk_sp<SkColorSpace>* texColorSpace,
+                                     SkScalar scaleAdjust[2]) const {
     if (texColorSpace) {
         *texColorSpace = this->fColorSpace;
     }
     GrTextureAdjuster adjuster(this->peekTexture(), this->alphaType(), this->bounds(),
                                this->uniqueID(), this->fColorSpace.get());
-    return adjuster.refTextureSafeForParams(params, nullptr);
+    return adjuster.refTextureSafeForParams(params, nullptr, scaleAdjust);
 }
 
 static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
@@ -386,8 +387,6 @@
     SkAlphaType                   fAlphaType;
     void*                         fColorSpace;
     size_t                        fColorSpaceSize;
-    int                           fColorTableCnt;
-    uint32_t*                     fColorTableData;
     int                           fMipMapLevelCount;
     // The fMipMapLevelData array may contain more than 1 element.
     // It contains fMipMapLevelCount elements.
@@ -474,15 +473,9 @@
     SkAutoPixmapStorage pixmap;
     SkImageInfo info;
     size_t pixelSize = 0;
-    size_t ctSize = 0;
-    int ctCount = 0;
-    if (!isScaled && this->peekPixels(&pixmap)) {
+    if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable()) {
         info = pixmap.info();
         pixelSize = SkAlign8(pixmap.getSafeSize());
-        if (pixmap.ctable()) {
-            ctCount = pixmap.ctable()->count();
-            ctSize = SkAlign8(pixmap.ctable()->count() * 4);
-        }
     } else {
         // Here we're just using presence of data to know whether there is a codec behind the image.
         // In the future we will access the cacherator and get the exact data that we want to (e.g.
@@ -497,10 +490,13 @@
                 dstColorSpace, proxy.fCaps.get());
             info = cacher->buildCacheInfo(cacheFormat).makeWH(scaledSize.width(),
                                                               scaledSize.height());
-
         } else {
             info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
         }
+        if (kIndex_8_SkColorType == info.colorType()) {
+            // Force Index8 to be N32 instead. Index8 is unsupported in Ganesh.
+            info = info.makeColorType(kN32_SkColorType);
+        }
         pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
         if (fillMode) {
             pixmap.alloc(info);
@@ -547,8 +543,6 @@
     // level in its size
     size_t pixelOffset = size;
     size += pixelSize;
-    size_t ctOffset = size;
-    size += ctSize;
     size_t colorSpaceOffset = 0;
     size_t colorSpaceSize = 0;
     if (info.colorSpace()) {
@@ -562,16 +556,9 @@
     char* bufferAsCharPtr = reinterpret_cast<char*>(buffer);
     char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset;
     void* pixels = pixelsAsCharPtr;
-    void* ct = nullptr;
-    if (ctSize) {
-        ct = bufferAsCharPtr + ctOffset;
-    }
 
     memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))),
                                    pixmap.addr(), pixmap.getSafeSize());
-    if (ctSize) {
-        memcpy(ct, pixmap.ctable()->readColors(), ctSize);
-    }
 
     // If the context has sRGB support, and we're intending to render to a surface with an attached
     // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB)
@@ -597,8 +584,6 @@
     FILL_MEMBER(dtiBufferFiller, fColorType, &colorType);
     SkAlphaType alphaType = info.alphaType();
     FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType);
-    FILL_MEMBER(dtiBufferFiller, fColorTableCnt, &ctCount);
-    FILL_MEMBER(dtiBufferFiller, fColorTableData, &ct);
     FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount);
     memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData),
            &pixels, sizeof(pixels));
@@ -670,11 +655,6 @@
     if (!context || context->uniqueID() != dti->fContextUniqueID) {
         return nullptr;
     }
-    sk_sp<SkColorTable> colorTable;
-    if (dti->fColorTableCnt) {
-        SkASSERT(dti->fColorTableData);
-        colorTable.reset(new SkColorTable(dti->fColorTableData, dti->fColorTableCnt));
-    }
     int mipLevelCount = dti->fMipMapLevelCount;
     SkASSERT(mipLevelCount >= 1);
     sk_sp<SkColorSpace> colorSpace;
@@ -685,8 +665,7 @@
                                          dti->fColorType, dti->fAlphaType, colorSpace);
     if (mipLevelCount == 1) {
         SkPixmap pixmap;
-        pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData,
-                     dti->fMipMapLevelData[0].fRowBytes, colorTable.get());
+        pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, dti->fMipMapLevelData[0].fRowBytes);
         return SkImage::MakeTextureFromPixmap(context, pixmap, budgeted);
     } else {
         std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 2d6f3bd..31e268c 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -40,7 +40,7 @@
 
     bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
     GrTexture* asTextureRef(GrContext* ctx, const GrSamplerParams& params, SkColorSpace*,
-                            sk_sp<SkColorSpace>*) const override;
+                            sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
     sk_sp<GrSurfaceProxy> refProxy() const;
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index cce1ed2..b287789 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -89,7 +89,7 @@
 
     bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
     GrTexture* asTextureRef(GrContext*, const GrSamplerParams&, SkColorSpace*,
-                            sk_sp<SkColorSpace>*) const override;
+                            sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
     SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
@@ -173,7 +173,8 @@
 
 GrTexture* SkImage_Raster::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
                                         SkColorSpace* dstColorSpace,
-                                        sk_sp<SkColorSpace>* texColorSpace) const {
+                                        sk_sp<SkColorSpace>* texColorSpace,
+                                        SkScalar scaleAdjust[2]) const {
 #if SK_SUPPORT_GPU
     if (!ctx) {
         return nullptr;
@@ -188,10 +189,10 @@
     if (tex) {
         GrTextureAdjuster adjuster(fPinnedTexture.get(), fBitmap.alphaType(), fBitmap.bounds(),
                                    fPinnedUniqueID, fBitmap.colorSpace());
-        return adjuster.refTextureSafeForParams(params, nullptr);
+        return adjuster.refTextureSafeForParams(params, nullptr, scaleAdjust);
     }
 
-    return GrRefCachedBitmapTexture(ctx, fBitmap, params);
+    return GrRefCachedBitmapTexture(ctx, fBitmap, params, scaleAdjust);
 #endif
 
     return nullptr;
@@ -218,7 +219,7 @@
         SkASSERT(fPinnedCount == 0);
         SkASSERT(fPinnedUniqueID == 0);
         fPinnedTexture.reset(
-            GrRefCachedBitmapTexture(ctx, fBitmap, GrSamplerParams::ClampNoFilter()));
+            GrRefCachedBitmapTexture(ctx, fBitmap, GrSamplerParams::ClampNoFilter(), nullptr));
         if (!fPinnedTexture) {
             return false;
         }
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
index 00b6ac7..aa66d73 100644
--- a/src/opts/SkRasterPipeline_opts.h
+++ b/src/opts/SkRasterPipeline_opts.h
@@ -872,7 +872,7 @@
 }
 
 SI SkNf clamp(const SkNf& v, float limit) {
-    SkNf result = SkNf::Max(0, SkNf::Min(v, limit - 0.5f));
+    SkNf result = SkNf::Max(0, SkNf::Min(v, nextafterf(limit, 0)));
     return assert_in_tile(result, limit);
 }
 SI SkNf repeat(const SkNf& v, float limit) {
@@ -1045,6 +1045,16 @@
     from_f16(&px, &r, &g, &b, &a);
 }
 
+STAGE_CTX(linear_gradient_2stops, const SkPM4f*) {
+    auto t = r;
+    SkPM4f c0 = ctx[0],
+           dc = ctx[1];
+
+    r = SkNf_fma(t, dc.r(), c0.r());
+    g = SkNf_fma(t, dc.g(), c0.g());
+    b = SkNf_fma(t, dc.b(), c0.b());
+    a = SkNf_fma(t, dc.a(), c0.a());
+}
 
 SI Fn enum_to_Fn(SkRasterPipeline::StockStage st) {
     switch (st) {
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index 17bc9e2..b47e7df 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -561,7 +561,7 @@
                 wn.segment()->debugValidate();
                 coinIndex = -1;
             }
-            SkASSERT(coinIndex < 0);  // expect coincidence to be paired
+            SkOPOBJASSERT(coincidence, coinIndex < 0);  // expect coincidence to be paired
         } while (wn.advance());
     } while (wt.advance());
     return true;
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index c07e8cc..f41ef23 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -62,23 +62,15 @@
     SkOpAngle* lh = test;
     SkOpAngle* rh = lh->fNext;
     SkASSERT(lh != rh);
-    fPart.fCurve = fOriginalCurvePart;
-    lh->fPart.fCurve = lh->fOriginalCurvePart;
-    lh->fPart.fCurve.offset(lh->segment()->verb(), fPart.fCurve[0] - lh->fPart.fCurve[0]);
-    rh->fPart.fCurve = rh->fOriginalCurvePart;
-    rh->fPart.fCurve.offset(rh->segment()->verb(), fPart.fCurve[0] - rh->fPart.fCurve[0]);
-
 #if DEBUG_ANGLE
     SkString bugOut;
-    bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
-                  " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
-                  " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
-            lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
-            lh->fStart->t(), lh->fEnd->t(),
-            segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
-            rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
-            rh->fStart->t(), rh->fEnd->t());
+    this->debugAfter(lh, rh, &bugOut);
     SkString bugPart[3] = { lh->debugPart(), this->debugPart(), rh->debugPart() };
+#if 0  // convenient place to set a breakpoint to trace through a specific angle compare
+    if (lh->debugID() == 4 && this->debugID() == 16 && rh->debugID() == 5) {
+        SkDebugf("");
+    }
+#endif
 #endif
     if (lh->fComputeSector && !lh->computeSector()) {
         return COMPARE_RESULT(1, true);
@@ -90,23 +82,106 @@
         return COMPARE_RESULT(3, true);
     }
 #if DEBUG_ANGLE  // reset bugOut with computed sectors
-    bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
-                  " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
-                  " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
-            lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
-            lh->fStart->t(), lh->fEnd->t(),
-            segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
-            rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
-            rh->fStart->t(), rh->fEnd->t());
+    this->debugAfter(lh, rh, &bugOut);
 #endif
-    bool ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
-    bool lrOverlap = lh->fSectorMask & rh->fSectorMask;
+    /* If the curve pairs share a point, the computed sector is valid. Otherwise, the sectors must
+       be sufficiently different that translating them won't change the sort order. For instance,
+       curves with different origins may mis-sort if the computed sectors are 1 and 5.
+
+       Curves with different origins have more information though -- there are more ways for their
+       convex hulls not to overlap. Try to resolve different origins directly before translating
+       one curve to share the opposite's origin.
+    */
+    bool lrOverlap, ltrOverlap;
+    SkDVector lhOffset = fOriginalCurvePart[0] - lh->fOriginalCurvePart[0];
+    bool lhHasOffset = lhOffset.fX || lhOffset.fY;
+    SkDVector rhOffset = fOriginalCurvePart[0] - rh->fOriginalCurvePart[0];
+    bool rhHasOffset = rhOffset.fX || rhOffset.fY;
+    int lhStart, lhEnd, thStart, thEnd, rhStart, rhEnd;
+    bool lhX0, thX0, rhX0;
+    if (lhHasOffset | rhHasOffset) {
+        lhX0 = lh->sectorRange(&lhStart, &lhEnd, lhHasOffset);
+        thX0 = this->sectorRange(&thStart, &thEnd, false);
+        rhX0 = rh->sectorRange(&rhStart, &rhEnd, rhHasOffset);
+        lrOverlap = lhX0 + rhX0 + (lhStart <= rhEnd) + (rhStart <= lhEnd) >= 2;
+        ltrOverlap = thX0 + lhX0 + (lhStart <= thEnd) + (thStart <= lhEnd) >= 2
+                || rhX0 + thX0 + (thStart <= rhEnd) + (rhStart <= thEnd) >= 2;
+    } else {
+        lrOverlap = lh->fSectorMask & rh->fSectorMask;
+        ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
+    }
+    if (!lrOverlap & !ltrOverlap) {  // no lh/this/rh sector overlap
+        return COMPARE_RESULT(4, (lh->fSectorEnd > rh->fSectorStart)
+                ^ (fSectorStart > lh->fSectorEnd) ^ (fSectorStart > rh->fSectorStart));
+    }
     int lrOrder;  // set to -1 if either order works
-    if (!lrOverlap) {  // no lh/rh sector overlap
-        if (!ltrOverlap) {  // no lh/this/rh sector overlap
-            return COMPARE_RESULT(4,  (lh->fSectorEnd > rh->fSectorStart)
-                    ^ (fSectorStart > lh->fSectorEnd) ^ (fSectorStart > rh->fSectorStart));
+    fPart.fCurve = fOriginalCurvePart;
+    lh->fPart.fCurve = lh->fOriginalCurvePart;
+    rh->fPart.fCurve = rh->fOriginalCurvePart;
+    if (lhHasOffset | rhHasOffset) {
+        bool lhSweepsCCW = lh->sweepsCCW();
+        bool thSweepsCCW = this->sweepsCCW();
+        bool rhSweepsCCW = rh->sweepsCCW();
+        Turn thStartFromLhEnd = this->ccwOf(lh, lhSweepsCCW, !thSweepsCCW);
+        Turn thEndFromRhStart = this->ccwOf(rh, !rhSweepsCCW, thSweepsCCW);
+        if (!lrOverlap && Turn::kCcw == thStartFromLhEnd && Turn::kCw == thEndFromRhStart) {
+            if (!this->sweepContains(lh) && !this->sweepContains(rh)) {
+                return COMPARE_RESULT(5, true); 
+            }
         }
+        Turn lhStartFromRhStart = lh->ccwOf(rh, !rhSweepsCCW, !lhSweepsCCW);
+        Turn lhEndFromRhStart = lh->fPart.isCurve()
+                ? lh->ccwOf(rh, !rhSweepsCCW, lhSweepsCCW) : lhStartFromRhStart;
+        bool lhOrRhIsCurve = lh->fPart.isCurve() || rh->fPart.isCurve();
+        Turn lhStartFromRhEnd;
+        if (lhOrRhIsCurve) {
+            if (rh->fPart.isCurve()) {
+                lhStartFromRhEnd = lh->ccwOf(rh, rhSweepsCCW, !lhSweepsCCW);
+            } else {
+                lhStartFromRhEnd = lhStartFromRhStart;
+            }
+            // cancel overlap only if sweep doesn't contain other curve's sweep pts
+            if (!lh->sweepContains(rh)) {
+                // clear overlap if both turn in the same direction
+                lrOverlap &= (int) lhStartFromRhEnd * (int) lhEndFromRhStart < 0;
+            }
+        } else {
+            lrOverlap = false;
+        }
+        Turn thStartFromRhEnd  SkDEBUGCODE(= Turn::kDebugUninitialized);
+        Turn thEndFromLhStart  SkDEBUGCODE(= Turn::kDebugUninitialized);
+        if (lhOrRhIsCurve || fPart.isCurve()) {
+            thStartFromRhEnd = rh->fPart.isCurve() || fPart.isCurve()
+                    ? this->ccwOf(rh, rhSweepsCCW, !thSweepsCCW) : thEndFromRhStart;
+            thEndFromLhStart = lh->fPart.isCurve() || fPart.isCurve()
+                    ? this->ccwOf(lh, !lhSweepsCCW, thSweepsCCW) : thStartFromLhEnd;
+            // clear overlap if both pairs turn in the same direction
+            if (!this->sweepContains(lh) && !this->sweepContains(rh)) {
+                ltrOverlap &= (int) thStartFromRhEnd * (int) thEndFromRhStart <= 0
+                        || (int) thStartFromLhEnd * (int) thEndFromLhStart <= 0;
+            }
+        } else {
+            ltrOverlap = false;
+        }
+        if (!lrOverlap & !ltrOverlap) {
+            Turn lhFromRh = (Turn) ((int) lhEndFromRhStart | (int) lhStartFromRhStart);
+            Turn thFromLh = (Turn) ((int) thEndFromLhStart | (int) thStartFromLhEnd);
+            Turn thFromRh = (Turn) ((int) thEndFromRhStart | (int) thStartFromRhEnd);
+            bool result = Turn::kCw == lhFromRh ?
+                     Turn::kCcw == thFromLh && Turn::kCw == thFromRh :
+                     Turn::kCcw == thFromLh || Turn::kCw == thFromRh;
+            return COMPARE_RESULT(6, result);
+        }
+        if (lhHasOffset) {
+            lh->fPart.fCurve.offset(lh->segment()->verb(), lhOffset);
+        }
+        if (rhHasOffset) {
+            rh->fPart.fCurve.offset(rh->segment()->verb(), rhOffset);
+        }
+        lrOverlap = lh->fSectorMask & rh->fSectorMask;
+        ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
+    }
+    if (!lrOverlap) {  // no lh/rh sector overlap, no offsets
         int lrGap = (rh->fSectorStart - lh->fSectorStart + 32) & 0x1f;
         /* A tiny change can move the start +/- 4. The order can only be determined if
            lr gap is not 12 to 20 or -12 to -20.
@@ -122,11 +197,13 @@
     } else {
         lrOrder = (int) lh->orderable(rh);
         if (!ltrOverlap) {
-            return COMPARE_RESULT(5, !lrOrder);
+            return COMPARE_RESULT(7, !lrOrder);
         }
     }
     int ltOrder;
-    SkASSERT((lh->fSectorMask & fSectorMask) || (rh->fSectorMask & fSectorMask));
+    SkDEBUGCODE(bool ltOverlap = lhHasOffset || lh->fSectorMask & fSectorMask);
+    SkDEBUGCODE(bool trOverlap = rhHasOffset || rh->fSectorMask & fSectorMask);
+    SkASSERT(ltOverlap || trOverlap);
     if (lh->fSectorMask & fSectorMask) {
         ltOrder = (int) lh->orderable(this);
     } else {
@@ -143,7 +220,7 @@
     this->alignmentSameSide(lh, &ltOrder);
     this->alignmentSameSide(rh, &trOrder);
     if (lrOrder >= 0 && ltOrder >= 0 && trOrder >= 0) {
-        return COMPARE_RESULT(7, lrOrder ? (ltOrder & trOrder) : (ltOrder | trOrder));
+        return COMPARE_RESULT(8, lrOrder ? (ltOrder & trOrder) : (ltOrder | trOrder));
     }
     SkASSERT(lrOrder >= 0 || ltOrder >= 0 || trOrder >= 0);
 // There's not enough information to sort. Get the pairs of angles in opposite planes.
@@ -155,25 +232,25 @@
         SkDEBUGCODE(bool lrOpposite = lh->oppositePlanes(rh));
         bool ltOpposite = lh->oppositePlanes(this);
         SkOPASSERT(lrOpposite != ltOpposite);
-        return COMPARE_RESULT(8, ltOpposite);
+        return COMPARE_RESULT(9, ltOpposite);
     } else if (ltOrder == 1 && trOrder == 0) {
         SkASSERT(lrOrder < 0);
         bool trOpposite = oppositePlanes(rh);
-        return COMPARE_RESULT(9, trOpposite);
+        return COMPARE_RESULT(10, trOpposite);
     } else if (lrOrder == 1 && trOrder == 1) {
         SkASSERT(ltOrder < 0);
 //        SkDEBUGCODE(bool trOpposite = oppositePlanes(rh));
         bool lrOpposite = lh->oppositePlanes(rh);
 //        SkASSERT(lrOpposite != trOpposite);
-        return COMPARE_RESULT(10, lrOpposite);
+        return COMPARE_RESULT(11, lrOpposite);
     }
     if (lrOrder < 0) {
         if (ltOrder < 0) {
-            return COMPARE_RESULT(11, trOrder);
+            return COMPARE_RESULT(12, trOrder);
         }
-        return COMPARE_RESULT(12, ltOrder);
+        return COMPARE_RESULT(13, ltOrder);
     }
-    return COMPARE_RESULT(13, !lrOrder);
+    return COMPARE_RESULT(14, !lrOrder);
 }
 
 // given a line, see if the opposite curve's convex hull is all on one side
@@ -248,10 +325,101 @@
     }
 }
 
-bool SkOpAngle::checkCrossesZero() const {
-    int start = SkTMin(fSectorStart, fSectorEnd);
-    int end = SkTMax(fSectorStart, fSectorEnd);
-    bool crossesZero = end - start > 16;
+static bool same_side(double cross1, double cross2) {
+    return cross1 * cross2 > 0 && !roughly_zero_when_compared_to(cross1, cross2)
+            && !roughly_zero_when_compared_to(cross2, cross1);
+}
+
+static double same_side_candidate(double cross1, double cross2) {
+    return same_side(cross1, cross2) ? SkTAbs(cross1) > SkTAbs(cross2) ? cross1 : cross2 : 0;
+}
+
+SkOpAngle::Turn SkOpAngle::ccwOf(const SkOpAngle* rh, bool rhCW, bool thisCCW, bool recursed)
+        const {
+    const SkDPoint& startPt = fPart.fCurve[0];
+    const SkDPoint& rhStartPt = rh->fPart.fCurve[0];
+    SkDVector startOffset = rhStartPt - startPt;
+    bool commonPt = 0 == startOffset.fX && 0 == startOffset.fY;
+    const SkDVector& sweep = fPart.fSweep[(int) thisCCW];
+    const SkDVector& rhSweep = rh->fPart.fSweep[(int) rhCW];
+    const SkDVector* testV;
+    SkDVector rhEndV;
+    if (commonPt) {
+        testV = &rhSweep;
+    } else {
+        rhEndV = rhSweep + startOffset;
+        testV = &rhEndV;
+    }
+    double endCheck = sweep.crossCheck(*testV);
+#if 0 && DEBUG_ANGLE  // too verbose to show on all the time
+    SkDebugf("%s {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}} id=1\n", __func__, rhStartPt.fX, rhStartPt.fY,
+            rhStartPt.fX + rhSweep.fX, rhStartPt.fY + rhSweep.fY);
+    SkDebugf("%s {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}} id=2\n", __func__, startPt.fX, startPt.fY,
+            startPt.fX + sweep.fX, startPt.fY + sweep.fY);
+#endif
+    if (0 == endCheck) {
+        if (sweep.dot(*testV) < 0) {
+            return Turn::kNone;  // neither clockwise nor counterclockwise
+        }
+        // if the pair of angles share an edge, use its other sweep to check the turn value
+        if ((fPart.isCurve() || rh->fPart.isCurve())) {
+            if (recursed) {
+                return Turn::kNone;
+            }
+            return this->ccwOf(rh, !rhCW, !thisCCW, true);
+        }
+    }
+    if (commonPt) {
+        return toTurn(endCheck > 0);
+    }
+    double startCheck = sweep.crossCheck(startOffset);
+    if ((startCheck == 0 || startOffset.lengthSquared() < FLT_EPSILON_SQUARED * 2049) &&
+            (endCheck == 0 || testV->lengthSquared() < FLT_EPSILON_SQUARED * 2049)) {
+        double cross = sweep.cross(rhSweep);
+        if (cross != 0)
+            return toTurn(cross > 0);
+    }
+    if (same_side(startCheck, endCheck)) {
+        return toTurn(startCheck > 0);
+    }
+    SkDVector reverseSweep = -sweep;
+    SkDVector rhReverseStartV = startOffset - sweep;
+    double reverseStartCheck = reverseSweep.crossCheck(rhReverseStartV);
+    SkDVector rhReverseEndV = rhReverseStartV + rhSweep;
+    double reverseEndCheck = reverseSweep.crossCheck(rhReverseEndV);
+    if (same_side(reverseStartCheck, reverseEndCheck)) {
+        return toTurn(reverseStartCheck < 0);
+    }
+    double rhEndCheck = rhSweep.crossCheck(rhReverseEndV);
+    double rhStartCheck = rhSweep.crossCheck(*testV);
+    double endSide = same_side_candidate(rhEndCheck, rhStartCheck);
+    SkDVector rhReverseSweep = -rhSweep;
+    double rhReverseEndCheck = rhReverseSweep.crossCheck(rhReverseStartV);
+    double rhReverseStartCheck = rhReverseSweep.crossCheck(startOffset);
+    double startSide = -same_side_candidate(rhReverseEndCheck, rhReverseStartCheck);
+    if (startSide || endSide) {
+        return toTurn((SkTAbs(startSide) > SkTAbs(endSide) ? startSide : endSide) > 0);
+    }
+    // if a pair crosses only slightly, no two ends will be on the same side
+    // look for the smaller ratio to guess which segment only crosses the other slightly
+    double crosses[] = { endCheck, reverseStartCheck, reverseEndCheck,
+        rhStartCheck, rhEndCheck, rhReverseStartCheck, rhReverseEndCheck };
+    int smallest = -1;
+    double smallCross = SkTAbs(startCheck);
+    for (int index = 0; index < (int) SK_ARRAY_COUNT(crosses); ++index) {
+        double testCross = SkTAbs(crosses[index]);
+        if (smallCross > testCross) {
+            smallCross = testCross;
+            smallest = index;
+        }
+    }
+    return toTurn((smallest <= 2 ? endCheck : -rhReverseEndCheck) > 0);
+}
+
+bool SkOpAngle::checkCrossesZero(int* start, int* end) const {
+    *start = SkTMin(fSectorStart, fSectorEnd);
+    *end = SkTMax(fSectorStart, fSectorEnd);
+    bool crossesZero = *end - *start > 16;
     return crossesZero;
 }
 
@@ -626,7 +794,6 @@
     return this->segment()->globalState();
 }
 
-
 // OPTIMIZE: if this loops to only one other angle, after first compare fails, insert on other side
 // OPTIMIZE: return where insertion succeeded. Then, start next insertion on opposite side
 bool SkOpAngle::insert(SkOpAngle* angle) {
@@ -849,6 +1016,19 @@
     } while (true);
 }
 
+// returns true if rounded sector range crosses zero
+bool SkOpAngle::sectorRange(int* start, int* end, bool roundOut) const {
+    if (checkCrossesZero(start, end)) {
+        SkTSwap(*start, *end);
+    }
+    // round away since the offset curves may swap order
+    if (roundOut) {
+        *start = (((*start + 1) & ~0x03) - 1) & 0x1f;
+        *end |= 0x03;
+    }
+    return *end < *start;
+}
+
 SkOpSegment* SkOpAngle::segment() const {
     return fStart->segment();
 }
@@ -985,23 +1165,25 @@
         fSectorMask = 1 << fSectorStart;
         return;
     }
-    bool crossesZero = this->checkCrossesZero();
-    int start = SkTMin(fSectorStart, fSectorEnd);
-    bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
-    // bump the start and end of the sector span if they are on exact compass points
-    if ((fSectorStart & 3) == 3) {
-        fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
+    int start, end;
+    bool crossesZero = this->checkCrossesZero(&start, &end);
+    bool bumpStart = (fSectorStart & 3) == 3;
+    bool bumpEnd = (fSectorEnd & 3) == 3;
+    if (bumpStart | bumpEnd) {
+        bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
+        // bump the start and end of the sector span if they are on exact compass points
+        if (bumpStart) {
+            fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
+        }
+        if (bumpEnd) {
+            fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
+        }
+        crossesZero = this->checkCrossesZero(&start, &end);
     }
-    if ((fSectorEnd & 3) == 3) {
-        fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
-    }
-    crossesZero = this->checkCrossesZero();
-    start = SkTMin(fSectorStart, fSectorEnd);
-    int end = SkTMax(fSectorStart, fSectorEnd);
     if (!crossesZero) {
         fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
     } else {
-        fSectorMask = (unsigned) -1 >> (31 - start) | ((unsigned) -1 << end);
+        fSectorMask = (unsigned) -1 >> (31 - start) | (unsigned) -1 << end;
     }
 }
 
@@ -1009,6 +1191,70 @@
     return fStart->starter(fEnd);
 }
 
+bool SkOpAngle::sweepsCCW() const {
+    if (!fPart.isCurve()) {
+        return false;   // lines have no sweep
+    }
+#if 0 && DEBUG_ANGLE  // too verbose to show all the time
+    SkDebugf("%s {{{0,0}, {%g,%g}}} id=1\n", __func__, fPart.fSweep[0].fX, fPart.fSweep[0].fY);
+    SkDebugf("%s {{{0,0}, {%g,%g}}} id=2\n", __func__, fPart.fSweep[1].fX, fPart.fSweep[1].fY);
+#endif
+    return fPart.fSweep[0].crossCheck(fPart.fSweep[1]) < 0;
+}
+
+static bool sweep_edge_contains(const SkDVector& edge, const SkDVector& v, double* direction) {
+    double cross = edge.crossCheck(v);
+    if (cross) {
+        if (cross * *direction < 0) {
+            return true;
+        }
+        *direction = cross;
+    }
+    return false;
+}
+
+static bool sweep_contains(const SkDVector sweep[2], const SkDVector& v, double* direction) {
+    if (sweep_edge_contains(sweep[0], v, direction)) {
+        return true;
+    }
+    return sweep_edge_contains(sweep[1], v, direction);
+}
+
+bool SkOpAngle::sweepContains(const SkOpAngle* rh) const {
+    if (!fPart.isCurve()) {
+        return false;
+    }
+    if (!rh->fPart.isCurve()) {
+        return false;
+    }
+    const SkDPoint& startPt = fPart.fCurve[0];
+    const SkDVector* sweep = fPart.fSweep;
+    const SkDPoint& rhStartPt = rh->fPart.fCurve[0];
+    double direction = 0;
+    if (startPt != rhStartPt) {
+        SkDVector vTest = rhStartPt - startPt;
+        if (sweep_contains(sweep, vTest, &direction)) {
+            return true;
+        }
+        for (int index = 0; index < (int) SK_ARRAY_COUNT(rh->fPart.fSweep); ++index) {
+            SkDPoint sweepEnd = rhStartPt;
+            sweepEnd += rh->fPart.fSweep[index];
+            SkDVector vTest = sweepEnd - startPt;
+            if (sweep_contains(sweep, vTest, &direction)) {
+                return true;
+            }
+        }
+    } else {
+        for (int index = 0; index < (int) SK_ARRAY_COUNT(rh->fPart.fSweep); ++index) {
+            const SkDVector& vTest = rh->fPart.fSweep[index];
+            if (sweep_contains(sweep, vTest, &direction)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) {
     if (s0xt0 == 0) {
         return false;
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index d8615c3..d11a1c1 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -41,8 +41,12 @@
 #endif
 
 #if DEBUG_ANGLE
+    void debugAfter(const SkOpAngle* lh, const SkOpAngle* rh, SkString* bugOut) const;
     bool debugCheckCoincidence() const { return fCheckCoincidence; }
     void debugCheckNearCoincidence() const;
+#endif
+    SkDPoint* debugFirstPt() { return &fOriginalCurvePart.fLine.fPts[0]; }
+#if DEBUG_ANGLE
     SkString debugPart() const;
 #endif
     const SkOpPtT* debugPtT(int id) const;
@@ -96,10 +100,20 @@
     }
 
 private:
+    enum class Turn {
+#ifdef SK_DEBUG
+        kDebugUninitialized = -2,
+#endif
+        kCcw = -1,
+        kNone,
+        kCw
+    };
+
     bool after(SkOpAngle* test);
     void alignmentSameSide(const SkOpAngle* test, int* order) const;
     int allOnOneSide(const SkOpAngle* test);
-    bool checkCrossesZero() const;
+    Turn ccwOf(const SkOpAngle* rh, bool rhCW, bool thisCCW, bool recursed = false) const;
+    bool checkCrossesZero(int* start, int* end) const;
     bool checkParallel(SkOpAngle* );
     bool computeSector();
     int convexHullOverlaps(const SkOpAngle* );
@@ -112,9 +126,13 @@
     bool midToSide(const SkOpAngle* rh, bool* inside) const;
     bool oppositePlanes(const SkOpAngle* rh) const;
     bool orderable(SkOpAngle* rh);  // false == this < rh ; true == this > rh
+    bool sectorRange(int* start, int* end, bool roundOut) const;
     void setSector();
     void setSpans();
+    bool sweepContains(const SkOpAngle* rh) const;
+    bool sweepsCCW() const;
     bool tangentsDiverge(const SkOpAngle* rh, double s0xt0);
+    Turn toTurn(bool ccw) const { return ccw ? Turn::kCcw : Turn::kCw; }
 
     SkDCurve fOriginalCurvePart;  // the curve from start to end
     SkDCurveSweep fPart;  // the curve from start to end offset as needed
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index f9ec157..b4854a8 100644
--- a/src/pathops/SkOpCoincidence.cpp
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -296,7 +296,7 @@
         SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
         const SkPoint& pt = base->pt();
         SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
-        SkIntersections i;
+        SkIntersections i  SkDEBUGCODE((this->globalState()));
         (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
         for (int index = 0; index < i.used(); ++index) {
             double t = i[0][index];
@@ -791,7 +791,7 @@
             return true;
         }
         const SkOpSegment* outerOpp = oos->segment();
-        SkASSERT(!outerOpp->done());
+        SkOPASSERT(!outerOpp->done());
         SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
         SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
         SkCoincidentSpans* inner = outer;
@@ -805,7 +805,7 @@
             const SkOpPtT* ios = inner->oppPtTStart();
             FAIL_IF(ios->deleted());
             const SkOpSegment* innerOpp = ios->segment();
-            SkASSERT(!innerOpp->done());
+            SkOPASSERT(!innerOpp->done());
             SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
             SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
             if (outerCoin == innerCoin) {
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index e3be828..92076b1 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -87,7 +87,7 @@
     }
 
     void setCoinPtTStart(const SkOpPtT* ptT) {
-        SkASSERT(ptT == ptT->span()->ptT());
+        SkOPASSERT(ptT == ptT->span()->ptT());
         SkOPASSERT(!fCoinPtTEnd || ptT->fT != fCoinPtTEnd->fT);
         SkASSERT(!fCoinPtTEnd || fCoinPtTEnd->segment() == ptT->segment());
         fCoinPtTStart = ptT;
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 45a1138..6ed37dc 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -18,7 +18,6 @@
 bool SkPathOpsDebug::gVerifyOp;  // set to true to compare result against regions
 #endif
 
-bool SkPathOpsDebug::gRunFail;  // set to true to check for success on tests known to fail
 bool SkPathOpsDebug::gVeryVerbose;  // set to true to run extensive checking tests
 
 #undef FAIL_IF
@@ -667,10 +666,6 @@
 }
 #endif
 
-bool SkOpGlobalState::DebugRunFail() {
-    return SkPathOpsDebug::gRunFail;
-}
-
 // this is const so it can be called by const methods that overwise don't alter state
 #if DEBUG_VALIDATE || DEBUG_COIN
 void SkOpGlobalState::debugSetPhase(const char* funcName  DEBUG_COIN_DECLARE_PARAMS()) const {
@@ -1249,6 +1244,17 @@
    This checks the distance between start points; the distance between
 */
 #if DEBUG_ANGLE
+void SkOpAngle::debugAfter(const SkOpAngle* lh, const SkOpAngle* rh, SkString* bugOut) const {
+        bugOut->printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
+                  " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
+                  " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
+            lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
+            lh->fStart->t(), lh->fEnd->t(),
+            segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
+            rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
+            rh->fStart->t(), rh->fEnd->t());
+}
+
 void SkOpAngle::debugCheckNearCoincidence() const {
     const SkOpAngle* test = this;
     do {
@@ -1376,8 +1382,8 @@
         }
         next = next->fNext;
     } while (next && next != first);
-    SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
-    SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
+    SkASSERT(wind == 0);
+    SkASSERT(opp == 0);
 #endif
 }
 
@@ -1404,8 +1410,8 @@
 #ifdef SK_DEBUG
 void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
         const SkOpGlobalState* debugState) const {
-    SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
-    SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
+    SkASSERT(coinPtTEnd()->span() == over);
+    SkASSERT(oppPtTEnd()->span() == outer);
 }
 #endif
 
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 97fa534..0f3fada 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -374,7 +374,6 @@
     static void DumpGlitchType(GlitchType );
 #endif
 
-    static bool gRunFail;
     static bool gVeryVerbose;
 
 #if DEBUG_DUMP_VERIFY
diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h
index f314f69..deadbc2 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -29,13 +29,29 @@
         fY += v.fY;
     }
 
+    SkDVector operator+(const SkDVector& v) const {
+        SkDVector result = *this;
+        result += v;
+        return result;
+    }
+
     // only called by nearestT, which is currently only used by testing
     void operator-=(const SkDVector& v) {
         fX -= v.fX;
         fY -= v.fY;
     }
 
-    // only used by testing
+    SkDVector operator-(const SkDVector& v) const {
+        SkDVector result = *this;
+        result -= v;
+        return result;
+    }
+
+    SkDVector operator-() const {
+        SkDVector result = { -fX, -fY };
+        return result;
+    }
+
     void operator/=(const double s) {
         fX /= s;
         fY /= s;
@@ -89,6 +105,13 @@
         fX *= inverseLength;
         fY *= inverseLength;
     }
+
+    void setLengthSquared(double lenSquared) {
+        double inverseLength = lenSquared / this->lengthSquared();
+        fX *= inverseLength;
+        fY *= inverseLength;
+    }
+
 };
 
 struct SkDPoint {
@@ -127,15 +150,14 @@
         fY -= v.fY;
     }
 
-    // only used by testing
-    SkDPoint operator+(const SkDVector& v) {
+    SkDPoint operator+(const SkDVector& v) const {
         SkDPoint result = *this;
         result += v;
         return result;
     }
 
     // only used by testing
-    SkDPoint operator-(const SkDVector& v) {
+    SkDPoint operator-(const SkDVector& v) const {
         SkDPoint result = *this;
         result -= v;
         return result;
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index f525ea4..4e9fbe9 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -77,8 +77,6 @@
     const class SkOpPtT* debugPtT(int id) const;
 #endif
 
-    static bool DebugRunFail();
-
 #ifdef SK_DEBUG
     const class SkOpSegment* debugSegment(int id) const;
     bool debugSkipAssert() const { return fDebugSkipAssert; }
diff --git a/src/splicer/SkSplicer.cpp b/src/splicer/SkSplicer.cpp
index b289871..4ed45e0 100644
--- a/src/splicer/SkSplicer.cpp
+++ b/src/splicer/SkSplicer.cpp
@@ -44,7 +44,7 @@
         12.46f, 0.411192f, 0.689206f, -0.0988f, 0.0043f,   //   to_srgb
     };
     static const SkSplicer_constants_lowp kConstants_lowp = {
-        0x0001, 0x8000,
+        0x8000, 0x8081,
     };
 
     // We do this a lot, so it's nice to infer the correct size.  Works fine with arrays.
@@ -256,6 +256,7 @@
             CASE(move_src_dst);
             CASE(move_dst_src);
             CASE(premul);
+            CASE(scale_u8);
             CASE(load_8888);
             CASE(store_8888);
         #undef CASE
diff --git a/src/splicer/SkSplicer_generated_lowp.h b/src/splicer/SkSplicer_generated_lowp.h
index 8e2796e..58e03c7 100644
--- a/src/splicer/SkSplicer_generated_lowp.h
+++ b/src/splicer/SkSplicer_generated_lowp.h
@@ -29,8 +29,7 @@
     0x6e670c63,                                 //  uqadd         v3.8h, v3.8h, v7.8h
 };
 static const unsigned int kSplice_srcover_lowp[] = {
-    0x91000868,                                 //  add           x8, x3, #0x2
-    0x4d40c510,                                 //  ld1r          {v16.8h}, [x8]
+    0x4d40c470,                                 //  ld1r          {v16.8h}, [x3]
     0x6e632e10,                                 //  uqsub         v16.8h, v16.8h, v3.8h
     0x6e70b491,                                 //  sqrdmulh      v17.8h, v4.8h, v16.8h
     0x4e241e12,                                 //  and           v18.16b, v16.16b, v4.16b
@@ -54,8 +53,7 @@
     0x6e630e83,                                 //  uqadd         v3.8h, v20.8h, v3.8h
 };
 static const unsigned int kSplice_dstover_lowp[] = {
-    0x91000868,                                 //  add           x8, x3, #0x2
-    0x4d40c510,                                 //  ld1r          {v16.8h}, [x8]
+    0x4d40c470,                                 //  ld1r          {v16.8h}, [x3]
     0x6e672e10,                                 //  uqsub         v16.8h, v16.8h, v7.8h
     0x6e70b411,                                 //  sqrdmulh      v17.8h, v0.8h, v16.8h
     0x4e201e12,                                 //  and           v18.16b, v16.16b, v0.16b
@@ -79,16 +77,14 @@
     0x6e670e87,                                 //  uqadd         v7.8h, v20.8h, v7.8h
 };
 static const unsigned int kSplice_clamp_1_lowp[] = {
-    0x91000868,                                 //  add           x8, x3, #0x2
-    0x4d40c510,                                 //  ld1r          {v16.8h}, [x8]
+    0x4d40c470,                                 //  ld1r          {v16.8h}, [x3]
     0x6e706c00,                                 //  umin          v0.8h, v0.8h, v16.8h
     0x6e706c21,                                 //  umin          v1.8h, v1.8h, v16.8h
     0x6e706c42,                                 //  umin          v2.8h, v2.8h, v16.8h
     0x6e706c63,                                 //  umin          v3.8h, v3.8h, v16.8h
 };
 static const unsigned int kSplice_clamp_a_lowp[] = {
-    0x91000868,                                 //  add           x8, x3, #0x2
-    0x4d40c510,                                 //  ld1r          {v16.8h}, [x8]
+    0x4d40c470,                                 //  ld1r          {v16.8h}, [x3]
     0x6e706c63,                                 //  umin          v3.8h, v3.8h, v16.8h
     0x6e636c00,                                 //  umin          v0.8h, v0.8h, v3.8h
     0x6e636c21,                                 //  umin          v1.8h, v1.8h, v3.8h
@@ -134,6 +130,28 @@
     0x4e60ba02,                                 //  abs           v2.8h, v16.8h
     0x6f111622,                                 //  usra          v2.8h, v17.8h, #15
 };
+static const unsigned int kSplice_scale_u8_lowp[] = {
+    0xf9400048,                                 //  ldr           x8, [x2]
+    0xfc606910,                                 //  ldr           d16, [x8,x0]
+    0x2f0fa610,                                 //  ushll         v16.8h, v16.8b, #7
+    0x6f183610,                                 //  ursra         v16.8h, v16.8h, #8
+    0x6e70b411,                                 //  sqrdmulh      v17.8h, v0.8h, v16.8h
+    0x6e70b433,                                 //  sqrdmulh      v19.8h, v1.8h, v16.8h
+    0x6e70b455,                                 //  sqrdmulh      v21.8h, v2.8h, v16.8h
+    0x6e70b477,                                 //  sqrdmulh      v23.8h, v3.8h, v16.8h
+    0x4e201e12,                                 //  and           v18.16b, v16.16b, v0.16b
+    0x4e211e14,                                 //  and           v20.16b, v16.16b, v1.16b
+    0x4e221e16,                                 //  and           v22.16b, v16.16b, v2.16b
+    0x4e231e10,                                 //  and           v16.16b, v16.16b, v3.16b
+    0x4e60ba20,                                 //  abs           v0.8h, v17.8h
+    0x4e60ba61,                                 //  abs           v1.8h, v19.8h
+    0x4e60baa2,                                 //  abs           v2.8h, v21.8h
+    0x4e60bae3,                                 //  abs           v3.8h, v23.8h
+    0x6f111640,                                 //  usra          v0.8h, v18.8h, #15
+    0x6f111681,                                 //  usra          v1.8h, v20.8h, #15
+    0x6f1116c2,                                 //  usra          v2.8h, v22.8h, #15
+    0x6f111603,                                 //  usra          v3.8h, v16.8h, #15
+};
 static const unsigned int kSplice_load_8888_lowp[] = {
     0xf9400048,                                 //  ldr           x8, [x2]
     0x8b000908,                                 //  add           x8, x8, x0, lsl #2
@@ -175,8 +193,7 @@
     0xf3133017,                                 //  vqadd.u16     d3, d3, d7
 };
 static const unsigned int kSplice_srcover_lowp[] = {
-    0xe283c002,                                 //  add           ip, r3, #2
-    0xf4ec0c5f,                                 //  vld1.16       {d16[]}, [ip :16]
+    0xf4e30c5f,                                 //  vld1.16       {d16[]}, [r3 :16]
     0xf3500293,                                 //  vqsub.u16     d16, d16, d3
     0xf3541b20,                                 //  vqrdmulh.s16  d17, d4, d16
     0xf3552b20,                                 //  vqrdmulh.s16  d18, d5, d16
@@ -200,8 +217,7 @@
     0xf3143093,                                 //  vqadd.u16     d3, d20, d3
 };
 static const unsigned int kSplice_dstover_lowp[] = {
-    0xe283c002,                                 //  add           ip, r3, #2
-    0xf4ec0c5f,                                 //  vld1.16       {d16[]}, [ip :16]
+    0xf4e30c5f,                                 //  vld1.16       {d16[]}, [r3 :16]
     0xf3500297,                                 //  vqsub.u16     d16, d16, d7
     0xf3501b20,                                 //  vqrdmulh.s16  d17, d0, d16
     0xf3512b20,                                 //  vqrdmulh.s16  d18, d1, d16
@@ -225,16 +241,14 @@
     0xf3147097,                                 //  vqadd.u16     d7, d20, d7
 };
 static const unsigned int kSplice_clamp_1_lowp[] = {
-    0xe283c002,                                 //  add           ip, r3, #2
-    0xf4ec0c5f,                                 //  vld1.16       {d16[]}, [ip :16]
+    0xf4e30c5f,                                 //  vld1.16       {d16[]}, [r3 :16]
     0xf3100630,                                 //  vmin.u16      d0, d0, d16
     0xf3111630,                                 //  vmin.u16      d1, d1, d16
     0xf3122630,                                 //  vmin.u16      d2, d2, d16
     0xf3133630,                                 //  vmin.u16      d3, d3, d16
 };
 static const unsigned int kSplice_clamp_a_lowp[] = {
-    0xe283c002,                                 //  add           ip, r3, #2
-    0xf4ec0c5f,                                 //  vld1.16       {d16[]}, [ip :16]
+    0xf4e30c5f,                                 //  vld1.16       {d16[]}, [r3 :16]
     0xf3133630,                                 //  vmin.u16      d3, d3, d16
     0xf3100613,                                 //  vmin.u16      d0, d0, d3
     0xf3111613,                                 //  vmin.u16      d1, d1, d3
@@ -280,6 +294,29 @@
     0xf3911134,                                 //  vsra.u16      d1, d20, #15
     0xf3912130,                                 //  vsra.u16      d2, d16, #15
 };
+static const unsigned int kSplice_scale_u8_lowp[] = {
+    0xe592c000,                                 //  ldr           ip, [r2]
+    0xe08cc000,                                 //  add           ip, ip, r0
+    0xf4ec0c8f,                                 //  vld1.32       {d16[]}, [ip]
+    0xf3cf0a30,                                 //  vshll.u8      q8, d16, #7
+    0xf3d80370,                                 //  vrsra.u16     q8, q8, #8
+    0xf3502b20,                                 //  vqrdmulh.s16  d18, d0, d16
+    0xf3513b20,                                 //  vqrdmulh.s16  d19, d1, d16
+    0xf3524b20,                                 //  vqrdmulh.s16  d20, d2, d16
+    0xf3535b20,                                 //  vqrdmulh.s16  d21, d3, d16
+    0xf2406190,                                 //  vand          d22, d16, d0
+    0xf3b50322,                                 //  vabs.s16      d0, d18
+    0xf2407191,                                 //  vand          d23, d16, d1
+    0xf2402192,                                 //  vand          d18, d16, d2
+    0xf2400193,                                 //  vand          d16, d16, d3
+    0xf3b51323,                                 //  vabs.s16      d1, d19
+    0xf3b52324,                                 //  vabs.s16      d2, d20
+    0xf3b53325,                                 //  vabs.s16      d3, d21
+    0xf3910136,                                 //  vsra.u16      d0, d22, #15
+    0xf3911137,                                 //  vsra.u16      d1, d23, #15
+    0xf3912132,                                 //  vsra.u16      d2, d18, #15
+    0xf3913130,                                 //  vsra.u16      d3, d16, #15
+};
 static const unsigned int kSplice_load_8888_lowp[] = {
     0xe592c000,                                 //  ldr           ip, [r2]
     0xe08cc100,                                 //  add           ip, ip, r0, lsl #2
@@ -331,7 +368,7 @@
     0xc5,0xe5,0xdd,0xdf,                        //  vpaddusw      %ymm7,%ymm3,%ymm3
 };
 static const unsigned char kSplice_srcover_lowp[] = {
-    0xc4,0x62,0x7d,0x79,0x41,0x02,              //  vpbroadcastw  0x2(%rcx),%ymm8
+    0xc4,0x62,0x7d,0x79,0x01,                   //  vpbroadcastw  (%rcx),%ymm8
     0xc5,0x3d,0xd9,0xc3,                        //  vpsubusw      %ymm3,%ymm8,%ymm8
     0xc4,0x42,0x5d,0x0b,0xc8,                   //  vpmulhrsw     %ymm8,%ymm4,%ymm9
     0xc4,0x42,0x7d,0x1d,0xc9,                   //  vpabsw        %ymm9,%ymm9
@@ -347,7 +384,7 @@
     0xc5,0xbd,0xdd,0xdb,                        //  vpaddusw      %ymm3,%ymm8,%ymm3
 };
 static const unsigned char kSplice_dstover_lowp[] = {
-    0xc4,0x62,0x7d,0x79,0x41,0x02,              //  vpbroadcastw  0x2(%rcx),%ymm8
+    0xc4,0x62,0x7d,0x79,0x01,                   //  vpbroadcastw  (%rcx),%ymm8
     0xc5,0x3d,0xd9,0xc7,                        //  vpsubusw      %ymm7,%ymm8,%ymm8
     0xc4,0x42,0x7d,0x0b,0xc8,                   //  vpmulhrsw     %ymm8,%ymm0,%ymm9
     0xc4,0x42,0x7d,0x1d,0xc9,                   //  vpabsw        %ymm9,%ymm9
@@ -363,14 +400,14 @@
     0xc5,0xbd,0xdd,0xff,                        //  vpaddusw      %ymm7,%ymm8,%ymm7
 };
 static const unsigned char kSplice_clamp_1_lowp[] = {
-    0xc4,0x62,0x7d,0x79,0x41,0x02,              //  vpbroadcastw  0x2(%rcx),%ymm8
+    0xc4,0x62,0x7d,0x79,0x01,                   //  vpbroadcastw  (%rcx),%ymm8
     0xc4,0xc2,0x7d,0x3a,0xc0,                   //  vpminuw       %ymm8,%ymm0,%ymm0
     0xc4,0xc2,0x75,0x3a,0xc8,                   //  vpminuw       %ymm8,%ymm1,%ymm1
     0xc4,0xc2,0x6d,0x3a,0xd0,                   //  vpminuw       %ymm8,%ymm2,%ymm2
     0xc4,0xc2,0x65,0x3a,0xd8,                   //  vpminuw       %ymm8,%ymm3,%ymm3
 };
 static const unsigned char kSplice_clamp_a_lowp[] = {
-    0xc4,0x62,0x7d,0x79,0x41,0x02,              //  vpbroadcastw  0x2(%rcx),%ymm8
+    0xc4,0x62,0x7d,0x79,0x01,                   //  vpbroadcastw  (%rcx),%ymm8
     0xc4,0xc2,0x65,0x3a,0xd8,                   //  vpminuw       %ymm8,%ymm3,%ymm3
     0xc4,0xe2,0x7d,0x3a,0xc3,                   //  vpminuw       %ymm3,%ymm0,%ymm0
     0xc4,0xe2,0x75,0x3a,0xcb,                   //  vpminuw       %ymm3,%ymm1,%ymm1
@@ -410,6 +447,21 @@
     0xc4,0xe2,0x6d,0x0b,0xd3,                   //  vpmulhrsw     %ymm3,%ymm2,%ymm2
     0xc4,0xe2,0x7d,0x1d,0xd2,                   //  vpabsw        %ymm2,%ymm2
 };
+static const unsigned char kSplice_scale_u8_lowp[] = {
+    0x48,0x8b,0x02,                             //  mov           (%rdx),%rax
+    0xc4,0x62,0x7d,0x30,0x04,0x38,              //  vpmovzxbw     (%rax,%rdi,1),%ymm8
+    0xc4,0xc1,0x3d,0x71,0xf0,0x08,              //  vpsllw        $0x8,%ymm8,%ymm8
+    0xc4,0x62,0x7d,0x79,0x49,0x02,              //  vpbroadcastw  0x2(%rcx),%ymm9
+    0xc4,0x41,0x3d,0xe4,0xc1,                   //  vpmulhuw      %ymm9,%ymm8,%ymm8
+    0xc4,0xc2,0x7d,0x0b,0xc0,                   //  vpmulhrsw     %ymm8,%ymm0,%ymm0
+    0xc4,0xe2,0x7d,0x1d,0xc0,                   //  vpabsw        %ymm0,%ymm0
+    0xc4,0xc2,0x75,0x0b,0xc8,                   //  vpmulhrsw     %ymm8,%ymm1,%ymm1
+    0xc4,0xe2,0x7d,0x1d,0xc9,                   //  vpabsw        %ymm1,%ymm1
+    0xc4,0xc2,0x6d,0x0b,0xd0,                   //  vpmulhrsw     %ymm8,%ymm2,%ymm2
+    0xc4,0xe2,0x7d,0x1d,0xd2,                   //  vpabsw        %ymm2,%ymm2
+    0xc4,0xc2,0x65,0x0b,0xd8,                   //  vpmulhrsw     %ymm8,%ymm3,%ymm3
+    0xc4,0xe2,0x7d,0x1d,0xdb,                   //  vpabsw        %ymm3,%ymm3
+};
 static const unsigned char kSplice_load_8888_lowp[] = {
     0x48,0x8b,0x02,                             //  mov           (%rdx),%rax
     0xc5,0xfa,0x6f,0x04,0xb8,                   //  vmovdqu       (%rax,%rdi,4),%xmm0
@@ -430,37 +482,21 @@
     0xc5,0x39,0x68,0xc1,                        //  vpunpckhbw    %xmm1,%xmm8,%xmm8
     0xc5,0xe9,0x6c,0xc3,                        //  vpunpcklqdq   %xmm3,%xmm2,%xmm0
     0xc4,0xe2,0x7d,0x30,0xc0,                   //  vpmovzxbw     %xmm0,%ymm0
-    0xc5,0xf5,0x71,0xf0,0x07,                   //  vpsllw        $0x7,%ymm0,%ymm1
-    0xc5,0xad,0x71,0xd0,0x01,                   //  vpsrlw        $0x1,%ymm0,%ymm10
-    0xc4,0xc1,0x75,0xdd,0xca,                   //  vpaddusw      %ymm10,%ymm1,%ymm1
-    0xc4,0x62,0x7d,0x79,0x11,                   //  vpbroadcastw  (%rcx),%ymm10
-    0xc4,0xc1,0x7d,0xdd,0xc2,                   //  vpaddusw      %ymm10,%ymm0,%ymm0
-    0xc5,0xfd,0x71,0xd0,0x08,                   //  vpsrlw        $0x8,%ymm0,%ymm0
-    0xc5,0xf5,0xdd,0xc0,                        //  vpaddusw      %ymm0,%ymm1,%ymm0
+    0xc5,0xfd,0x71,0xf0,0x08,                   //  vpsllw        $0x8,%ymm0,%ymm0
+    0xc4,0x62,0x7d,0x79,0x51,0x02,              //  vpbroadcastw  0x2(%rcx),%ymm10
+    0xc4,0xc1,0x7d,0xe4,0xc2,                   //  vpmulhuw      %ymm10,%ymm0,%ymm0
     0xc5,0xe9,0x6d,0xcb,                        //  vpunpckhqdq   %xmm3,%xmm2,%xmm1
     0xc4,0xe2,0x7d,0x30,0xc9,                   //  vpmovzxbw     %xmm1,%ymm1
-    0xc5,0xed,0x71,0xf1,0x07,                   //  vpsllw        $0x7,%ymm1,%ymm2
-    0xc5,0xe5,0x71,0xd1,0x01,                   //  vpsrlw        $0x1,%ymm1,%ymm3
-    0xc5,0xed,0xdd,0xd3,                        //  vpaddusw      %ymm3,%ymm2,%ymm2
-    0xc4,0xc1,0x75,0xdd,0xca,                   //  vpaddusw      %ymm10,%ymm1,%ymm1
-    0xc5,0xf5,0x71,0xd1,0x08,                   //  vpsrlw        $0x8,%ymm1,%ymm1
-    0xc5,0xed,0xdd,0xc9,                        //  vpaddusw      %ymm1,%ymm2,%ymm1
+    0xc5,0xf5,0x71,0xf1,0x08,                   //  vpsllw        $0x8,%ymm1,%ymm1
+    0xc4,0xc1,0x75,0xe4,0xca,                   //  vpmulhuw      %ymm10,%ymm1,%ymm1
     0xc4,0xc1,0x31,0x6c,0xd0,                   //  vpunpcklqdq   %xmm8,%xmm9,%xmm2
     0xc4,0xe2,0x7d,0x30,0xd2,                   //  vpmovzxbw     %xmm2,%ymm2
-    0xc5,0xe5,0x71,0xf2,0x07,                   //  vpsllw        $0x7,%ymm2,%ymm3
-    0xc5,0xa5,0x71,0xd2,0x01,                   //  vpsrlw        $0x1,%ymm2,%ymm11
-    0xc4,0xc1,0x65,0xdd,0xdb,                   //  vpaddusw      %ymm11,%ymm3,%ymm3
-    0xc4,0xc1,0x6d,0xdd,0xd2,                   //  vpaddusw      %ymm10,%ymm2,%ymm2
-    0xc5,0xed,0x71,0xd2,0x08,                   //  vpsrlw        $0x8,%ymm2,%ymm2
-    0xc5,0xe5,0xdd,0xd2,                        //  vpaddusw      %ymm2,%ymm3,%ymm2
+    0xc5,0xed,0x71,0xf2,0x08,                   //  vpsllw        $0x8,%ymm2,%ymm2
+    0xc4,0xc1,0x6d,0xe4,0xd2,                   //  vpmulhuw      %ymm10,%ymm2,%ymm2
     0xc4,0xc1,0x31,0x6d,0xd8,                   //  vpunpckhqdq   %xmm8,%xmm9,%xmm3
     0xc4,0xe2,0x7d,0x30,0xdb,                   //  vpmovzxbw     %xmm3,%ymm3
-    0xc5,0xbd,0x71,0xf3,0x07,                   //  vpsllw        $0x7,%ymm3,%ymm8
-    0xc5,0xb5,0x71,0xd3,0x01,                   //  vpsrlw        $0x1,%ymm3,%ymm9
-    0xc4,0x41,0x3d,0xdd,0xc1,                   //  vpaddusw      %ymm9,%ymm8,%ymm8
-    0xc4,0xc1,0x65,0xdd,0xda,                   //  vpaddusw      %ymm10,%ymm3,%ymm3
-    0xc5,0xe5,0x71,0xd3,0x08,                   //  vpsrlw        $0x8,%ymm3,%ymm3
-    0xc5,0xbd,0xdd,0xdb,                        //  vpaddusw      %ymm3,%ymm8,%ymm3
+    0xc5,0xe5,0x71,0xf3,0x08,                   //  vpsllw        $0x8,%ymm3,%ymm3
+    0xc4,0xc1,0x65,0xe4,0xda,                   //  vpmulhuw      %ymm10,%ymm3,%ymm3
 };
 static const unsigned char kSplice_store_8888_lowp[] = {
     0x48,0x8b,0x02,                             //  mov           (%rdx),%rax
diff --git a/src/splicer/SkSplicer_shared.h b/src/splicer/SkSplicer_shared.h
index 7f4db14..9f69aa7 100644
--- a/src/splicer/SkSplicer_shared.h
+++ b/src/splicer/SkSplicer_shared.h
@@ -41,8 +41,8 @@
 };
 
 struct SkSplicer_constants_lowp {
-    uint16_t _0x0001;      // 0x0001 ==     1 == epsilon
     uint16_t _1;           // 0x8000 == 32768 == 1.0
+    uint16_t _0x8081;      // 0x8081 == 32897, closest value to 32768 * (256/255).
 };
 
 #endif//SkSplicer_shared_DEFINED
diff --git a/src/splicer/SkSplicer_stages.cpp b/src/splicer/SkSplicer_stages.cpp
index c61a267..4112779 100644
--- a/src/splicer/SkSplicer_stages.cpp
+++ b/src/splicer/SkSplicer_stages.cpp
@@ -12,6 +12,9 @@
     #error This file is not like the rest of Skia.  It must be compiled with clang.
 #endif
 
+// It's tricky to relocate code referencing ordinary constants, so we read them from this struct.
+using K = const SkSplicer_constants;
+
 #if defined(__aarch64__)
     #include <arm_neon.h>
 
@@ -95,7 +98,6 @@
 #endif
 
 // Stages all fit a common interface that allows SkSplicer to splice them together.
-using K = const SkSplicer_constants;
 using Stage = void(size_t x, size_t limit, void* ctx, K* k, F,F,F,F, F,F,F,F);
 
 // Stage's arguments act as the working set of registers within the final spliced function.
diff --git a/src/splicer/SkSplicer_stages_lowp.cpp b/src/splicer/SkSplicer_stages_lowp.cpp
index ef3ab40..9e5ea4e 100644
--- a/src/splicer/SkSplicer_stages_lowp.cpp
+++ b/src/splicer/SkSplicer_stages_lowp.cpp
@@ -15,9 +15,14 @@
     #error This file is not like the rest of Skia.  It must be compiled with clang.
 #endif
 
+// We use a set of constants suitable for SkFixed15 math.
+using K = const SkSplicer_constants_lowp;
+
 #if defined(__aarch64__)
     #include <arm_neon.h>
 
+    using U8 = uint8_t __attribute__((ext_vector_type(8)));
+
     // In this file, F is a vector of SkFixed15.
     // See SkFixed15.h for notes on its various operations.
     struct F {
@@ -43,12 +48,24 @@
     static F min(F a, F b) { return vminq_u16(a,b); }
     static F max(F a, F b) { return vmaxq_u16(a,b); }
 
+    static F from_u8(U8 u8, K*) {
+        // u8 * (32768/255) == u8 * 128.50196... == u8*128 + u8/2 + (u8+1)>>8
+        //
+        // Here we do (u8*128 <rounding +> u8/2), which is correct for 0 and 255,
+        // and never off by more than 1 anywhere.  It's just 2 instructions in NEON:
+        auto u16 = vshll_n_u8(u8, 7);     // u16 =   u8*128
+        u16 = vrsraq_n_u16(u16, u16, 8);  // u16 += u16/256, with rounding
+        return u16;
+    };
+
 #elif defined(__ARM_NEON__)
     #if defined(__thumb2__) || !defined(__ARM_ARCH_7A__) || !defined(__ARM_VFPV4__)
         #error On ARMv7, compile with -march=armv7-a -mfpu=neon-vfp4, without -mthumb.
     #endif
     #include <arm_neon.h>
 
+    using U8 = uint8_t __attribute__((ext_vector_type(8)));  // But, only low 4 lanes active.
+
     struct F {
         using V = uint16_t __attribute__((ext_vector_type(4)));
 
@@ -72,12 +89,20 @@
     static F min(F a, F b) { return vmin_u16(a,b); }
     static F max(F a, F b) { return vmax_u16(a,b); }
 
+    static F from_u8(U8 u8, K*) {
+        auto u16 = vshll_n_u8(u8, 7);     // Identical to aarch64...
+        u16 = vrsraq_n_u16(u16, u16, 8);  //
+        return vget_low_u16(u16);         // ...but only the low 4 lanes are active.
+    }
+
 #else
     #if !defined(__AVX2__) || !defined(__FMA__) || !defined(__F16C__)
         #error On x86, compile with -mavx2 -mfma -mf16c.
     #endif
     #include <immintrin.h>
 
+    using U8 = uint8_t __attribute__((ext_vector_type(16)));
+
     struct F {
         using V = uint16_t __attribute__((ext_vector_type(16)));
 
@@ -97,20 +122,33 @@
     };
     static F min(F a, F b) { return _mm256_min_epu16(a,b); }
     static F max(F a, F b) { return _mm256_max_epu16(a,b); }
+
+    static F from_u8(U8 u8, K* k) {
+        // Ideally we'd multiply by 32768/255 = 128.50196...
+        // We can approximate that very cheaply as 256*32897/65536 = 128.50391...
+        // 0 and 255 map to 0 and 32768 correctly, and the max error is 1 (on about 1/4 of values).
+        F u16 = _mm256_cvtepu8_epi16(u8);
+        return _mm256_mulhi_epu16(u16 << 8, F(k->_0x8081));
+    }
 #endif
 
 // No platform actually supports FMA for SkFixed15.
 // This fma() method just makes it easier to port stages to lowp.
 static F fma(F f, F m, F a) { return f*m+a; }
 
+template <typename T, typename P>
+static T unaligned_load(const P* p) {
+    T v;
+    memcpy(&v, p, sizeof(v));
+    return v;
+}
+
 #if defined(__ARM_NEON__)
     #define C extern "C" __attribute__((pcs("aapcs-vfp")))
 #else
     #define C extern "C"
 #endif
 
-// We use a set of constants suitable for SkFixed15 math.
-using K = const SkSplicer_constants_lowp;
 using Stage = void(size_t x, size_t limit, void* ctx, K* k, F,F,F,F, F,F,F,F);
 
 // The armv7 aapcs-vfp calling convention makes us pass F::V instead of F if we want them in
@@ -198,32 +236,34 @@
     b = b * a;
 }
 
+STAGE(scale_u8) {
+    auto ptr = *(const uint8_t**)ctx + x;
+
+#if defined(__ARM_NEON__)
+    // On armv7, U8 can fit 8 bytes, but we only want to load 4.
+    U8 scales = vdup_n_u32(unaligned_load<uint32_t>(ptr));
+#else
+    U8 scales = unaligned_load<U8>(ptr);
+#endif
+
+    auto c = from_u8(scales, k);
+    r = r * c;
+    g = g * c;
+    b = b * c;
+    a = a * c;
+}
+
 STAGE(load_8888) {
     auto ptr = *(const uint32_t**)ctx + x;
 
 #if defined(__aarch64__)
-    auto to_fixed15 = [](uint8x8_t u8) {
-        // u8 * (32768/255) == u8 * 128.50196... == u8*128 + u8/2 + (u8+1)>>8  ( see SkFixed15.h)
-        //
-        // Here we do (u8*128 <rounding +> u8/2), which is the same as our canonical math for 0
-        // and 255, and never off by more than 1 in between.  Thanks to NEON, it's 2 instructions!
-        auto u16 = vshll_n_u8(u8, 7);      // u16 =  u8*128
-        return vrsraq_n_u16(u16, u16, 8);  // u16 + u16/256, with rounding
-    };
-
     uint8x8x4_t rgba = vld4_u8((const uint8_t*)ptr);
-    r = to_fixed15(rgba.val[0]);
-    g = to_fixed15(rgba.val[1]);
-    b = to_fixed15(rgba.val[2]);
-    a = to_fixed15(rgba.val[3]);
+    r = from_u8(rgba.val[0], k);
+    g = from_u8(rgba.val[1], k);
+    b = from_u8(rgba.val[2], k);
+    a = from_u8(rgba.val[3], k);
 
 #elif defined(__ARM_NEON__)
-    auto to_fixed15 = [](uint8x8_t u8) {
-        // Same as aarch64, but only keeping the bottom 4 lanes.
-        auto u16 = vshll_n_u8(u8, 7);
-        return vget_low_u16(vrsraq_n_u16(u16, u16, 8));
-    };
-
     // I can't get quite the code generation I want using vld4_lane_u8(),
     // so we're going to drop into assembly to do the loads.  :/
 
@@ -233,17 +273,12 @@
         "vld4.8 {%1[2],%2[2],%3[2],%4[2]}, [%0]!\n"
         "vld4.8 {%1[3],%2[3],%3[3],%4[3]}, [%0]!\n"
         : "+r"(ptr), "=w"(R), "=w"(G), "=w"(B), "=w"(A));
-    r = to_fixed15(R);
-    g = to_fixed15(G);
-    b = to_fixed15(B);
-    a = to_fixed15(A);
+    r = from_u8(R, k);
+    g = from_u8(G, k);
+    b = from_u8(B, k);
+    a = from_u8(A, k);
 
 #else
-    auto to_fixed15 = [k](__m128i u8) {
-        F u16 = _mm256_cvtepu8_epi16(u8);
-        return (u16 << 7) + (u16 >> 1) + ((u16+k->_0x0001)>>8);
-    };
-
     // TODO: shorter, more confusing, faster with 256-bit loads and shuffles
 
     // Load 16 interplaced pixels.
@@ -268,10 +303,10 @@
          rg_89ABCDEF = _mm_unpacklo_epi8(_8ACE, _9BDF),  // r89ABCDEF g89ABCDEF
          ba_89ABCDEF = _mm_unpackhi_epi8(_8ACE, _9BDF);  // b89ABCDEF a89ABCDEF
 
-    r = to_fixed15(_mm_unpacklo_epi64(rg_01234567, rg_89ABCDEF));
-    g = to_fixed15(_mm_unpackhi_epi64(rg_01234567, rg_89ABCDEF));
-    b = to_fixed15(_mm_unpacklo_epi64(ba_01234567, ba_89ABCDEF));
-    a = to_fixed15(_mm_unpackhi_epi64(ba_01234567, ba_89ABCDEF));
+    r = from_u8(_mm_unpacklo_epi64(rg_01234567, rg_89ABCDEF), k);
+    g = from_u8(_mm_unpackhi_epi64(rg_01234567, rg_89ABCDEF), k);
+    b = from_u8(_mm_unpacklo_epi64(ba_01234567, ba_89ABCDEF), k);
+    a = from_u8(_mm_unpackhi_epi64(ba_01234567, ba_89ABCDEF), k);
 #endif
 }
 
@@ -279,7 +314,7 @@
     auto ptr = *(uint32_t**)ctx + x;
 
 #if defined(__aarch64__)
-    auto from_fixed15 = [](F v) {
+    auto to_u8 = [](F v) {
         // The canonical math for this from SkFixed15.h is (v - (v>>8)) >> 7.
         // But what's really most important is that all bytes round trip.
 
@@ -288,14 +323,14 @@
     };
 
     uint8x8x4_t rgba = {{
-        from_fixed15(r),
-        from_fixed15(g),
-        from_fixed15(b),
-        from_fixed15(a),
+        to_u8(r),
+        to_u8(g),
+        to_u8(b),
+        to_u8(a),
     }};
     vst4_u8((uint8_t*)ptr, rgba);
 #elif defined(__ARM_NEON__)
-    auto from_fixed15 = [](F v) {
+    auto to_u8 = [](F v) {
         // Same as aarch64, but first we need to pad our vectors from 8 to 16 bytes.
         F whatever;
         return vqshrn_n_u16(vcombine_u8(v, whatever), 7);
@@ -307,22 +342,22 @@
         "vst4.8 {%1[2],%2[2],%3[2],%4[2]}, [%0]!\n"
         "vst4.8 {%1[3],%2[3],%3[3],%4[3]}, [%0]!\n"
         : "+r"(ptr)
-        : "w"(from_fixed15(r)), "w"(from_fixed15(g)), "w"(from_fixed15(b)), "w"(from_fixed15(a))
+        : "w"(to_u8(r)), "w"(to_u8(g)), "w"(to_u8(b)), "w"(to_u8(a))
         : "memory");
 
 #else
-    auto from_fixed15 = [](F v) {
-        // See the note in aarch64's from_fixed15().  The same roundtrip goal applies here.
+    auto to_u8 = [](F v) {
+        // See the note in aarch64's to_u8().  The same roundtrip goal applies here.
         // Here we take a different approach: (v saturated+ v) >> 8.
         v = (v+v) >> 8;
         return _mm_packus_epi16(_mm256_extracti128_si256(v, 0),
                                 _mm256_extracti128_si256(v, 1));
     };
 
-    auto R = from_fixed15(r),
-         G = from_fixed15(g),
-         B = from_fixed15(b),
-         A = from_fixed15(a);
+    auto R = to_u8(r),
+         G = to_u8(g),
+         B = to_u8(b),
+         A = to_u8(a);
 
     auto rg_01234567 = _mm_unpacklo_epi8(R,G),  // rg0 rg1 rg2 ... rg7
          rg_89ABCDEF = _mm_unpackhi_epi8(R,G),  // rg8 rg9 rgA ... rgF
diff --git a/tests/DFPathRendererTest.cpp b/tests/DFPathRendererTest.cpp
index 7fb8c8a..e4d87a2 100644
--- a/tests/DFPathRendererTest.cpp
+++ b/tests/DFPathRendererTest.cpp
@@ -7,18 +7,19 @@
 
 #include "Test.h"
 
+#include "SkPath.h"
+
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
-#include "GrTest.h"
-#include "SkPath.h"
 #include "ops/GrAADistanceFieldPathRenderer.h"
 
 // This test case including path coords and matrix taken from crbug.com/627443.
 // Because of inaccuracies in large floating point values this causes the
 // the path renderer to attempt to add a path DF to its atlas that is larger
 // than the plot size which used to crash rather than fail gracefully.
-static void test_far_from_origin(GrRenderTargetContext* renderTargetContext, GrPathRenderer* pr,
-                                 GrResourceProvider* rp) {
+static void test_far_from_origin(GrResourceProvider* rp,
+                                 GrRenderTargetContext* renderTargetContext,
+                                 GrPathRenderer* pr) {
     SkPath path;
     path.lineTo(49.0255089839f, 0.473541f);
     // This extra line wasn't in the original bug but was added to fake out GrShape's special
@@ -58,27 +59,25 @@
 }
 
 DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(AADistanceFieldPathRenderer, reporter, ctxInfo) {
+    GrContext* ctx = ctxInfo.grContext();
     // The DF PR only works with contexts that support derivatives
-    if (!ctxInfo.grContext()->caps()->shaderCaps()->shaderDerivativeSupport()) {
+    if (!ctx->caps()->shaderCaps()->shaderDerivativeSupport()) {
         return;
     }
-    sk_sp<GrRenderTargetContext> rtc(ctxInfo.grContext()->makeRenderTargetContext(
-                                                                         SkBackingFit::kApprox,
-                                                                         800, 800,
-                                                                         kRGBA_8888_GrPixelConfig,
-                                                                         nullptr,
-                                                                         0,
-                                                                         kTopLeft_GrSurfaceOrigin));
+    sk_sp<GrRenderTargetContext> rtc(ctx->makeRenderTargetContext(SkBackingFit::kApprox,
+                                                                  800, 800,
+                                                                  kRGBA_8888_GrPixelConfig,
+                                                                  nullptr,
+                                                                  0,
+                                                                  kTopLeft_GrSurfaceOrigin));
     if (!rtc) {
         return;
     }
 
     GrAADistanceFieldPathRenderer dfpr;
-    GrTestTarget tt;
-    ctxInfo.grContext()->getTestTarget(&tt, rtc);
-    GrResourceProvider* rp = tt.resourceProvider();
 
-    test_far_from_origin(rtc.get(), &dfpr, rp);
-    ctxInfo.grContext()->flush();
+    ctx->flush();
+    test_far_from_origin(ctx->resourceProvider(), rtc.get(), &dfpr);
+    ctx->flush();
 }
 #endif
diff --git a/tests/ImageNewShaderTest.cpp b/tests/ImageNewShaderTest.cpp
index a92a4be..4091db0 100644
--- a/tests/ImageNewShaderTest.cpp
+++ b/tests/ImageNewShaderTest.cpp
@@ -16,7 +16,7 @@
 #include "GrContext.h"
 #endif
 
-void testBitmapEquality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) {
+static void test_bitmap_equality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) {
     SkAutoLockPixels lockBm1(bm1);
     SkAutoLockPixels lockBm2(bm2);
 
@@ -24,7 +24,7 @@
     REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getPixels(), bm2.getPixels(), bm1.getSize()));
 }
 
-void paintSource(SkSurface* sourceSurface) {
+static void paint_source(SkSurface* sourceSurface) {
     SkCanvas* sourceCanvas = sourceSurface->getCanvas();
     sourceCanvas->clear(0xFFDEDEDE);
 
@@ -41,8 +41,9 @@
     sourceCanvas->drawRect(rect, paintColor);
 }
 
-void runShaderTest(skiatest::Reporter* reporter, SkSurface* sourceSurface, SkSurface* destinationSurface, SkImageInfo& info) {
-    paintSource(sourceSurface);
+static void run_shader_test(skiatest::Reporter* reporter, SkSurface* sourceSurface,
+                            SkSurface* destinationSurface, SkImageInfo& info) {
+    paint_source(sourceSurface);
 
     sk_sp<SkImage> sourceImage(sourceSurface->makeImageSnapshot());
     sk_sp<SkShader> sourceShader = sourceImage->makeShader(
@@ -65,9 +66,7 @@
     SkBitmap bm;
     destinationCanvas->readPixels(rect, &bm);
 
-    testBitmapEquality(reporter, bmOrig, bm);
-
-
+    test_bitmap_equality(reporter, bmOrig, bm);
 
     // Test with a translated shader
     SkMatrix matrix;
@@ -107,47 +106,47 @@
     auto sourceSurface(SkSurface::MakeRaster(info));
     auto destinationSurface(SkSurface::MakeRaster(info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
 #if SK_SUPPORT_GPU
 
-void gpuToGpu(skiatest::Reporter* reporter, GrContext* context) {
+static void gpu_to_gpu(skiatest::Reporter* reporter, GrContext* context) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);
 
     auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
     auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
-void gpuToRaster(skiatest::Reporter* reporter, GrContext* context) {
+static void gpu_to_raster(skiatest::Reporter* reporter, GrContext* context) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);
 
     auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
     auto destinationSurface(SkSurface::MakeRaster(info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
-void rasterToGpu(skiatest::Reporter* reporter, GrContext* context) {
+static void raster_to_gpu(skiatest::Reporter* reporter, GrContext* context) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);
 
     auto sourceSurface(SkSurface::MakeRaster(info));
     auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageNewShader_GPU, reporter, ctxInfo) {
     //  GPU -> GPU
-    gpuToGpu(reporter, ctxInfo.grContext());
+    gpu_to_gpu(reporter, ctxInfo.grContext());
 
     //  GPU -> RASTER
-    gpuToRaster(reporter, ctxInfo.grContext());
+    gpu_to_raster(reporter, ctxInfo.grContext());
 
     //  RASTER -> GPU
-    rasterToGpu(reporter, ctxInfo.grContext());
+    raster_to_gpu(reporter, ctxInfo.grContext());
 }
 
 #endif
diff --git a/tests/IntTextureTest.cpp b/tests/IntTextureTest.cpp
index a3e17bf..3edd4cc 100644
--- a/tests/IntTextureTest.cpp
+++ b/tests/IntTextureTest.cpp
@@ -207,9 +207,8 @@
     };
 
     for (auto filter : kNamedFilters) {
-        SkMatrix m;
-        m.setIDiv(kS, kS);
-        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(texture.get(), nullptr, m,
+        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(texture.get(), nullptr,
+                                                                  SkMatrix::I(),
                                                                   filter.fMode));
         REPORTER_ASSERT(reporter, fp);
         if (!fp) {
diff --git a/tests/PathOpsAngleTest.cpp b/tests/PathOpsAngleTest.cpp
index d5285c8..a598aea 100644
--- a/tests/PathOpsAngleTest.cpp
+++ b/tests/PathOpsAngleTest.cpp
@@ -478,6 +478,67 @@
     }
 }
 
+static void offset_start(SkOpAngle* angle, const SkPoint* line, bool offsetStart) {
+    SkDPoint* pt = angle->debugFirstPt();
+    if (!offsetStart) {
+        pt->fX = pt->fY = 0;
+        return;
+    }
+    pt->fX = line[1].fX * 0.001f;
+    pt->fY = line[1].fY * 0.001f;
+}
+
+// test if offset line edges return the same results as edges without offsets
+DEF_TEST(PathOpsOffsetLineAngle, reporter) {
+    static SkPoint radialLines[][2] = {
+        { {0, 0}, { 2, -1} }, { {0, 0}, { 2, -2} }, { {0, 0}, { 1, -2} }, { {0, 0}, { 0, -2} },
+        { {0, 0}, {-1, -2} }, { {0, 0}, {-2, -2} }, { {0, 0}, {-2, -1} }, { {0, 0}, {-2,  0} }, 
+        { {0, 0}, {-2,  1} }, { {0, 0}, {-2,  2} }, { {0, 0}, {-1,  2} }, { {0, 0}, { 0,  2} }, 
+        { {0, 0}, { 1,  2} }, { {0, 0}, { 2,  2} }, { {0, 0}, { 2,  1} }, { {0, 0}, { 2,  0} }, 
+    };
+    SkChunkAlloc allocator(4096);
+    SkOpContourHead contour;
+    SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+    contour.init(&state, false, false);
+    for (int lh = 0; lh < 16; ++lh) {
+        for (int mid = 0; mid < 16; ++mid) {
+            if (lh == mid) {
+                continue;
+            }
+            for (int rh = 0; rh < 16; ++rh) {
+                if (lh == rh || mid == rh) {
+                    continue;
+                }
+                allocator.reset();
+                contour.reset();
+                contour.addLine(radialLines[lh]);
+                contour.addLine(radialLines[mid]);
+                contour.addLine(radialLines[rh]);
+                SkOpSegment* seg1 = contour.first();
+                seg1->debugAddAngle(0, 1);
+                SkOpSegment* seg2 = seg1->next();
+                seg2->debugAddAngle(0, 1);
+                SkOpSegment* seg3 = seg2->next();
+                seg3->debugAddAngle(0, 1);
+                SkOpAngle* angle1 = seg1->debugLastAngle();
+                SkOpAngle* angle2 = seg2->debugLastAngle();
+                SkOpAngle* angle3 = seg3->debugLastAngle();
+                PathOpsAngleTester::SetNext(*angle1, *angle3);
+                /* int expected = */ PathOpsAngleTester::After(*angle2, *angle1);
+                for (int test = 1; test <= 8; ++test) {
+                    offset_start(angle1, radialLines[lh], test & 1);
+                    offset_start(angle2, radialLines[mid], test & 2);
+                    offset_start(angle3, radialLines[rh], test & 4);
+                    /* int found =  */ PathOpsAngleTester::After(*angle2, *angle1);
+#if 0  // this test fails; haven't explored if this is a real bug or not
+                    REPORTER_ASSERT(reporter, expected == found);
+#endif
+                }
+            }
+        }
+    }
+}
+
 void SkOpSegment::debugAddAngle(double startT, double endT) {
     SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
             : this->addT(startT);
diff --git a/tests/PathOpsChalkboardTest.cpp b/tests/PathOpsChalkboardTest.cpp
index 8c74e7f..acfa46f 100644
--- a/tests/PathOpsChalkboardTest.cpp
+++ b/tests/PathOpsChalkboardTest.cpp
@@ -91,9 +91,6 @@
 }
 
 static void chalkboard_threaded(skiatest::Reporter* reporter, const char* filename) {
-#if DEBUG_UNDER_DEVELOPMENT
-    return;
-#endif
     initializeTests(reporter, "chalkboard");
     PathOpsThreadedTestRunner testRunner(reporter);
     SkRandom r;
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp
index d70deb3..06fee80 100644
--- a/tests/PathOpsExtendedTest.cpp
+++ b/tests/PathOpsExtendedTest.cpp
@@ -320,21 +320,26 @@
 
 static SkTDArray<SkPathOp> gTestOp;
 
-static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two,
-        const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo,
-        const SkPathOp shapeOp, const SkMatrix& scale) {
-    SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
+static void dumpPathOpFunction(const char* testName, const SkPath& a, const SkPath& b,
+        const SkPathOp shapeOp) {
     if (!testName) {
         testName = "xOp";
     }
     SkDebugf("static void %s_%s(skiatest::Reporter* reporter, const char* filename) {\n",
         testName, opSuffixes[shapeOp]);
-    *gTestOp.append() = shapeOp;
     SkDebugf("    SkPath path, pathB;\n");
     SkPathOpsDebug::ShowOnePath(a, "path", false);
     SkPathOpsDebug::ShowOnePath(b, "pathB", false);
     SkDebugf("    testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]);
     SkDebugf("}\n");
+}
+
+static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two,
+        const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo,
+        const SkPathOp shapeOp, const SkMatrix& scale) {
+    SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
+    *gTestOp.append() = shapeOp;
+    dumpPathOpFunction(testName, a, b, shapeOp);
     drawAsciiPaths(scaledOne, scaledTwo, true);
 }
 
@@ -344,11 +349,15 @@
         const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
         const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale,
         ExpectMatch expectMatch) {
+    if (ExpectMatch::kFlaky == expectMatch) {
+        return 0;  // fuzz data may cause asserts in region generating code, so don't try
+    }
     int errors2x2;
     const int MAX_ERRORS = 8;
     (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
     if (ExpectMatch::kNo == expectMatch) {
         if (errors2x2 < MAX_ERRORS) {
+            SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, testName);
             REPORTER_ASSERT(reporter, 0);
         }
         return 0;
@@ -505,6 +514,11 @@
             ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
 }
 
+bool testSimplifyTry(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
+    return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo,
+            ExpectMatch::kNo);
+}
+
 #if DEBUG_SHOW_TEST_NAME
 static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
     SkDebugf("\n");
@@ -525,18 +539,23 @@
             SkDEBUGPARAMS(testName))) {
         if (ExpectSuccess::kYes == expectSuccess) {
             SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName);
+            dumpPathOpFunction(testName, a, b, shapeOp);
             REPORTER_ASSERT(reporter, 0);
         }
         return false;
     } else {
         if (ExpectSuccess::kNo == expectSuccess) {
                 SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName);
+                dumpPathOpFunction(testName, a, b, shapeOp);
                 REPORTER_ASSERT(reporter, 0);
         }
     }
     if (!reporter->verbose()) {
         return true;
     }
+    if (ExpectMatch::kFlaky == expectMatch) {
+        return true;  // fuzzy data may assert in region construction: see bug.skia.org/6129
+    }
     SkPath pathOut, scaledPathOut;
     SkRegion rgnA, rgnB, openClip, rgnOut;
     openClip.setRect(-16000, -16000, 16000, 16000);
@@ -579,29 +598,18 @@
             ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
 }
 
+bool testPathOpTry(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+        const SkPathOp shapeOp, const char* testName) {
+    return innerPathOp(reporter, a, b, shapeOp, testName, 
+            ExpectSuccess::kYes, SkipAssert::kNo, ExpectMatch::kNo);
+}
+
 bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
         const SkPathOp shapeOp, const char* testName) {
     return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kFlaky, SkipAssert::kYes,
             ExpectMatch::kFlaky);
 }
 
-bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                 const SkPathOp shapeOp, const char* testName) {
-#if DEBUG_SHOW_TEST_NAME
-    showName(a, b, shapeOp);
-#endif
-    SkPath orig;
-    orig.lineTo(54, 43);
-    SkPath out = orig;
-    if (Op(a, b, shapeOp, &out) ) {
-        SkDebugf("%s test is expected to fail\n", __FUNCTION__);
-        REPORTER_ASSERT(reporter, 0);
-        return false;
-    }
-    SkASSERT(out == orig);
-    return true;
-}
-
 SK_DECLARE_STATIC_MUTEX(gMutex);
 
 void initializeTests(skiatest::Reporter* reporter, const char* test) {
diff --git a/tests/PathOpsExtendedTest.h b/tests/PathOpsExtendedTest.h
index 9f8b0ae..7c8ceb1 100644
--- a/tests/PathOpsExtendedTest.h
+++ b/tests/PathOpsExtendedTest.h
@@ -36,17 +36,18 @@
                        const SkPathOp , const char* testName);
 extern bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
                             const SkPathOp , const char* testName, bool checkFail);
-extern bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                           const SkPathOp, const char* testName);
 extern bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a,
                            const SkPath& b, const SkPathOp , const char* testName);
+extern bool testPathOpTry(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+                          const SkPathOp , const char* testName);
 extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                          const char* pathStr);
 extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
 extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path,
                               const char* filename, bool checkFail);
 extern bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path,
-                                       const char* filename);
+                             const char* filename);
+extern bool testSimplifyTry(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
 
 void initializeTests(skiatest::Reporter* reporter, const char* testName);
 
diff --git a/tests/PathOpsFuzz763Test.cpp b/tests/PathOpsFuzz763Test.cpp
index 90d5723..996f4e7 100644
--- a/tests/PathOpsFuzz763Test.cpp
+++ b/tests/PathOpsFuzz763Test.cpp
@@ -2092,8 +2092,7 @@
 path.close();
 
     SkPath path2(path);
-    // DEBUG_UNDER_DEVELOPMENT  fuzz763_1026368 disable expectation check for now
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !SkOpGlobalState::DebugRunFail());
+    testPathOpTry(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_5485218(skiatest::Reporter* reporter, const char* filename) {
diff --git a/tests/PathOpsIssue3651.cpp b/tests/PathOpsIssue3651.cpp
index bd4ed2d..dc93657 100644
--- a/tests/PathOpsIssue3651.cpp
+++ b/tests/PathOpsIssue3651.cpp
@@ -472,9 +472,7 @@
 static void issue3651_1a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path = path1_a();
     SkPath pathB = path2_a();
-    // DEBUG_UNDER_DEVELOPMENT  issue3651_1a disable expectation check for now
-    testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename,
-            !SkOpGlobalState::DebugRunFail());
+    testPathOpTry(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
 }
 
 static SkPath path3() {
@@ -1204,9 +1202,7 @@
 static void issue3651_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path = path1();
     SkPath pathB = path2();
-    // DEBUG_UNDER_DEVELOPMENT  issue3651_1 disable expectation check for now
-    testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename,
-            !SkOpGlobalState::DebugRunFail());
+    testPathOpTry(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
 }
 
 static void issue3651_2(skiatest::Reporter* reporter, const char* filename) {
diff --git a/tests/PathOpsSimplifyFailTest.cpp b/tests/PathOpsSimplifyFailTest.cpp
index b6f210e..44bb50c 100644
--- a/tests/PathOpsSimplifyFailTest.cpp
+++ b/tests/PathOpsSimplifyFailTest.cpp
@@ -215,9 +215,278 @@
     testSimplifyFuzz(reporter, path, filename);
 }
 
+static void fuzz_twister(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(0, 600);
+path.lineTo(3.35544e+07f, 600);
+path.lineTo(3.35544e+07f, 0);
+path.lineTo(0, 0);
+path.lineTo(0, 600);
+path.close();
+path.moveTo(63, 600);
+path.lineTo(3.35545e+07f, 600);
+path.lineTo(3.35545e+07f, 0);
+path.lineTo(63, 0);
+path.lineTo(63, 600);
+path.close();
+path.moveTo(93, 600);
+path.lineTo(3.35545e+07f, 600);
+path.lineTo(3.35545e+07f, 0);
+path.lineTo(93, 0);
+path.lineTo(93, 600);
+path.close();
+path.moveTo(123, 600);
+path.lineTo(3.35546e+07f, 600);
+path.lineTo(3.35546e+07f, 0);
+path.lineTo(123, 0);
+path.lineTo(123, 600);
+path.close();
+    testSimplifyFuzz(reporter, path, filename);
+}
+
+static void fuzz_twister2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x44160000));  // 0, 600
+path.lineTo(SkBits2Float(0x4bfffffe), SkBits2Float(0x44160000));  // 3.35544e+07f, 600
+path.lineTo(SkBits2Float(0x4bfffffe), SkBits2Float(0x00000000));  // 3.35544e+07f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x44160000));  // 0, 600
+path.close();
+
+path.moveTo(SkBits2Float(0x427c0000), SkBits2Float(0x00000000));  // 63, 0
+path.lineTo(SkBits2Float(0x4c00000f), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x4c00000f), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x427c0000), SkBits2Float(0x00000000));  // 63, 0
+path.close();
+
+path.moveTo(SkBits2Float(0x42ba0000), SkBits2Float(0x00000000));  // 93, 0
+path.lineTo(SkBits2Float(0x4c000016), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x4c000016), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x42ba0000), SkBits2Float(0x00000000));  // 93, 0
+path.close();
+
+path.moveTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000));  // 123, 0
+path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000));  // 3.35546e+07f, 0
+path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000));  // 3.35546e+07f, 0
+path.lineTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000));  // 123, 0
+path.close();
+
+    testSimplifyFuzz(reporter, path, filename);
+}
+
+static void fuzz994s_11(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000));  // 110, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x46ff4c00), SkBits2Float(0x41f00000));  // 32678, 30
+path.lineTo(SkBits2Float(0x46ff4c00), SkBits2Float(0x41f00000));  // 32678, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x4c000006));  // 110, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x439d8000));  // 10, 315
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x439d8000));  // 110, 315
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x437f0000));  // 110, 255
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x437f0000));  // 10, 255
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x439d8000));  // 10, 315
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x42700000));  // 0, 60
+path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x42700000));  // 100, 60
+path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000));  // 100, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x42700000));  // 0, 60
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000));  // 110, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x4c000006));  // 110, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000));  // 110, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+
+    testSimplifyFuzz(reporter, path, filename);
+}
+
+static void fuzz994s_3414(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000));  // 100, 50
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100, 0, 50, 0, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0, 0, 0, 50, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0, 100, 50, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100, 100, 100, 50, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42c84964), SkBits2Float(0x42480000));  // 100.143f, 50
+path.conicTo(SkBits2Float(0x42c84964), SkBits2Float(0x00000000), SkBits2Float(0x424892c8), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100.143f, 0, 50.1433f, 0, 0.707107f
+path.conicTo(SkBits2Float(0x3e12c788), SkBits2Float(0x00000000), SkBits2Float(0x3e12c788), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0.143339f, 0, 0.143339f, 50, 0.707107f
+path.conicTo(SkBits2Float(0x3e12c788), SkBits2Float(0x42c80000), SkBits2Float(0x424892c8), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0.143339f, 100, 50.1433f, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c84964), SkBits2Float(0x42c80000), SkBits2Float(0x42c84964), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100.143f, 100, 100.143f, 50, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000));  // 100, 50
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100, 0, 50, 0, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0, 0, 0, 50, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0, 100, 50, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100, 100, 100, 50, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x4c00006b), SkBits2Float(0x424c0000));  // 3.35549e+07f, 51
+path.conicTo(SkBits2Float(0x4c00006b), SkBits2Float(0xcbffffe5), SkBits2Float(0x43d6e720), SkBits2Float(0xcbffffe5), SkBits2Float(0x3f3504f3));  // 3.35549e+07f, -3.35544e+07f, 429.806f, -3.35544e+07f, 0.707107f
+path.conicTo(SkBits2Float(0xcbffff28), SkBits2Float(0xcbffffe5), SkBits2Float(0xcbffff28), SkBits2Float(0x424c0000), SkBits2Float(0x3f3504f3));  // -3.3554e+07f, -3.35544e+07f, -3.3554e+07f, 51, 0.707107f
+path.conicTo(SkBits2Float(0xcbffff28), SkBits2Float(0x4c00000c), SkBits2Float(0x43d6e720), SkBits2Float(0x4c00000c), SkBits2Float(0x3f3504f3));  // -3.3554e+07f, 3.35545e+07f, 429.806f, 3.35545e+07f, 0.707107f
+path.conicTo(SkBits2Float(0x4c00006b), SkBits2Float(0x4c00000c), SkBits2Float(0x4c00006b), SkBits2Float(0x424c0000), SkBits2Float(0x3f3504f3));  // 3.35549e+07f, 3.35545e+07f, 3.35549e+07f, 51, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43ef6720), SkBits2Float(0x42480000));  // 478.806f, 50
+path.conicTo(SkBits2Float(0x43ef6720), SkBits2Float(0x00000000), SkBits2Float(0x43d66720), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 478.806f, 0, 428.806f, 0, 0.707107f
+path.conicTo(SkBits2Float(0x43bd6720), SkBits2Float(0x00000000), SkBits2Float(0x43bd6720), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 378.806f, 0, 378.806f, 50, 0.707107f
+path.conicTo(SkBits2Float(0x43bd6720), SkBits2Float(0x42c80000), SkBits2Float(0x43d66720), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 378.806f, 100, 428.806f, 100, 0.707107f
+path.conicTo(SkBits2Float(0x43ef6720), SkBits2Float(0x42c80000), SkBits2Float(0x43ef6720), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 478.806f, 100, 478.806f, 50, 0.707107f
+path.close();
+
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz763_4713_b(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.close();
+
+path.moveTo(SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
+path.quadTo(SkBits2Float(0x42123842), SkBits2Float(0x41c52adf), SkBits2Float(0x421baed7), SkBits2Float(0x41d7bac6));
+path.quadTo(SkBits2Float(0x4225256d), SkBits2Float(0x41ea4aad), SkBits2Float(0x42254667), SkBits2Float(0x4202666b));
+path.quadTo(SkBits2Float(0x42256760), SkBits2Float(0x420fa77f), SkBits2Float(0x421c1f6c), SkBits2Float(0x42191e14));
+path.quadTo(SkBits2Float(0x421bff97), SkBits2Float(0x42193e89), SkBits2Float(0x421bdf6b), SkBits2Float(0x42195eb8));
+path.quadTo(SkBits2Float(0x421bbff6), SkBits2Float(0x42197f32), SkBits2Float(0x421ba03b), SkBits2Float(0x42199f57));
+path.quadTo(SkBits2Float(0x421b605e), SkBits2Float(0x4219e00a), SkBits2Float(0x421b1fa8), SkBits2Float(0x421a1f22));
+path.quadTo(SkBits2Float(0x421ae0f1), SkBits2Float(0x421a604b), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41db19b1), SkBits2Float(0x41da63d5), SkBits2Float(0x41db755b), SkBits2Float(0x41da0a9b));
+path.quadTo(SkBits2Float(0x41dbce01), SkBits2Float(0x41d9ae59), SkBits2Float(0x41dc285e), SkBits2Float(0x41d952ce));
+path.quadTo(SkBits2Float(0x41dc55b6), SkBits2Float(0x41d924df), SkBits2Float(0x41dc82cd), SkBits2Float(0x41d8f7cd));
+path.quadTo(SkBits2Float(0x41dcaf1e), SkBits2Float(0x41d8ca01), SkBits2Float(0x41dcdc4c), SkBits2Float(0x41d89bf0));
+path.quadTo(SkBits2Float(0x41ef6c33), SkBits2Float(0x41c5aec5), SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
+path.close();
+testSimplify(reporter, path, filename);
+}
+
+static void fuzz864a(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(10, 90);
+    path.lineTo(10, 90);
+    path.lineTo(10, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(10, 90);
+    path.lineTo(10, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(110, 90);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 30);
+    path.lineTo(32678, 30);
+    path.lineTo(32678, 30);
+    path.lineTo(10, 30);
+    path.close();
+    path.moveTo(10, 3.35545e+07f);
+    path.lineTo(110, 3.35545e+07f);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 3.35545e+07f);
+    path.close();
+    path.moveTo(10, 315);
+    path.lineTo(110, 315);
+    path.lineTo(110, 255);
+    path.lineTo(10, 255);
+    path.lineTo(10, 315);
+    path.close();
+    path.moveTo(0, 60);
+    path.lineTo(100, 60);
+    path.lineTo(100, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 60);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(110, 90);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 3.35545e+07f);
+    path.lineTo(110, 3.35545e+07f);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 3.35545e+07f);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(110, 90);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    testSimplifyFuzz(reporter, path, filename);
+}
+
 #define TEST(test) test(reporter, #test)
 
 DEF_TEST(PathOpsSimplifyFail, reporter) {
+    TEST(fuzz864a);
+    TEST(fuzz763_4713_b);
+    TEST(fuzz994s_3414);
+    TEST(fuzz994s_11);
+    TEST(fuzz_twister2);
+    TEST(fuzz_twister);
     TEST(fuzz763_2);
     TEST(fuzz763_1);
     TEST(fuzz_x2);
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index 2de67ff..7daa5e0 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -4871,7 +4871,7 @@
     path.lineTo(10, 30);
     path.lineTo(10, 90);
     path.close();
-    testSimplify(reporter, path, filename);
+    testSimplifyFuzz(reporter, path, filename);
 }
 
 static void cr514118(skiatest::Reporter* reporter,const char* filename) {
@@ -4954,7 +4954,7 @@
 path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
 path.close();
 
-    testSimplify(reporter, path, filename);
+    testSimplifyFuzz(reporter, path, filename);
 }
 
 static void fuzz994s_3414(skiatest::Reporter* reporter, const char* filename) {
@@ -5021,7 +5021,7 @@
 path.lineTo(123, 0);
 path.lineTo(123, 600);
 path.close();
-    testSimplify(reporter, path, filename);
+    testSimplifyFuzz(reporter, path, filename);
 }
 
 static void fuzz_twister2(skiatest::Reporter* reporter, const char* filename) {
@@ -6919,8 +6919,6 @@
 }

 
 static void joel_9(skiatest::Reporter* reporter, const char* filename) {
-#if DEBUG_UNDER_DEVELOPMENT
-// fails with image mismatch
     SkPath path;
 path.moveTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604));  // 144.859f, 285.172f

 path.lineTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604));  // 144.859f, 285.172f

@@ -6973,13 +6971,10 @@
 path.cubicTo(SkBits2Float(0x430fefe0), SkBits2Float(0x438a3f1a), SkBits2Float(0x43123811), SkBits2Float(0x438c7d0e), SkBits2Float(0x4310dbe8), SkBits2Float(0x438e9624));  // 143.937f, 276.493f, 146.219f, 280.977f, 144.859f, 285.173f

 path.lineTo(SkBits2Float(0x430d67f0), SkBits2Float(0x438e070a));  // 141.406f, 284.055f

 path.close();
-testSimplify(reporter, path, filename);
-#endif
+testSimplifyTry(reporter, path, filename);
 }

 
 static void joel_10(skiatest::Reporter* reporter, const char* filename) {
-#if DEBUG_UNDER_DEVELOPMENT
-// fails with image mismatch
     SkPath path;
 path.moveTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000));  // 575.148f, 433

 path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000));  // 575.148f, 433

@@ -7032,12 +7027,11 @@
 path.cubicTo(SkBits2Float(0x4410048b), SkBits2Float(0x43dcd7f0), SkBits2Float(0x440f720c), SkBits2Float(0x43da99dc), SkBits2Float(0x440fc989), SkBits2Float(0x43d87fe0));  // 576.071f, 441.687f, 573.782f, 437.202f, 575.149f, 432.999f

 path.lineTo(SkBits2Float(0x4410a687), SkBits2Float(0x43d91000));  // 578.602f, 434.125f

 path.close();
-testSimplify(reporter, path, filename);
-#endif
+// DEBUG_UNDER_DEVELOPMENT  fails with angle instability
+testSimplifyFuzz(reporter, path, filename);
 }

 
 static void joel_11(skiatest::Reporter* reporter, const char* filename) {
-#if DEBUG_UNDER_DEVELOPMENT
 // fails with image mismatch
     SkPath path;
 path.moveTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d));  // 403.625f, 582.367f

@@ -7092,8 +7086,7 @@
 path.cubicTo(SkBits2Float(0x43c64810), SkBits2Float(0x4412e116), SkBits2Float(0x43c7a70a), SkBits2Float(0x4411d28f), SkBits2Float(0x43c9d000), SkBits2Float(0x4411978d));  // 396.563f, 587.517f, 399.305f, 583.29f, 403.625f, 582.368f

 path.lineTo(SkBits2Float(0x43ca3106), SkBits2Float(0x44127b02));  // 404.383f, 585.922f

 path.close();
-testSimplify(reporter, path, filename);
-#endif
+testSimplifyFuzz(reporter, path, filename);
 }

 
 static void make_joel_12(SkPath& path) {
diff --git a/tests/ReadWriteAlphaTest.cpp b/tests/ReadWriteAlphaTest.cpp
index c0b7e94..27c2260 100644
--- a/tests/ReadWriteAlphaTest.cpp
+++ b/tests/ReadWriteAlphaTest.cpp
@@ -11,6 +11,9 @@
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
 #include "SkCanvas.h"
 #include "SkSurface.h"
 
@@ -35,6 +38,7 @@
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadWriteAlpha, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
     unsigned char alphaData[X_SIZE * Y_SIZE];
 
     static const int kClearValue = 0x2;
@@ -50,17 +54,21 @@
 
         // We are initializing the texture with zeros here
         memset(alphaData, 0, X_SIZE * Y_SIZE);
-        sk_sp<GrTexture> texture(
-            ctxInfo.grContext()->textureProvider()->createTexture(desc, SkBudgeted::kNo, alphaData,
-                                                                  0));
-        if (!texture) {
+
+        sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeDeferred(*context->caps(),
+                                                                  context->textureProvider(),
+                                                                  desc,
+                                                                  SkBudgeted::kNo,
+                                                                  alphaData, 0));
+        if (!sProxy) {
             ERRORF(reporter, "Could not create alpha texture.");
             return;
         }
+        sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
+                                                                  sProxy, nullptr));
 
         const SkImageInfo ii = SkImageInfo::MakeA8(X_SIZE, Y_SIZE);
-        sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.grContext(),
-                                                          SkBudgeted::kNo, ii));
+        sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii));
 
         // create a distinctive texture
         for (int y = 0; y < Y_SIZE; ++y) {
@@ -70,9 +78,9 @@
         }
 
         for (auto rowBytes : kRowBytes) {
+
             // upload the texture (do per-rowbytes iteration because we may overwrite below).
-            bool result = texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                                               alphaData, 0);
+            bool result = sContext->writePixels(ii, alphaData, 0, 0, 0);
             REPORTER_ASSERT_MESSAGE(reporter, result, "Initial A8 writePixels failed");
 
             size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE;
@@ -81,8 +89,7 @@
             memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE);
 
             // read the texture back
-            result = texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                                         readback.get(), rowBytes);
+            result = sContext->readPixels(ii, readback.get(), rowBytes, 0, 0);
             REPORTER_ASSERT_MESSAGE(reporter, result, "Initial A8 readPixels failed");
 
             // make sure the original & read back versions match
@@ -153,8 +160,7 @@
                 }
             }
             sk_sp<GrTexture> texture(
-                ctxInfo.grContext()->textureProvider()->createTexture(desc, SkBudgeted::kNo,
-                                                                      rgbaData, 0));
+                context->textureProvider()->createTexture(desc, SkBudgeted::kNo, rgbaData, 0));
             if (!texture) {
                 // We always expect to be able to create a RGBA texture
                 if (!rt  && kRGBA_8888_GrPixelConfig == desc.fConfig) {
diff --git a/tests/RectangleTextureTest.cpp b/tests/RectangleTextureTest.cpp
index a0c5409..6d075df 100644
--- a/tests/RectangleTextureTest.cpp
+++ b/tests/RectangleTextureTest.cpp
@@ -107,14 +107,12 @@
             context->makeRenderTargetContext(SkBackingFit::kExact, rectangleTexture->width(),
                                              rectangleTexture->height(), rectangleTexture->config(),
                                              nullptr));
-    SkMatrix m;
-    m.setIDiv(rectangleTexture->width(), rectangleTexture->height());
     for (auto filter : {GrSamplerParams::kNone_FilterMode,
                         GrSamplerParams::kBilerp_FilterMode,
                         GrSamplerParams::kMipMap_FilterMode}) {
         rtContext->clear(nullptr, 0xDDCCBBAA, true);
-        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(rectangleTexture,
-                                                                  nullptr, m, filter));
+        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(rectangleTexture, nullptr,
+                                                                  SkMatrix::I(), filter));
         GrPaint paint;
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
         paint.addColorFragmentProcessor(std::move(fp));
diff --git a/tests/SRGBMipMapTest.cpp b/tests/SRGBMipMapTest.cpp
index 79d8691..22d6890 100644
--- a/tests/SRGBMipMapTest.cpp
+++ b/tests/SRGBMipMapTest.cpp
@@ -132,7 +132,7 @@
     GrPaint paint;
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     GrSamplerParams mipMapParams(SkShader::kRepeat_TileMode, GrSamplerParams::kMipMap_FilterMode);
-    paint.addColorTextureProcessor(texture.get(), nullptr, SkMatrix::MakeScale(0.5f), mipMapParams);
+    paint.addColorTextureProcessor(texture.get(), nullptr, SkMatrix::MakeScale(rtS), mipMapParams);
 
     // 1) Draw texture to S32 surface (should generate/use sRGB mips)
     paint.setGammaCorrect(true);
diff --git a/tests/TessellatingPathRendererTests.cpp b/tests/TessellatingPathRendererTests.cpp
index b03db18..90d2ba5 100644
--- a/tests/TessellatingPathRendererTests.cpp
+++ b/tests/TessellatingPathRendererTests.cpp
@@ -5,12 +5,12 @@
  * found in the LICENSE file.
  */
 
+#include "Test.h"
+
 #include "SkPath.h"
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
-#include "GrTest.h"
-#include "Test.h"
 #include "ops/GrTessellatingPathRenderer.h"
 
 /*
@@ -249,7 +249,7 @@
     return path;
 }
 
-static void test_path(GrRenderTargetContext* renderTargetContext, GrResourceProvider* rp,
+static void test_path(GrResourceProvider* rp, GrRenderTargetContext* renderTargetContext,
                       const SkPath& path) {
     GrTessellatingPathRenderer tess;
 
@@ -272,37 +272,36 @@
 }
 
 DEF_GPUTEST_FOR_ALL_CONTEXTS(TessellatingPathRendererTests, reporter, ctxInfo) {
-    sk_sp<GrRenderTargetContext> rtc(ctxInfo.grContext()->makeRenderTargetContext(
-                                                                         SkBackingFit::kApprox,
-                                                                         800, 800,
-                                                                         kRGBA_8888_GrPixelConfig,
-                                                                         nullptr,
-                                                                         0,
-                                                                         kTopLeft_GrSurfaceOrigin));
+    GrContext* ctx = ctxInfo.grContext();
+    sk_sp<GrRenderTargetContext> rtc(ctx->makeRenderTargetContext(SkBackingFit::kApprox,
+                                                                  800, 800,
+                                                                  kRGBA_8888_GrPixelConfig,
+                                                                  nullptr,
+                                                                  0,
+                                                                  kTopLeft_GrSurfaceOrigin));
     if (!rtc) {
         return;
     }
 
-    GrTestTarget tt;
-    ctxInfo.grContext()->getTestTarget(&tt, rtc);
-    GrResourceProvider* rp = tt.resourceProvider();
+    GrResourceProvider* rp = ctx->resourceProvider();
 
-    test_path(rtc.get(), rp, create_path_0());
-    test_path(rtc.get(), rp, create_path_1());
-    test_path(rtc.get(), rp, create_path_2());
-    test_path(rtc.get(), rp, create_path_3());
-    test_path(rtc.get(), rp, create_path_4());
-    test_path(rtc.get(), rp, create_path_5());
-    test_path(rtc.get(), rp, create_path_6());
-    test_path(rtc.get(), rp, create_path_7());
-    test_path(rtc.get(), rp, create_path_8());
-    test_path(rtc.get(), rp, create_path_9());
-    test_path(rtc.get(), rp, create_path_10());
-    test_path(rtc.get(), rp, create_path_11());
-    test_path(rtc.get(), rp, create_path_12());
-    test_path(rtc.get(), rp, create_path_13());
-    test_path(rtc.get(), rp, create_path_14());
-    test_path(rtc.get(), rp, create_path_15());
-    test_path(rtc.get(), rp, create_path_16());
+    ctx->flush();
+    test_path(rp, rtc.get(), create_path_0());
+    test_path(rp, rtc.get(), create_path_1());
+    test_path(rp, rtc.get(), create_path_2());
+    test_path(rp, rtc.get(), create_path_3());
+    test_path(rp, rtc.get(), create_path_4());
+    test_path(rp, rtc.get(), create_path_5());
+    test_path(rp, rtc.get(), create_path_6());
+    test_path(rp, rtc.get(), create_path_7());
+    test_path(rp, rtc.get(), create_path_8());
+    test_path(rp, rtc.get(), create_path_9());
+    test_path(rp, rtc.get(), create_path_10());
+    test_path(rp, rtc.get(), create_path_11());
+    test_path(rp, rtc.get(), create_path_12());
+    test_path(rp, rtc.get(), create_path_13());
+    test_path(rp, rtc.get(), create_path_14());
+    test_path(rp, rtc.get(), create_path_15());
+    test_path(rp, rtc.get(), create_path_16());
 }
 #endif
diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp
index 4cb55d0..969c1ff 100644
--- a/tests/skia_test.cpp
+++ b/tests/skia_test.cpp
@@ -29,7 +29,6 @@
 
 DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash.");
 DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
-DEFINE_bool2(runFail, f, false, "check for success on tests known to fail.");
 DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region.");
 
 #if DEBUG_COIN
@@ -141,7 +140,6 @@
     SkPathOpsDebug::gDumpOp = FLAGS_dumpOp;
     SkPathOpsDebug::gVerifyOp = FLAGS_verifyOp;
 #endif
-    SkPathOpsDebug::gRunFail = FLAGS_runFail;
     SkPathOpsDebug::gVeryVerbose = FLAGS_veryVerbose;
     SetupCrashHandler();
 
@@ -171,11 +169,6 @@
         if (FLAGS_dumpOp) {
             header.appendf(" -d");
         }
-#ifdef SK_DEBUG
-        if (FLAGS_runFail) {
-            header.appendf(" -f");
-        }
-#endif
         if (FLAGS_verbose) {
             header.appendf(" -v");
         }
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index 0760a4f..975d1ec 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -55,13 +55,6 @@
 }
 };
 
-void GrTestTarget::init(GrContext* ctx, sk_sp<GrRenderTargetContext> renderTargetContext) {
-    SkASSERT(!fContext);
-
-    fContext.reset(SkRef(ctx));
-    fRenderTargetContext = renderTargetContext;
-}
-
 bool GrSurfaceProxy::isWrapped_ForTesting() const {
     return SkToBool(fTarget);
 }
@@ -70,16 +63,6 @@
     return fRenderTargetProxy->isWrapped_ForTesting();
 }
 
-void GrContext::getTestTarget(GrTestTarget* tar, sk_sp<GrRenderTargetContext> renderTargetContext) {
-    this->flush();
-    SkASSERT(renderTargetContext);
-    // We could create a proxy GrOpList that passes through to fGpu until ~GrTextTarget() and
-    // then disconnects. This would help prevent test writers from mixing using the returned
-    // GrOpList and regular drawing. We could also assert or fail in GrContext drawing methods
-    // until ~GrTestTarget().
-    tar->init(this, std::move(renderTargetContext));
-}
-
 void GrContext::setTextBlobCacheLimit_ForTesting(size_t bytes) {
     fTextBlobCache->setBudget(bytes);
 }
@@ -138,7 +121,7 @@
     SkDebugf("%s", out.c_str());
 }
 
-sk_sp<SkImage> GrContext::getFontAtlasImage(GrMaskFormat format) {
+sk_sp<SkImage> GrContext::getFontAtlasImage_ForTesting(GrMaskFormat format) {
     GrAtlasGlyphCache* cache = this->getAtlasGlyphCache();
 
     GrTexture* tex = cache->getTexture(format);
diff --git a/tools/gpu/GrTest.h b/tools/gpu/GrTest.h
index 0ef2140..db4c451 100644
--- a/tools/gpu/GrTest.h
+++ b/tools/gpu/GrTest.h
@@ -9,7 +9,6 @@
 #define GrTest_DEFINED
 
 #include "GrContext.h"
-#include "GrRenderTargetContext.h"
 
 namespace GrTest {
     /**
@@ -19,21 +18,4 @@
     void SetupAlwaysEvictAtlas(GrContext*);
 };
 
-/** TODO Please do not use this if you can avoid it.  We are in the process of deleting it.
-    Allows a test to temporarily draw to a GrOpList owned by a GrContext. Tests that use this
-    should be careful not to mix using the GrOpList directly and drawing via SkCanvas or
-    GrContext. In the future this object may provide some guards to prevent this. */
-class GrTestTarget {
-public:
-    GrTestTarget() {}
-
-    void init(GrContext*, sk_sp<GrRenderTargetContext>);
-
-    GrResourceProvider* resourceProvider() { return fContext->resourceProvider(); }
-
-private:
-    sk_sp<GrContext>             fContext;
-    sk_sp<GrRenderTargetContext> fRenderTargetContext;
-};
-
 #endif
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index 967ac31..e31a196 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -6,28 +6,16 @@
     <title></title>
 <div style="height:0">
 
-<div id="cubics">
-{{{403.28299,497.196991}, {403.424011,497.243988}, {391.110992,495.556}, {391.110992,495.556}}}

-{{{398.375,501.976013}, {403.28299,497.196991}}}

-{{{403.42401123046875, 497.243988037109375}, {378.7979736328125, 493.868011474609375}}}, id=0

-{{{415.5605946972181641, 498.8838844379989155}, {378.8318672594865006, 493.8897667505245295}}}, id=1

-{{{403.2954048004600622, 497.117149457477467}, {403.2729768294798873, 497.2781265045034615}}}, id=2

-{{{403.42401123046875, 497.243988037109375}, {378.7979736328125, 493.868011474609375}}}, id=3

-

-</div>
-
-<div id="c1">
-{{{403.28299f, 497.196991f}, {403.284241f, 497.197388f}, {403.28418f, 497.197632f}}}
-{{{403.28418f, 497.197632f}, {403.280487f, 497.224304f}, {391.110992f, 495.556f}, {391.110992f, 495.556f}}}    

-

+<div id="halfplane">
+ccwOf {{{380.294464,140.44487}, {379.597463,140.82048}}} id=1

+ccwOf {{{380.294495,140.444855}, {378.965302,141.103577}}} id=2

 </div>
     </div>
 
 <script type="text/javascript">
 
     var testDivs = [
-    c1,
-    cubics,
+    halfplane,
     ];
 
     var decimal_places = 3;
diff --git a/tools/pathops_visualizer.htm b/tools/pathops_visualizer.htm
index 1844dfd..5acbeb1 100644
--- a/tools/pathops_visualizer.htm
+++ b/tools/pathops_visualizer.htm
@@ -1,779 +1,9 @@
 <html>
 <head>
-<div height="0" hidden="true">
+<div height="0" hidden="true">

 

-<div id="joel_11">

-SkDCubic::ComplexBreak

-{{{407.66400146484375, 586.52301025390625}, {406.718994140625, 585.91400146484375}, {405.56201171875, 585.6710205078125}, {404.38299560546875, 585.9210205078125}}},

-maxCurvature[0]=0.210691815 {{{410.1179280337341311, 587.5354084049533867}, {403.9625372513739308, 584.8405979797089458}}},

-SkDCubic::ComplexBreak

-{{{403.625, 582.36602783203125}, {407.9530029296875, 581.4520263671875}, {412.17999267578125, 584.2020263671875}, {413.10198974609375, 588.5140380859375}}},

-maxCurvature[0]=0.505742375 {{{399.4806060513494685, 576.7555289603596975}, {419.9185900147107304, 590.2821868178559725}}},

-SkDCubic::ComplexBreak

-{{{408.92901611328125, 592.5469970703125}, {409.53802490234375, 591.60198974609375}, {409.78802490234375, 590.4530029296875}, {409.53802490234375, 589.27398681640625}}},

-maxCurvature[0]=0.2464997 {{{408.0414711863251682, 594.9185862710903621}, {410.5821756824746558, 588.7085892000006879}}},

-SkDCubic::ComplexBreak

-{{{413.10003662109375, 588.5159912109375}, {414.00604248046875, 592.83599853515625}, {411.27203369140625, 597.06298828125}, {406.94403076171875, 597.9849853515625}}},

-maxCurvature[0]=0.50027635 {{{418.6547935965468241, 584.2584103487866969}, {405.3110940414140941, 604.7967504597730795}}},

-SkDCubic::ComplexBreak

-{{{402.91400146484375, 593.82000732421875}, {403.8590087890625, 594.42901611328125}, {405.0159912109375, 594.67901611328125}, {406.19500732421875, 594.42901611328125}}},

-maxCurvature[0]=0.20172566 {{{400.4418179242405245, 592.7682938621098856}, {406.5786156780874876, 595.518866839962584}}},

-SkDCubic::ComplexBreak

-{{{406.94500732421875, 597.9840087890625}, {402.625, 598.89801025390625}, {398.39801025390625, 596.156005859375}, {397.47601318359375, 591.83599853515625}}},

-maxCurvature[0]=0.500281451 {{{411.2024837739085115, 603.5428096059653171}, {390.6642477196397749, 590.1989499052259589}}},

-SkDCubic::ComplexBreak

-{{{401.6409912109375, 587.81298828125}, {401.031982421875, 588.7509765625}, {400.781982421875, 589.89898681640625}, {401.031982421875, 591.0789794921875}}},

-maxCurvature[0]=0.208772223 {{{402.6666692341399312, 585.3725424991555428}, {399.9489005783761968, 591.4800732145131406}}},

-SkDCubic::ComplexBreak

-{{{397.47698974609375, 591.83697509765625}, {396.56298828125, 587.5169677734375}, {399.30499267578125, 583.28997802734375}, {403.625, 582.36798095703125}}},

-maxCurvature[0]=0.500281451 {{{391.9181889291909329, 596.0944515473461252}, {405.2620486299302911, 575.5562154930773886}}},

-<empty>

-<empty>

-seg=1 {{{409.539001f, 589.27301f}, {409.296997f, 588.085022f}, {408.593994f, 587.140015f}, {407.664001f, 586.52301f}}}

-seg=2 {{{407.664001f, 586.52301f}, {406.718994f, 585.914001f}, {405.562012f, 585.671021f}, {404.382996f, 585.921021f}}}

-seg=3 {{{404.382996f, 585.921021f}, {403.625f, 582.366028f}}}

-seg=4 {{{403.625f, 582.366028f}, {407.953003f, 581.452026f}, {412.179993f, 584.202026f}, {413.10199f, 588.514038f}}}

-seg=5 {{{413.10199f, 588.514038f}, {409.539001f, 589.27301f}}}

-<empty>

-seg=6 {{{406.195007f, 594.429993f}, {407.375f, 594.179993f}, {408.320007f, 593.484985f}, {408.929016f, 592.546997f}}}

-seg=7 {{{408.929016f, 592.546997f}, {409.538025f, 591.60199f}, {409.788025f, 590.453003f}, {409.538025f, 589.273987f}}}

-seg=8 {{{409.538025f, 589.273987f}, {413.100037f, 588.515991f}}}

-seg=9 {{{413.100037f, 588.515991f}, {414.006042f, 592.835999f}, {411.272034f, 597.062988f}, {406.944031f, 597.984985f}}}

-seg=10 {{{406.944031f, 597.984985f}, {406.195007f, 594.429993f}}}

-<empty>

-seg=11 {{{401.031006f, 591.078003f}, {401.289001f, 592.257996f}, {401.984009f, 593.210999f}, {402.914001f, 593.820007f}}}

-seg=12 {{{402.914001f, 593.820007f}, {403.859009f, 594.429016f}, {405.015991f, 594.679016f}, {406.195007f, 594.429016f}}}

-seg=13 {{{406.195007f, 594.429016f}, {406.945007f, 597.984009f}}}

-seg=14 {{{406.945007f, 597.984009f}, {402.625f, 598.89801f}, {398.39801f, 596.156006f}, {397.476013f, 591.835999f}}}

-seg=15 {{{397.476013f, 591.835999f}, {401.031006f, 591.078003f}}}

-seg=16 {{{404.382996f, 585.921997f}, {403.203003f, 586.171997f}, {402.25f, 586.867004f}, {401.640991f, 587.812988f}}}

-seg=17 {{{401.640991f, 587.812988f}, {401.031982f, 588.750977f}, {400.781982f, 589.898987f}, {401.031982f, 591.078979f}}}

-seg=18 {{{401.031982f, 591.078979f}, {397.47699f, 591.836975f}}}

-seg=19 {{{397.47699f, 591.836975f}, {396.562988f, 587.516968f}, {399.304993f, 583.289978f}, {403.625f, 582.367981f}}}

-seg=20 {{{403.625f, 582.367981f}, {404.382996f, 585.921997f}}}

-debugShowCubicIntersection wtTs[0]=1 {{{409.539001,589.27301}, {409.296997,588.085022}, {408.593994,587.140015}, {407.664001,586.52301}}} {{407.664001,586.52301}} wnTs[0]=0 {{{407.664001,586.52301}, {406.718994,585.914001}, {405.562012,585.671021}, {404.382996,585.921021}}}

-debugShowCubicIntersection no intersect {{{409.539001,589.27301}, {409.296997,588.085022}, {408.593994,587.140015}, {407.664001,586.52301}}} {{{403.625,582.366028}, {407.953003,581.452026}, {412.179993,584.202026}, {413.10199,588.514038}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{409.539001,589.27301}, {409.296997,588.085022}, {408.593994,587.140015}, {407.664001,586.52301}}} {{409.539001,589.27301}} wnTs[0]=1 {{{413.10199,588.514038}, {409.539001,589.27301}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{407.664001,586.52301}, {406.718994,585.914001}, {405.562012,585.671021}, {404.382996,585.921021}}} {{404.382996,585.921021}} wnTs[0]=0 {{{404.382996,585.921021}, {403.625,582.366028}}}

-debugShowCubicIntersection no intersect {{{407.664001,586.52301}, {406.718994,585.914001}, {405.562012,585.671021}, {404.382996,585.921021}}} {{{403.625,582.366028}, {407.953003,581.452026}, {412.179993,584.202026}, {413.10199,588.514038}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{403.625,582.366028}, {407.953003,581.452026}, {412.179993,584.202026}, {413.10199,588.514038}}} {{403.625,582.366028}} wnTs[0]=1 {{{404.382996,585.921021}, {403.625,582.366028}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{403.625,582.366028}, {407.953003,581.452026}, {412.179993,584.202026}, {413.10199,588.514038}}} {{413.10199,588.514038}} wnTs[0]=0 {{{413.10199,588.514038}, {409.539001,589.27301}}}

-debugShowCubicIntersection no intersect {{{407.664001,586.52301}, {406.718994,585.914001}, {405.562012,585.671021}, {404.382996,585.921021}}} {{{404.382996,585.921997}, {403.203003,586.171997}, {402.25,586.867004}, {401.640991,587.812988}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{407.664001,586.52301}, {406.718994,585.914001}, {405.562012,585.671021}, {404.382996,585.921021}}} {{404.382996,585.921021}} wnTs[0]=0.999737 {{{403.625,582.367981}, {404.382996,585.921997}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}} {{403.625,582.367981}} wnTs[0]=0.999474 {{{404.382996,585.921021}, {403.625,582.366028}}}

-SkOpSegment::addT insert t=0.999474488 segID=3 spanID=41

-debugShowLineIntersection wtTs[0]=0 {{{404.382996,585.921021}, {403.625,582.366028}}} {{404.382996,585.921021}} wtTs[1]=0.999474488 {{403.625,582.367981}} wnTs[0]=0.999737 {{{403.625,582.367981}, {404.382996,585.921997}}} wnTs[1]=0

-debugShowCubicIntersection no intersect {{{403.625,582.366028}, {407.953003,581.452026}, {412.179993,584.202026}, {413.10199,588.514038}}} {{{404.382996,585.921997}, {403.203003,586.171997}, {402.25,586.867004}, {401.640991,587.812988}}}

-debugShowCubicIntersection no intersect {{{403.625,582.366028}, {407.953003,581.452026}, {412.179993,584.202026}, {413.10199,588.514038}}} {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}}

-debugShowCubicLineIntersection no intersect {{{403.625,582.366028}, {407.953003,581.452026}, {412.179993,584.202026}, {413.10199,588.514038}}} {{{403.625,582.367981}, {404.382996,585.921997}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{409.539001,589.27301}, {409.296997,588.085022}, {408.593994,587.140015}, {407.664001,586.52301}}} {{409.539001,589.27301}} wnTs[0]=0.000318097 {{{409.538025,589.273987}, {413.100037,588.515991}}}

-SkOpSegment::addT insert t=0.000318097039 segID=8 spanID=42

-debugShowCubicIntersection no intersect {{{409.539001,589.27301}, {409.296997,588.085022}, {408.593994,587.140015}, {407.664001,586.52301}}} {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}}

-debugShowLineIntersection wtTs[0]=1 {{{413.10199,588.514038}, {409.539001,589.27301}}} {{409.539001,589.27301}} wnTs[0]=0.000318097 {{{409.538025,589.273987}, {413.100037,588.515991}}}

-debugShowCubicLineIntersection no intersect {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}} {{{413.10199,588.514038}, {409.539001,589.27301}}}

-debugShowCubicIntersection wtTs[0]=1 {{{404.382996,585.921997}, {403.203003,586.171997}, {402.25,586.867004}, {401.640991,587.812988}}} {{401.640991,587.812988}} wnTs[0]=0 {{{401.640991,587.812988}, {401.031982,588.750977}, {400.781982,589.898987}, {401.031982,591.078979}}}

-debugShowCubicIntersection no intersect {{{404.382996,585.921997}, {403.203003,586.171997}, {402.25,586.867004}, {401.640991,587.812988}}} {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{404.382996,585.921997}, {403.203003,586.171997}, {402.25,586.867004}, {401.640991,587.812988}}} {{404.382996,585.921997}} wnTs[0]=1 {{{403.625,582.367981}, {404.382996,585.921997}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{401.640991,587.812988}, {401.031982,588.750977}, {400.781982,589.898987}, {401.031982,591.078979}}} {{401.031982,591.078979}} wnTs[0]=0 {{{401.031982,591.078979}, {397.47699,591.836975}}}

-debugShowCubicIntersection no intersect {{{401.640991,587.812988}, {401.031982,588.750977}, {400.781982,589.898987}, {401.031982,591.078979}}} {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}} {{397.47699,591.836975}} wnTs[0]=1 {{{401.031982,591.078979}, {397.47699,591.836975}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}} {{403.625,582.367981}} wnTs[0]=0 {{{403.625,582.367981}, {404.382996,585.921997}}}

-debugShowCubicIntersection no intersect {{{401.640991,587.812988}, {401.031982,588.750977}, {400.781982,589.898987}, {401.031982,591.078979}}} {{{401.031006,591.078003}, {401.289001,592.257996}, {401.984009,593.210999}, {402.914001,593.820007}}}

-debugShowCubicLineIntersection no intersect {{{401.640991,587.812988}, {401.031982,588.750977}, {400.781982,589.898987}, {401.031982,591.078979}}} {{{397.476013,591.835999}, {401.031006,591.078003}}}

-debugShowCubicLineIntersection wtTs[0]=0.000319790508 {{{401.031006,591.078003}, {401.289001,592.257996}, {401.984009,593.210999}, {402.914001,593.820007}}} {{401.03125,591.079163}} wnTs[0]=0.00020504 {{{401.031982,591.078979}, {397.47699,591.836975}}}

-SkOpSegment::addT insert t=0.000319790508 segID=11 spanID=43

-debugShowCubicLineIntersection no intersect {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}} {{{401.031982,591.078979}, {397.47699,591.836975}}}

-debugShowLineIntersection no intersect {{{401.031982,591.078979}, {397.47699,591.836975}}} {{{397.476013,591.835999}, {401.031006,591.078003}}}

-debugShowCubicIntersection no intersect {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}} {{{401.031006,591.078003}, {401.289001,592.257996}, {401.984009,593.210999}, {402.914001,593.820007}}}

-debugShowCubicIntersection no intersect {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}} {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}}

-debugShowCubicLineIntersection wtTs[0]=8.74738929e-05 {{{397.47699,591.836975}, {396.562988,587.516968}, {399.304993,583.289978}, {403.625,582.367981}}} {{397.476746,591.835815}} wnTs[0]=0.000207256 {{{397.476013,591.835999}, {401.031006,591.078003}}}

-SkOpSegment::addT insert t=8.74738929e-05 segID=19 spanID=44

-debugShowCubicIntersection wtTs[0]=1 {{{406.195007,594.429993}, {407.375,594.179993}, {408.320007,593.484985}, {408.929016,592.546997}}} {{408.929016,592.546997}} wnTs[0]=0 {{{408.929016,592.546997}, {409.538025,591.60199}, {409.788025,590.453003}, {409.538025,589.273987}}}

-debugShowCubicIntersection no intersect {{{406.195007,594.429993}, {407.375,594.179993}, {408.320007,593.484985}, {408.929016,592.546997}}} {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{406.195007,594.429993}, {407.375,594.179993}, {408.320007,593.484985}, {408.929016,592.546997}}} {{406.195007,594.429993}} wnTs[0]=1 {{{406.944031,597.984985}, {406.195007,594.429993}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{408.929016,592.546997}, {409.538025,591.60199}, {409.788025,590.453003}, {409.538025,589.273987}}} {{409.538025,589.273987}} wnTs[0]=0 {{{409.538025,589.273987}, {413.100037,588.515991}}}

-debugShowCubicIntersection no intersect {{{408.929016,592.546997}, {409.538025,591.60199}, {409.788025,590.453003}, {409.538025,589.273987}}} {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}} {{413.100037,588.515991}} wnTs[0]=1 {{{409.538025,589.273987}, {413.100037,588.515991}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}} {{406.944031,597.984985}} wnTs[0]=0 {{{406.944031,597.984985}, {406.195007,594.429993}}}

-debugShowCubicIntersection no intersect {{{406.195007,594.429993}, {407.375,594.179993}, {408.320007,593.484985}, {408.929016,592.546997}}} {{{402.914001,593.820007}, {403.859009,594.429016}, {405.015991,594.679016}, {406.195007,594.429016}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{406.195007,594.429993}, {407.375,594.179993}, {408.320007,593.484985}, {408.929016,592.546997}}} {{406.195007,594.429993}} wtTs[1]=5.57101392e-05 {{406.19519,594.429932}} wnTs[0]=0.000262996 {{{406.195007,594.429016}, {406.945007,597.984009}}} wnTs[1]=0.000262947308

-debugShowCubicIntersection no intersect {{{406.195007,594.429993}, {407.375,594.179993}, {408.320007,593.484985}, {408.929016,592.546997}}} {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}}

-debugShowCubicLineIntersection no intersect {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}} {{{406.195007,594.429016}, {406.945007,597.984009}}}

-debugShowCubicIntersection no intersect {{{413.100037,588.515991}, {414.006042,592.835999}, {411.272034,597.062988}, {406.944031,597.984985}}} {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}}

-debugShowCubicLineIntersection no intersect {{{402.914001,593.820007}, {403.859009,594.429016}, {405.015991,594.679016}, {406.195007,594.429016}}} {{{406.944031,597.984985}, {406.195007,594.429993}}}

-debugShowLineIntersection wtTs[0]=1 {{{406.944031,597.984985}, {406.195007,594.429993}}} {{406.195007,594.429993}} wnTs[0]=0.000262996 {{{406.195007,594.429016}, {406.945007,597.984009}}}

-debugShowCubicLineIntersection wtTs[0]=8.73365293e-05 {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}} {{406.943878,597.984253}} wnTs[0]=0.000207362 {{{406.944031,597.984985}, {406.195007,594.429993}}}

-SkOpSegment::addT insert t=8.73365293e-05 segID=14 spanID=45

-debugShowCubicIntersection wtTs[0]=1 {{{401.031006,591.078003}, {401.289001,592.257996}, {401.984009,593.210999}, {402.914001,593.820007}}} {{402.914001,593.820007}} wnTs[0]=0 {{{402.914001,593.820007}, {403.859009,594.429016}, {405.015991,594.679016}, {406.195007,594.429016}}}

-debugShowCubicIntersection no intersect {{{401.031006,591.078003}, {401.289001,592.257996}, {401.984009,593.210999}, {402.914001,593.820007}}} {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{401.031006,591.078003}, {401.289001,592.257996}, {401.984009,593.210999}, {402.914001,593.820007}}} {{401.031006,591.078003}} wnTs[0]=1 {{{397.476013,591.835999}, {401.031006,591.078003}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{402.914001,593.820007}, {403.859009,594.429016}, {405.015991,594.679016}, {406.195007,594.429016}}} {{406.195007,594.429016}} wnTs[0]=0 {{{406.195007,594.429016}, {406.945007,597.984009}}}

-debugShowCubicIntersection no intersect {{{402.914001,593.820007}, {403.859009,594.429016}, {405.015991,594.679016}, {406.195007,594.429016}}} {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}} {{406.945007,597.984009}} wnTs[0]=1 {{{406.195007,594.429016}, {406.945007,597.984009}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{406.945007,597.984009}, {402.625,598.89801}, {398.39801,596.156006}, {397.476013,591.835999}}} {{397.476013,591.835999}} wnTs[0]=0 {{{397.476013,591.835999}, {401.031006,591.078003}}}

-------------------x--x---------------- addExpanded

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

-SkOpSegment::debugShowActiveSpans id=1 (409.539001,589.27301 409.296997,588.085022 408.593994,587.140015 407.664001,586.52301) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (407.664001,586.52301 406.718994,585.914001 405.562012,585.671021 404.382996,585.921021) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (404.382996,585.921021 403.625397,582.36792) t=0 tEnd=0.999474488 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (403.625397,582.36792 403.625,582.366028) t=0.999474488 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=4 (403.625,582.366028 407.953003,581.452026 412.179993,584.202026 413.10199,588.514038) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (413.10199,588.514038 409.539001,589.27301) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=16 (404.382996,585.921997 403.203003,586.171997 402.25,586.867004 401.640991,587.812988) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=17 (401.640991,587.812988 401.031982,588.750977 400.781982,589.898987 401.031982,591.078979) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=18 (401.031982,591.078979 397.47699,591.836975) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (397.47699,591.836975 397.47691,591.836597 397.476826,591.836193 397.476746,591.835815) t=0 tEnd=8.74738929e-05 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (397.476746,591.835815 396.563464,587.516202 399.305371,583.289897 403.625,582.367981) t=8.74738929e-05 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=20 (403.625,582.367981 404.382996,585.921997) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=6 (406.195007,594.429993 407.375,594.179993 408.320007,593.484985 408.929016,592.546997) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (408.929016,592.546997 409.538025,591.60199 409.788025,590.453003 409.538025,589.273987) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=8 (409.538025,589.273987 409.539154,589.273743) t=0 tEnd=0.000318097039 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=8 (409.539154,589.273743 413.100037,588.515991) t=0.000318097039 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=9 (413.100037,588.515991 414.006042,592.835999 411.272034,597.062988 406.944031,597.984985) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (406.944031,597.984985 406.195007,594.429993) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=11 (401.031006,591.078003 401.031088,591.07838 401.031167,591.078785 401.03125,591.079163) t=0 tEnd=0.000319790508 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=11 (401.03125,591.079163 401.289442,592.258633 401.984306,593.211193 402.914001,593.820007) t=0.000319790508 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=12 (402.914001,593.820007 403.859009,594.429016 405.015991,594.679016 406.195007,594.429016) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=13 (406.195007,594.429016 406.945007,597.984009) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=14 (406.945007,597.984009 406.94463,597.984009 406.944255,597.984253 406.943878,597.984253) t=0 tEnd=8.73365293e-05 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=14 (406.943878,597.984253 402.624264,598.897536 398.39793,596.155629 397.476013,591.835999) t=8.73365293e-05 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=15 (397.476013,591.835999 401.031006,591.078003) t=0 tEnd=1 windSum=? windValue=1

-------------------x--x---------------- move_multiples

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

-------------------x--x---------------- move_nearby

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

-------------------x--x---------------- correctEnds

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

-------------------x--x---------------- addEndMovedSpans

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

-------------------x--x---------------- expand

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

-------------------x--x---------------- addExpanded

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

-------------------x--x---------------- mark

-00:  seg/base=20/39 seg/base=3/5 MarkCoinStart

-01:  seg/base=20/40 seg/base=3/41 MarkCoinEnd

--------------------------------------- missing_coincidence

--------------------------------------- expand

--------------------------------------- expand

--------------------------------------- apply

-SkOpSegment::markDone id=20 (403.625,582.367981 404.382996,585.921997) t=0 [39] (403.625,582.367981) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0

-SkOpSegment::markDone id=3 (404.382996,585.921021 403.625,582.366028) t=0 [5] (404.382996,585.921021) tEnd=0.999474488 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0

--------------------------------------- findOverlaps

-SkOpSegment::debugShowActiveSpans id=1 (409.539001,589.27301 409.296997,588.085022 408.593994,587.140015 407.664001,586.52301) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (407.664001,586.52301 406.718994,585.914001 405.562012,585.671021 404.382996,585.921021) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (403.625397,582.36792 403.625,582.366028) t=0.999474488 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=4 (403.625,582.366028 407.953003,581.452026 412.179993,584.202026 413.10199,588.514038) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (413.10199,588.514038 409.539001,589.27301) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=16 (404.382996,585.921997 403.203003,586.171997 402.25,586.867004 401.640991,587.812988) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=17 (401.640991,587.812988 401.031982,588.750977 400.781982,589.898987 401.031982,591.078979) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=18 (401.031982,591.078979 397.47699,591.836975) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (397.47699,591.836975 397.47691,591.836597 397.476826,591.836193 397.476746,591.835815) t=0 tEnd=8.74738929e-05 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (397.476746,591.835815 396.563464,587.516202 399.305371,583.289897 403.625,582.367981) t=8.74738929e-05 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=6 (406.195007,594.429993 407.375,594.179993 408.320007,593.484985 408.929016,592.546997) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (408.929016,592.546997 409.538025,591.60199 409.788025,590.453003 409.538025,589.273987) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=8 (409.538025,589.273987 409.539154,589.273743) t=0 tEnd=0.000318097039 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=8 (409.539154,589.273743 413.100037,588.515991) t=0.000318097039 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=9 (413.100037,588.515991 414.006042,592.835999 411.272034,597.062988 406.944031,597.984985) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (406.944031,597.984985 406.195007,594.429993) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=11 (401.031006,591.078003 401.031088,591.07838 401.031167,591.078785 401.03125,591.079163) t=0 tEnd=0.000319790508 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=11 (401.03125,591.079163 401.289442,592.258633 401.984306,593.211193 402.914001,593.820007) t=0.000319790508 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=12 (402.914001,593.820007 403.859009,594.429016 405.015991,594.679016 406.195007,594.429016) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=13 (406.195007,594.429016 406.945007,597.984009) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=14 (406.945007,597.984009 406.94463,597.984009 406.944255,597.984253 406.943878,597.984253) t=0 tEnd=8.73365293e-05 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=14 (406.943878,597.984253 402.624264,598.897536 398.39793,596.155629 397.476013,591.835999) t=8.73365293e-05 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=15 (397.476013,591.835999 401.031006,591.078003) t=0 tEnd=1 windSum=? windValue=1

--------------------------------------- calc_angles

-SkOpSegment::sortAngles [1] tStart=0 [1]

-SkOpAngle::after [1/1] 9/9 tStart=0 tEnd=1 < [8/13] 1/1 tStart=0.000318097039 tEnd=1 < [8/12] 17/17 tStart=0.000318097039 tEnd=0  F 4

-SkOpAngle::afterPart {{{409.539154,589.273743}, {409.29715,588.085754}, {408.594147,587.140747}, {407.664154,586.523743}}} id=1

-SkOpAngle::afterPart {{{409.539154,589.273743}, {413.100037,588.515991}}} id=8

-SkOpAngle::afterPart {{{409.539154,589.273743}, {409.538025,589.273987}}} id=8

-SkOpAngle::after [1/1] 9/9 tStart=0 tEnd=1 < [5/4] 1/1 tStart=1 tEnd=0 < [8/12] 17/17 tStart=0.000318097039 tEnd=0  F 4

-SkOpAngle::afterPart {{{409.539001,589.27301}, {409.296997,588.085022}, {408.593994,587.140015}, {407.664001,586.52301}}} id=1

-SkOpAngle::afterPart {{{409.539001,589.27301}, {413.10199,588.514038}}} id=5

-SkOpAngle::afterPart {{{409.539001,589.27301}, {409.537872,589.273254}}} id=8

-SkOpAngle::after [8/12] 17/17 tStart=0.000318097039 tEnd=0 < [5/4] 1/1 tStart=1 tEnd=0 < [8/13] 1/1 tStart=0.000318097039 tEnd=1  F 11

-SkOpAngle::afterPart {{{409.539001,589.27301}, {409.537872,589.273254}}} id=8

-SkOpAngle::afterPart {{{409.539001,589.27301}, {413.10199,588.514038}}} id=5

-SkOpAngle::afterPart {{{409.539001,589.27301}, {413.099884,588.515259}}} id=8

-SkOpAngle::after [8/13] 1/1 tStart=0.000318097039 tEnd=1 < [5/4] 1/1 tStart=1 tEnd=0 < [1/1] 9/9 tStart=0 tEnd=1  T 7

-SkOpAngle::afterPart {{{409.539001,589.27301}, {413.099884,588.515259}}} id=8

-SkOpAngle::afterPart {{{409.539001,589.27301}, {413.10199,588.514038}}} id=5

-SkOpAngle::afterPart {{{409.539001,589.27301}, {409.296997,588.085022}, {408.593994,587.140015}, {407.664001,586.52301}}} id=1

-SkOpSegment::sortAngles [2] tStart=1 [4]

-SkOpSegment::sortAngles [3] tStart=0.999474488 [41]

-SkOpSegment::sortAngles [5] tStart=1 [10]

-SkOpSegment::sortAngles [16] tStart=0 [31]

-SkOpSegment::sortAngles [17] tStart=1 [34]

-SkOpAngle::after [17/6] 9/5 tStart=1 tEnd=0 < [11/17] 9/9 tStart=0.000319790508 tEnd=0 < [18/7] 17/17 tStart=0 tEnd=1  T 7

-SkOpAngle::afterPart {{{401.03125,591.079163}, {400.78125,589.89917}, {401.03125,588.75116}, {401.640259,587.813171}}} id=17

-SkOpAngle::afterPart {{{401.03125,591.079163}, {401.031006,591.078003}, {401.031088,591.07838}, {401.031006,591.078003}}} id=11

-SkOpAngle::afterPart {{{401.03125,591.079163}, {397.476257,591.837158}}} id=18

-SkOpAngle::after [17/6] 9/5 tStart=1 tEnd=0 < [11/18] 25/25 tStart=0.000319790508 tEnd=1 < [11/17] 9/9 tStart=0.000319790508 tEnd=0  F 5

-SkOpAngle::afterPart {{{401.03125,591.079163}, {400.78125,589.89917}, {401.03125,588.75116}, {401.640259,587.813171}}} id=17

-SkOpAngle::afterPart {{{401.03125,591.079163}, {401.289442,592.258633}, {401.984306,593.211193}, {402.914001,593.820007}}} id=11

-SkOpAngle::afterPart {{{401.03125,591.079163}, {401.031006,591.078003}, {401.031088,591.07838}, {401.031006,591.078003}}} id=11

-SkOpAngle::after [11/17] 9/9 tStart=0.000319790508 tEnd=0 < [11/18] 25/25 tStart=0.000319790508 tEnd=1 < [18/7] 17/17 tStart=0 tEnd=1  F 4

-SkOpAngle::afterPart {{{401.03125,591.079163}, {401.031006,591.078003}, {401.031088,591.07838}, {401.031006,591.078003}}} id=11

-SkOpAngle::afterPart {{{401.03125,591.079163}, {401.289442,592.258633}, {401.984306,593.211193}, {402.914001,593.820007}}} id=11

-SkOpAngle::afterPart {{{401.03125,591.079163}, {397.476257,591.837158}}} id=18

-SkOpAngle::after [18/7] 17/17 tStart=0 tEnd=1 < [11/18] 25/25 tStart=0.000319790508 tEnd=1 < [17/6] 9/5 tStart=1 tEnd=0  T 4

-SkOpAngle::afterPart {{{401.03125,591.079163}, {397.476257,591.837158}}} id=18

-SkOpAngle::afterPart {{{401.03125,591.079163}, {401.289442,592.258633}, {401.984306,593.211193}, {402.914001,593.820007}}} id=11

-SkOpAngle::afterPart {{{401.03125,591.079163}, {400.78125,589.89917}, {401.03125,588.75116}, {401.640259,587.813171}}} id=17

-SkOpSegment::sortAngles [18] tStart=0 [35]

-SkOpSegment::sortAngles [19] tStart=8.74738929e-05 [44]

-SkOpAngle::after [19/8] 25/25 tStart=8.74738929e-05 tEnd=0 < [14/23] 25/29 tStart=1 tEnd=8.73365293e-05 < [19/9] 9/5 tStart=8.74738929e-05 tEnd=1  T 12

-SkOpAngle::afterPart {{{397.476013,591.835999}, {397.476257,591.837158}, {397.476177,591.83678}, {397.476257,591.837158}}} id=19

-SkOpAngle::afterPart {{{397.476013,591.835999}, {398.39793,596.155629}, {402.624264,598.897536}, {406.943878,597.984253}}} id=14

-SkOpAngle::afterPart {{{397.476013,591.835999}, {396.562731,587.516385}, {399.304638,583.29008}, {403.624268,582.368164}}} id=19

-SkOpAngle::after [19/8] 25/25 tStart=8.74738929e-05 tEnd=0 < [15/24] 1/1 tStart=0 tEnd=1 < [14/23] 25/29 tStart=1 tEnd=8.73365293e-05  F 5

-SkOpAngle::afterPart {{{397.476013,591.835999}, {397.476257,591.837158}, {397.476177,591.83678}, {397.476257,591.837158}}} id=19

-SkOpAngle::afterPart {{{397.476013,591.835999}, {401.031006,591.078003}}} id=15

-SkOpAngle::afterPart {{{397.476013,591.835999}, {398.39793,596.155629}, {402.624264,598.897536}, {406.943878,597.984253}}} id=14

-SkOpAngle::after [14/23] 25/29 tStart=1 tEnd=8.73365293e-05 < [15/24] 1/1 tStart=0 tEnd=1 < [19/9] 9/5 tStart=8.74738929e-05 tEnd=1  T 4

-SkOpAngle::afterPart {{{397.476013,591.835999}, {398.39793,596.155629}, {402.624264,598.897536}, {406.943878,597.984253}}} id=14

-SkOpAngle::afterPart {{{397.476013,591.835999}, {401.031006,591.078003}}} id=15

-SkOpAngle::afterPart {{{397.476013,591.835999}, {396.562731,587.516385}, {399.304638,583.29008}, {403.624268,582.368164}}} id=19

-SkOpSegment::sortAngles [19] tStart=1 [38]

-SkOpSegment::sortAngles [6] tStart=0 [11]

-SkOpAngle::after [6/11] 1/1 tStart=0 tEnd=1 < [13/20] 25/25 tStart=0 tEnd=1 < [12/19] 17/13 tStart=1 tEnd=0  F 4

-SkOpAngle::afterPart {{{406.195007,594.429016}, {407.375,594.179016}, {408.320007,593.484009}, {408.929016,592.546021}}} id=6

-SkOpAngle::afterPart {{{406.195007,594.429016}, {406.945007,597.984009}}} id=13

-SkOpAngle::afterPart {{{406.195007,594.429016}, {405.015991,594.679016}, {403.859009,594.429016}, {402.914001,593.820007}}} id=12

-SkOpAngle::after [6/11] 1/1 tStart=0 tEnd=1 < [10/16] 25/25 tStart=1 tEnd=0 < [12/19] 17/13 tStart=1 tEnd=0  F 4

-SkOpAngle::afterPart {{{406.195007,594.429993}, {407.375,594.179993}, {408.320007,593.484985}, {408.929016,592.546997}}} id=6

-SkOpAngle::afterPart {{{406.195007,594.429993}, {406.944031,597.984985}}} id=10

-SkOpAngle::afterPart {{{406.195007,594.429993}, {405.015991,594.679993}, {403.859009,594.429993}, {402.914001,593.820984}}} id=12

-SkOpAngle::after [12/19] 17/13 tStart=1 tEnd=0 < [10/16] 25/25 tStart=1 tEnd=0 < [13/20] 25/25 tStart=0 tEnd=1  T 7

-SkOpAngle::afterPart {{{406.195007,594.429993}, {405.015991,594.679993}, {403.859009,594.429993}, {402.914001,593.820984}}} id=12

-SkOpAngle::afterPart {{{406.195007,594.429993}, {406.944031,597.984985}}} id=10

-SkOpAngle::afterPart {{{406.195007,594.429993}, {406.945007,597.984985}}} id=13

-SkOpSegment::sortAngles [8] tStart=0.000318097039 [42]

-SkOpSegment::sortAngles [9] tStart=1 [18]

-SkOpAngle::after [9/14] 1/5 tStart=1 tEnd=0 < [14/21] 31/31 tStart=8.73365293e-05 tEnd=0 < [10/15] 9/9 tStart=0 tEnd=1  F 4

-SkOpAngle::afterPart {{{406.943878,597.984253}, {411.271881,597.062256}, {414.00589,592.835266}, {413.099884,588.515259}}} id=9

-SkOpAngle::afterPart {{{406.943878,597.984253}, {406.945007,597.984009}, {406.94463,597.984009}, {406.945007,597.984009}}} id=14

-SkOpAngle::afterPart {{{406.943878,597.984253}, {406.194855,594.42926}}} id=10

-SkOpAngle::after [9/14] 1/5 tStart=1 tEnd=0 < [14/22] 17/13 tStart=8.73365293e-05 tEnd=1 < [10/15] 9/9 tStart=0 tEnd=1  F 4

-SkOpAngle::afterPart {{{406.943878,597.984253}, {411.271881,597.062256}, {414.00589,592.835266}, {413.099884,588.515259}}} id=9

-SkOpAngle::afterPart {{{406.943878,597.984253}, {402.624264,598.897536}, {398.39793,596.155629}, {397.476013,591.835999}}} id=14

-SkOpAngle::afterPart {{{406.943878,597.984253}, {406.194855,594.42926}}} id=10

-SkOpAngle::after [10/15] 9/9 tStart=0 tEnd=1 < [14/22] 17/13 tStart=8.73365293e-05 tEnd=1 < [14/21] 31/31 tStart=8.73365293e-05 tEnd=0  T 4

-SkOpAngle::afterPart {{{406.943878,597.984253}, {406.194855,594.42926}}} id=10

-SkOpAngle::afterPart {{{406.943878,597.984253}, {402.624264,598.897536}, {398.39793,596.155629}, {397.476013,591.835999}}} id=14

-SkOpAngle::afterPart {{{406.943878,597.984253}, {406.945007,597.984009}, {406.94463,597.984009}, {406.945007,597.984009}}} id=14

-SkOpSegment::sortAngles [10] tStart=0 [19]

-SkOpSegment::sortAngles [10] tStart=1 [20]

-SkOpSegment::sortAngles [11] tStart=0.000319790508 [43]

-SkOpSegment::sortAngles [12] tStart=1 [24]

-SkOpSegment::sortAngles [13] tStart=0 [25]

-SkOpSegment::sortAngles [14] tStart=8.73365293e-05 [45]

-SkOpSegment::sortAngles [14] tStart=1 [28]

-SkOpSegment::sortAngles [15] tStart=0 [29]

-coinSpan - id=20 t=0 tEnd=1

-coinSpan + id=3 t=0.999474488 tEnd=0

-SkOpSpan::sortableTop dir=kLeft seg=1 t=0.5 pt=(408.859497,587.683899)

-SkOpSpan::sortableTop [0] valid=1 operand=0 span=44 ccw=1 seg=19 {{{397.47699f, 591.836975f}, {396.562988f, 587.516968f}, {399.304993f, 583.289978f}, {403.625f, 582.367981f}}} t=0.33188452 pt=(397.699097,587.683899) slope=(3.85156666,-11.7134239)

-SkOpSpan::sortableTop [1] valid=1 operand=0 span=31 ccw=0 seg=16 {{{404.382996f, 585.921997f}, {403.203003f, 586.171997f}, {402.25f, 586.867004f}, {401.640991f, 587.812988f}}} t=0.953943751 pt=(401.727325,587.683899) slope=(-1.92134028,2.7673627)

-SkOpSpan::sortableTop [2] valid=1 operand=0 span=1 ccw=1 seg=1 {{{409.539001f, 589.27301f}, {409.296997f, 588.085022f}, {408.593994f, 587.140015f}, {407.664001f, 586.52301f}}} t=0.5 pt=(408.859497,587.683899) slope=(-1.9335022,-2.77125549)

-SkOpSegment::markWinding id=19 (397.47699,591.836975 396.562988,587.516968 399.304993,583.289978 403.625,582.367981) t=8.74738929e-05 [44] (397.476746,591.835815) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markWinding id=3 (404.382996,585.921021 403.625,582.366028) t=0.999474488 [41] (403.625397,582.36792) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=4 (403.625,582.366028 407.953003,581.452026 412.179993,584.202026 413.10199,588.514038) t=0 [7] (403.625,582.366028) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=5 (413.10199,588.514038 409.539001,589.27301) t=0 [9] (413.10199,588.514038) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=19 (397.47699,591.836975 396.562988,587.516968 399.304993,583.289978 403.625,582.367981) t=8.74738929e-05 [44] (397.476746,591.835815) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::markWinding id=16 (404.382996,585.921997 403.203003,586.171997 402.25,586.867004 401.640991,587.812988) t=0 [31] (404.382996,585.921997) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::markWinding id=17 (401.640991,587.812988 401.031982,588.750977 400.781982,589.898987 401.031982,591.078979) t=0 [33] (401.640991,587.812988) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=16 (404.382996,585.921997 403.203003,586.171997 402.25,586.867004 401.640991,587.812988) t=0 [31] (404.382996,585.921997) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markWinding id=2 (407.664001,586.52301 406.718994,585.914001 405.562012,585.671021 404.382996,585.921021) t=0 [3] (407.664001,586.52301) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=1 (409.539001,589.27301 409.296997,588.085022 408.593994,587.140015 407.664001,586.52301) t=0 [1] (409.539001,589.27301) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=8 (409.538025,589.273987 413.100037,588.515991) t=0 [15] (409.538025,589.273987) tEnd=0.000318097039 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=7 (408.929016,592.546997 409.538025,591.60199 409.788025,590.453003 409.538025,589.273987) t=0 [13] (408.929016,592.546997) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=6 (406.195007,594.429993 407.375,594.179993 408.320007,593.484985 408.929016,592.546997) t=0 [11] (406.195007,594.429993) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=6 span=11 windSum=-1

-SkOpSegment::markWinding id=8 (409.538025,589.273987 413.100037,588.515991) t=0.000318097039 [42] (409.539154,589.273743) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=9 (413.100037,588.515991 414.006042,592.835999 411.272034,597.062988 406.944031,597.984985) t=0 [17] (413.100037,588.515991) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=9 span=18

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [1/1] next=8/12 sect=9/9  s=0 [1] e=1 [2] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpAngle::dumpOne [8/12] next=8/13 sect=17/17  s=0.000318097039 [42] e=0 [15] sgn=1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [8/13] next=5/4 sect=1/1  s=0.000318097039 [42] e=1 [16] sgn=-1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [5/4] next=1/1 sect=1/1  s=1 [10] e=0 [9] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpSegment::findNextWinding chase.append segment=6 span=11 windSum=-1

-SkOpSegment::findNextWinding chase.append segment=9 span=18

-SkOpSegment::markDone id=1 (409.539001,589.27301 409.296997,588.085022 408.593994,587.140015 407.664001,586.52301) t=0 [1] (409.539001,589.27301) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[1] to:[8] start=4291816 end=4286928

-bridgeWinding current id=1 from=(407.664001,586.52301) to=(409.539001,589.27301)

-path.moveTo(407.664001,586.52301);

-path.cubicTo(408.593994,587.140015, 409.296997,588.085022, 409.539001,589.27301);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=8 (409.538025,589.273987 413.100037,588.515991) t=0 [15] (409.538025,589.273987) tEnd=0.000318097039 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=8 from=(409.539154,589.273743) to=(409.538025,589.273987)

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=7 (408.929016,592.546997 409.538025,591.60199 409.788025,590.453003 409.538025,589.273987) t=0 [13] (408.929016,592.546997) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=7 from=(409.538025,589.273987) to=(408.929016,592.546997)

-path.lineTo(409.538025,589.273987);

-path.cubicTo(409.788025,590.453003, 409.538025,591.60199, 408.929016,592.546997);

-SkOpSegment::markWinding id=12 (402.914001,593.820007 403.859009,594.429016 405.015991,594.679016 406.195007,594.429016) t=0 [23] (402.914001,593.820007) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=11 (401.031006,591.078003 401.289001,592.257996 401.984009,593.210999 402.914001,593.820007) t=0.000319790508 [43] (401.03125,591.079163) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=11 span=43 windSum=-1

-SkOpSegment::markWinding id=10 (406.944031,597.984985 406.195007,594.429993) t=0 [19] (406.944031,597.984985) tEnd=1 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=10 span=19 windSum=-2

-SkOpSegment::markWinding id=13 (406.195007,594.429016 406.945007,597.984009) t=0 [25] (406.195007,594.429016) tEnd=1 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markWinding id=14 (406.945007,597.984009 402.625,598.89801 398.39801,596.156006 397.476013,591.835999) t=0 [27] (406.945007,597.984009) tEnd=8.73365293e-05 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=14 span=45 windSum=?

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [6/11] next=12/19 sect=1/1  s=0 [11] e=1 [12] sgn=-1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [12/19] next=10/16 sect=17/13  s=1 [24] e=0 [23] sgn=1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [10/16] next=13/20 sect=25/25  s=1 [20] e=0 [19] sgn=1 windVal=1 windSum=-2

-SkOpAngle::dumpOne [13/20] next=6/11 sect=25/25  s=0 [25] e=1 [26] sgn=-1 windVal=1 windSum=-2

-SkOpSegment::findNextWinding chase.append segment=11 span=43 windSum=-1

-SkOpSegment::markDone id=10 (406.944031,597.984985 406.195007,594.429993) t=0 [19] (406.944031,597.984985) tEnd=1 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=10 span=19 windSum=-2

-SkOpSegment::markDone id=13 (406.195007,594.429016 406.945007,597.984009) t=0 [25] (406.195007,594.429016) tEnd=1 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::markDone id=14 (406.945007,597.984009 402.625,598.89801 398.39801,596.156006 397.476013,591.835999) t=0 [27] (406.945007,597.984009) tEnd=8.73365293e-05 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=14 span=45 windSum=-2147483647

-SkOpSegment::markDone id=6 (406.195007,594.429993 407.375,594.179993 408.320007,593.484985 408.929016,592.546997) t=0 [11] (406.195007,594.429993) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[6] to:[12] start=4288824 end=4288680

-bridgeWinding current id=6 from=(408.929016,592.546997) to=(406.195007,594.429993)

-path.cubicTo(408.320007,593.484985, 407.375,594.179993, 406.195007,594.429993);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=12 (402.914001,593.820007 403.859009,594.429016 405.015991,594.679016 406.195007,594.429016) t=0 [23] (402.914001,593.820007) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=12 from=(406.195007,594.429016) to=(402.914001,593.820007)

-path.cubicTo(405.015991,594.679016, 403.859009,594.429016, 402.914001,593.820007);

-SkOpSegment::markWinding id=11 (401.031006,591.078003 401.289001,592.257996 401.984009,593.210999 402.914001,593.820007) t=0 [21] (401.031006,591.078003) tEnd=0.000319790508 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markWinding id=15 (397.476013,591.835999 401.031006,591.078003) t=0 [29] (397.476013,591.835999) tEnd=1 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=15 span=29 windSum=-2

-SkOpSegment::markWinding id=18 (401.031982,591.078979 397.47699,591.836975) t=0 [35] (401.031982,591.078979) tEnd=1 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markWinding id=19 (397.47699,591.836975 396.562988,587.516968 399.304993,583.289978 403.625,582.367981) t=0 [37] (397.47699,591.836975) tEnd=8.74738929e-05 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=19 span=44 windSum=-1

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [11/18] next=17/6 sect=25/25  s=0.000319790508 [43] e=1 [22] sgn=-1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [17/6] next=11/17 sect=9/5  s=1 [34] e=0 [33] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpAngle::dumpOne [11/17] next=18/7 sect=9/9  s=0.000319790508 [43] e=0 [21] sgn=1 windVal=1 windSum=-2

-SkOpAngle::dumpOne [18/7] next=11/18 sect=17/17  s=0 [35] e=1 [36] sgn=-1 windVal=1 windSum=-2

-SkOpSegment::markDone id=11 (401.031006,591.078003 401.289001,592.257996 401.984009,593.210999 402.914001,593.820007) t=0 [21] (401.031006,591.078003) tEnd=0.000319790508 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::markDone id=15 (397.476013,591.835999 401.031006,591.078003) t=0 [29] (397.476013,591.835999) tEnd=1 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=15 span=29 windSum=-2

-SkOpSegment::markDone id=18 (401.031982,591.078979 397.47699,591.836975) t=0 [35] (401.031982,591.078979) tEnd=1 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::markDone id=19 (397.47699,591.836975 396.562988,587.516968 399.304993,583.289978 403.625,582.367981) t=0 [37] (397.47699,591.836975) tEnd=8.74738929e-05 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=19 span=44 windSum=-1

-SkOpSegment::markDone id=11 (401.031006,591.078003 401.289001,592.257996 401.984009,593.210999 402.914001,593.820007) t=0.000319790508 [43] (401.03125,591.079163) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[11] to:[17] start=4290488 end=4290344

-bridgeWinding current id=11 from=(402.914001,593.820007) to=(401.03125,591.079163)

-path.cubicTo(401.984314,593.211182, 401.289429,592.258606, 401.03125,591.079163);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=17 (401.640991,587.812988 401.031982,588.750977 400.781982,589.898987 401.031982,591.078979) t=0 [33] (401.640991,587.812988) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=17 from=(401.031982,591.078979) to=(401.640991,587.812988)

-path.cubicTo(400.781982,589.898987, 401.031982,588.750977, 401.640991,587.812988);

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=16 (404.382996,585.921997 403.203003,586.171997 402.25,586.867004 401.640991,587.812988) t=0 [31] (404.382996,585.921997) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=16 from=(401.640991,587.812988) to=(404.382996,585.921997)

-path.cubicTo(402.25,586.867004, 403.203003,586.171997, 404.382996,585.921997);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=2 (407.664001,586.52301 406.718994,585.914001 405.562012,585.671021 404.382996,585.921021) t=0 [3] (407.664001,586.52301) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=2 from=(404.382996,585.921021) to=(407.664001,586.52301)

-path.cubicTo(405.562012,585.671021, 406.718994,585.914001, 407.664001,586.52301);

-path.close();

-SkOpSegment::markWinding id=19 (397.47699,591.836975 396.562988,587.516968 399.304993,583.289978 403.625,582.367981) t=8.74738929e-05 [44] (397.476746,591.835815) tEnd=1 newWindSum=-1 windSum=-1 windValue=1

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markWinding id=14 (406.945007,597.984009 402.625,598.89801 398.39801,596.156006 397.476013,591.835999) t=8.73365293e-05 [45] (406.943878,597.984253) tEnd=1 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=14 span=45 windSum=-2

-SkOpSegment::debugShowActiveSpans id=3 (403.625397,582.36792 403.625,582.366028) t=0.999474488 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0

-SkOpSegment::debugShowActiveSpans id=4 (403.625,582.366028 407.953003,581.452026 412.179993,584.202026 413.10199,588.514038) t=0 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0

-SkOpSegment::debugShowActiveSpans id=5 (413.10199,588.514038 409.539001,589.27301) t=0 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0

-SkOpSegment::debugShowActiveSpans id=19 (397.476746,591.835815 396.563464,587.516202 399.305371,583.289897 403.625,582.367981) t=8.74738929e-05 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0

-SkOpSegment::debugShowActiveSpans id=8 (409.539154,589.273743 413.100037,588.515991) t=0.000318097039 tEnd=1 windSum=-1 windValue=1

-SkOpSegment::debugShowActiveSpans id=9 (413.100037,588.515991 414.006042,592.835999 411.272034,597.062988 406.944031,597.984985) t=0 tEnd=1 windSum=-1 windValue=1

-SkOpSegment::debugShowActiveSpans id=14 (406.943878,597.984253 402.624264,598.897536 398.39793,596.155629 397.476013,591.835999) t=8.73365293e-05 tEnd=1 windSum=-2 windValue=1

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=19 (397.47699,591.836975 396.562988,587.516968 399.304993,583.289978 403.625,582.367981) t=8.74738929e-05 [44] (397.476746,591.835815) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=19 from=(397.476746,591.835815) to=(403.625,582.367981)

-path.moveTo(397.476746,591.835815);

-path.cubicTo(396.563477,587.516174, 399.305359,583.289917, 403.625,582.367981);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=3 (404.382996,585.921021 403.625,582.366028) t=0.999474488 [41] (403.625397,582.36792) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=3 from=(403.625397,582.36792) to=(403.625,582.366028)

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=4 (403.625,582.366028 407.953003,581.452026 412.179993,584.202026 413.10199,588.514038) t=0 [7] (403.625,582.366028) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=4 from=(403.625,582.366028) to=(413.10199,588.514038)

-path.lineTo(403.625,582.366028);

-path.cubicTo(407.953003,581.452026, 412.179993,584.202026, 413.10199,588.514038);

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [5/4] next=1/1 sect=1/1  s=1 [10] e=0 [9] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpAngle::dumpOne [1/1] next=8/12 sect=9/9  s=0 [1] e=1 [2] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done

-SkOpAngle::dumpOne [8/12] next=8/13 sect=17/17  s=0.000318097039 [42] e=0 [15] sgn=1 windVal=1 windSum=-1 done

-SkOpAngle::dumpOne [8/13] next=5/4 sect=1/1  s=0.000318097039 [42] e=1 [16] sgn=-1 windVal=1 windSum=-1

-SkOpSegment::markDone id=5 (413.10199,588.514038 409.539001,589.27301) t=0 [9] (413.10199,588.514038) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[5] to:[8] start=4291816 end=4287072

-bridgeWinding current id=5 from=(413.10199,588.514038) to=(409.539001,589.27301)

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=8 (409.538025,589.273987 413.100037,588.515991) t=0.000318097039 [42] (409.539154,589.273743) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=8 from=(409.539154,589.273743) to=(413.100037,588.515991)

-path.lineTo(409.539001,589.27301);

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [9/14] next=10/15 sect=1/5  s=1 [18] e=0 [17] sgn=1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [10/15] next=14/22 sect=9/9  s=0 [19] e=1 [20] sgn=-1 windVal=1 windSum=-2 done

-SkOpAngle::dumpOne [14/22] next=14/21 sect=17/13  s=8.73365293e-05 [45] e=1 [28] sgn=-1 windVal=1 windSum=-2

-SkOpAngle::dumpOne [14/21] next=9/14 sect=31/31  s=8.73365293e-05 [45] e=0 [27] sgn=1 windVal=1 windSum=-2 done

-SkOpSegment::markDone id=9 (413.100037,588.515991 414.006042,592.835999 411.272034,597.062988 406.944031,597.984985) t=0 [17] (413.100037,588.515991) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[9] to:[14] start=4292248 end=4289320

-bridgeWinding current id=9 from=(413.100037,588.515991) to=(406.944031,597.984985)

-path.lineTo(413.100037,588.515991);

-path.cubicTo(414.006042,592.835999, 411.272034,597.062988, 406.944031,597.984985);

-SkOpSegment::findNextWinding simple

-SkOpSegment::debugShowActiveSpans id=14 (406.943878,597.984253 402.624264,598.897536 398.39793,596.155629 397.476013,591.835999) t=8.73365293e-05 tEnd=1 windSum=-2 windValue=1

-SkOpSegment::markDone id=14 (406.945007,597.984009 402.625,598.89801 398.39801,596.156006 397.476013,591.835999) t=8.73365293e-05 [45] (406.943878,597.984253) tEnd=1 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-</div>

-
-<div id="joel_10">

-SkDCubic::ComplexBreak

-{{{584.08599853515625, 431.33599853515625}, {582.93798828125, 430.96099853515625}, {581.75799560546875, 431.08599853515625}, {580.7659912109375, 431.593994140625}}},

-maxCurvature[0]=0.908113421 {{{584.1181209116214177, 430.1538818130687787}, {577.969954809597084, 432.7736222667205084}}},

-SkDCubic::ComplexBreak

-{{{575.14801025390625, 433}, {576.5150146484375, 428.7969970703125}, {581.0150146484375, 426.5159912109375}, {585.21002197265625, 427.875}}},

-maxCurvature[0]=0.50036075 {{{568.1978874470240726, 433.8982271481503972}, {590.0470235470908165, 422.8012586633995511}}},

-SkDCubic::ComplexBreak

-{{{586.875, 436.81201171875}, {587.25, 435.66400146484375}, {587.125, 434.5}, {586.61700439453125, 433.49200439453125}}},

-maxCurvature[0]=0.709758914 {{{587.7871278617965345, 437.6571056882418702}, {586.1322255770696756, 431.1527144185091061}}},

-SkDCubic::ComplexBreak

-{{{585.21099853515625, 427.875}, {589.406005859375, 429.24200439453125}, {591.69500732421875, 433.7340087890625}, {590.3280029296875, 437.93701171875}}},

-maxCurvature[0]=0.49729326 {{{584.2405287567108871, 420.9205840752692325}, {595.4397901231312744, 442.7054003127302053}}},

-SkDCubic::ComplexBreak

-{{{581.39801025390625, 439.60198974609375}, {582.53900146484375, 439.968994140625}, {583.71002197265625, 439.85198974609375}, {584.71002197265625, 439.343994140625}}},

-maxCurvature[0]=0.769822111 {{{580.7905223461929154, 440.6007184045780036}, {587.1989920634028977, 438.6622846164399903}}},

-SkDCubic::ComplexBreak

-{{{590.3270263671875, 437.93798828125}, {588.9520263671875, 442.13299560546875}, {584.468017578125, 444.4219970703125}, {580.2650146484375, 443.05499267578125}}},

-maxCurvature[0]=0.50179849 {{{597.2616281410381589, 437.0677298509480124}, {575.4121463613508922, 448.1166853435209987}}},

-SkDCubic::ComplexBreak

-{{{578.60198974609375, 434.125}, {578.2349853515625, 435.27301025390625}, {578.35198974609375, 436.43701171875}, {578.8599853515625, 437.44500732421875}}},

-maxCurvature[0]=0.714831074 {{{577.6906339162001132, 433.299635239093675}, {579.3552391524597169, 439.7975429835167347}}},

-SkDCubic::ComplexBreak

-{{{580.2659912109375, 443.05401611328125}, {576.07098388671875, 441.68701171875}, {573.781982421875, 437.2020263671875}, {575.14898681640625, 432.9990234375}}},

-maxCurvature[0]=0.498616268 {{{581.2070198879381451, 449.9982918524905813}, {570.0518531862154532, 428.2119094507434625}}},

-<empty>

-<empty>

-seg=1 {{{584.085999f, 431.335999f}, {582.937988f, 430.960999f}, {581.757996f, 431.085999f}, {580.765991f, 431.593994f}}}

-seg=2 {{{580.765991f, 431.593994f}, {579.773987f, 432.10199f}, {578.97699f, 432.97699f}, {578.60199f, 434.125f}}}

-seg=3 {{{578.60199f, 434.125f}, {575.14801f, 433}}}

-seg=4 {{{575.14801f, 433}, {576.515015f, 428.796997f}, {581.015015f, 426.515991f}, {585.210022f, 427.875f}}}

-seg=5 {{{585.210022f, 427.875f}, {584.085999f, 431.335999f}}}

-<empty>

-seg=6 {{{586.875f, 436.812012f}, {587.25f, 435.664001f}, {587.125f, 434.5f}, {586.617004f, 433.492004f}}}

-seg=7 {{{586.617004f, 433.492004f}, {586.101013f, 432.5f}, {585.234009f, 431.703003f}, {584.085999f, 431.335999f}}}

-seg=8 {{{584.085999f, 431.335999f}, {585.210999f, 427.875f}}}

-seg=9 {{{585.210999f, 427.875f}, {589.406006f, 429.242004f}, {591.695007f, 433.734009f}, {590.328003f, 437.937012f}}}

-seg=10 {{{590.328003f, 437.937012f}, {586.875f, 436.812012f}}}

-<empty>

-seg=11 {{{581.39801f, 439.60199f}, {582.539001f, 439.968994f}, {583.710022f, 439.85199f}, {584.710022f, 439.343994f}}}

-seg=12 {{{584.710022f, 439.343994f}, {585.702026f, 438.835999f}, {586.499023f, 437.960999f}, {586.874023f, 436.812988f}}}

-seg=13 {{{586.874023f, 436.812988f}, {590.327026f, 437.937988f}}}

-seg=14 {{{590.327026f, 437.937988f}, {588.952026f, 442.132996f}, {584.468018f, 444.421997f}, {580.265015f, 443.054993f}}}

-seg=15 {{{580.265015f, 443.054993f}, {581.39801f, 439.60199f}}}

-seg=16 {{{578.60199f, 434.125f}, {578.234985f, 435.27301f}, {578.35199f, 436.437012f}, {578.859985f, 437.445007f}}}

-seg=17 {{{578.859985f, 437.445007f}, {579.367981f, 438.437012f}, {580.250977f, 439.226013f}, {581.398987f, 439.601013f}}}

-seg=18 {{{581.398987f, 439.601013f}, {580.265991f, 443.054016f}}}

-seg=19 {{{580.265991f, 443.054016f}, {576.070984f, 441.687012f}, {573.781982f, 437.202026f}, {575.148987f, 432.999023f}}}

-seg=20 {{{575.148987f, 432.999023f}, {578.60199f, 434.125f}}}

-debugShowCubicIntersection wtTs[0]=1 {{{584.085999,431.335999}, {582.937988,430.960999}, {581.757996,431.085999}, {580.765991,431.593994}}} {{580.765991,431.593994}} wnTs[0]=0 {{{580.765991,431.593994}, {579.773987,432.10199}, {578.97699,432.97699}, {578.60199,434.125}}}

-debugShowCubicIntersection no intersect {{{584.085999,431.335999}, {582.937988,430.960999}, {581.757996,431.085999}, {580.765991,431.593994}}} {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{584.085999,431.335999}, {582.937988,430.960999}, {581.757996,431.085999}, {580.765991,431.593994}}} {{584.085999,431.335999}} wnTs[0]=1 {{{585.210022,427.875}, {584.085999,431.335999}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{580.765991,431.593994}, {579.773987,432.10199}, {578.97699,432.97699}, {578.60199,434.125}}} {{578.60199,434.125}} wnTs[0]=0 {{{578.60199,434.125}, {575.14801,433}}}

-debugShowCubicIntersection no intersect {{{580.765991,431.593994}, {579.773987,432.10199}, {578.97699,432.97699}, {578.60199,434.125}}} {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}} {{575.14801,433}} wnTs[0]=1 {{{578.60199,434.125}, {575.14801,433}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}} {{585.210022,427.875}} wnTs[0]=0 {{{585.210022,427.875}, {584.085999,431.335999}}}

-debugShowCubicIntersection wtTs[0]=0 {{{584.085999,431.335999}, {582.937988,430.960999}, {581.757996,431.085999}, {580.765991,431.593994}}} {{584.085999,431.335999}} wnTs[0]=1 {{{586.617004,433.492004}, {586.101013,432.5}, {585.234009,431.703003}, {584.085999,431.335999}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{584.085999,431.335999}, {582.937988,430.960999}, {581.757996,431.085999}, {580.765991,431.593994}}} {{584.085999,431.335999}} wnTs[0]=0 {{{584.085999,431.335999}, {585.210999,427.875}}}

-debugShowCubicIntersection no intersect {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}} {{{586.617004,433.492004}, {586.101013,432.5}, {585.234009,431.703003}, {584.085999,431.335999}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}} {{585.210022,427.875}} wnTs[0]=0.999917 {{{584.085999,431.335999}, {585.210999,427.875}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{586.617004,433.492004}, {586.101013,432.5}, {585.234009,431.703003}, {584.085999,431.335999}}} {{584.085999,431.335999}} wnTs[0]=1 {{{585.210022,427.875}, {584.085999,431.335999}}}

-debugShowLineIntersection wtTs[0]=0 {{{585.210022,427.875}, {584.085999,431.335999}}} {{585.210022,427.875}} wtTs[1]=1 {{584.085999,431.335999}} wnTs[0]=0.999917 {{{584.085999,431.335999}, {585.210999,427.875}}} wnTs[1]=0

-debugShowCubicIntersection wtTs[0]=1 {{{580.765991,431.593994}, {579.773987,432.10199}, {578.97699,432.97699}, {578.60199,434.125}}} {{578.60199,434.125}} wnTs[0]=0 {{{578.60199,434.125}, {578.234985,435.27301}, {578.35199,436.437012}, {578.859985,437.445007}}}

-debugShowCubicIntersection no intersect {{{580.765991,431.593994}, {579.773987,432.10199}, {578.97699,432.97699}, {578.60199,434.125}}} {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{580.765991,431.593994}, {579.773987,432.10199}, {578.97699,432.97699}, {578.60199,434.125}}} {{578.60199,434.125}} wnTs[0]=1 {{{575.148987,432.999023}, {578.60199,434.125}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{578.60199,434.125}, {578.234985,435.27301}, {578.35199,436.437012}, {578.859985,437.445007}}} {{578.60199,434.125}} wnTs[0]=0 {{{578.60199,434.125}, {575.14801,433}}}

-debugShowCubicLineIntersection wtTs[0]=0.999907158 {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}} {{575.148621,433.000183}} wnTs[0]=0.999827 {{{578.60199,434.125}, {575.14801,433}}}

-SkOpSegment::addT insert t=0.999907158 segID=19 spanID=41

-debugShowLineIntersection wtTs[0]=0 {{{578.60199,434.125}, {575.14801,433}}} {{578.60199,434.125}} wnTs[0]=1 {{{575.148987,432.999023}, {578.60199,434.125}}}

-debugShowCubicIntersection no intersect {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}} {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}}

-debugShowCubicLineIntersection no intersect {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}} {{{575.148987,432.999023}, {578.60199,434.125}}}

-debugShowCubicIntersection wtTs[0]=1 {{{586.875,436.812012}, {587.25,435.664001}, {587.125,434.5}, {586.617004,433.492004}}} {{586.617004,433.492004}} wnTs[0]=0 {{{586.617004,433.492004}, {586.101013,432.5}, {585.234009,431.703003}, {584.085999,431.335999}}}

-debugShowCubicIntersection no intersect {{{586.875,436.812012}, {587.25,435.664001}, {587.125,434.5}, {586.617004,433.492004}}} {{{585.210999,427.875}, {589.406006,429.242004}, {591.695007,433.734009}, {590.328003,437.937012}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{586.875,436.812012}, {587.25,435.664001}, {587.125,434.5}, {586.617004,433.492004}}} {{586.875,436.812012}} wnTs[0]=1 {{{590.328003,437.937012}, {586.875,436.812012}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{586.617004,433.492004}, {586.101013,432.5}, {585.234009,431.703003}, {584.085999,431.335999}}} {{584.085999,431.335999}} wnTs[0]=0 {{{584.085999,431.335999}, {585.210999,427.875}}}

-debugShowCubicIntersection no intersect {{{586.617004,433.492004}, {586.101013,432.5}, {585.234009,431.703003}, {584.085999,431.335999}}} {{{585.210999,427.875}, {589.406006,429.242004}, {591.695007,433.734009}, {590.328003,437.937012}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{585.210999,427.875}, {589.406006,429.242004}, {591.695007,433.734009}, {590.328003,437.937012}}} {{585.210999,427.875}} wnTs[0]=1 {{{584.085999,431.335999}, {585.210999,427.875}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{585.210999,427.875}, {589.406006,429.242004}, {591.695007,433.734009}, {590.328003,437.937012}}} {{590.328003,437.937012}} wnTs[0]=0 {{{590.328003,437.937012}, {586.875,436.812012}}}

-debugShowCubicIntersection no intersect {{{585.210999,427.875}, {589.406006,429.242004}, {591.695007,433.734009}, {590.328003,437.937012}}} {{{584.710022,439.343994}, {585.702026,438.835999}, {586.499023,437.960999}, {586.874023,436.812988}}}

-debugShowCubicLineIntersection no intersect {{{585.210999,427.875}, {589.406006,429.242004}, {591.695007,433.734009}, {590.328003,437.937012}}} {{{586.874023,436.812988}, {590.327026,437.937988}}}

-debugShowLineIntersection no intersect {{{590.328003,437.937012}, {586.875,436.812012}}} {{{586.874023,436.812988}, {590.327026,437.937988}}}

-debugShowCubicIntersection wtTs[0]=1 {{{578.60199,434.125}, {578.234985,435.27301}, {578.35199,436.437012}, {578.859985,437.445007}}} {{578.859985,437.445007}} wnTs[0]=0 {{{578.859985,437.445007}, {579.367981,438.437012}, {580.250977,439.226013}, {581.398987,439.601013}}}

-debugShowCubicIntersection no intersect {{{578.60199,434.125}, {578.234985,435.27301}, {578.35199,436.437012}, {578.859985,437.445007}}} {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{578.60199,434.125}, {578.234985,435.27301}, {578.35199,436.437012}, {578.859985,437.445007}}} {{578.60199,434.125}} wnTs[0]=1 {{{575.148987,432.999023}, {578.60199,434.125}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{578.859985,437.445007}, {579.367981,438.437012}, {580.250977,439.226013}, {581.398987,439.601013}}} {{581.398987,439.601013}} wnTs[0]=0 {{{581.398987,439.601013}, {580.265991,443.054016}}}

-debugShowCubicIntersection no intersect {{{578.859985,437.445007}, {579.367981,438.437012}, {580.250977,439.226013}, {581.398987,439.601013}}} {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}} {{580.265991,443.054016}} wnTs[0]=1 {{{581.398987,439.601013}, {580.265991,443.054016}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}} {{575.148987,432.999023}} wnTs[0]=0 {{{575.148987,432.999023}, {578.60199,434.125}}}

-debugShowCubicIntersection no intersect {{{578.859985,437.445007}, {579.367981,438.437012}, {580.250977,439.226013}, {581.398987,439.601013}}} {{{581.39801,439.60199}, {582.539001,439.968994}, {583.710022,439.85199}, {584.710022,439.343994}}}

-debugShowCubicIntersection no intersect {{{578.859985,437.445007}, {579.367981,438.437012}, {580.250977,439.226013}, {581.398987,439.601013}}} {{{590.327026,437.937988}, {588.952026,442.132996}, {584.468018,444.421997}, {580.265015,443.054993}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{581.39801,439.60199}, {582.539001,439.968994}, {583.710022,439.85199}, {584.710022,439.343994}}} {{581.39801,439.60199}} wtTs[1]=0.000173389113 {{581.398621,439.602173}} wnTs[0]=0.000339104 {{{581.398987,439.601013}, {580.265991,443.054016}}} wnTs[1]=0.000338089069

-SkOpSegment::addT insert t=0.000339103907 segID=18 spanID=42

-debugShowCubicLineIntersection no intersect {{{590.327026,437.937988}, {588.952026,442.132996}, {584.468018,444.421997}, {580.265015,443.054993}}} {{{581.398987,439.601013}, {580.265991,443.054016}}}

-debugShowLineIntersection wtTs[0]=0.000339103907 {{{581.398987,439.601013}, {580.265991,443.054016}}} {{581.39801,439.60199}} wtTs[1]=1 {{580.265991,443.054016}} wnTs[0]=1 {{{580.265015,443.054993}, {581.39801,439.60199}}} wnTs[1]=0.000339103907

-SkOpSegment::addT insert t=0.000339103907 segID=15 spanID=43

-debugShowCubicIntersection no intersect {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}} {{{590.327026,437.937988}, {588.952026,442.132996}, {584.468018,444.421997}, {580.265015,443.054993}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{580.265991,443.054016}, {576.070984,441.687012}, {573.781982,437.202026}, {575.148987,432.999023}}} {{580.265991,443.054016}} wtTs[1]=4.71004667e-05 {{580.265381,443.053833}} wnTs[0]=0.000339104 {{{580.265015,443.054993}, {581.39801,439.60199}}} wnTs[1]=0.000338761046

-debugShowCubicIntersection wtTs[0]=1 {{{581.39801,439.60199}, {582.539001,439.968994}, {583.710022,439.85199}, {584.710022,439.343994}}} {{584.710022,439.343994}} wnTs[0]=0 {{{584.710022,439.343994}, {585.702026,438.835999}, {586.499023,437.960999}, {586.874023,436.812988}}}

-debugShowCubicIntersection no intersect {{{581.39801,439.60199}, {582.539001,439.968994}, {583.710022,439.85199}, {584.710022,439.343994}}} {{{590.327026,437.937988}, {588.952026,442.132996}, {584.468018,444.421997}, {580.265015,443.054993}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{581.39801,439.60199}, {582.539001,439.968994}, {583.710022,439.85199}, {584.710022,439.343994}}} {{581.39801,439.60199}} wnTs[0]=1 {{{580.265015,443.054993}, {581.39801,439.60199}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{584.710022,439.343994}, {585.702026,438.835999}, {586.499023,437.960999}, {586.874023,436.812988}}} {{586.874023,436.812988}} wnTs[0]=0 {{{586.874023,436.812988}, {590.327026,437.937988}}}

-debugShowCubicIntersection no intersect {{{584.710022,439.343994}, {585.702026,438.835999}, {586.499023,437.960999}, {586.874023,436.812988}}} {{{590.327026,437.937988}, {588.952026,442.132996}, {584.468018,444.421997}, {580.265015,443.054993}}}

-debugShowCubicLineIntersection wtTs[0]=0 {{{590.327026,437.937988}, {588.952026,442.132996}, {584.468018,444.421997}, {580.265015,443.054993}}} {{590.327026,437.937988}} wnTs[0]=1 {{{586.874023,436.812988}, {590.327026,437.937988}}}

-debugShowCubicLineIntersection wtTs[0]=1 {{{590.327026,437.937988}, {588.952026,442.132996}, {584.468018,444.421997}, {580.265015,443.054993}}} {{580.265015,443.054993}} wnTs[0]=0 {{{580.265015,443.054993}, {581.39801,439.60199}}}

-------------------x--x---------------- addExpanded

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

-SkOpSegment::debugShowActiveSpans id=1 (584.085999,431.335999 582.937988,430.960999 581.757996,431.085999 580.765991,431.593994) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (580.765991,431.593994 579.773987,432.10199 578.97699,432.97699 578.60199,434.125) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (578.60199,434.125 575.14801,433) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=4 (575.14801,433 576.515015,428.796997 581.015015,426.515991 585.210022,427.875) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (585.210022,427.875 584.085999,431.335999) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=6 (586.875,436.812012 587.25,435.664001 587.125,434.5 586.617004,433.492004) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (586.617004,433.492004 586.101013,432.5 585.234009,431.703003 584.085999,431.335999) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=8 (584.085999,431.335999 585.210999,427.875) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=9 (585.210999,427.875 589.406006,429.242004 591.695007,433.734009 590.328003,437.937012) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (590.328003,437.937012 586.875,436.812012) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=16 (578.60199,434.125 578.234985,435.27301 578.35199,436.437012 578.859985,437.445007) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=17 (578.859985,437.445007 579.367981,438.437012 580.250977,439.226013 581.398987,439.601013) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=18 (581.398987,439.601013 581.398621,439.602173) t=0 tEnd=0.000339103907 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=18 (581.398621,439.602173 580.265991,443.054016) t=0.000339103907 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (580.265991,443.054016 576.071373,441.687139 573.782422,437.202848 575.148621,433.000183) t=0 tEnd=0.999907158 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (575.148621,433.000183 575.148747,432.999793 575.14886,432.999414 575.148987,432.999023) t=0.999907158 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=20 (575.148987,432.999023 578.60199,434.125) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=11 (581.39801,439.60199 582.539001,439.968994 583.710022,439.85199 584.710022,439.343994) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=12 (584.710022,439.343994 585.702026,438.835999 586.499023,437.960999 586.874023,436.812988) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=13 (586.874023,436.812988 590.327026,437.937988) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=14 (590.327026,437.937988 588.952026,442.132996 584.468018,444.421997 580.265015,443.054993) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=15 (580.265015,443.054993 580.265381,443.053833) t=0 tEnd=0.000339103907 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=15 (580.265381,443.053833 581.39801,439.60199) t=0.000339103907 tEnd=1 windSum=? windValue=1

-------------------x--x---------------- move_multiples

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

-------------------x--x---------------- move_nearby

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

-------------------x--x---------------- correctEnds

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

-------------------x--x---------------- addEndMovedSpans

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

-------------------x--x---------------- expand

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

-------------------x--x---------------- addExpanded

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

-------------------x--x---------------- mark

-00:  seg/base=15/43 seg/base=18/42 MarkCoinStart

-01:  seg/base=15/30 seg/base=18/36 MarkCoinEnd

-02:  seg/base=8/15 seg/base=5/9 MarkCoinStart

-03:  seg/base=8/16 seg/base=5/10 MarkCoinEnd

--------------------------------------- missing_coincidence

--------------------------------------- expand

--------------------------------------- expand

--------------------------------------- apply

-SkOpSegment::markDone id=15 (580.265015,443.054993 581.39801,439.60199) t=0.000339103907 [43] (580.265381,443.053833) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0

-SkOpSegment::markDone id=18 (581.398987,439.601013 580.265991,443.054016) t=0.000339103907 [42] (581.398621,439.602173) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0

-SkOpSegment::markDone id=8 (584.085999,431.335999 585.210999,427.875) t=0 [15] (584.085999,431.335999) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0

-SkOpSegment::markDone id=5 (585.210022,427.875 584.085999,431.335999) t=0 [9] (585.210022,427.875) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0

--------------------------------------- findOverlaps

-SkOpSegment::debugShowActiveSpans id=1 (584.085999,431.335999 582.937988,430.960999 581.757996,431.085999 580.765991,431.593994) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (580.765991,431.593994 579.773987,432.10199 578.97699,432.97699 578.60199,434.125) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (578.60199,434.125 575.14801,433) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=4 (575.14801,433 576.515015,428.796997 581.015015,426.515991 585.210022,427.875) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=6 (586.875,436.812012 587.25,435.664001 587.125,434.5 586.617004,433.492004) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (586.617004,433.492004 586.101013,432.5 585.234009,431.703003 584.085999,431.335999) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=9 (585.210999,427.875 589.406006,429.242004 591.695007,433.734009 590.328003,437.937012) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (590.328003,437.937012 586.875,436.812012) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=16 (578.60199,434.125 578.234985,435.27301 578.35199,436.437012 578.859985,437.445007) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=17 (578.859985,437.445007 579.367981,438.437012 580.250977,439.226013 581.398987,439.601013) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=18 (581.398987,439.601013 581.398621,439.602173) t=0 tEnd=0.000339103907 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (580.265991,443.054016 576.071373,441.687139 573.782422,437.202848 575.148621,433.000183) t=0 tEnd=0.999907158 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=19 (575.148621,433.000183 575.148747,432.999793 575.14886,432.999414 575.148987,432.999023) t=0.999907158 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=20 (575.148987,432.999023 578.60199,434.125) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=11 (581.39801,439.60199 582.539001,439.968994 583.710022,439.85199 584.710022,439.343994) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=12 (584.710022,439.343994 585.702026,438.835999 586.499023,437.960999 586.874023,436.812988) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=13 (586.874023,436.812988 590.327026,437.937988) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=14 (590.327026,437.937988 588.952026,442.132996 584.468018,444.421997 580.265015,443.054993) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=15 (580.265015,443.054993 580.265381,443.053833) t=0 tEnd=0.000339103907 windSum=? windValue=1

--------------------------------------- calc_angles

-SkOpSegment::sortAngles [1] tStart=0 [1]

-SkOpSegment::sortAngles [2] tStart=1 [4]

-SkOpAngle::after [2/2] 5/5 tStart=1 tEnd=0 < [16/9] 21/25 tStart=0 tEnd=1 < [20/14] 13/13 tStart=1 tEnd=0  F 4

-SkOpAngle::afterPart {{{578.60199,434.125}, {578.97699,432.97699}, {579.773987,432.10199}, {580.765991,431.593994}}} id=2

-SkOpAngle::afterPart {{{578.60199,434.125}, {578.234985,435.27301}, {578.35199,436.437012}, {578.859985,437.445007}}} id=16

-SkOpAngle::afterPart {{{578.60199,434.125}, {575.148987,432.999023}}} id=20

-SkOpAngle::after [2/2] 5/5 tStart=1 tEnd=0 < [3/3] 13/13 tStart=0 tEnd=1 < [20/14] 13/13 tStart=1 tEnd=0  F 7

-SkOpAngle::afterPart {{{578.60199,434.125}, {578.97699,432.97699}, {579.773987,432.10199}, {580.765991,431.593994}}} id=2

-SkOpAngle::afterPart {{{578.60199,434.125}, {575.14801,433}}} id=3

-SkOpAngle::afterPart {{{578.60199,434.125}, {575.148987,432.999023}}} id=20

-SkOpAngle::after [20/14] 13/13 tStart=1 tEnd=0 < [3/3] 13/13 tStart=0 tEnd=1 < [16/9] 21/25 tStart=0 tEnd=1  T 7

-SkOpAngle::afterPart {{{578.60199,434.125}, {575.148987,432.999023}}} id=20

-SkOpAngle::afterPart {{{578.60199,434.125}, {575.14801,433}}} id=3

-SkOpAngle::afterPart {{{578.60199,434.125}, {578.234985,435.27301}, {578.35199,436.437012}, {578.859985,437.445007}}} id=16

-SkOpSegment::sortAngles [3] tStart=0 [5]

-SkOpSegment::sortAngles [3] tStart=1 [6]

-SkOpAngle::after [3/4] 29/29 tStart=1 tEnd=0 < [19/13] 5/5 tStart=0.999907158 tEnd=1 < [19/12] 21/25 tStart=0.999907158 tEnd=0  T 4

-SkOpAngle::afterPart {{{575.148621,433.000183}, {578.6026,434.125183}}} id=3

-SkOpAngle::afterPart {{{575.148621,433.000183}, {575.148987,432.999023}, {575.14886,432.999414}, {575.148987,432.999023}}} id=19

-SkOpAngle::afterPart {{{575.148621,433.000183}, {573.782422,437.202848}, {576.071373,441.687139}, {580.265991,443.054016}}} id=19

-SkOpAngle::after [3/4] 29/29 tStart=1 tEnd=0 < [4/5] 5/1 tStart=0 tEnd=1 < [19/13] 5/5 tStart=0.999907158 tEnd=1  T 7

-SkOpAngle::afterPart {{{575.14801,433}, {578.60199,434.125}}} id=3

-SkOpAngle::afterPart {{{575.14801,433}, {576.515015,428.796997}, {581.015015,426.515991}, {585.210022,427.875}}} id=4

-SkOpAngle::afterPart {{{575.14801,433}, {575.148376,432.99884}, {575.14825,432.999231}, {575.148376,432.99884}}} id=19

-SkOpSegment::sortAngles [4] tStart=0 [7]

-SkOpSegment::sortAngles [4] tStart=1 [8]

-SkOpSegment::sortAngles [7] tStart=1 [14]

-SkOpSegment::sortAngles [9] tStart=0 [17]

-SkOpSegment::sortAngles [16] tStart=0 [31]

-SkOpSegment::sortAngles [18] tStart=0.000339103907 [42]

-SkOpSegment::sortAngles [19] tStart=0 [37]

-SkOpSegment::sortAngles [19] tStart=0.999907158 [41]

-SkOpSegment::sortAngles [20] tStart=1 [40]

-SkOpSegment::sortAngles [11] tStart=0 [21]

-SkOpSegment::sortAngles [15] tStart=0.000339103907 [43]

-coinSpan - id=15 t=0.000339103907 tEnd=1

-coinSpan + id=18 t=1 tEnd=0.000339103907

-coinSpan - id=8 t=0 tEnd=1

-coinSpan + id=5 t=1 tEnd=0

-SkOpSpan::sortableTop dir=kTop seg=1 t=0.5 pt=(582.367493,431.133881)

-SkOpSpan::sortableTop [0] valid=1 operand=0 span=7 ccw=1 seg=4 {{{575.14801f, 433}, {576.515015f, 428.796997f}, {581.015015f, 426.515991f}, {585.210022f, 427.875f}}} t=0.774700227 pt=(582.367493,427.491089) slope=(12.4737739,-0.581920821)

-SkOpSpan::sortableTop [1] valid=1 operand=0 span=1 ccw=0 seg=1 {{{584.085999f, 431.335999f}, {582.937988f, 430.960999f}, {581.757996f, 431.085999f}, {580.765991f, 431.593994f}}} t=0.5 pt=(582.367493,431.133881) slope=(-3.375,0.287246704)

-SkOpSegment::markWinding id=4 (575.14801,433 576.515015,428.796997 581.015015,426.515991 585.210022,427.875) t=0 [7] (575.14801,433) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markWinding id=9 (585.210999,427.875 589.406006,429.242004 591.695007,433.734009 590.328003,437.937012) t=0 [17] (585.210999,427.875) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=10 (590.328003,437.937012 586.875,436.812012) t=0 [19] (590.328003,437.937012) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=6 (586.875,436.812012 587.25,435.664001 587.125,434.5 586.617004,433.492004) t=0 [11] (586.875,436.812012) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=7 (586.617004,433.492004 586.101013,432.5 585.234009,431.703003 584.085999,431.335999) t=0 [13] (586.617004,433.492004) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markWinding id=1 (584.085999,431.335999 582.937988,430.960999 581.757996,431.085999 580.765991,431.593994) t=0 [1] (584.085999,431.335999) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=2 (580.765991,431.593994 579.773987,432.10199 578.97699,432.97699 578.60199,434.125) t=0 [3] (580.765991,431.593994) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=4 (575.14801,433 576.515015,428.796997 581.015015,426.515991 585.210022,427.875) t=0 [7] (575.14801,433) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=1 (584.085999,431.335999 582.937988,430.960999 581.757996,431.085999 580.765991,431.593994) t=0 [1] (584.085999,431.335999) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=1 from=(580.765991,431.593994) to=(584.085999,431.335999)

-path.moveTo(580.765991,431.593994);

-path.cubicTo(581.757996,431.085999, 582.937988,430.960999, 584.085999,431.335999);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=7 (586.617004,433.492004 586.101013,432.5 585.234009,431.703003 584.085999,431.335999) t=0 [13] (586.617004,433.492004) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=7 from=(584.085999,431.335999) to=(586.617004,433.492004)

-path.cubicTo(585.234009,431.703003, 586.101013,432.5, 586.617004,433.492004);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=6 (586.875,436.812012 587.25,435.664001 587.125,434.5 586.617004,433.492004) t=0 [11] (586.875,436.812012) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=6 from=(586.617004,433.492004) to=(586.875,436.812012)

-path.cubicTo(587.125,434.5, 587.25,435.664001, 586.875,436.812012);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=10 (590.328003,437.937012 586.875,436.812012) t=0 [19] (590.328003,437.937012) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=10 from=(586.875,436.812012) to=(590.328003,437.937012)

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=9 (585.210999,427.875 589.406006,429.242004 591.695007,433.734009 590.328003,437.937012) t=0 [17] (585.210999,427.875) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=9 from=(590.328003,437.937012) to=(585.210999,427.875)

-path.lineTo(590.328003,437.937012);

-path.cubicTo(591.695007,433.734009, 589.406006,429.242004, 585.210999,427.875);

-SkOpSegment::markWinding id=19 (580.265991,443.054016 576.070984,441.687012 573.781982,437.202026 575.148987,432.999023) t=0.999907158 [41] (575.148621,433.000183) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markWinding id=20 (575.148987,432.999023 578.60199,434.125) t=0 [39] (575.148987,432.999023) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=20 span=40

-SkOpSegment::markWinding id=19 (580.265991,443.054016 576.070984,441.687012 573.781982,437.202026 575.148987,432.999023) t=0 [37] (580.265991,443.054016) tEnd=0.999907158 newWindSum=1 windSum=? windValue=1

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markWinding id=15 (580.265015,443.054993 581.39801,439.60199) t=0 [29] (580.265015,443.054993) tEnd=0.000339103907 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markWinding id=14 (590.327026,437.937988 588.952026,442.132996 584.468018,444.421997 580.265015,443.054993) t=0 [27] (590.327026,437.937988) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markWinding id=13 (586.874023,436.812988 590.327026,437.937988) t=0 [25] (586.874023,436.812988) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markWinding id=12 (584.710022,439.343994 585.702026,438.835999 586.499023,437.960999 586.874023,436.812988) t=0 [23] (584.710022,439.343994) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markWinding id=11 (581.39801,439.60199 582.539001,439.968994 583.710022,439.85199 584.710022,439.343994) t=0 [21] (581.39801,439.60199) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markWinding id=18 (581.398987,439.601013 580.265991,443.054016) t=0 [35] (581.398987,439.601013) tEnd=0.000339103907 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markWinding id=17 (578.859985,437.445007 579.367981,438.437012 580.250977,439.226013 581.398987,439.601013) t=0 [33] (578.859985,437.445007) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markWinding id=16 (578.60199,434.125 578.234985,435.27301 578.35199,436.437012 578.859985,437.445007) t=0 [31] (578.60199,434.125) tEnd=1 newWindSum=1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=16 span=31 windSum=1

-SkOpSegment::markWinding id=3 (578.60199,434.125 575.14801,433) t=0 [5] (578.60199,434.125) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=3 span=5 windSum=-1

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [4/5] next=19/13 sect=5/1  s=0 [7] e=1 [8] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpAngle::dumpOne [19/13] next=19/12 sect=5/5  s=0.999907158 [41] e=1 [38] sgn=-1 windVal=1 windSum=1

-SkOpAngle::dumpOne [19/12] next=3/4 sect=21/25  s=0.999907158 [41] e=0 [37] sgn=1 windVal=1 windSum=1

-SkOpAngle::dumpOne [3/4] next=4/5 sect=29/29  s=1 [6] e=0 [5] sgn=1 windVal=1 windSum=-1

-SkOpSegment::findNextWinding chase.append segment=20 span=40

-SkOpSegment::findNextWinding chase.append segment=16 span=31 windSum=1

-SkOpSegment::findNextWinding chase.append segment=3 span=5 windSum=-1

-SkOpSegment::markDone id=4 (575.14801,433 576.515015,428.796997 581.015015,426.515991 585.210022,427.875) t=0 [7] (575.14801,433) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[4] to:[19] start=107589448 end=107588904

-bridgeWinding current id=4 from=(585.210022,427.875) to=(575.14801,433)

-path.cubicTo(581.015015,426.515991, 576.515015,428.796997, 575.14801,433);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=19 (580.265991,443.054016 576.070984,441.687012 573.781982,437.202026 575.148987,432.999023) t=0.999907158 [41] (575.148621,433.000183) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-bridgeWinding current id=19 from=(575.148621,433.000183) to=(575.148987,432.999023)

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [20/14] next=3/3 sect=13/13  s=1 [40] e=0 [39] sgn=1 windVal=1 windSum=1

-SkOpAngle::dumpOne [3/3] next=16/9 sect=13/13  s=0 [5] e=1 [6] sgn=-1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [16/9] next=2/2 sect=21/25  s=0 [31] e=1 [32] sgn=-1 windVal=1 windSum=1

-SkOpAngle::dumpOne [2/2] next=20/14 sect=5/5  s=1 [4] e=0 [3] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpSegment::markDone id=16 (578.60199,434.125 578.234985,435.27301 578.35199,436.437012 578.859985,437.445007) t=0 [31] (578.60199,434.125) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=17 (578.859985,437.445007 579.367981,438.437012 580.250977,439.226013 581.398987,439.601013) t=0 [33] (578.859985,437.445007) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=18 (581.398987,439.601013 580.265991,443.054016) t=0 [35] (581.398987,439.601013) tEnd=0.000339103907 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markDone id=11 (581.39801,439.60199 582.539001,439.968994 583.710022,439.85199 584.710022,439.343994) t=0 [21] (581.39801,439.60199) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=12 (584.710022,439.343994 585.702026,438.835999 586.499023,437.960999 586.874023,436.812988) t=0 [23] (584.710022,439.343994) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=13 (586.874023,436.812988 590.327026,437.937988) t=0 [25] (586.874023,436.812988) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=14 (590.327026,437.937988 588.952026,442.132996 584.468018,444.421997 580.265015,443.054993) t=0 [27] (590.327026,437.937988) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=15 (580.265015,443.054993 581.39801,439.60199) t=0 [29] (580.265015,443.054993) tEnd=0.000339103907 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::nextChase mismatched signs

-SkOpSegment::markDone id=19 (580.265991,443.054016 576.070984,441.687012 573.781982,437.202026 575.148987,432.999023) t=0 [37] (580.265991,443.054016) tEnd=0.999907158 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=2 (580.765991,431.593994 579.773987,432.10199 578.97699,432.97699 578.60199,434.125) t=0 [3] (580.765991,431.593994) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::markDone id=20 (575.148987,432.999023 578.60199,434.125) t=0 [39] (575.148987,432.999023) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[20] to:[3] start=84323264 end=84323408

-bridgeWinding current id=20 from=(575.148987,432.999023) to=(578.60199,434.125)

-path.lineTo(575.148987,432.999023);

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [3/4] next=4/5 sect=29/29  s=1 [6] e=0 [5] sgn=1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [4/5] next=19/13 sect=5/1  s=0 [7] e=1 [8] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done

-SkOpAngle::dumpOne [19/13] next=19/12 sect=5/5  s=0.999907158 [41] e=1 [38] sgn=-1 windVal=1 windSum=1 done

-SkOpAngle::dumpOne [19/12] next=3/4 sect=21/25  s=0.999907158 [41] e=0 [37] sgn=1 windVal=1 windSum=1 done

-SkOpSegment::markDone id=3 (578.60199,434.125 575.14801,433) t=0 [5] (578.60199,434.125) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[3] to:[19] start=107589448 end=107588760

-bridgeWinding current id=3 from=(578.60199,434.125) to=(575.14801,433)

-path.lineTo(578.60199,434.125);

-path.lineTo(575.14801,433);

-</div>
-
+Skia UnitTests: --match Simplify$ --resourcePath resources\ -v -V SK_DEBUG

+

 <div id="joel_9">

 SkDCubic::ComplexBreak

 {{{135.9219970703125, 286.843994140625}, {137.0699920654296875, 287.218994140625}, {138.24200439453125, 287.08599853515625}, {139.24200439453125, 286.5780029296875}}},

@@ -987,59 +217,103 @@
 -------------------------------------- calc_angles

 SkOpSegment::sortAngles [11] tStart=0 [21]

 SkOpSegment::sortAngles [12] tStart=1 [24]

-SkOpAngle::after [12/2] 5/5 tStart=1 tEnd=0 < [6/13] 21/25 tStart=0 tEnd=1 < [10/18] 13/13 tStart=1 tEnd=0  F 4

+SkOpAngle::debugAfter [12/2] 5/5 tStart=1 tEnd=0 < [6/13] 21/25 tStart=0 tEnd=1 < [10/18] 13/13 tStart=1 tEnd=0  F 4

 SkOpAngle::afterPart {{{133.132996,281.367004}, {133.507996,280.218994}, {134.296997,279.343994}, {135.296997,278.835999}}} id=12

 SkOpAngle::afterPart {{{133.132996,281.367004}, {132.757996,282.507996}, {132.882996,283.687012}, {133.390991,284.679016}}} id=6

 SkOpAngle::afterPart {{{133.132996,281.367004}, {129.679993,280.241028}}} id=10

-SkOpAngle::after [12/2] 5/5 tStart=1 tEnd=0 < [13/3] 13/13 tStart=0 tEnd=1 < [10/18] 13/13 tStart=1 tEnd=0  F 7

+SkOpAngle::debugAfter [12/2] 5/5 tStart=1 tEnd=0 < [13/3] 13/13 tStart=0 tEnd=1 < [10/18] 13/13 tStart=1 tEnd=0  F 9

 SkOpAngle::afterPart {{{133.132996,281.367004}, {133.507996,280.218994}, {134.296997,279.343994}, {135.296997,278.835999}}} id=12

 SkOpAngle::afterPart {{{133.132996,281.367004}, {129.679993,280.242004}}} id=13

 SkOpAngle::afterPart {{{133.132996,281.367004}, {129.679993,280.241028}}} id=10

-SkOpAngle::after [10/18] 13/13 tStart=1 tEnd=0 < [13/3] 13/13 tStart=0 tEnd=1 < [6/13] 21/25 tStart=0 tEnd=1  T 7

+SkOpAngle::debugAfter [10/18] 13/13 tStart=1 tEnd=0 < [13/3] 13/13 tStart=0 tEnd=1 < [6/13] 21/25 tStart=0 tEnd=1  T 9

 SkOpAngle::afterPart {{{133.132996,281.367004}, {129.679993,280.241028}}} id=10

 SkOpAngle::afterPart {{{133.132996,281.367004}, {129.679993,280.242004}}} id=13

 SkOpAngle::afterPart {{{133.132996,281.367004}, {132.757996,282.507996}, {132.882996,283.687012}, {133.390991,284.679016}}} id=6

 SkOpSegment::sortAngles [13] tStart=0 [25]

 SkOpSegment::sortAngles [14] tStart=7.00240426e-05 [43]

-SkOpAngle::after [14/4] 21/21 tStart=7.00240426e-05 tEnd=0 < [9/16] 21/25 tStart=1 tEnd=0 < [14/5] 5/1 tStart=7.00240426e-05 tEnd=1  T 12

-SkOpAngle::afterPart {{{129.679993,280.241028}, {129.679703,280.241913}, {129.679798,280.241619}, {129.679703,280.241913}}} id=14

+ccwOf {{{129.680283,280.241119}, {129.680187,280.241414}}} id=1

+ccwOf {{{129.679993,280.241028}, {128.313004,284.437012}}} id=2

+ccwOf {{{129.680283,280.241119}, {139.741989,275.117004}}} id=1

+ccwOf {{{129.679993,280.241028}, {134.796997,290.296021}}} id=2

+ccwOf {{{129.680283,280.241119}, {139.741989,275.117004}}} id=1

+ccwOf {{{129.680283,280.241119}, {129.680088,280.24171}}} id=2

+ccwOf {{{129.680283,280.241119}, {131.047629,276.03868}}} id=1

+ccwOf {{{129.680283,280.241119}, {129.680088,280.24171}}} id=2

+ccwOf {{{129.680283,280.241119}, {131.047629,276.03868}}} id=1

+ccwOf {{{129.679993,280.241028}, {128.313004,284.437012}}} id=2

+ccwOf {{{129.680283,280.241119}, {129.680088,280.24171}}} id=1

+ccwOf {{{129.679993,280.241028}, {134.796997,290.296021}}} id=2

+SkOpAngle::debugAfter [14/4] 21/21 tStart=7.00240426e-05 tEnd=0 < [9/16] 21/25 tStart=1 tEnd=0 < [14/5] 5/1 tStart=7.00240426e-05 tEnd=1  T 14

+SkOpAngle::afterPart {{{129.680283,280.241119}, {129.679993,280.242004}, {129.680088,280.24171}, {129.679993,280.242004}}} id=14

 SkOpAngle::afterPart {{{129.679993,280.241028}, {128.313004,284.437012}, {130.60199,288.929016}, {134.796997,290.296021}}} id=9

-SkOpAngle::afterPart {{{129.679993,280.241028}, {131.047339,276.038588}, {135.538991,273.757999}, {139.741699,275.116913}}} id=14

-SkOpAngle::after [14/4] 21/21 tStart=7.00240426e-05 tEnd=0 < [10/17] 29/29 tStart=0 tEnd=1 < [9/16] 21/25 tStart=1 tEnd=0  F 5

+SkOpAngle::afterPart {{{129.680283,280.241119}, {131.047629,276.03868}, {135.539281,273.758091}, {139.741989,275.117004}}} id=14

+ccwOf {{{129.680283,280.241119}, {129.680187,280.241414}}} id=1

+ccwOf {{{129.679993,280.241028}, {133.132996,281.367004}}} id=2

+ccwOf {{{129.679993,280.241028}, {128.313004,284.437012}}} id=1

+ccwOf {{{129.679993,280.241028}, {133.132996,281.367004}}} id=2

+ccwOf {{{129.679993,280.241028}, {128.313004,284.437012}}} id=1

+ccwOf {{{129.680283,280.241119}, {129.680088,280.24171}}} id=2

+ccwOf {{{129.679993,280.241028}, {134.796997,290.296021}}} id=1

+ccwOf {{{129.680283,280.241119}, {129.680088,280.24171}}} id=2

+ccwOf {{{129.679993,280.241028}, {134.796997,290.296021}}} id=1

+ccwOf {{{129.679993,280.241028}, {133.132996,281.367004}}} id=2

+SkOpAngle::debugAfter [14/4] 21/21 tStart=7.00240426e-05 tEnd=0 < [10/17] 29/29 tStart=0 tEnd=1 < [9/16] 21/25 tStart=1 tEnd=0  F 8

 SkOpAngle::afterPart {{{129.679993,280.241028}, {129.679703,280.241913}, {129.679798,280.241619}, {129.679703,280.241913}}} id=14

 SkOpAngle::afterPart {{{129.679993,280.241028}, {133.132996,281.367004}}} id=10

 SkOpAngle::afterPart {{{129.679993,280.241028}, {128.313004,284.437012}, {130.60199,288.929016}, {134.796997,290.296021}}} id=9

-SkOpAngle::after [9/16] 21/25 tStart=1 tEnd=0 < [10/17] 29/29 tStart=0 tEnd=1 < [14/5] 5/1 tStart=7.00240426e-05 tEnd=1  T 4

+SkOpAngle::debugAfter [9/16] 21/25 tStart=1 tEnd=0 < [10/17] 29/29 tStart=0 tEnd=1 < [14/5] 5/1 tStart=7.00240426e-05 tEnd=1  T 4

 SkOpAngle::afterPart {{{129.679993,280.241028}, {128.313004,284.437012}, {130.60199,288.929016}, {134.796997,290.296021}}} id=9

 SkOpAngle::afterPart {{{129.679993,280.241028}, {133.132996,281.367004}}} id=10

 SkOpAngle::afterPart {{{129.679993,280.241028}, {131.047339,276.038588}, {135.538991,273.757999}, {139.741699,275.116913}}} id=14

 SkOpSegment::sortAngles [15] tStart=0.00025401744 [42]

 SkOpSegment::sortAngles [16] tStart=0 [31]

-SkOpAngle::after [16/7] 5/9 tStart=0 tEnd=1 < [2/20] 21/21 tStart=1 tEnd=0 < [3/21] 29/29 tStart=0 tEnd=1  T 4

+SkOpAngle::debugAfter [16/7] 5/9 tStart=0 tEnd=1 < [2/20] 21/21 tStart=1 tEnd=0 < [3/21] 29/29 tStart=0 tEnd=1  T 4

 SkOpAngle::afterPart {{{141.406006,284.054993}, {141.77301,282.906982}, {141.64801,281.734985}, {141.14801,280.734985}}} id=16

 SkOpAngle::afterPart {{{141.406006,284.054993}, {141.031006,285.203003}, {140.234009,286.078003}, {139.242004,286.578003}}} id=2

 SkOpAngle::afterPart {{{141.406006,284.054993}, {144.859009,285.171997}}} id=3

-SkOpAngle::after [16/7] 5/9 tStart=0 tEnd=1 < [20/12] 29/29 tStart=1 tEnd=0 < [2/20] 21/21 tStart=1 tEnd=0  F 4

+SkOpAngle::debugAfter [16/7] 5/9 tStart=0 tEnd=1 < [20/12] 29/29 tStart=1 tEnd=0 < [2/20] 21/21 tStart=1 tEnd=0  F 4

 SkOpAngle::afterPart {{{141.406006,284.054993}, {141.77301,282.906982}, {141.64801,281.734985}, {141.14801,280.734985}}} id=16

 SkOpAngle::afterPart {{{141.406006,284.054993}, {144.859009,285.172974}}} id=20

 SkOpAngle::afterPart {{{141.406006,284.054993}, {141.031006,285.203003}, {140.234009,286.078003}, {139.242004,286.578003}}} id=2

-SkOpAngle::after [2/20] 21/21 tStart=1 tEnd=0 < [20/12] 29/29 tStart=1 tEnd=0 < [3/21] 29/29 tStart=0 tEnd=1  T 7

+SkOpAngle::debugAfter [2/20] 21/21 tStart=1 tEnd=0 < [20/12] 29/29 tStart=1 tEnd=0 < [3/21] 29/29 tStart=0 tEnd=1  T 9

 SkOpAngle::afterPart {{{141.406006,284.054993}, {141.031006,285.203003}, {140.234009,286.078003}, {139.242004,286.578003}}} id=2

 SkOpAngle::afterPart {{{141.406006,284.054993}, {144.859009,285.172974}}} id=20

 SkOpAngle::afterPart {{{141.406006,284.054993}, {144.859009,285.171997}}} id=3

 SkOpSegment::sortAngles [18] tStart=0.00025401744 [41]

 SkOpSegment::sortAngles [19] tStart=0 [37]

 SkOpSegment::sortAngles [19] tStart=1 [38]

-SkOpAngle::after [19/10] 5/9 tStart=1 tEnd=0 < [4/22] 5/5 tStart=7.00717611e-05 tEnd=0 < [20/11] 13/13 tStart=0 tEnd=1  F 7

-SkOpAngle::afterPart {{{144.858719,285.172882}, {146.218719,280.976898}, {143.936722,276.492889}, {139.741714,275.117889}}} id=19

+ccwOf {{{144.859009,285.172974}, {139.742004,275.117981}}} id=1

+ccwOf {{{144.858719,285.172882}, {144.858913,285.172292}}} id=2

+ccwOf {{{144.859009,285.172974}, {141.406006,284.054993}}} id=1

+ccwOf {{{144.858719,285.172882}, {144.858815,285.172588}}} id=2

+ccwOf {{{144.859009,285.172974}, {141.406006,284.054993}}} id=1

+ccwOf {{{144.859009,285.172974}, {146.219009,280.97699}}} id=2

+ccwOf {{{144.859009,285.172974}, {141.406006,284.054993}}} id=1

+ccwOf {{{144.859009,285.172974}, {139.742004,275.117981}}} id=2

+ccwOf {{{144.859009,285.172974}, {146.219009,280.97699}}} id=1

+ccwOf {{{144.858719,285.172882}, {144.858815,285.172588}}} id=2

+SkOpAngle::debugAfter [19/10] 5/9 tStart=1 tEnd=0 < [4/22] 5/5 tStart=7.00717611e-05 tEnd=0 < [20/11] 13/13 tStart=0 tEnd=1  F 9

+SkOpAngle::afterPart {{{144.859009,285.172974}, {146.219009,280.97699}, {143.937012,276.492981}, {139.742004,275.117981}}} id=19

 SkOpAngle::afterPart {{{144.858719,285.172882}, {144.859009,285.171997}, {144.858913,285.172292}, {144.859009,285.171997}}} id=4

-SkOpAngle::afterPart {{{144.858719,285.172882}, {141.405716,284.054901}}} id=20

-SkOpAngle::after [19/10] 5/9 tStart=1 tEnd=0 < [4/23] 21/17 tStart=7.00717611e-05 tEnd=1 < [20/11] 13/13 tStart=0 tEnd=1  F 4

+SkOpAngle::afterPart {{{144.859009,285.172974}, {141.406006,284.054993}}} id=20

+ccwOf {{{144.859009,285.172974}, {139.742004,275.117981}}} id=1

+ccwOf {{{144.858719,285.172882}, {134.797012,290.296997}}} id=2

+ccwOf {{{144.859009,285.172974}, {141.406006,284.054993}}} id=1

+ccwOf {{{144.858719,285.172882}, {143.491371,289.375321}}} id=2

+ccwOf {{{144.859009,285.172974}, {141.406006,284.054993}}} id=1

+ccwOf {{{144.859009,285.172974}, {146.219009,280.97699}}} id=2

+ccwOf {{{144.859009,285.172974}, {141.406006,284.054993}}} id=1

+ccwOf {{{144.859009,285.172974}, {139.742004,275.117981}}} id=2

+ccwOf {{{144.859009,285.172974}, {141.406006,284.054993}}} id=1

+ccwOf {{{144.858719,285.172882}, {134.797012,290.296997}}} id=2

+ccwOf {{{144.859009,285.172974}, {146.219009,280.97699}}} id=1

+ccwOf {{{144.858719,285.172882}, {143.491371,289.375321}}} id=2

+SkOpAngle::debugAfter [19/10] 5/9 tStart=1 tEnd=0 < [4/23] 21/17 tStart=7.00717611e-05 tEnd=1 < [20/11] 13/13 tStart=0 tEnd=1  F 7

 SkOpAngle::afterPart {{{144.858719,285.172882}, {146.218719,280.976898}, {143.936722,276.492889}, {139.741714,275.117889}}} id=19

 SkOpAngle::afterPart {{{144.858719,285.172882}, {143.491371,289.375321}, {138.99171,291.655911}, {134.797012,290.296997}}} id=4

 SkOpAngle::afterPart {{{144.858719,285.172882}, {141.405716,284.054901}}} id=20

-SkOpAngle::after [20/11] 13/13 tStart=0 tEnd=1 < [4/23] 21/17 tStart=7.00717611e-05 tEnd=1 < [4/22] 5/5 tStart=7.00717611e-05 tEnd=0  T 4

-SkOpAngle::afterPart {{{144.858719,285.172882}, {141.405716,284.054901}}} id=20

+SkOpAngle::debugAfter [20/11] 13/13 tStart=0 tEnd=1 < [4/23] 21/17 tStart=7.00717611e-05 tEnd=1 < [4/22] 5/5 tStart=7.00717611e-05 tEnd=0  T 4

+SkOpAngle::afterPart {{{144.859009,285.172974}, {141.406006,284.054993}}} id=20

 SkOpAngle::afterPart {{{144.858719,285.172882}, {143.491371,289.375321}, {138.99171,291.655911}, {134.797012,290.296997}}} id=4

 SkOpAngle::afterPart {{{144.858719,285.172882}, {144.859009,285.171997}, {144.858913,285.172292}, {144.859009,285.171997}}} id=4

 SkOpSegment::sortAngles [20] tStart=0 [39]

@@ -1112,7 +386,7 @@
 SkOpSegment::markDone id=4 (144.859009,285.171997 143.492004,289.375 138.992004,291.656006 134.797012,290.296997) t=0 [7] (144.859009,285.171997) tEnd=7.00717611e-05 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

 SkOpSegment::findNextWinding chase.append segment=4 span=44 windSum=-2147483647

 SkOpSegment::markDone id=16 (141.406006,284.054993 141.77301,282.906982 141.64801,281.734985 141.14801,280.734985) t=0 [31] (141.406006,284.054993) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[16] to:[2] start=84323080 end=84322936

+SkOpSegment::findNextWinding from:[16] to:[2] start=4746648 end=4746504

 bridgeWinding current id=16 from=(141.14801,280.734985) to=(141.406006,284.054993)

 path.cubicTo(141.64801,281.734985, 141.77301,282.906982, 141.406006,284.054993);

 SkOpSegment::findNextWinding simple

@@ -1148,7 +422,7 @@
 SkOpSegment::markDone id=14 (129.679993,280.242004 131.046997,276.039001 135.538986,273.757996 139.741989,275.117004) t=0 [27] (129.679993,280.242004) tEnd=7.00240426e-05 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

 SkOpSegment::findNextWinding chase.append segment=14 span=43 windSum=-1

 SkOpSegment::markDone id=6 (133.132996,281.367004 132.757996,282.507996 132.882996,283.687012 133.390991,284.679016) t=0 [11] (133.132996,281.367004) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[6] to:[12] start=107586600 end=107586456

+SkOpSegment::findNextWinding from:[6] to:[12] start=4750776 end=4750632

 bridgeWinding current id=6 from=(133.390991,284.679016) to=(133.132996,281.367004)

 path.cubicTo(132.882996,283.687012, 132.757996,282.507996, 133.132996,281.367004);

 SkOpSegment::findNextWinding simple

@@ -1183,7 +457,7 @@
 SkOpAngle::dumpOne [4/23] next=4/22 sect=21/17  s=7.00717611e-05 [44] e=1 [8] sgn=-1 windVal=1 windSum=-2

 SkOpAngle::dumpOne [4/22] next=19/10 sect=5/5  s=7.00717611e-05 [44] e=0 [7] sgn=1 windVal=1 windSum=-2 done

 SkOpSegment::markDone id=19 (139.742004,275.117981 143.937012,276.492981 146.219009,280.97699 144.859009,285.172974) t=0 [37] (139.742004,275.117981) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[19] to:[4] start=107589880 end=84323576

+SkOpSegment::findNextWinding from:[19] to:[4] start=4754056 end=4747144

 bridgeWinding current id=19 from=(139.742004,275.117981) to=(144.859009,285.172974)

 path.lineTo(139.741699,275.117889);

 path.cubicTo(143.937012,276.492981, 146.219009,280.97699, 144.859009,285.172974);

@@ -1197,400 +471,11 @@
 SkOpSegment::markDone id=4 (144.859009,285.171997 143.492004,289.375 138.992004,291.656006 134.797012,290.296997) t=7.00717611e-05 [44] (144.858719,285.172882) tEnd=1 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

 </div>

 
-<div id="issue3838">

-seg=1 {{{200, 170}, {220, 170}}}

-seg=2 {{{220, 170}, {220, 230}}}

-seg=3 {{{220, 230}, {240, 230}}}

-seg=4 {{{240, 230}, {240, 210}}}

-seg=5 {{{240, 210}, {180, 210}}}

-seg=6 {{{180, 210}, {180, 190}}}

-seg=7 {{{180, 190}, {260, 190}}}

-seg=8 {{{260, 190}, {260, 250}}}

-seg=9 {{{260, 250}, {200, 250}}}

-seg=10 {{{200, 250}, {200, 170}}}

-debugShowLineIntersection wtTs[0]=0 {{{220,170}, {220,230}}} {{220,170}} wnTs[0]=1 {{{200,170}, {220,170}}}

-debugShowLineIntersection wtTs[0]=1 {{{200,250}, {200,170}}} {{200,170}} wnTs[0]=0 {{{200,170}, {220,170}}}

-debugShowLineIntersection wtTs[0]=0 {{{220,230}, {240,230}}} {{220,230}} wnTs[0]=1 {{{220,170}, {220,230}}}

-debugShowLineIntersection wtTs[0]=0.333333333 {{{240,210}, {180,210}}} {{220,210}} wnTs[0]=0.666667 {{{220,170}, {220,230}}}

-SkOpSegment::addT insert t=0.666666667 segID=2 spanID=21

-SkOpSegment::addT insert t=0.333333333 segID=5 spanID=22

-debugShowLineIntersection wtTs[0]=0.5 {{{180,190}, {260,190}}} {{220,190}} wnTs[0]=0.333333 {{{220,170}, {220,230}}}

-SkOpSegment::addT insert t=0.333333333 segID=2 spanID=23

-SkOpSegment::addT insert t=0.5 segID=7 spanID=24

-debugShowLineIntersection wtTs[0]=0 {{{240,230}, {240,210}}} {{240,230}} wnTs[0]=1 {{{220,230}, {240,230}}}

-debugShowLineIntersection wtTs[0]=0 {{{240,210}, {180,210}}} {{240,210}} wnTs[0]=1 {{{240,230}, {240,210}}}

-debugShowLineIntersection wtTs[0]=0 {{{180,210}, {180,190}}} {{180,210}} wnTs[0]=1 {{{240,210}, {180,210}}}

-debugShowLineIntersection wtTs[0]=0.5 {{{200,250}, {200,170}}} {{200,210}} wnTs[0]=0.666667 {{{240,210}, {180,210}}}

-SkOpSegment::addT insert t=0.666666667 segID=5 spanID=25

-SkOpSegment::addT insert t=0.5 segID=10 spanID=26

-debugShowLineIntersection wtTs[0]=0 {{{180,190}, {260,190}}} {{180,190}} wnTs[0]=1 {{{180,210}, {180,190}}}

-debugShowLineIntersection wtTs[0]=0 {{{260,190}, {260,250}}} {{260,190}} wnTs[0]=1 {{{180,190}, {260,190}}}

-debugShowLineIntersection wtTs[0]=0.75 {{{200,250}, {200,170}}} {{200,190}} wnTs[0]=0.25 {{{180,190}, {260,190}}}

-SkOpSegment::addT insert t=0.25 segID=7 spanID=27

-SkOpSegment::addT insert t=0.75 segID=10 spanID=28

-debugShowLineIntersection wtTs[0]=0 {{{260,250}, {200,250}}} {{260,250}} wnTs[0]=1 {{{260,190}, {260,250}}}

-debugShowLineIntersection wtTs[0]=0 {{{200,250}, {200,170}}} {{200,250}} wnTs[0]=1 {{{260,250}, {200,250}}}

--------------------------------------- addExpanded

-SkOpSegment::debugShowActiveSpans id=1 (200,170 220,170) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (220,170 220,190) t=0 tEnd=0.333333333 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (220,190 220,210) t=0.333333333 tEnd=0.666666667 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (220,210 220,230) t=0.666666667 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (220,230 240,230) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=4 (240,230 240,210) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (240,210 220,210) t=0 tEnd=0.333333333 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (220,210 200,210) t=0.333333333 tEnd=0.666666667 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (200,210 180,210) t=0.666666667 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=6 (180,210 180,190) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (180,190 200,190) t=0 tEnd=0.25 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (200,190 220,190) t=0.25 tEnd=0.5 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (220,190 260,190) t=0.5 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=8 (260,190 260,250) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=9 (260,250 200,250) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (200,250 200,210) t=0 tEnd=0.5 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (200,210 200,190) t=0.5 tEnd=0.75 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (200,190 200,170) t=0.75 tEnd=1 windSum=? windValue=1

--------------------------------------- move_multiples

--------------------------------------- move_nearby

--------------------------------------- correctEnds

--------------------------------------- addEndMovedSpans

--------------------------------------- expand

--------------------------------------- addExpanded

--------------------------------------- mark

--------------------------------------- missing_coincidence

--------------------------------------- expand

--------------------------------------- expand

--------------------------------------- apply

--------------------------------------- findOverlaps

--------------------------------------- calc_angles

-SkOpSegment::sortAngles [2] tStart=0.333333333 [23]

-SkOpAngle::after [2/1] 7/7 tStart=0.333333333 tEnd=0 < [7/11] 15/15 tStart=0.5 tEnd=0.25 < [2/2] 23/23 tStart=0.333333333 tEnd=0.666666667  T 4

-SkOpAngle::afterPart {{{220,190}, {220,170}}} id=2

-SkOpAngle::afterPart {{{220,190}, {200,190}}} id=7

-SkOpAngle::afterPart {{{220,190}, {220,210}}} id=2

-SkOpAngle::after [2/1] 7/7 tStart=0.333333333 tEnd=0 < [7/12] 31/31 tStart=0.5 tEnd=1 < [7/11] 15/15 tStart=0.5 tEnd=0.25  F 4

-SkOpAngle::afterPart {{{220,190}, {220,170}}} id=2

-SkOpAngle::afterPart {{{220,190}, {260,190}}} id=7

-SkOpAngle::afterPart {{{220,190}, {200,190}}} id=7

-SkOpAngle::after [7/11] 15/15 tStart=0.5 tEnd=0.25 < [7/12] 31/31 tStart=0.5 tEnd=1 < [2/2] 23/23 tStart=0.333333333 tEnd=0.666666667  F 4

-SkOpAngle::afterPart {{{220,190}, {200,190}}} id=7

-SkOpAngle::afterPart {{{220,190}, {260,190}}} id=7

-SkOpAngle::afterPart {{{220,190}, {220,210}}} id=2

-SkOpAngle::after [2/2] 23/23 tStart=0.333333333 tEnd=0.666666667 < [7/12] 31/31 tStart=0.5 tEnd=1 < [2/1] 7/7 tStart=0.333333333 tEnd=0  T 4

-SkOpAngle::afterPart {{{220,190}, {220,210}}} id=2

-SkOpAngle::afterPart {{{220,190}, {260,190}}} id=7

-SkOpAngle::afterPart {{{220,190}, {220,170}}} id=2

-SkOpSegment::sortAngles [2] tStart=0.666666667 [21]

-SkOpAngle::after [2/3] 7/7 tStart=0.666666667 tEnd=0.333333333 < [5/5] 31/31 tStart=0.333333333 tEnd=0 < [2/4] 23/23 tStart=0.666666667 tEnd=1  F 4

-SkOpAngle::afterPart {{{220,210}, {220,190}}} id=2

-SkOpAngle::afterPart {{{220,210}, {240,210}}} id=5

-SkOpAngle::afterPart {{{220,210}, {220,230}}} id=2

-SkOpAngle::after [2/3] 7/7 tStart=0.666666667 tEnd=0.333333333 < [5/6] 15/15 tStart=0.333333333 tEnd=0.666666667 < [2/4] 23/23 tStart=0.666666667 tEnd=1  T 4

-SkOpAngle::afterPart {{{220,210}, {220,190}}} id=2

-SkOpAngle::afterPart {{{220,210}, {200,210}}} id=5

-SkOpAngle::afterPart {{{220,210}, {220,230}}} id=2

-SkOpSegment::sortAngles [5] tStart=0.333333333 [22]

-SkOpSegment::sortAngles [5] tStart=0.666666667 [25]

-SkOpAngle::after [5/7] 31/31 tStart=0.666666667 tEnd=0.333333333 < [10/13] 23/23 tStart=0.5 tEnd=0 < [5/8] 15/15 tStart=0.666666667 tEnd=1  F 4

-SkOpAngle::afterPart {{{200,210}, {220,210}}} id=5

-SkOpAngle::afterPart {{{200,210}, {200,250}}} id=10

-SkOpAngle::afterPart {{{200,210}, {180,210}}} id=5

-SkOpAngle::after [5/7] 31/31 tStart=0.666666667 tEnd=0.333333333 < [10/14] 7/7 tStart=0.5 tEnd=0.75 < [5/8] 15/15 tStart=0.666666667 tEnd=1  T 4

-SkOpAngle::afterPart {{{200,210}, {220,210}}} id=5

-SkOpAngle::afterPart {{{200,210}, {200,190}}} id=10

-SkOpAngle::afterPart {{{200,210}, {180,210}}} id=5

-SkOpSegment::sortAngles [7] tStart=0.25 [27]

-SkOpAngle::after [7/9] 15/15 tStart=0.25 tEnd=0 < [10/15] 23/23 tStart=0.75 tEnd=0.5 < [7/10] 31/31 tStart=0.25 tEnd=0.5  T 4

-SkOpAngle::afterPart {{{200,190}, {180,190}}} id=7

-SkOpAngle::afterPart {{{200,190}, {200,210}}} id=10

-SkOpAngle::afterPart {{{200,190}, {220,190}}} id=7

-SkOpAngle::after [7/9] 15/15 tStart=0.25 tEnd=0 < [10/16] 7/7 tStart=0.75 tEnd=1 < [10/15] 23/23 tStart=0.75 tEnd=0.5  F 4

-SkOpAngle::afterPart {{{200,190}, {180,190}}} id=7

-SkOpAngle::afterPart {{{200,190}, {200,170}}} id=10

-SkOpAngle::afterPart {{{200,190}, {200,210}}} id=10

-SkOpAngle::after [10/15] 23/23 tStart=0.75 tEnd=0.5 < [10/16] 7/7 tStart=0.75 tEnd=1 < [7/10] 31/31 tStart=0.25 tEnd=0.5  F 4

-SkOpAngle::afterPart {{{200,190}, {200,210}}} id=10

-SkOpAngle::afterPart {{{200,190}, {200,170}}} id=10

-SkOpAngle::afterPart {{{200,190}, {220,190}}} id=7

-SkOpAngle::after [7/10] 31/31 tStart=0.25 tEnd=0.5 < [10/16] 7/7 tStart=0.75 tEnd=1 < [7/9] 15/15 tStart=0.25 tEnd=0  T 4

-SkOpAngle::afterPart {{{200,190}, {220,190}}} id=7

-SkOpAngle::afterPart {{{200,190}, {200,170}}} id=10

-SkOpAngle::afterPart {{{200,190}, {180,190}}} id=7

-SkOpSegment::sortAngles [7] tStart=0.5 [24]

-SkOpSegment::sortAngles [10] tStart=0.5 [26]

-SkOpSegment::sortAngles [10] tStart=0.75 [28]

-SkOpSpan::sortableTop dir=kTop seg=1 t=0.5 pt=(210,170)

-SkOpSpan::sortableTop [0] valid=1 operand=0 span=1 ccw=1 seg=1 {{{200, 170}, {220, 170}}} t=0.5 pt=(210,170) slope=(20,0)

-SkOpSegment::markWinding id=1 (200,170 220,170) t=0 [1] (200,170) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::markWinding id=2 (220,170 220,230) t=0 [3] (220,170) tEnd=0.333333333 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=1 (200,170 220,170) t=0 [1] (200,170) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::markWinding id=10 (200,250 200,170) t=0.75 [28] (200,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=1 (200,170 220,170) t=0 [1] (200,170) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=1 from=(220,170) to=(200,170)

-SkOpSegment::markWinding id=7 (180,190 260,190) t=0 [13] (180,190) tEnd=0.25 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=6 (180,210 180,190) t=0 [11] (180,210) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=5 (240,210 180,210) t=0.666666667 [25] (200,210) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=5 span=25 windSum=-1

-SkOpSegment::markWinding id=10 (200,250 200,170) t=0.5 [26] (200,210) tEnd=0.75 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=10 span=26 windSum=-2

-SkOpSegment::markWinding id=7 (180,190 260,190) t=0.25 [27] (200,190) tEnd=0.5 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=7 span=24 windSum=?

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [10/16] next=7/9 sect=7/7  s=0.75 [28] e=1 [20] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpAngle::dumpOne [7/9] next=10/15 sect=15/15  s=0.25 [27] e=0 [13] sgn=1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [10/15] next=7/10 sect=23/23  s=0.75 [28] e=0.5 [26] sgn=1 windVal=1 windSum=-2

-SkOpAngle::dumpOne [7/10] next=10/16 sect=31/31  s=0.25 [27] e=0.5 [24] sgn=-1 windVal=1 windSum=-2

-SkOpSegment::findNextWinding chase.append segment=5 span=25 windSum=-1

-SkOpSegment::markDone id=10 (200,250 200,170) t=0.5 [26] (200,210) tEnd=0.75 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=10 span=26 windSum=-2

-SkOpSegment::markDone id=7 (180,190 260,190) t=0.25 [27] (200,190) tEnd=0.5 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=7 span=24 windSum=-2147483647

-SkOpSegment::markDone id=10 (200,250 200,170) t=0.75 [28] (200,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[10] to:[7] start=5333632 end=5331472

-bridgeWinding current id=10 from=(200,170) to=(200,190)

-path.moveTo(220,170);

-path.lineTo(200,170);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=7 (180,190 260,190) t=0 [13] (180,190) tEnd=0.25 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=7 from=(200,190) to=(180,190)

-path.lineTo(200,190);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=6 (180,210 180,190) t=0 [11] (180,210) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=6 from=(180,190) to=(180,210)

-path.lineTo(180,190);

-SkOpSegment::markWinding id=10 (200,250 200,170) t=0 [19] (200,250) tEnd=0.5 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=9 (260,250 200,250) t=0 [17] (260,250) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=8 (260,190 260,250) t=0 [15] (260,190) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=7 (180,190 260,190) t=0.5 [24] (220,190) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=7 span=24 windSum=-1

-SkOpSegment::markWinding id=5 (240,210 180,210) t=0.333333333 [22] (220,210) tEnd=0.666666667 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=5 span=22 windSum=-2

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [5/8] next=10/13 sect=15/15  s=0.666666667 [25] e=1 [10] sgn=-1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [10/13] next=5/7 sect=23/23  s=0.5 [26] e=0 [19] sgn=1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [5/7] next=10/14 sect=31/31  s=0.666666667 [25] e=0.333333333 [22] sgn=1 windVal=1 windSum=-2

-SkOpAngle::dumpOne [10/14] next=5/8 sect=7/7  s=0.5 [26] e=0.75 [28] sgn=-1 windVal=1 windSum=-2 done

-SkOpSegment::markDone id=5 (240,210 180,210) t=0.333333333 [22] (220,210) tEnd=0.666666667 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=5 span=22 windSum=-2

-SkOpSegment::markDone id=5 (240,210 180,210) t=0.666666667 [25] (200,210) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[5] to:[10] start=5333488 end=5332456

-bridgeWinding current id=5 from=(180,210) to=(200,210)

-path.lineTo(180,210);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=10 (200,250 200,170) t=0 [19] (200,250) tEnd=0.5 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=10 from=(200,210) to=(200,250)

-path.lineTo(200,210);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=9 (260,250 200,250) t=0 [17] (260,250) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=9 from=(200,250) to=(260,250)

-path.lineTo(200,250);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=8 (260,190 260,250) t=0 [15] (260,190) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=8 from=(260,250) to=(260,190)

-path.lineTo(260,250);

-SkOpSegment::markWinding id=2 (220,170 220,230) t=0.333333333 [23] (220,190) tEnd=0.666666667 newWindSum=-2 windSum=? windValue=1

-SkOpSegment::markAngle last seg=2 span=21 windSum=?

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [7/12] next=2/1 sect=31/31  s=0.5 [24] e=1 [14] sgn=-1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [2/1] next=7/11 sect=7/7  s=0.333333333 [23] e=0 [3] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0

-SkOpAngle::dumpOne [7/11] next=2/2 sect=15/15  s=0.5 [24] e=0.25 [27] sgn=1 windVal=1 windSum=-2 done

-SkOpAngle::dumpOne [2/2] next=7/12 sect=23/23  s=0.333333333 [23] e=0.666666667 [21] sgn=-1 windVal=1 windSum=-2

-SkOpSegment::markDone id=2 (220,170 220,230) t=0.333333333 [23] (220,190) tEnd=0.666666667 newWindSum=-2 newOppSum=? oppSum=? windSum=-2 windValue=1 oppValue=0

-SkOpSegment::findNextWinding chase.append segment=2 span=21 windSum=-2147483647

-SkOpSegment::markDone id=7 (180,190 260,190) t=0.5 [24] (220,190) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[7] to:[2] start=5333056 end=5329832

-bridgeWinding current id=7 from=(260,190) to=(220,190)

-path.lineTo(260,190);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=2 (220,170 220,230) t=0 [3] (220,170) tEnd=0.333333333 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=2 from=(220,190) to=(220,170)

-path.lineTo(220,190);

-path.lineTo(220,170);

-path.close();

-SkOpSegment::markWinding id=2 (220,170 220,230) t=0.666666667 [21] (220,210) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=3 (220,230 240,230) t=0 [5] (220,230) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=4 (240,230 240,210) t=0 [7] (240,230) tEnd=1 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markWinding id=5 (240,210 180,210) t=0 [9] (240,210) tEnd=0.333333333 newWindSum=-1 windSum=? windValue=1

-SkOpSegment::markAngle last seg=5 span=22 windSum=-2

-SkOpSegment::markWinding id=5 (240,210 180,210) t=0 [9] (240,210) tEnd=0.333333333 newWindSum=-1 windSum=-1 windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (220,210 220,230) t=0.666666667 tEnd=1 windSum=-1 windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (220,230 240,230) t=0 tEnd=1 windSum=-1 windValue=1

-SkOpSegment::debugShowActiveSpans id=4 (240,230 240,210) t=0 tEnd=1 windSum=-1 windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (240,210 220,210) t=0 tEnd=0.333333333 windSum=-1 windValue=1

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=2 (220,170 220,230) t=0.666666667 [21] (220,210) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=2 from=(220,210) to=(220,230)

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=3 (220,230 240,230) t=0 [5] (220,230) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=3 from=(220,230) to=(240,230)

-path.moveTo(220,210);

-path.lineTo(220,230);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=4 (240,230 240,210) t=0 [7] (240,230) tEnd=1 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=4 from=(240,230) to=(240,210)

-path.lineTo(240,230);

-SkOpSegment::findNextWinding

-SkOpAngle::dumpOne [5/5] next=2/3 sect=31/31  s=0.333333333 [22] e=0 [9] sgn=1 windVal=1 windSum=-1

-SkOpAngle::dumpOne [2/3] next=5/6 sect=7/7  s=0.666666667 [21] e=0.333333333 [23] sgn=1 windVal=1 windSum=-2 done

-SkOpAngle::dumpOne [5/6] next=2/4 sect=15/15  s=0.333333333 [22] e=0.666666667 [25] sgn=-1 windVal=1 windSum=-2 done

-SkOpAngle::dumpOne [2/4] next=5/5 sect=23/23  s=0.666666667 [21] e=1 [4] sgn=-1 windVal=1 windSum=-1 done

-SkOpSegment::markDone id=5 (240,210 180,210) t=0 [9] (240,210) tEnd=0.333333333 newWindSum=-1 newOppSum=? oppSum=? windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding from:[5] to:[2] start=5332768 end=5329976

-bridgeWinding current id=5 from=(240,210) to=(220,210)

-path.lineTo(240,210);

-path.lineTo(220,210);

-path.close();

-</div>

-

-<div  id="issue3838_a">

-SkOpSpan::sortableTop dir=kTop seg=1 t=0.5 pt=(210,170)

-SkOpSpan::sortableTop [0] valid=1 operand=0 span=1 ccw=0 seg=1 {{{220, 170}, {200, 170}}} t=0.5 pt=(210,170) slope=(-20,0)

-SkOpBuilder::FixWinding id=1 nested=1 ccw=0

-SkOpSegment::markDone id=1 (220,170 200,170) t=0 [1] (220,170) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0

-SkOpSegment::markDone id=2 (200,170 200,190) t=0 [3] (200,170) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=3 (200,190 180,190) t=0 [5] (200,190) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=4 (180,190 180,210) t=0 [7] (180,190) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=5 (180,210 200,210) t=0 [9] (180,210) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=6 (200,210 200,250) t=0 [11] (200,210) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=7 (200,250 260,250) t=0 [13] (200,250) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=8 (260,250 260,190) t=0 [15] (260,250) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=9 (260,190 220,190) t=0 [17] (260,190) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=10 (220,190 220,170) t=0 [19] (220,190) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSpan::sortableTop dir=kLeft seg=11 t=0.5 pt=(220,220)

-SkOpSpan::sortableTop [0] valid=1 operand=0 span=11 ccw=0 seg=6 {{{200, 210}, {200, 250}}} t=0.25 pt=(200,220) slope=(0,40)

-SkOpSpan::sortableTop [1] valid=1 operand=0 span=21 ccw=0 seg=11 {{{220, 210}, {220, 230}}} t=0.5 pt=(220,220) slope=(0,20)

-SkOpBuilder::FixWinding id=11 nested=2 ccw=0

-SkOpSegment::markDone id=11 (220,210 220,230) t=0 [21] (220,210) tEnd=1 newWindSum=2 newOppSum=0 oppSum=0 windSum=2 windValue=1 oppValue=0

-SkOpSegment::markDone id=12 (220,230 240,230) t=0 [23] (220,230) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=13 (240,230 240,210) t=0 [25] (240,230) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markDone id=14 (240,210 220,210) t=0 [27] (240,210) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=1 oppValue=0

-path.moveTo(220,170);

-path.lineTo(220,190);

-path.lineTo(260,190);

-path.lineTo(260,250);

-path.lineTo(200,250);

-path.lineTo(200,210);

-path.lineTo(180,210);

-path.lineTo(180,190);

-path.lineTo(200,190);

-path.lineTo(200,170);

-path.lineTo(220,170);

-path.close();

-</div>

-

-<div  id="issue3838_b">

-seg=1 {{{220, 170}, {220, 190}}}

-seg=2 {{{220, 190}, {260, 190}}}

-seg=3 {{{260, 190}, {260, 250}}}

-seg=4 {{{260, 250}, {200, 250}}}

-seg=5 {{{200, 250}, {200, 210}}}

-seg=6 {{{200, 210}, {180, 210}}}

-seg=7 {{{180, 210}, {180, 190}}}

-seg=8 {{{180, 190}, {200, 190}}}

-seg=9 {{{200, 190}, {200, 170}}}

-seg=10 {{{200, 170}, {220, 170}}}

-debugShowLineIntersection wtTs[0]=0 {{{220,190}, {260,190}}} {{220,190}} wnTs[0]=1 {{{220,170}, {220,190}}}

-debugShowLineIntersection wtTs[0]=1 {{{200,170}, {220,170}}} {{220,170}} wnTs[0]=0 {{{220,170}, {220,190}}}

-debugShowLineIntersection wtTs[0]=0 {{{260,190}, {260,250}}} {{260,190}} wnTs[0]=1 {{{220,190}, {260,190}}}

-debugShowLineIntersection wtTs[0]=0 {{{260,250}, {200,250}}} {{260,250}} wnTs[0]=1 {{{260,190}, {260,250}}}

-debugShowLineIntersection wtTs[0]=0 {{{200,250}, {200,210}}} {{200,250}} wnTs[0]=1 {{{260,250}, {200,250}}}

-debugShowLineIntersection wtTs[0]=0 {{{200,210}, {180,210}}} {{200,210}} wnTs[0]=1 {{{200,250}, {200,210}}}

-debugShowLineIntersection wtTs[0]=0 {{{180,210}, {180,190}}} {{180,210}} wnTs[0]=1 {{{200,210}, {180,210}}}

-debugShowLineIntersection wtTs[0]=0 {{{180,190}, {200,190}}} {{180,190}} wnTs[0]=1 {{{180,210}, {180,190}}}

-debugShowLineIntersection wtTs[0]=0 {{{200,190}, {200,170}}} {{200,190}} wnTs[0]=1 {{{180,190}, {200,190}}}

-debugShowLineIntersection wtTs[0]=0 {{{200,170}, {220,170}}} {{200,170}} wnTs[0]=1 {{{200,190}, {200,170}}}

--------------------------------------- addExpanded

-SkOpSegment::debugShowActiveSpans id=1 (220,170 220,190) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=2 (220,190 260,190) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=3 (260,190 260,250) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=4 (260,250 200,250) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=5 (200,250 200,210) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=6 (200,210 180,210) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=7 (180,210 180,190) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=8 (180,190 200,190) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=9 (200,190 200,170) t=0 tEnd=1 windSum=? windValue=1

-SkOpSegment::debugShowActiveSpans id=10 (200,170 220,170) t=0 tEnd=1 windSum=? windValue=1

--------------------------------------- move_multiples

--------------------------------------- move_nearby

--------------------------------------- correctEnds

--------------------------------------- addEndMovedSpans

--------------------------------------- expand

--------------------------------------- addExpanded

--------------------------------------- mark

--------------------------------------- missing_coincidence

--------------------------------------- expand

--------------------------------------- expand

--------------------------------------- apply

--------------------------------------- findOverlaps

--------------------------------------- calc_angles

-SkOpSpan::sortableTop dir=kLeft seg=1 t=0.5 pt=(220,180)

-SkOpSpan::sortableTop [0] valid=1 operand=0 span=17 ccw=1 seg=9 {{{200, 190}, {200, 170}}} t=0.5 pt=(200,180) slope=(0,-20)

-SkOpSpan::sortableTop [1] valid=1 operand=0 span=1 ccw=0 seg=1 {{{220, 170}, {220, 190}}} t=0.5 pt=(220,180) slope=(0,20)

-SkOpSegment::markWinding id=9 (200,190 200,170) t=0 [17] (200,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::markWinding id=10 (200,170 220,170) t=0 [19] (200,170) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=1 (220,170 220,190) t=0 [1] (220,170) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=2 (220,190 260,190) t=0 [3] (220,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=3 (260,190 260,250) t=0 [5] (260,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=4 (260,250 200,250) t=0 [7] (260,250) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=5 (200,250 200,210) t=0 [9] (200,250) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=6 (200,210 180,210) t=0 [11] (200,210) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=7 (180,210 180,190) t=0 [13] (180,210) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=8 (180,190 200,190) t=0 [15] (180,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0

-SkOpSegment::markWinding id=9 (200,190 200,170) t=0 [17] (200,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=1 (220,170 220,190) t=0 [1] (220,170) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=1 from=(220,190) to=(220,170)

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=10 (200,170 220,170) t=0 [19] (200,170) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=10 from=(220,170) to=(200,170)

-path.moveTo(220,190);

-path.lineTo(220,170);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=9 (200,190 200,170) t=0 [17] (200,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=9 from=(200,170) to=(200,190)

-path.lineTo(200,170);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=8 (180,190 200,190) t=0 [15] (180,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=8 from=(200,190) to=(180,190)

-path.lineTo(200,190);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=7 (180,210 180,190) t=0 [13] (180,210) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=7 from=(180,190) to=(180,210)

-path.lineTo(180,190);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=6 (200,210 180,210) t=0 [11] (200,210) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=6 from=(180,210) to=(200,210)

-path.lineTo(180,210);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=5 (200,250 200,210) t=0 [9] (200,250) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=5 from=(200,210) to=(200,250)

-path.lineTo(200,210);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=4 (260,250 200,250) t=0 [7] (260,250) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=4 from=(200,250) to=(260,250)

-path.lineTo(200,250);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=3 (260,190 260,250) t=0 [5] (260,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=3 from=(260,250) to=(260,190)

-path.lineTo(260,250);

-SkOpSegment::findNextWinding simple

-SkOpSegment::markDone id=2 (220,190 260,190) t=0 [3] (220,190) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0

-bridgeWinding current id=2 from=(260,190) to=(220,190)

-path.lineTo(260,190);

-path.lineTo(220,190);

-path.close();

-</div>

-
 </div>
 
 <script type="text/javascript">
 
     var testDivs = [
-        issue3838,
-        issue3838_a,
-        issue3838_b,
-        joel_11,
-        joel_10,
         joel_9,
     ];
 
@@ -1951,6 +836,7 @@
                 : line.lastIndexOf("debugShowCoincidence", 0) === 0 ? REC_TYPE_COINCIDENCE
                 : line.lastIndexOf("((SkOpSegment*)", 0) === 0 ? REC_TYPE_PATH2
                 : line.lastIndexOf("debugShowTs", 0) === 0 ? REC_TYPE_COIN
+                : line.lastIndexOf("debugAfter", 0) === 0 ? REC_TYPE_ANGLE
                 : line.lastIndexOf("afterPart", 0) === 0 ? REC_TYPE_AFTERPART
                 : line.lastIndexOf("debugShow", 0) === 0 ? REC_TYPE_SECT
                 : line.lastIndexOf("activeOp", 0) === 0 ? REC_TYPE_ACTIVE_OP
@@ -1961,7 +847,6 @@
                 : line.lastIndexOf("findTop", 0) === 0 ? REC_TYPE_TOP
                 : line.lastIndexOf("pathB.", 0) === 0 ? REC_TYPE_ADD
                 : line.lastIndexOf("path.", 0) === 0 ? REC_TYPE_ADD
-                : line.lastIndexOf("after", 0) === 0 ? REC_TYPE_ANGLE
                 : line.lastIndexOf("mark", 0) === 0 ? REC_TYPE_MARK
                 : line.lastIndexOf("  {{", 0) === 0 ? REC_TYPE_COMPUTED
                 : line.lastIndexOf("seg=", 0) === 0 ? REC_TYPE_PATH
@@ -2054,7 +939,7 @@
                 );
                 break;
             case REC_TYPE_ANGLE:
-                found = match_regexp(line, lineNo, record, ANGLE_AFTER, "after " +
+                found = match_regexp(line, lineNo, record, ANGLE_AFTER, "debugAfter " +
 "[IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL  T_F IDX");
                 break;
             case REC_TYPE_COIN:
@@ -3600,9 +2485,9 @@
     return dxy;
 }
 
-function dpt_at_t(curve, t) {

+function dpt_at_t(curve, t) {
     var type = PATH_LINE + (curve.length / 2 - 2);
-    return dxy_at_t(curve, type, t);

+    return dxy_at_t(curve, type, t);
 }
 
 function drawLabel(num, px, py) {
@@ -3720,11 +2605,11 @@
     return a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
 }
 
-function pt_at_t(curve, t) {

-    var pt = {};

-    pt.x = x_at_t(curve, t);

-    pt.y = y_at_t(curve, t);

-    return pt;

+function pt_at_t(curve, t) {
+    var pt = {};
+    pt.x = x_at_t(curve, t);
+    pt.y = y_at_t(curve, t);
+    return pt;
 }
 
 function drawOrder(curve, t, label) {
@@ -3751,59 +2636,59 @@
     ctx.font = "normal 10px Arial";
 }
 
-function drawVisibleOrder(curve, label) {

-    var s = pt_at_t(curve, 0);

-    var e = pt_at_t(curve, 1);

-    var sOn = ptOnScreen(s);

-    var eOn = ptOnScreen(e);

-    var defaultT = 0.85;

-    if (sOn && eOn)

-        return drawOrder(curve, defaultT, label);

-    if (sOn || eOn) {

-        if (eOn) {

-            defaultT = 1 - defaultT;

-        }

-        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;

-        var t = defaultT;

-        var tries = 16;

-        do {

-            var mid = pt_at_t(curve, t);

-            if (ptOnScreen(mid))

-                return drawOrder(curve, t, label);

-            t += step;

-            step /= 2;

-        } while (--tries > 0);

-        drawOrder(curve, defaultT, label);

-    }

-    // scattershot until we find a visible point

-    var denom = 2;  // visit odd number num / denom to hit unique pts

-    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...

-    do {

-        for (var numer = 1; numer < denom; numer += 2) {

-            var t = numer / denom + 0.1;

-            if (t >= 1) {

-                break;

-            }

-            var mid = pt_at_t(curve, t);

-            if (ptOnScreen(mid))

-                return drawOrder(curve, t, label);

-        }

-        denom *= 2;

-    } while (--tries > 0);

-    drawOrder(curve, defaultT, label);

+function drawVisibleOrder(curve, label) {
+    var s = pt_at_t(curve, 0);
+    var e = pt_at_t(curve, 1);
+    var sOn = ptOnScreen(s);
+    var eOn = ptOnScreen(e);
+    var defaultT = 0.85;
+    if (sOn && eOn)
+        return drawOrder(curve, defaultT, label);
+    if (sOn || eOn) {
+        if (eOn) {
+            defaultT = 1 - defaultT;
+        }
+        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;
+        var t = defaultT;
+        var tries = 16;
+        do {
+            var mid = pt_at_t(curve, t);
+            if (ptOnScreen(mid))
+                return drawOrder(curve, t, label);
+            t += step;
+            step /= 2;
+        } while (--tries > 0);
+        drawOrder(curve, defaultT, label);
+    }
+    // scattershot until we find a visible point
+    var denom = 2;  // visit odd number num / denom to hit unique pts
+    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...
+    do {
+        for (var numer = 1; numer < denom; numer += 2) {
+            var t = numer / denom + 0.1;
+            if (t >= 1) {
+                break;
+            }
+            var mid = pt_at_t(curve, t);
+            if (ptOnScreen(mid))
+                return drawOrder(curve, t, label);
+        }
+        denom *= 2;
+    } while (--tries > 0);
+    drawOrder(curve, defaultT, label);
 }
 
-function set_length(pt, newLen) {

-    var len = Math.sqrt(pt.x * pt.x + pt.y * pt.y);

-    var scale = newLen / len;

-    var newPt = { x: pt.x * scale, y: pt.y * scale };

-    return newPt;

+function set_length(pt, newLen) {
+    var len = Math.sqrt(pt.x * pt.x + pt.y * pt.y);
+    var scale = newLen / len;
+    var newPt = { x: pt.x * scale, y: pt.y * scale };
+    return newPt;
 }
 
-function drawDirection(curve, t) {

+function drawDirection(curve, t) {
     var d = dpt_at_t(curve, t);
     d = set_length(d, 16);
-    var pt = localToGlobal(pt_at_t(curve, t));

+    var pt = localToGlobal(pt_at_t(curve, t));
     ctx.beginPath();
     ctx.moveTo(pt.x - d.y, pt.y + d.x);
     ctx.lineTo(pt.x + d.x, pt.y + d.y);
@@ -3812,47 +2697,47 @@
     ctx.stroke();
 }
 
-function drawVisibleDirection(curve) {

-    var s = pt_at_t(curve, 0);

-    var e = pt_at_t(curve, 1);

-    var sOn = ptOnScreen(s);

-    var eOn = ptOnScreen(e);

-    var defaultT = 0.65;

-    if (sOn && eOn) {

-        return drawDirection(curve, defaultT);

-    }

-    if (sOn || eOn) {

-        if (eOn) {

-            defaultT = 1 - defaultT;

-        }

-        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;

-        var t = defaultT;

-        var tries = 16;

-        do {

-            var mid = pt_at_t(curve, t);

-            if (ptOnScreen(mid))

-                return drawDirection(curve, t);

-            t += step;

-            step /= 2;

-        } while (--tries > 0);

-        drawDirection(curve, defaultT);

-    }

-    // scattershot until we find a visible point

-    var denom = 2;  // visit odd number num / denom to hit unique pts

-    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...

-    do {

-        for (var numer = 1; numer < denom; numer += 2) {

-            var t = numer / denom + 0.1;

-            if (t >= 1) {

-                break;

-            }

-            var mid = pt_at_t(curve, t);

-            if (ptOnScreen(mid))

-                return drawDirection(curve, t);

-        }

-        denom *= 2;

-    } while (--tries > 0);

-    drawDirection(curve, defaultT);

+function drawVisibleDirection(curve) {
+    var s = pt_at_t(curve, 0);
+    var e = pt_at_t(curve, 1);
+    var sOn = ptOnScreen(s);
+    var eOn = ptOnScreen(e);
+    var defaultT = 0.65;
+    if (sOn && eOn) {
+        return drawDirection(curve, defaultT);
+    }
+    if (sOn || eOn) {
+        if (eOn) {
+            defaultT = 1 - defaultT;
+        }
+        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;
+        var t = defaultT;
+        var tries = 16;
+        do {
+            var mid = pt_at_t(curve, t);
+            if (ptOnScreen(mid))
+                return drawDirection(curve, t);
+            t += step;
+            step /= 2;
+        } while (--tries > 0);
+        drawDirection(curve, defaultT);
+    }
+    // scattershot until we find a visible point
+    var denom = 2;  // visit odd number num / denom to hit unique pts
+    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...
+    do {
+        for (var numer = 1; numer < denom; numer += 2) {
+            var t = numer / denom + 0.1;
+            if (t >= 1) {
+                break;
+            }
+            var mid = pt_at_t(curve, t);
+            if (ptOnScreen(mid))
+                return drawDirection(curve, t);
+        }
+        denom *= 2;
+    } while (--tries > 0);
+    drawDirection(curve, defaultT);
 }
 
 function drawID(curve, t, id) {
@@ -3863,53 +2748,53 @@
     draw_id_at(id, _px, _py);
 }
 
-function localToGlobal(local) {

-    var global = {};

-    global.x = (local.x - srcLeft) * scale;

-    global.y = (local.y - srcTop) * scale;

-    return global;

+function localToGlobal(local) {
+    var global = {};
+    global.x = (local.x - srcLeft) * scale;
+    global.y = (local.y - srcTop) * scale;
+    return global;
 }
 
-function ptOnScreen(local) {

-    var pt = localToGlobal(local);

-    return 10 <= pt.x && pt.x <= screenWidth - 10

-        && 10 <= pt.y && pt.y <= screenHeight - 10;

+function ptOnScreen(local) {
+    var pt = localToGlobal(local);
+    return 10 <= pt.x && pt.x <= screenWidth - 10
+        && 10 <= pt.y && pt.y <= screenHeight - 10;
 }
 
-function drawVisibleID(curve, defaultT, id) {

-    // determine if either or both ends are visible

-    var s = pt_at_t(curve, 0);

-    var e = pt_at_t(curve, 1);

-    var sOn = ptOnScreen(s);

-    var eOn = ptOnScreen(e);

-    if (sOn && eOn)

-        return drawID(curve, defaultT, id);

-    if (sOn || eOn) {

-        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;

-        var t = defaultT;

-        var tries = 16;

-        do {

-            var mid = pt_at_t(curve, t);

-            if (ptOnScreen(mid))

-                return drawID(curve, t, id);

-            t += step;

-            step /= 2;

-        } while (--tries > 0);

-        drawID(curve, defaultT, id);

-    }

-    // scattershot until we find a visible point

-    var denom = 2;  // visit odd number num / denom to hit unique pts

-    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...

-    do {

-        for (var numer = 1; numer < denom; numer += 2) {

-            var t = numer / denom;

-            var mid = pt_at_t(curve, t);

-            if (ptOnScreen(mid))

-                return drawID(curve, t, id);

-        }

-        denom *= 2;

-    } while (--tries > 0);

-    drawID(curve, defaultT, id);

+function drawVisibleID(curve, defaultT, id) {
+    // determine if either or both ends are visible
+    var s = pt_at_t(curve, 0);
+    var e = pt_at_t(curve, 1);
+    var sOn = ptOnScreen(s);
+    var eOn = ptOnScreen(e);
+    if (sOn && eOn)
+        return drawID(curve, defaultT, id);
+    if (sOn || eOn) {
+        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;
+        var t = defaultT;
+        var tries = 16;
+        do {
+            var mid = pt_at_t(curve, t);
+            if (ptOnScreen(mid))
+                return drawID(curve, t, id);
+            t += step;
+            step /= 2;
+        } while (--tries > 0);
+        drawID(curve, defaultT, id);
+    }
+    // scattershot until we find a visible point
+    var denom = 2;  // visit odd number num / denom to hit unique pts
+    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...
+    do {
+        for (var numer = 1; numer < denom; numer += 2) {
+            var t = numer / denom;
+            var mid = pt_at_t(curve, t);
+            if (ptOnScreen(mid))
+                return drawID(curve, t, id);
+        }
+        denom *= 2;
+    } while (--tries > 0);
+    drawID(curve, defaultT, id);
 }
 
 function draw_id_at(id, _px, _py) {
@@ -3987,8 +2872,8 @@
             drawVisibleID(curve, 0.5, id);
         }
     }
-    if (draw_direction) {

-        drawVisibleDirection(curve);

+    if (draw_direction) {
+        drawVisibleDirection(curve);
     }
     if (type == PATH_LINE) {
         return;
@@ -4579,10 +3464,10 @@
                         drawVisibleOrder(leftCurve, 'L');
                         drawVisibleOrder(rightCurve, 'R');
                     }
-                    if (draw_id) {

-                        drawVisibleID(leftCurve, 0.5, frags[0]);

-                        drawVisibleID(midCurve, 0.5, frags[6]);

-                        drawVisibleID(rightCurve, 0.5, frags[12]);

+                    if (draw_id) {
+                        drawVisibleID(leftCurve, 0.5, frags[0]);
+                        drawVisibleID(midCurve, 0.5, frags[6]);
+                        drawVisibleID(rightCurve, 0.5, frags[12]);
                     }
                     break;
                 case REC_TYPE_AFTERPART:
@@ -4619,8 +3504,8 @@
                             throw "stop execution";
                     }
                     drawCurve(curve);
-                    if (draw_id) {

-                        drawVisibleID(curve, 0.5, id);

+                    if (draw_id) {
+                        drawVisibleID(curve, 0.5, id);
                     }
                     ++afterIndex;
                     break;
diff --git a/whitespace.txt b/whitespace.txt
index e99bdff..28bd227 100644
--- a/whitespace.txt
+++ b/whitespace.txt
@@ -8,4 +8,4 @@
 florin was here
 more  whitespace
 all the things!!!
-Driver update.
+Driver update again.