Roll external/skia e94871bf8..2e86634ae (5 commits)

https://skia.googlesource.com/skia.git/+log/e94871bf8..2e86634ae

2017-08-28 bsalomon@google.com Move transformation of clip elements to SkClipStack::Element
2017-08-28 csmartdalton@google.com Add a GrCCPRGeometry file
2017-08-28 angle-deps-roller@chromium.org Roll skia/third_party/externals/angle2/ 7e1197e01..3ef140a97 (2 commits)
2017-08-28 reed@google.com add 'a8' config for nanobench, specialize blitV for raster-pipeline
2017-08-28 halcanary@skia.org SkPDF: SkWStream::write -> SkWStream::writeText

Test: Presubmit checks will test this change.
Change-Id: Id7b665e549bf34f5d9ab410a946e7feec18634ed
diff --git a/Android.bp b/Android.bp
index c77b1a2..e1fa264 100644
--- a/Android.bp
+++ b/Android.bp
@@ -422,6 +422,7 @@
         "src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp",
         "src/gpu/ccpr/GrCCPRCoverageProcessor.cpp",
         "src/gpu/ccpr/GrCCPRCubicProcessor.cpp",
+        "src/gpu/ccpr/GrCCPRGeometry.cpp",
         "src/gpu/ccpr/GrCCPRPathProcessor.cpp",
         "src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp",
         "src/gpu/ccpr/GrCCPRTriangleProcessor.cpp",
