Bug 1457284 - add Skia m66 cherry-picks. r=rhunt
authorLee Salzman <lsalzman@mozilla.com>
Thu, 26 Apr 2018 22:45:34 -0400
changeset 472078 1ecf63f0cca6b2fa65edc893dd4ce51bbd979364
parent 472077 30448613bb72e41e386baf2e5e88cabd5ff720a8
child 472079 eea837f5ac160dcec9ec03cc598acd57ae42a487
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt
bugs1457284
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1457284 - add Skia m66 cherry-picks. r=rhunt MozReview-Commit-ID: 4naJirEFhiV
gfx/skia/moz.build
gfx/skia/skia/src/core/SkBitmapDevice.cpp
gfx/skia/skia/src/core/SkBitmapDevice.h
gfx/skia/skia/src/core/SkDeviceLooper.cpp
gfx/skia/skia/src/core/SkDeviceLooper.h
gfx/skia/skia/src/core/SkDraw.cpp
gfx/skia/skia/src/core/SkScan_Hairline.cpp
gfx/skia/skia/src/gpu/ops/GrAAHairLinePathRenderer.cpp
gfx/skia/skia/src/pdf/SkPDFDevice.cpp
gfx/skia/skia/src/pdf/SkPDFUtils.cpp
gfx/skia/skia/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
layout/reftests/css-grid/reftest.list
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -59,17 +59,16 @@ UNIFIED_SOURCES += [
     'skia/src/core/SkCubicMap.cpp',
     'skia/src/core/SkData.cpp',
     'skia/src/core/SkDataTable.cpp',
     'skia/src/core/SkDebug.cpp',
     'skia/src/core/SkDeferredDisplayList.cpp',
     'skia/src/core/SkDeferredDisplayListRecorder.cpp',
     'skia/src/core/SkDeque.cpp',
     'skia/src/core/SkDevice.cpp',
-    'skia/src/core/SkDeviceLooper.cpp',
     'skia/src/core/SkDeviceProfile.cpp',
     'skia/src/core/SkDistanceFieldGen.cpp',
     'skia/src/core/SkDither.cpp',
     'skia/src/core/SkDocument.cpp',
     'skia/src/core/SkDraw.cpp',
     'skia/src/core/SkDraw_vertices.cpp',
     'skia/src/core/SkDrawable.cpp',
     'skia/src/core/SkDrawLooper.cpp',
--- a/gfx/skia/skia/src/core/SkBitmapDevice.cpp
+++ b/gfx/skia/skia/src/core/SkBitmapDevice.cpp
@@ -18,16 +18,126 @@
 #include "SkRasterClip.h"
 #include "SkRasterHandleAllocator.h"
 #include "SkShader.h"
 #include "SkSpecialImage.h"
 #include "SkSurface.h"
 #include "SkTLazy.h"
 #include "SkVertices.h"
 
+class SkDrawTiler {
+    enum {
+        // 8K is 1 too big, since 8K << supersample == 32768 which is too big for SkFixed
+        kMaxDim = 8192 - 1
+    };
+
+    SkBitmapDevice* fDevice;
+    SkPixmap        fRootPixmap;
+
+    // Used for tiling and non-tiling
+    SkDraw          fDraw;
+
+    // fCurr... are only used if fNeedTiling
+    SkMatrix        fTileMatrix;
+    SkRasterClip    fTileRC;
+    SkIPoint        fCurrOrigin, fOrigin;
+
+    bool            fDone, fNeedsTiling;
+
+public:
+    SkDrawTiler(SkBitmapDevice* dev) : fDevice(dev) {
+        // we need fDst to be set, and if we're actually drawing, to dirty the genID
+        if (!dev->accessPixels(&fRootPixmap)) {
+            // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
+            fRootPixmap.reset(dev->imageInfo(), nullptr, 0);
+        }
+
+        fDone = false;
+        fNeedsTiling = fRootPixmap.width() > kMaxDim || fRootPixmap.height() > kMaxDim;
+        fOrigin.set(0, 0);
+        fCurrOrigin = fOrigin;
+
+        if (fNeedsTiling) {
+            // fDraw.fDst is reset each time in setupTileDraw()
+            fDraw.fMatrix = &fTileMatrix;
+            fDraw.fRC = &fTileRC;
+        } else {
+            fDraw.fDst = fRootPixmap;
+            fDraw.fMatrix = &dev->ctm();
+            fDraw.fRC = &dev->fRCStack.rc();
+        }
+    }
+
+    bool needsTiling() const { return fNeedsTiling; }
+
+    const SkDraw* next() {
+        if (fDone) {
+            return nullptr;
+        }
+        if (fNeedsTiling) {
+            do {
+                this->setupTileDraw();  // might set the clip to empty
+                this->stepOrigin();     // might set fDone to true
+            } while (!fDone && fTileRC.isEmpty());
+            // if we exit the loop and we're still empty, we're (past) done
+            if (fTileRC.isEmpty()) {
+                SkASSERT(fDone);
+                return nullptr;
+            }
+            SkASSERT(!fTileRC.isEmpty());
+        } else {
+            fDone = true;   // only draw untiled once
+        }
+        return &fDraw;
+    }
+
+    int curr_x() const { return fCurrOrigin.x(); }
+    int curr_y() const { return fCurrOrigin.y(); }
+
+private:
+    void setupTileDraw() {
+        SkASSERT(!fDone);
+        SkIRect bounds = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), kMaxDim, kMaxDim);
+        SkASSERT(!bounds.isEmpty());
+        bool success = fRootPixmap.extractSubset(&fDraw.fDst, bounds);
+        SkASSERT_RELEASE(success);
+        // now don't use bounds, since fDst has the clipped dimensions.
+
+        fTileMatrix = fDevice->ctm();
+        fTileMatrix.postTranslate(SkIntToScalar(-fOrigin.x()), SkIntToScalar(-fOrigin.y()));
+        fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC);
+        fTileRC.op(SkIRect::MakeWH(fDraw.fDst.width(), fDraw.fDst.height()),
+                   SkRegion::kIntersect_Op);
+
+        fCurrOrigin = fOrigin;
+    }
+
+    void stepOrigin() {
+        SkASSERT(!fDone);
+        SkASSERT(fNeedsTiling);
+        fOrigin.fX += kMaxDim;
+        if (fOrigin.fX >= fRootPixmap.width()) {    // too far
+            fOrigin.fX = 0;
+            fOrigin.fY += kMaxDim;
+            if (fOrigin.fY >= fRootPixmap.height()) {
+                fDone = true;   // way too far
+            }
+        }
+    }
+};
+
+#define LOOP_TILER(code)                                    \
+    SkDrawTiler priv_tiler(this);                           \
+    while (const SkDraw* priv_draw = priv_tiler.next()) {   \
+        priv_draw->code;                                    \
+    }
+#define TILER_X(x)  (x) - priv_tiler.curr_x()
+#define TILER_Y(y)  (y) - priv_tiler.curr_y()
+
+
 class SkColorTable;
 
 static bool valid_for_bitmap_device(const SkImageInfo& info,
                                     SkAlphaType* newAlphaType) {
     if (info.width() < 0 || info.height() < 0) {
         return false;
     }
 
@@ -168,40 +278,27 @@ bool SkBitmapDevice::onWritePixels(const
 }
 
 bool SkBitmapDevice::onReadPixels(const SkPixmap& pm, int x, int y) {
     return fBitmap.readPixels(pm, x, y);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class SkBitmapDevice::BDDraw : public SkDraw {
-public:
-    BDDraw(SkBitmapDevice* dev) {
-        // we need fDst to be set, and if we're actually drawing, to dirty the genID
-        if (!dev->accessPixels(&fDst)) {
-            // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
-            fDst.reset(dev->imageInfo(), nullptr, 0);
-        }
-        fMatrix = &dev->ctm();
-        fRC = &dev->fRCStack.rc();
-    }
-};
-
 void SkBitmapDevice::drawPaint(const SkPaint& paint) {
-    BDDraw(this).drawPaint(paint);
+    LOOP_TILER( drawPaint(paint))
 }
 
 void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
                                 const SkPoint pts[], const SkPaint& paint) {
-    BDDraw(this).drawPoints(mode, count, pts, paint, nullptr);
+    LOOP_TILER( drawPoints(mode, count, pts, paint, nullptr))
 }
 
 void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) {
-    BDDraw(this).drawRect(r, paint);
+    LOOP_TILER( drawRect(r, paint))
 }
 
 void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
     SkPath path;
     path.addOval(oval);
     // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
     // required to override drawOval.
     this->drawPath(path, paint, nullptr, true);