diff --git a/DEPS b/DEPS
index f1d50a4..2016208 100644
--- a/DEPS
+++ b/DEPS
@@ -3,7 +3,7 @@
 deps = {
   "buildtools"                          : "https://chromium.googlesource.com/chromium/buildtools.git@e6b510a9daf822bbe9f922c200c58150803d2fd8",
   "common"                              : "https://skia.googlesource.com/common.git@9737551d7a52c3db3262db5856e6bcd62c462b92",
-  "third_party/externals/angle2"        : "https://chromium.googlesource.com/angle/angle.git@7e1197e01aed3a23d47a29658d7f1a114e39d4f0",
+  "third_party/externals/angle2"        : "https://chromium.googlesource.com/angle/angle.git@3ef140a9741e6f8410c7e3f464963f05730d17f3",
   "third_party/externals/dng_sdk"       : "https://android.googlesource.com/platform/external/dng_sdk.git@96443b262250c390b0caefbf3eed8463ba35ecae",
   "third_party/externals/expat"         : "https://android.googlesource.com/platform/external/expat.git@android-6.0.1_r55",
   "third_party/externals/freetype"      : "https://skia.googlesource.com/third_party/freetype2.git@447a0b62634802d8acdb56008cff5ff4e50be244",
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index ab7c689..95977c5 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -475,6 +475,8 @@
     CPU_CONFIG(nonrendering, kNonRendering_Backend,
                kUnknown_SkColorType, kUnpremul_SkAlphaType, nullptr)
 
+    CPU_CONFIG(a8, kRaster_Backend,
+               kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr)
     CPU_CONFIG(8888, kRaster_Backend,
                kN32_SkColorType, kPremul_SkAlphaType, nullptr)
     CPU_CONFIG(565,  kRaster_Backend,
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 51ac125..3853b4e 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -295,6 +295,8 @@
   "$_src/gpu/ccpr/GrCCPRCoverageProcessor.h",
   "$_src/gpu/ccpr/GrCCPRCubicProcessor.cpp",
   "$_src/gpu/ccpr/GrCCPRCubicProcessor.h",
+  "$_src/gpu/ccpr/GrCCPRGeometry.cpp",
+  "$_src/gpu/ccpr/GrCCPRGeometry.h",
   "$_src/gpu/ccpr/GrCCPRPathProcessor.cpp",
   "$_src/gpu/ccpr/GrCCPRPathProcessor.h",
   "$_src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp",
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index 9749e45..b4bf2bd 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -10,7 +10,6 @@
 #if SK_SUPPORT_GPU
 
 #include "GrContextPriv.h"
-#include "GrPathUtils.h"
 #include "GrRenderTargetContext.h"
 #include "GrRenderTargetContextPriv.h"
 #include "GrResourceProvider.h"
@@ -22,6 +21,7 @@
 #include "SkPath.h"
 #include "SkView.h"
 #include "ccpr/GrCCPRCoverageProcessor.h"
+#include "ccpr/GrCCPRGeometry.h"
 #include "gl/GrGLGpu.cpp"
 #include "ops/GrDrawOp.h"
 
@@ -202,10 +202,9 @@
             fGpuInstances.push_back().fCubicData = {controlPointsIdx + i * 4, i};
         }
     } else if (is_curve(fMode)) {
-        SkPoint P[3] = {fPoints[0], fPoints[1], fPoints[3]};
         SkPoint chopped[5];
-        fGpuPoints.push_back(P[0]);
-        if (GrPathUtils::chopMonotonicQuads(P, chopped)) {
+        fGpuPoints.push_back(fPoints[0]);
+        if (GrCCPRChopMonotonicQuadratics(fPoints[0], fPoints[1], fPoints[3], chopped)) {
             // Endpoints.
             fGpuPoints.push_back(chopped[2]);
             fGpuPoints.push_back(chopped[4]);
@@ -215,8 +214,8 @@
             fGpuInstances.push_back().fQuadraticData = {3, 0};
             fGpuInstances.push_back().fQuadraticData = {4, 1};
         } else {
-            fGpuPoints.push_back(P[2]);
-            fGpuPoints.push_back(P[1]);
+            fGpuPoints.push_back(fPoints[3]);
+            fGpuPoints.push_back(fPoints[1]);
             fGpuInstances.push_back().fQuadraticData = {2, 0};
         }
     } else {
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 783f016..2f0a493 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -154,40 +154,62 @@
     fGenID = kInvalidGenID;
 }
 
-void SkClipStack::Element::initRect(int saveCount, const SkRect& rect, SkClipOp op, bool doAA) {
-    fRRect.setRect(rect);
-    fType = kRect_Type;
-    this->initCommon(saveCount, op, doAA);
-}
-
-void SkClipStack::Element::initRRect(int saveCount, const SkRRect& rrect, SkClipOp op, bool doAA) {
-    SkRRect::Type type = rrect.getType();
-    fRRect = rrect;
-    if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
+void SkClipStack::Element::initRect(int saveCount, const SkRect& rect, const SkMatrix& m,
+                                    SkClipOp op, bool doAA) {
+    if (m.rectStaysRect()) {
+        SkRect devRect;
+        m.mapRect(&devRect, rect);
+        fRRect.setRect(devRect);
         fType = kRect_Type;
-    } else {
-        fType = kRRect_Type;
+        this->initCommon(saveCount, op, doAA);
+        return;
     }
-    this->initCommon(saveCount, op, doAA);
+    SkPath path;
+    path.addRect(rect);
+    path.setIsVolatile(true);
+    this->initAsPath(saveCount, path, m, op, doAA);
 }
 
-void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkClipOp op,
-                                    bool doAA) {
+void SkClipStack::Element::initRRect(int saveCount, const SkRRect& rrect, const SkMatrix& m,
+                                     SkClipOp op, bool doAA) {
+    if (rrect.transform(m, &fRRect)) {
+        SkRRect::Type type = fRRect.getType();
+        if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
+            fType = kRect_Type;
+        } else {
+            fType = kRRect_Type;
+        }
+        this->initCommon(saveCount, op, doAA);
+        return;
+    }
+    SkPath path;
+    path.addRRect(rrect);
+    path.setIsVolatile(true);
+    this->initAsPath(saveCount, path, m, op, doAA);
+}
+
+void SkClipStack::Element::initPath(int saveCount, const SkPath& path, const SkMatrix& m,
+                                    SkClipOp op, bool doAA) {
     if (!path.isInverseFillType()) {
         SkRect r;
         if (path.isRect(&r)) {
-            this->initRect(saveCount, r, op, doAA);
+            this->initRect(saveCount, r, m, op, doAA);
             return;
         }
         SkRect ovalRect;
         if (path.isOval(&ovalRect)) {
             SkRRect rrect;
             rrect.setOval(ovalRect);
-            this->initRRect(saveCount, rrect, op, doAA);
+            this->initRRect(saveCount, rrect, m, op, doAA);
             return;
         }
     }
-    fPath.set(path);
+    this->initAsPath(saveCount, path, m, op, doAA);
+}
+
+void SkClipStack::Element::initAsPath(int saveCount, const SkPath& path, const SkMatrix& m,
+                                      SkClipOp op, bool doAA) {
+    path.transform(m, fPath.init());
     fPath.get()->setIsVolatile(true);
     fType = kPath_Type;
     this->initCommon(saveCount, op, doAA);
@@ -808,51 +830,34 @@
 
 void SkClipStack::clipRRect(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op,
                             bool doAA) {
-    SkRRect transformedRRect;
-    if (rrect.transform(matrix, &transformedRRect)) {
-        Element element(fSaveCount, transformedRRect, op, doAA);
-        this->pushElement(element);
-        if (this->hasClipRestriction(op)) {
-            Element element(fSaveCount, fClipRestrictionRect, kIntersect_SkClipOp, false);
-            this->pushElement(element);
-        }
-        return;
+    Element element(fSaveCount, rrect, matrix, op, doAA);
+    this->pushElement(element);
+    if (this->hasClipRestriction(op)) {
+        Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), kIntersect_SkClipOp,
+                            false);
+        this->pushElement(restriction);
     }
-    SkPath path;
-    path.addRRect(rrect);
-    path.setIsVolatile(true);
-    this->clipPath(path, matrix, op, doAA);
 }
 
 void SkClipStack::clipRect(const SkRect& rect, const SkMatrix& matrix, SkClipOp op,
                            bool doAA) {
-    if (matrix.rectStaysRect()) {
-        SkRect devRect;
-        matrix.mapRect(&devRect, rect);
-        if (this->hasClipRestriction(op)) {
-            if (!devRect.intersect(fClipRestrictionRect)) {
-                devRect.setEmpty();
-            }
-        }
-        Element element(fSaveCount, devRect, op, doAA);
-        this->pushElement(element);
-        return;
+    Element element(fSaveCount, rect, matrix, op, doAA);
+    this->pushElement(element);
+    if (this->hasClipRestriction(op)) {
+        Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), kIntersect_SkClipOp,
+                            false);
+        this->pushElement(restriction);
     }
-    SkPath path;
-    path.addRect(rect);
-    path.setIsVolatile(true);
-    this->clipPath(path, matrix, op, doAA);
 }
 
 void SkClipStack::clipPath(const SkPath& path, const SkMatrix& matrix, SkClipOp op,
                            bool doAA) {
-    SkPath devPath;
-    path.transform(matrix, &devPath);
-    Element element(fSaveCount, devPath, op, doAA);
+    Element element(fSaveCount, path, matrix, op, doAA);
     this->pushElement(element);
     if (this->hasClipRestriction(op)) {
-        Element element(fSaveCount, fClipRestrictionRect, kIntersect_SkClipOp, false);
-        this->pushElement(element);
+        Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), kIntersect_SkClipOp,
+                            false);
+        this->pushElement(restriction);
     }
 }
 