@@ -211,31 +308,37 @@ void SkBitmapDevice::drawRRect(const SkR
 #ifdef SK_IGNORE_BLURRED_RRECT_OPT
     SkPath  path;
 
     path.addRRect(rrect);
     // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
     // required to override drawRRect.
     this->drawPath(path, paint, nullptr, true);
 #else
-    BDDraw(this).drawRRect(rrect, paint);
+    LOOP_TILER( drawRRect(rrect, paint))
 #endif
 }
 
 void SkBitmapDevice::drawPath(const SkPath& path,
                               const SkPaint& paint, const SkMatrix* prePathMatrix,
                               bool pathIsMutable) {
-    BDDraw(this).drawPath(path, paint, prePathMatrix, pathIsMutable);
+    SkDrawTiler tiler(this);
+    if (tiler.needsTiling()) {
+        pathIsMutable = false;
+    }
+    while (const SkDraw* draw = tiler.next()) {
+        draw->drawPath(path, paint, prePathMatrix, pathIsMutable);
+    }
 }
 
 void SkBitmapDevice::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
                                 const SkPaint& paint) {
     SkMatrix matrix = SkMatrix::MakeTrans(x, y);
     LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality());
-    BDDraw(this).drawBitmap(bitmap, matrix, nullptr, paint);
+    LOOP_TILER( drawBitmap(bitmap, matrix, nullptr, paint))
 }
 
 static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) {
     if (!paint.getMaskFilter()) {
         return true;
     }
 
     // Some mask filters parameters (sigma) depend on the CTM/scale.
@@ -320,17 +423,17 @@ void SkBitmapDevice::drawBitmapRect(cons
             goto USE_DRAWBITMAP;
         }
     } else {
         USE_DRAWBITMAP:
         // We can go faster by just calling drawBitmap, which will concat the
         // matrix with the CTM, and try to call drawSprite if it can. If not,
         // it will make a shader and call drawRect, as we do below.
         if (CanApplyDstMatrixAsCTM(matrix, paint)) {
-            BDDraw(this).drawBitmap(*bitmapPtr, matrix, dstPtr, paint);
+            LOOP_TILER( drawBitmap(*bitmapPtr, matrix, dstPtr, paint))
             return;
         }
     }
 
     USE_SHADER:
 
     // TODO(herb): Move this over to SkArenaAlloc when arena alloc has a facility to return sk_sps.
     // Since the shader need only live for our stack-frame, pass in a custom allocator. This