diff --git a/src/core/SkClipStack.h b/src/core/SkClipStack.h
index b09c7f4..466df6c 100644
--- a/src/core/SkClipStack.h
+++ b/src/core/SkClipStack.h
@@ -64,13 +64,17 @@
 
         Element(const Element&);
 
-        Element(const SkRect& rect, SkClipOp op, bool doAA) { this->initRect(0, rect, op, doAA); }
-
-        Element(const SkRRect& rrect, SkClipOp op, bool doAA) {
-            this->initRRect(0, rrect, op, doAA);
+        Element(const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
+            this->initRect(0, rect, m, op, doAA);
         }
 
-        Element(const SkPath& path, SkClipOp op, bool doAA) { this->initPath(0, path, op, doAA); }
+        Element(const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
+            this->initRRect(0, rrect, m, op, doAA);
+        }
+
+        Element(const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
+            this->initPath(0, path, m, op, doAA);
+        }
 
         ~Element() {
 #if SK_SUPPORT_GPU
@@ -202,22 +206,23 @@
             this->setEmpty();
         }
 
-        Element(int saveCount, const SkRRect& rrect, SkClipOp op, bool doAA) {
-            this->initRRect(saveCount, rrect, op, doAA);
+        Element(int saveCount, const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
+            this->initRRect(saveCount, rrect, m, op, doAA);
         }
 
-        Element(int saveCount, const SkRect& rect, SkClipOp op, bool doAA) {
-            this->initRect(saveCount, rect, op, doAA);
+        Element(int saveCount, const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
+            this->initRect(saveCount, rect, m, op, doAA);
         }
 
-        Element(int saveCount, const SkPath& path, SkClipOp op, bool doAA) {
-            this->initPath(saveCount, path, op, doAA);
+        Element(int saveCount, const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
+            this->initPath(saveCount, path, m, op, doAA);
         }
 
         void initCommon(int saveCount, SkClipOp op, bool doAA);
-        void initRect(int saveCount, const SkRect& rect, SkClipOp op, bool doAA);
-        void initRRect(int saveCount, const SkRRect& rrect, SkClipOp op, bool doAA);
-        void initPath(int saveCount, const SkPath& path, SkClipOp op, bool doAA);
+        void initRect(int saveCount, const SkRect&, const SkMatrix&, SkClipOp, bool doAA);
+        void initRRect(int saveCount, const SkRRect&, const SkMatrix&, SkClipOp, bool doAA);
+        void initPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
+        void initAsPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
 
         void setEmpty();
 
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index 4c484a0..863fb92 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -44,6 +44,7 @@
     void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1)               override;
     void blitMask  (const SkMask&, const SkIRect& clip)             override;
     void blitRect  (int x, int y, int width, int height)            override;
+    void blitV     (int x, int y, int height, SkAlpha alpha)        override;
 
     // TODO: The default implementations of the other blits look fine,
     // but some of them like blitV could probably benefit from custom
@@ -389,6 +390,18 @@
     this->blitMask(mask, clip);
 }
 
+void SkRasterPipelineBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    SkIRect clip = {x,y, x+1,y+height};
+
+    SkMask mask;
+    mask.fImage    = α
+    mask.fBounds   = clip;
+    mask.fRowBytes = 0;     // so we reuse the 1 "row" for all of height
+    mask.fFormat   = SkMask::kA8_Format;
+
+    this->blitMask(mask, clip);
+}
+
 void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
     if (mask.fFormat == SkMask::kBW_Format) {
         // TODO: native BW masks?
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index 0089fc1..a8abf81 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -567,66 +567,6 @@
     }
 }
 
-static inline Sk2f normalize(const Sk2f& n) {
-    Sk2f nn = n*n;
-    return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
-}
-
-bool GrPathUtils::chopMonotonicQuads(const SkPoint p[3], SkPoint dst[5]) {
-    GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
-    GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
-    GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
-
-    Sk2f p0 = Sk2f::Load(&p[0]);
-    Sk2f p1 = Sk2f::Load(&p[1]);
-    Sk2f p2 = Sk2f::Load(&p[2]);
-
-    Sk2f tan0 = p1 - p0;
-    Sk2f tan1 = p2 - p1;
-    Sk2f v = p2 - p0;
-
-    // Check if the curve is already monotonic (i.e. (tan0 dot v) >= 0 and (tan1 dot v) >= 0).
-    // This should almost always be this case for well-behaved curves in the real world.
-    float dot0[2], dot1[2];
-    (tan0 * v).store(dot0);
-    (tan1 * v).store(dot1);
-    if (dot0[0] + dot0[1] >= 0 && dot1[0] + dot1[1] >= 0) {
-        return false;
-    }
-
-    // Chop the curve into two segments with equal curvature. To do this we find the T value whose
-    // tangent is perpendicular to the vector that bisects tan0 and -tan1.
-    Sk2f n = normalize(tan0) - normalize(tan1);
-
-    // This tangent can be found where (dQ(t) dot n) = 0:
-    //
-    //   0 = (dQ(t) dot n) = | 2*t  1 | * | p0 - 2*p1 + p2 | * | n |
-    //                                    | -2*p0 + 2*p1   |   | . |
-    //
-    //                     = | 2*t  1 | * | tan1 - tan0 | * | n |
-    //                                    | 2*tan0      |   | . |
-    //
-    //                     = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
-    //
-    //   t = (tan0 dot n) / ((tan0 - tan1) dot n)
-    Sk2f dQ1n = (tan0 - tan1) * n;
-    Sk2f dQ0n = tan0 * n;
-    Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
-    t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
-
-    Sk2f p01 = SkNx_fma(t, tan0, p0);
-    Sk2f p12 = SkNx_fma(t, tan1, p1);
-    Sk2f p012 = SkNx_fma(t, p12 - p01, p01);
-
-    p0.store(&dst[0]);
-    p01.store(&dst[1]);
-    p012.store(&dst[2]);
-    p12.store(&dst[3]);
-    p2.store(&dst[4]);
-
-    return true;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
index 4643bff..e9dee73 100644
--- a/src/gpu/GrPathUtils.h
+++ b/src/gpu/GrPathUtils.h
@@ -124,14 +124,6 @@
                                                 SkPathPriv::FirstDirection dir,
                                                 SkTArray<SkPoint, true>* quads);
 