@@ -349,47 +452,48 @@ void SkBitmapDevice::drawBitmapRect(cons
     paintWithShader.setShader(s);
 
     // Call ourself, in case the subclass wanted to share this setup code
     // but handle the drawRect code themselves.
     this->drawRect(*dstPtr, paintWithShader);
 }
 
 void SkBitmapDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
-    BDDraw(this).drawSprite(bitmap, x, y, paint);
+    LOOP_TILER( drawSprite(bitmap, TILER_X(x), TILER_Y(y), paint))
 }
 
 void SkBitmapDevice::drawText(const void* text, size_t len,
                               SkScalar x, SkScalar y, const SkPaint& paint) {
-    BDDraw(this).drawText((const char*)text, len, x, y, paint, &fSurfaceProps);
+    LOOP_TILER( drawText((const char*)text, len, x, y, paint, &fSurfaceProps))
 }
 
 void SkBitmapDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
                                  int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
-    BDDraw(this).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint,
-                             &fSurfaceProps);
+    LOOP_TILER( drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint,
+                            &fSurfaceProps))
 }
 
 void SkBitmapDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
                                   const SkPaint& paint) {
-    BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
-                              vertices->texCoords(), vertices->colors(), bmode,
-                              vertices->indices(), vertices->indexCount(), paint);
+    LOOP_TILER( drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
+                             vertices->texCoords(), vertices->colors(), bmode,
+                             vertices->indices(), vertices->indexCount(), paint))
 }
 
 void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& origPaint) {
     SkASSERT(!origPaint.getImageFilter());
 
     // todo: can we unify with similar adjustment in SkGpuDevice?
     SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
     if (paint->getMaskFilter()) {
         paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm()));
     }
 
-    BDDraw(this).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, *paint);
+    LOOP_TILER( drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap,
+                           TILER_X(x), TILER_Y(y), *paint))
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace {
 
 class SkAutoDeviceClipRestore {
 public:
--- a/gfx/skia/skia/src/core/SkBitmapDevice.h
+++ b/gfx/skia/skia/src/core/SkBitmapDevice.h
@@ -139,16 +139,17 @@ protected:
     void validateDevBounds(const SkIRect& r) override;
     ClipType onGetClipType() const override;
 
 private:
     friend class SkCanvas;
     friend struct DeviceCM; //for setMatrixClip
     friend class SkDraw;
     friend class SkDrawIter;
+    friend class SkDrawTiler;
     friend class SkDeviceFilteredPaint;
     friend class SkSurface_Raster;
     friend class SkThreadedBMPDevice; // to copy fRCStack
 
     class BDDraw;
 
     // used to change the backend's pixels (and possibly config/rowbytes)
     // but cannot change the width/height, so there should be no change to
deleted file mode 100644
--- a/gfx/skia/skia/src/core/SkDeviceLooper.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkDeviceLooper.h"
-
-SkDeviceLooper::SkDeviceLooper(const SkPixmap& base, const SkRasterClip& rc, const SkIRect& bounds,
-                               bool aa)
-    : fBaseDst(base)
-    , fBaseRC(rc)
-    , fDelta(aa ? kAA_Delta : kBW_Delta)
-{
-    // sentinels that next() has not yet been called, and so our mapper functions
-    // should not be called either.
-    fCurrDst = nullptr;
-    fCurrRC = nullptr;
-
-    if (!rc.isEmpty()) {
-        // clip must be contained by the bitmap
-        SkASSERT(SkIRect::MakeWH(base.width(), base.height()).contains(rc.getBounds()));
-    }
-
-    if (rc.isEmpty() || !fClippedBounds.intersect(bounds, rc.getBounds())) {
-        fState = kDone_State;
-    } else if (this->fitsInDelta(fClippedBounds)) {
-        fState = kSimple_State;
-    } else {
-        // back up by 1 DX, so that next() will put us in a correct starting
-        // position.
-        fCurrOffset.set(fClippedBounds.left() - fDelta,
-                        fClippedBounds.top());
-        fState = kComplex_State;
-    }
-}
-
-SkDeviceLooper::~SkDeviceLooper() {}
-
-void SkDeviceLooper::mapRect(SkRect* dst, const SkRect& src) const {
-    SkASSERT(kDone_State != fState);
-    SkASSERT(fCurrDst);
-    SkASSERT(fCurrRC);
-
-    *dst = src;
-    dst->offset(SkIntToScalar(-fCurrOffset.fX),
-                SkIntToScalar(-fCurrOffset.fY));
-}
-
-void SkDeviceLooper::mapMatrix(SkMatrix* dst, const SkMatrix& src) const {
-    SkASSERT(kDone_State != fState);
-    SkASSERT(fCurrDst);
-    SkASSERT(fCurrRC);
-
-    *dst = src;
-    dst->postTranslate(SkIntToScalar(-fCurrOffset.fX), SkIntToScalar(-fCurrOffset.fY));
-}
-
-bool SkDeviceLooper::computeCurrBitmapAndClip() {
-    SkASSERT(kComplex_State == fState);
-
-    SkIRect r = SkIRect::MakeXYWH(fCurrOffset.x(), fCurrOffset.y(),
-                                  fDelta, fDelta);
-    if (!fBaseDst.extractSubset(&fSubsetDst, r)) {
-        fSubsetRC.setEmpty();
-    } else {
-        fBaseRC.translate(-r.left(), -r.top(), &fSubsetRC);
-        (void)fSubsetRC.op(SkIRect::MakeWH(fDelta, fDelta), SkRegion::kIntersect_Op);
-    }
-
-    fCurrDst = &fSubsetDst;
-    fCurrRC = &fSubsetRC;
-    return !fCurrRC->isEmpty();
-}
-
-static bool next_tile(const SkIRect& boundary, int delta, SkIPoint* offset) {
-    // can we move to the right?
-    if (offset->x() + delta < boundary.right()) {
-        offset->fX += delta;
-        return true;
-    }
-
-    // reset to the left, but move down a row
-    offset->fX = boundary.left();
-    if (offset->y() + delta < boundary.bottom()) {
-        offset->fY += delta;
-        return true;
-    }
-
-    // offset is now outside of boundary, so we're done
-    return false;
-}
-
-bool SkDeviceLooper::next() {
-    switch (fState) {
-        case kDone_State:
-            // in theory, we should not get called here, since we must have
-            // previously returned false, but we check anyway.
-            break;
-
-        case kSimple_State:
-            // first time for simple
-            if (nullptr == fCurrDst) {
-                fCurrDst = &fBaseDst;
-                fCurrRC = &fBaseRC;
-                fCurrOffset.set(0, 0);
-                return true;
-            }
-            // 2nd time for simple, we are done
-            break;
-
-        case kComplex_State:
-            // need to propogate fCurrOffset through clippedbounds
-            // left to right, until we wrap around and move down
-
-            while (next_tile(fClippedBounds, fDelta, &fCurrOffset)) {
-                if (this->computeCurrBitmapAndClip()) {
-                    return true;
-                }
-            }
-            break;
-    }
-    fState = kDone_State;
-    return false;
-}
deleted file mode 100644
--- a/gfx/skia/skia/src/core/SkDeviceLooper.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkDeviceLooper_DEFINED
-#define SkDeviceLooper_DEFINED
-
-#include "SkBitmap.h"
-#include "SkMatrix.h"
-#include "SkRasterClip.h"
-
-/**
- *  Helper class to manage "tiling" a large coordinate space into managable
- *  chunks, where managable means areas that are <= some max critical coordinate
- *  size.
- *
- *  The constructor takes an antialiasing bool, which affects what this maximum
- *  allowable size is: If we're drawing BW, then we need coordinates to stay
- *  safely within fixed-point range (we use +- 16K, to give ourselves room to
- *  add/subtract two fixed values and still be in range. If we're drawing AA,
- *  then we reduce that size by the amount that the supersampler scan converter
- *  needs (at the moment, that is 4X, so the "safe" range is +- 4K).
- *
- *  For performance reasons, the class first checks to see if any help is needed
- *  at all, and if not (i.e. the specified bounds and base bitmap area already
- *  in the safe-zone, then the class does nothing (effectively).
- */
-class SkDeviceLooper {
-public:
-    SkDeviceLooper(const SkPixmap& base, const SkRasterClip&, const SkIRect& bounds, bool aa);
-    ~SkDeviceLooper();
-
-    const SkPixmap& getPixmap() const {
-        SkASSERT(kDone_State != fState);
-        SkASSERT(fCurrDst);
-        return *fCurrDst;
-    }
-
-    const SkRasterClip& getRC() const {
-        SkASSERT(kDone_State != fState);
-        SkASSERT(fCurrRC);
-        return *fCurrRC;
-    }
-
-    void mapRect(SkRect* dst, const SkRect& src) const;
-    void mapMatrix(SkMatrix* dst, const SkMatrix& src) const;
-
-    /**
-     *  Call next to setup the looper to return a valid coordinate chunk.
-     *  Each time this returns true, it is safe to call mapRect() and
-     *  mapMatrix(), to convert from "global" coordinate values to ones that
-     *  are local to this chunk.
-     *
-     *  When next() returns false, the list of chunks is done, and mapRect()
-     *  and mapMatrix() should no longer be called.
-     */
-    bool next();
-
-private:
-    const SkPixmap&     fBaseDst;
-    const SkRasterClip& fBaseRC;
-
-    enum State {
-        kDone_State,    // iteration is complete, getters will assert
-        kSimple_State,  // no translate/clip mods needed
-        kComplex_State
-    };
-
-    // storage for our tiled versions. Perhaps could use SkTLazy
-    SkPixmap            fSubsetDst;
-    SkRasterClip        fSubsetRC;
-
-    const SkPixmap*     fCurrDst;
-    const SkRasterClip* fCurrRC;
-    SkIRect             fClippedBounds;
-    SkIPoint            fCurrOffset;
-    int                 fDelta;
-    State               fState;
-
-    enum Delta {
-        kBW_Delta = 1 << 14,        // 16K, gives room to spare for fixedpoint
-        kAA_Delta = kBW_Delta >> 2  // supersample 4x
-    };
-
-    bool fitsInDelta(const SkIRect& r) const {
-        return r.right() < fDelta && r.bottom() < fDelta;
-    }
-
-    bool computeCurrBitmapAndClip();
-};
-
-#endif
--- a/gfx/skia/skia/src/core/SkDraw.cpp
+++ b/gfx/skia/skia/src/core/SkDraw.cpp
@@ -9,17 +9,16 @@
 
 #include "SkArenaAlloc.h"
 #include "SkAutoBlitterChoose.h"
 #include "SkBlendModePriv.h"
 #include "SkBlitter.h"
 #include "SkCanvas.h"
 #include "SkColorData.h"
 #include "SkDevice.h"
-#include "SkDeviceLooper.h"
 #include "SkDraw.h"
 #include "SkDrawProcs.h"
 #include "SkFindAndPlaceGlyph.h"
 #include "SkMaskFilterBase.h"
 #include "SkMatrix.h"
 #include "SkMatrixUtils.h"
 #include "SkPaint.h"
 #include "SkPathEffect.h"
@@ -780,66 +779,57 @@ void SkDraw::drawRect(const SkRect& preP
             // For kStroke_RectType, strokeSize is already computed.
             const SkPoint& ssize = (kStroke_RectType == rtype)
                 ? strokeSize
                 : compute_stroke_size(paint, *fMatrix);
             bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
         }
     }
 
-    if (!SkRectPriv::MakeLargeS32().contains(bbox)) {
-        // bbox.roundOut() is undefined; use slow path.
+    if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
         draw_rect_as_path(*this, prePaintRect, paint, matrix);
         return;
     }
 
     SkIRect ir = bbox.roundOut();
     if (fRC->quickReject(ir)) {
         return;
     }
 
-    SkDeviceLooper looper(fDst, *fRC, ir, paint.isAntiAlias());
-    while (looper.next()) {
-        SkRect localDevRect;
-        looper.mapRect(&localDevRect, devRect);
-        SkMatrix localMatrix;
-        looper.mapMatrix(&localMatrix, *matrix);
-
-        SkAutoBlitterChoose blitterStorage(looper.getPixmap(), localMatrix, paint);
-        const SkRasterClip& clip = looper.getRC();
-        SkBlitter*          blitter = blitterStorage.get();
+    SkAutoBlitterChoose blitterStorage(fDst, *matrix, paint);
+    const SkRasterClip& clip = *fRC;
+    SkBlitter*          blitter = blitterStorage.get();
 
-        // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
-        // case we are also hairline (if we've gotten to here), which devolves to
-        // effectively just kFill
-        switch (rtype) {
-            case kFill_RectType:
-                if (paint.isAntiAlias()) {
-                    SkScan::AntiFillRect(localDevRect, clip, blitter);
-                } else {
-                    SkScan::FillRect(localDevRect, clip, blitter);
-                }
-                break;
-            case kStroke_RectType:
-                if (paint.isAntiAlias()) {
-                    SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
-                } else {
-                    SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
-                }
-                break;
-            case kHair_RectType:
-                if (paint.isAntiAlias()) {
-                    SkScan::AntiHairRect(localDevRect, clip, blitter);
-                } else {
-                    SkScan::HairRect(localDevRect, clip, blitter);
-                }
-                break;
-            default:
-                SkDEBUGFAIL("bad rtype");
-        }
+    // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
+    // case we are also hairline (if we've gotten to here), which devolves to
+    // effectively just kFill
+    switch (rtype) {
+        case kFill_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFillRect(devRect, clip, blitter);
+            } else {
+                SkScan::FillRect(devRect, clip, blitter);
+            }
+            break;
+        case kStroke_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
+            } else {
+                SkScan::FrameRect(devRect, strokeSize, clip, blitter);
+            }
+            break;
+        case kHair_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiHairRect(devRect, clip, blitter);
+            } else {
+                SkScan::HairRect(devRect, clip, blitter);
+            }
+            break;
+        default:
+            SkDEBUGFAIL("bad rtype");
     }
 }
 
 void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
     if (srcM.fBounds.isEmpty()) {
         return;
     }
 
--- a/gfx/skia/skia/src/core/SkScan_Hairline.cpp
+++ b/gfx/skia/skia/src/core/SkScan_Hairline.cpp
@@ -139,24 +139,35 @@ void SkScan::HairLineRgn(const SkPoint a
 
             vertline(iy0, iy1, startX, slope, blitter);
         }
     }
 }
 
 // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
 // and double-hit the top-left.
-void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip,
-                      SkBlitter* blitter) {
+void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip, SkBlitter* blitter) {
     SkAAClipBlitterWrapper wrapper;
     SkBlitterClipper clipper;
-    const SkIRect r = SkIRect::MakeLTRB(SkScalarFloorToInt(rect.fLeft),
-                                        SkScalarFloorToInt(rect.fTop),
-                                        SkScalarFloorToInt(rect.fRight) + 1,
-                                        SkScalarFloorToInt(rect.fBottom) + 1);
+    // Create the enclosing bounds of the hairrect. i.e. we will stroke the interior of r.
+    SkIRect r = SkIRect::MakeLTRB(SkScalarFloorToInt(rect.fLeft),
+                                  SkScalarFloorToInt(rect.fTop),
+                                  SkScalarFloorToInt(rect.fRight + 1),
+                                  SkScalarFloorToInt(rect.fBottom + 1));
+
+    // Note: r might be crazy big, if rect was huge, possibly getting pinned to max/min s32.
+    // We need to trim it back to something reasonable before we can query its width etc.
+    // since r.fRight - r.fLeft might wrap around to negative even if fRight > fLeft.
+    //
+    // We outset the clip bounds by 1 before intersecting, since r is being stroked and not filled
+    // so we don't want to pin an edge of it to the clip. The intersect's job is mostly to just
+    // get the actual edge values into a reasonable range (e.g. so width() can't overflow).
+    if (!r.intersect(clip.getBounds().makeOutset(1, 1))) {
+        return;
+    }
 
     if (clip.quickReject(r)) {
         return;
     }
     if (!clip.quickContains(r)) {
         const SkRegion* clipRgn;
         if (clip.isBW()) {
             clipRgn = &clip.bwRgn();
--- a/gfx/skia/skia/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/gfx/skia/skia/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -928,16 +928,23 @@ void AAHairlineOp::onPrepareDraws(Target
         const PathData& args = fPaths[i];
         quadCount += gather_lines_and_quads(args.fPath, args.fViewMatrix, args.fDevClipBounds,
                                             args.fCapLength, &lines, &quads, &conics, &qSubdivs,
                                             &cWeights);
     }
 
     int lineCount = lines.count() / 2;
     int conicCount = conics.count() / 3;
+    int quadAndConicCount = conicCount + quadCount;
+
+    static constexpr int kMaxLines = SK_MaxS32 / kLineSegNumVertices;
+    static constexpr int kMaxQuadsAndConics = SK_MaxS32 / kQuadNumVertices;
+    if (lineCount > kMaxLines || quadAndConicCount > kMaxQuadsAndConics) {
+        return;
+    }
 
     const GrPipeline* pipeline = fHelper.makePipeline(target);
     // do lines first
     if (lineCount) {
         sk_sp<GrGeometryProcessor> lineGP;
         {
             using namespace GrDefaultGeoProcFactory;
 
@@ -995,17 +1002,17 @@ void AAHairlineOp::onPrepareDraws(Target
                                                                this->coverage()));
 
         const GrBuffer* vertexBuffer;
         int firstVertex;
 
         sk_sp<const GrBuffer> quadsIndexBuffer = get_quads_index_buffer(target->resourceProvider());
 
         size_t vertexStride = sizeof(BezierVertex);
-        int vertexCount = kQuadNumVertices * quadCount + kQuadNumVertices * conicCount;
+        int vertexCount = kQuadNumVertices * quadAndConicCount;
         void *vertices = target->makeVertexSpace(vertexStride, vertexCount,
                                                  &vertexBuffer, &firstVertex);
 
         if (!vertices || !quadsIndexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
--- a/gfx/skia/skia/src/pdf/SkPDFDevice.cpp
+++ b/gfx/skia/skia/src/pdf/SkPDFDevice.cpp
@@ -204,18 +204,17 @@ class GraphicStackState {
 public:
     GraphicStackState(const SkClipStack& existingClipStack,
                       SkWStream* contentStream)
             : fStackDepth(0),
               fContentStream(contentStream) {
         fEntries[0].fClipStack = existingClipStack;
     }
 
-    void updateClip(const SkClipStack& clipStack,
-                    const SkPoint& translation, const SkRect& bounds);
+    void updateClip(const SkClipStack& clipStack, const SkIRect& bounds);
     void updateMatrix(const SkMatrix& matrix);
     void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state);
 
     void drainStack();
 
 private:
     void push();
     void pop();
@@ -278,85 +277,45 @@ bool apply_clip(SkClipOp op, const SkPat
             *r = v;
             return true;
 #endif
         default:
             return false;
     }
 }
 
-/* Uses Path Ops to calculate a vector SkPath clip from a clip stack.
- * Returns true if successful, or false if not successful.
- * If successful, the resulting clip is stored in outClipPath.
- * If not successful, outClipPath is undefined, and a fallback method
- * should be used.
- */
-static bool get_clip_stack_path(const SkMatrix& transform,
-                                const SkClipStack& clipStack,
-                                const SkRect& bounds,
-                                SkPath* outClipPath) {
-    outClipPath->reset();
-    outClipPath->setFillType(SkPath::kInverseWinding_FillType);
-
-    const SkClipStack::Element* clipEntry;
-    SkClipStack::Iter iter;
-    iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
-    for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
-        SkPath entryPath;
-        if (SkClipStack::Element::DeviceSpaceType::kEmpty == clipEntry->getDeviceSpaceType()) {
-            outClipPath->reset();
-            outClipPath->setFillType(SkPath::kInverseWinding_FillType);
-            continue;
-        } else {
-            clipEntry->asDeviceSpacePath(&entryPath);
-        }
-        entryPath.transform(transform);
-        if (!apply_clip(clipEntry->getOp(), *outClipPath, entryPath, outClipPath)) {
-            return false;
-        }
-    }
-
-    if (outClipPath->isInverseFillType()) {
-        // The bounds are slightly outset to ensure this is correct in the
-        // face of floating-point accuracy and possible SkRegion bitmap
-        // approximations.
-        SkRect clipBounds = bounds;
-        clipBounds.outset(SK_Scalar1, SK_Scalar1);
-        if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) {
-            return false;
-        }
-    }
-    return true;
-}
-
 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
 // graphic state stack, and the fact that we can know all the clips used
 // on the page to optimize this.
 void GraphicStackState::updateClip(const SkClipStack& clipStack,
-                                   const SkPoint& translation,
-                                   const SkRect& bounds) {
+                                   const SkIRect& bounds) {
     if (clipStack == currentEntry()->fClipStack) {
         return;
     }
 
     while (fStackDepth > 0) {
         pop();
         if (clipStack == currentEntry()->fClipStack) {
             return;
         }
     }
     push();
 
     currentEntry()->fClipStack = clipStack;
 
-    SkMatrix transform;
-    transform.setTranslate(translation.fX, translation.fY);
+    SkPath clipPath;
+    (void)clipStack.asPath(&clipPath);
 
-    SkPath clipPath;
-    if (get_clip_stack_path(transform, clipStack, bounds, &clipPath)) {
+    // The bounds are slightly outset to ensure this is correct in the
+    // face of floating-point accuracy and possible SkRegion bitmap
+    // approximations.
+    SkPath clipBoundsPath;
+    clipBoundsPath.addRect(SkRect::Make(bounds.makeOutset(1, 1)));
+
+    if (Op(clipPath, clipBoundsPath, kIntersect_SkPathOp, &clipPath)) {
         SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, fContentStream);
         SkPath::FillType clipFill = clipPath.getFillType();
         NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
         NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
         if (clipFill == SkPath::kEvenOdd_FillType) {
             fContentStream->writeText("W* n\n");
         } else {
             fContentStream->writeText("W n\n");
@@ -1771,18 +1730,17 @@ sk_sp<SkPDFArray> SkPDFDevice::copyMedia
 std::unique_ptr<SkStreamAsset> SkPDFDevice::content() const {
     SkDynamicMemoryWStream buffer;
     if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
         SkPDFUtils::AppendTransform(fInitialTransform, &buffer);
     }
 
     GraphicStackState gsState(fExistingClipStack, &buffer);
     for (const auto& entry : fContentEntries) {
-        gsState.updateClip(entry.fState.fClipStack,
-                {0, 0}, SkRect::Make(this->bounds()));
+        gsState.updateClip(entry.fState.fClipStack, this->bounds());
         gsState.updateMatrix(entry.fState.fMatrix);
         gsState.updateDrawingState(entry.fState);
 
         entry.fContent.writeToStream(&buffer);
     }
     gsState.drainStack();
     if (buffer.bytesWritten() > 0) {
         return std::unique_ptr<SkStreamAsset>(buffer.detachAsStream());
--- a/gfx/skia/skia/src/pdf/SkPDFUtils.cpp
+++ b/gfx/skia/skia/src/pdf/SkPDFUtils.cpp
@@ -115,16 +115,20 @@ void SkPDFUtils::AppendRectangle(const S
     SkPDFUtils::AppendScalar(rect.height(), content);
     content->writeText(" re\n");
 }
 
 // static
 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
                           bool doConsumeDegerates, SkWStream* content,
                           SkScalar tolerance) {
+    if (path.isEmpty() && SkPaint::kFill_Style == paintStyle) {
+        SkPDFUtils::AppendRectangle({0, 0, 0, 0}, content);
+        return;
+    }
     // Filling a path with no area results in a drawing in PDF renderers but
     // Chrome expects to be able to draw some such entities with no visible
     // result, so we detect those cases and discard the drawing for them.
     // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
 
     SkRect rect;
     bool isClosed; // Both closure and direction need to be checked.
     SkPath::Direction direction;
--- a/gfx/skia/skia/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/gfx/skia/skia/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -87,17 +87,19 @@ public:
 
     // Whether the t we solved is always valid (so we don't need to check r(t) > 0).
     bool isWellBehaved() const { return this->isFocal() && fData.fFocalData.isWellBehaved(); }
 
     // Whether r0 == 0 so it's focal without any transformation
     bool isNativelyFocal() const { return this->isFocal() && fData.fFocalData.isNativelyFocal(); }
 
     // Note that focalX = f = r0 / (r0 - r1), so 1 - focalX > 0 == r0 < r1
-    bool isRadiusIncreasing() const { return this->isFocal() && 1 - fData.fFocalData.fFocalX > 0; }
+    bool isRadiusIncreasing() const {
+        return this->isFocal() ? 1 - fData.fFocalData.fFocalX > 0 : this->diffRadius() > 0;
+    }
 
 protected:
     void onGetGLSLProcessorKey(const GrShaderCaps& c, GrProcessorKeyBuilder* b) const override {
         INHERITED::onGetGLSLProcessorKey(c, b);
         uint32_t key = 0;
         key |= static_cast<int>(fData.fType);
         SkASSERT(key < (1 << 2));
         key |= (this->isFocalOnCircle() << 2);
@@ -232,17 +234,18 @@ protected:
         p0.appendf("%s", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
         const char* tName = "t"; // the gradient
 
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
         SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
         const char* p = coords2D.c_str();
 
         if (effect.getType() == Type::kRadial) {
-            fragBuilder->codeAppendf("half %s = length(%s) - %s;", tName, p, p0.c_str());
+            char sign = effect.diffRadius() < 0 ? '-' : '+';
+            fragBuilder->codeAppendf("half %s = %clength(%s) - %s;", tName, sign, p, p0.c_str());
         } else {
             // output will default to transparent black (we simply won't write anything
             // else to it if invalid, instead of discarding or returning prematurely)
             fragBuilder->codeAppendf("%s = half4(0.0,0.0,0.0,0.0);", args.fOutputColor);
             fragBuilder->codeAppendf("half temp = %s - %s.y * %s.y;", p0.c_str(), p, p);
             fragBuilder->codeAppendf("if (temp >= 0) {");
             fragBuilder->codeAppendf("half %s = %s.x + sqrt(temp);", tName, p);
         }
@@ -258,18 +261,19 @@ protected:
         if (effect.getType() != Type::kRadial) {
             fragBuilder->codeAppendf("}");
         }
     }
 
     void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override {
         INHERITED::onSetData(pdman, p);
         const TwoPointConicalEffect& effect = p.cast<TwoPointConicalEffect>();
-        // kRadialType should imply r1 - r0 = 1 (after our transformation) so r0 = r0 / (r1 - r0)
-        SkASSERT(effect.getType() == Type::kStrip || SkScalarNearlyZero(effect.diffRadius() - 1));
+        // kRadialType should imply |r1 - r0| = 1 (after our transformation)
+        SkASSERT(effect.getType() == Type::kStrip ||
+                 SkScalarNearlyZero(SkTAbs(effect.diffRadius()) - 1));
         pdman.set1f(fParamUni, effect.getType() == Type::kRadial ? effect.r0()
                                                                  : effect.r0() * effect.r0());
     }
 
     UniformHandle fParamUni;
 
 private:
     typedef GrGradientEffect::GLSLProcessor INHERITED;
@@ -402,22 +406,24 @@ std::unique_ptr<GrFragmentProcessor> Gr2
     // class can have the right matrix to work with during construction.
     TwoPointConicalEffect::Data data(shader, matrix);
     return TwoPointConicalEffect::Make(newArgs, data);
 }
 
 TwoPointConicalEffect::Data::Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix) {
     fType = shader.getType();
     if (fType == Type::kRadial) {
+        // Map center to (0, 0)
+        matrix.postTranslate(-shader.getStartCenter().fX, -shader.getStartCenter().fY);
+
+        // scale |fDiffRadius| to 1
         SkScalar dr = shader.getDiffRadius();
-        // Map center to (0, 0) and scale dr to 1
-        matrix.postTranslate(-shader.getStartCenter().fX, -shader.getStartCenter().fY);
         matrix.postScale(1 / dr, 1 / dr);
         fRadius0 = shader.getStartRadius() / dr;
-        fDiffRadius = 1;
+        fDiffRadius = dr < 0 ? -1 : 1;
     } else if (fType == Type::kStrip) {
         fRadius0 = shader.getStartRadius() / shader.getCenterX1();
         fDiffRadius = 0;
         matrix.postConcat(shader.getGradientMatrix());
     } else if (fType == Type::kFocal) {
         fFocalData = shader.getFocalData();
         matrix.postConcat(shader.getGradientMatrix());
     }
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -30,17 +30,17 @@ fuzzy(180,3) == grid-abspos-items-002.ht
 == grid-abspos-items-013.html grid-abspos-items-013-ref.html
 == grid-abspos-items-014.html grid-abspos-items-014-ref.html
 == grid-abspos-items-015.html grid-abspos-items-015-ref.html
 == grid-order-abspos-items-001.html grid-order-abspos-items-001-ref.html
 == grid-order-placement-auto-001.html grid-order-placement-auto-001-ref.html
 fuzzy-if(skiaContent,1,200) == grid-order-placement-definite-001.html grid-order-placement-definite-001-ref.html
 skip-if(Android) == grid-placement-definite-implicit-001.html grid-placement-definite-implicit-001-ref.html
 == grid-placement-definite-implicit-002.html grid-placement-definite-implicit-002-ref.html
-skip-if(Android) fuzzy-if(winWidget,1,32) == grid-placement-auto-implicit-001.html grid-placement-auto-implicit-001-ref.html
+fuzzy-if(skiaContent,64,1) skip-if(Android) == grid-placement-auto-implicit-001.html grid-placement-auto-implicit-001-ref.html
 == grid-placement-abspos-implicit-001.html grid-placement-abspos-implicit-001-ref.html
 == rtl-grid-placement-definite-001.html rtl-grid-placement-definite-001-ref.html
 == rtl-grid-placement-auto-row-sparse-001.html rtl-grid-placement-auto-row-sparse-001-ref.html
 == vlr-grid-placement-auto-row-sparse-001.html vlr-grid-placement-auto-row-sparse-001-ref.html
 == vrl-grid-placement-auto-row-sparse-001.html vrl-grid-placement-auto-row-sparse-001-ref.html
 == grid-relpos-items-001.html grid-relpos-items-001-ref.html
 == grid-item-sizing-percent-001.html grid-item-sizing-percent-001-ref.html
 fails == grid-item-sizing-percent-002.html grid-item-sizing-percent-002-ref.html # bug 1434397