-    // Ensures that a quadratic bezier is monotonic with respect to its vector [P2 - P0] (the vector
-    // between its endpoints). In the event that the curve is not monotonic, it is chopped into two
-    // segments that are monotonic. This should be rare for well-behaved curves in the real world.
-    //
-    // Returns false if the curve was already monotonic.
-    //         true if it was chopped into two monotonic segments, now contained in dst.
-    bool chopMonotonicQuads(const SkPoint p[3], SkPoint dst[5]);
-
     // Computes the KLM linear functionals for the cubic implicit form. The "right" side of the
     // curve (when facing in the direction of increasing parameter values) will be the area that
     // satisfies:
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index e6cea3e..f7bb42a 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -79,7 +79,7 @@
         fHasIBounds = true;
 
         // Implement the clip with an AA rect element.
-        fElements.addToHead(stackBounds, kReplace_SkClipOp, true/*doAA*/);
+        fElements.addToHead(stackBounds, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
         fElementsGenID = stack.getTopmostGenID();
         fRequiresAA = true;
 
@@ -335,7 +335,8 @@
             if (isFlip) {
                 SkASSERT(kXOR_SkClipOp == element->getOp() ||
                          kReverseDifference_SkClipOp == element->getOp());
-                fElements.addToHead(SkRect::Make(fIBounds), kReverseDifference_SkClipOp, false);
+                fElements.addToHead(SkRect::Make(fIBounds), SkMatrix::I(),
+                                    kReverseDifference_SkClipOp, false);
             } else {
                 Element* newElement = fElements.addToHead(*element);
                 if (newElement->isAA()) {
diff --git a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
index 361a159..f2d27e8 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
@@ -11,7 +11,6 @@
 #include "GrGpuCommandBuffer.h"
 #include "GrOnFlushResourceProvider.h"
 #include "GrOpFlushState.h"
-#include "GrPathUtils.h"
 #include "SkGeometry.h"
 #include "SkMakeUnique.h"
 #include "SkMathPriv.h"
@@ -19,6 +18,7 @@
 #include "SkPathPriv.h"
 #include "SkPoint.h"
 #include "SkNx.h"
+#include "ccpr/GrCCPRGeometry.h"
 #include "ops/GrDrawOp.h"
 #include "../pathops/SkPathOpsCubic.h"
 #include <numeric>
@@ -311,9 +311,8 @@
 void GrCCPRCoverageOpsBuilder::quadraticTo(SkPoint controlPt, SkPoint endPt) {
     SkASSERT(fCurrPathIndices.fQuadratics+2 <= fBaseInstances[(int)fCurrScissorMode].fSerpentines);
 
-    SkPoint P[3] = {fCurrFanPoint, controlPt, endPt};
     SkPoint chopped[5];
-    if (GrPathUtils::chopMonotonicQuads(P, chopped)) {
+    if (GrCCPRChopMonotonicQuadratics(fCurrFanPoint, controlPt, endPt, chopped)) {
         this->fanTo(chopped[2]);
         fPointsData[fControlPtsIdx++] = chopped[1];
         fInstanceData[fCurrPathIndices.fQuadratics++].fQuadraticData = {
diff --git a/src/gpu/ccpr/GrCCPRGeometry.cpp b/src/gpu/ccpr/GrCCPRGeometry.cpp
new file mode 100644
index 0000000..f756f6e
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRGeometry.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCCPRGeometry.h"
+
+#include "GrTypes.h"
+#include "SkPoint.h"
+#include "SkNx.h"
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+
+// We convert between SkPoint and Sk2f freely throughout this file.
+GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
+GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
+GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
+
+static inline Sk2f normalize(const Sk2f& n) {
+    Sk2f nn = n*n;
+    return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
+}
+
+static inline float dot(const Sk2f& a, const Sk2f& b) {
+    float product[2];
+    (a * b).store(product);
+    return product[0] + product[1];
+}
+
+// Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt].
+static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& startTan,
+                                             const Sk2f& endPt, const Sk2f& endTan) {
+    Sk2f v = endPt - startPt;
+    float dot0 = dot(startTan, v);
+    float dot1 = dot(endTan, v);
+
+    // A small, negative tolerance handles floating-point error in the case when one tangent
+    // approaches 0 length, meaning the (convex) curve segment is effectively a flat line.
+    float tolerance = -std::max(std::abs(dot0), std::abs(dot1)) * SK_ScalarNearlyZero;
+    return dot0 >= tolerance && dot1 >= tolerance;
+}
+
+static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) {
+    return SkNx_fma(t, b - a, a);
+}
+
+bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt,
+                                   const SkPoint& endPt, SkPoint dst[5]) {
+    Sk2f p0 = Sk2f::Load(&startPt);
+    Sk2f p1 = Sk2f::Load(&controlPt);
+    Sk2f p2 = Sk2f::Load(&endPt);
+
+    Sk2f tan0 = p1 - p0;
+    Sk2f tan1 = p2 - p1;
+    // This should almost always be this case for well-behaved curves in the real world.
+    if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
+        return false;
+    }
+
+    // Chop the curve into two segments with equal curvature. To do this we find the T value whose
+    // tangent is perpendicular to the vector that bisects tan0 and -tan1.
+    Sk2f n = normalize(tan0) - normalize(tan1);
+
+    // This tangent can be found where (dQ(t) dot n) = 0:
+    //
+    //   0 = (dQ(t) dot n) = | 2*t  1 | * | p0 - 2*p1 + p2 | * | n |
+    //                                    | -2*p0 + 2*p1   |   | . |
+    //
+    //                     = | 2*t  1 | * | tan1 - tan0 | * | n |
+    //                                    | 2*tan0      |   | . |
+    //
+    //                     = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
+    //
+    //   t = (tan0 dot n) / ((tan0 - tan1) dot n)
+    Sk2f dQ1n = (tan0 - tan1) * n;
+    Sk2f dQ0n = tan0 * n;
+    Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
+    t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
+
+    Sk2f p01 = SkNx_fma(t, tan0, p0);
+    Sk2f p12 = SkNx_fma(t, tan1, p1);
+    Sk2f p012 = lerp(p01, p12, t);
+
+    p0.store(&dst[0]);
+    p01.store(&dst[1]);
+    p012.store(&dst[2]);
+    p12.store(&dst[3]);
+    p2.store(&dst[4]);
+
+    return true;
+}
diff --git a/src/gpu/ccpr/GrCCPRGeometry.h b/src/gpu/ccpr/GrCCPRGeometry.h
new file mode 100644
index 0000000..cb8bb3a
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRGeometry.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGrCCPRGeometry_DEFINED
+#define GrGrCCPRGeometry_DEFINED
+
+#include "SkTypes.h"
+
+struct SkPoint;
+
+/*
+ * Ensures that a quadratic bezier is monotonic with respect to the vector between its endpoints
+ * [P2 - P0]. In the event that the curve is not monotonic, it is chopped into two segments that
+ * are. This should be rare for well-behaved curves in the real world.
+ *
+ * NOTE: This must be done in device space, since an affine transformation can change whether a
+ * curve is monotonic.
+ *
+ * Returns false if the curve was already monotonic.
+ *         true if it was chopped into two monotonic segments, now contained in dst.
+ */
+bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt,
+                                   const SkPoint& endPt, SkPoint dst[5]);
+
+#endif
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
index 2360953..ca4257a 100644
--- a/src/pdf/SkPDFBitmap.cpp
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -30,15 +30,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-static void pdf_stream_begin(SkWStream* stream) {
-    static const char streamBegin[] = " stream\n";
-    stream->write(streamBegin, strlen(streamBegin));
-}
+static const char kStreamBegin[] = " stream\n";
 
-static void pdf_stream_end(SkWStream* stream) {
-    static const char streamEnd[] = "\nendstream";
-    stream->write(streamEnd, strlen(streamEnd));
-}
+static const char kStreamEnd[] = "\nendstream";
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -321,9 +315,9 @@
     pdfDict.insertInt("Length", buffer.bytesWritten());
     pdfDict.emitObject(stream, objNumMap);
 
-    pdf_stream_begin(stream);
+    stream->writeText(kStreamBegin);
     buffer.writeToAndReset(stream);
-    pdf_stream_end(stream);
+    stream->writeText(kStreamEnd);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -405,9 +399,9 @@
     pdfDict.insertInt("ColorTransform", 0);
     pdfDict.insertInt("Length", SkToInt(fData->size()));
     pdfDict.emitObject(stream, objNumMap);
-    pdf_stream_begin(stream);
+    stream->writeText(kStreamBegin);
     stream->write(fData->data(), fData->size());
-    pdf_stream_end(stream);
+    stream->writeText(kStreamEnd);
 }
 }  // namespace
 
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index 9ed3356..9cac080 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -39,7 +39,7 @@
                                             const SkDocument::PDFMetadata& md) {
     fBaseOffset = wStream->bytesWritten();
     static const char kHeader[] = "%PDF-1.4\n%" SKPDF_MAGIC "\n";
-    wStream->write(kHeader, strlen(kHeader));
+    wStream->writeText(kHeader);
     // The PDF spec recommends including a comment with four
     // bytes, all with their high bits set.  "\xD3\xEB\xE9\xE1" is
     // "Skia" with the high bits set.
diff --git a/src/pdf/SkPDFMetadata.cpp b/src/pdf/SkPDFMetadata.cpp
index edef851..cbdec77 100644
--- a/src/pdf/SkPDFMetadata.cpp
+++ b/src/pdf/SkPDFMetadata.cpp
@@ -76,7 +76,7 @@
     // format of the data that will be hashed is not important.
     SkMD5 md5;
     const char uuidNamespace[] = "org.skia.pdf\n";
-    md5.write(uuidNamespace, strlen(uuidNamespace));
+    md5.writeText(uuidNamespace);
     double msec = SkTime::GetMSecs();
     md5.write(&msec, sizeof(msec));
     SkTime::DateTime dateTime;
@@ -92,7 +92,7 @@
     }
 
     for (const auto keyValuePtr : gMetadataKeys) {
-        md5.write(keyValuePtr.key, strlen(keyValuePtr.key));
+        md5.writeText(keyValuePtr.key);
         md5.write("\037", 1);
         const SkString& value = metadata.*(keyValuePtr.valuePtr);
         md5.write(value.c_str(), value.size());
@@ -164,13 +164,13 @@
         dict.insertInt("Length", fXML.size());
         dict.emitObject(stream, omap);
         static const char streamBegin[] = " stream\n";
-        stream->write(streamBegin, strlen(streamBegin));
+        stream->writeText(streamBegin);
         // Do not compress this.  The standard requires that a
         // program that does not understand PDF can grep for
         // "<?xpacket" and extract the entire XML.
         stream->write(fXML.c_str(), fXML.size());
         static const char streamEnd[] = "\nendstream";
-        stream->write(streamEnd, strlen(streamEnd));
+        stream->writeText(streamEnd);
     }
 
 private: