Rename SkDevice::ctm() to localToDevice()

This helps differentiate the SkCanvas' total CTM
(canvas transform matrix), from the device-specific transform matrix
that maps draws from local to device space.

Bug: 9545
Change-Id: I67f40d9b1bc92377bf0520fbc9245f1058e179e6
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/249424
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 9bf45a0..285ff54 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -91,7 +91,7 @@
                 //        fSrcBounds = devBounds.roundOut();
                 // The problem being that the promotion of clipR to SkRect was unreliable
                 //
-                fSrcBounds = dev->ctm().mapRect(*bounds).roundOut();
+                fSrcBounds = dev->localToDevice().mapRect(*bounds).roundOut();
                 if (fSrcBounds.intersect(clipR)) {
                     // Check again, now that we have computed srcbounds.
                     fNeedsTiling = fSrcBounds.right() > kMaxDim || fSrcBounds.bottom() > kMaxDim;
@@ -113,7 +113,7 @@
         } else {
             // don't reference fSrcBounds, as it may not have been set
             fDraw.fDst = fRootPixmap;
-            fDraw.fMatrix = &dev->ctm();
+            fDraw.fMatrix = &dev->localToDevice();
             fDraw.fRC = &dev->fRCStack.rc();
             fOrigin.set(0, 0);
 
@@ -165,7 +165,7 @@
         SkASSERT_RELEASE(success);
         // now don't use bounds, since fDst has the clipped dimensions.
 
-        fTileMatrix = fDevice->ctm();
+        fTileMatrix = fDevice->localToDevice();
         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()),
@@ -192,7 +192,7 @@
             // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
             fDst.reset(dev->imageInfo(), nullptr, 0);
         }
-        fMatrix = &dev->ctm();
+        fMatrix = &dev->localToDevice();
         fRC = &dev->fRCStack.rc();
         fCoverage = dev->accessCoverage();
     }
@@ -446,7 +446,7 @@
     }
     matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
 
-    LogDrawScaleFactor(this->ctm(), matrix, paint.getFilterQuality());
+    LogDrawScaleFactor(this->localToDevice(), matrix, paint.getFilterQuality());
 
     const SkRect* dstPtr = &dst;
     const SkBitmap* bitmapPtr = &bitmap;
@@ -565,7 +565,8 @@
     // todo: can we unify with similar adjustment in SkGpuDevice?
     SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
     if (paint->getMaskFilter()) {
-        paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithMatrix(this->ctm()));
+        paint.writable()->setMaskFilter(
+                paint->getMaskFilter()->makeWithMatrix(this->localToDevice()));
     }
 
     // hack to test coverage
@@ -603,20 +604,20 @@
 public:
     SkAutoDeviceClipRestore(SkBaseDevice* device, const SkIRect& clip)
         : fDevice(device)
-        , fPrevCTM(device->ctm()) {
+        , fPrevLocalToDevice(device->localToDevice()) {
         fDevice->save();
-        fDevice->setCTM(SkMatrix::I());
+        fDevice->setLocalToDevice(SkMatrix::I());
         fDevice->clipRect(SkRect::Make(clip), SkClipOp::kIntersect, false);
-        fDevice->setCTM(fPrevCTM);
+        fDevice->setLocalToDevice(fPrevLocalToDevice);
     }
 
     ~SkAutoDeviceClipRestore() {
-        fDevice->restore(fPrevCTM);
+        fDevice->restoreLocal(fPrevLocalToDevice);
     }
 
 private:
     SkBaseDevice*  fDevice;
-    const SkMatrix fPrevCTM;
+    const SkMatrix fPrevLocalToDevice;
 };
 
 }  // anonymous ns
@@ -631,7 +632,7 @@
     if (SkImageFilter* filter = paint->getImageFilter()) {
         SkIPoint offset = SkIPoint::Make(0, 0);
         const SkMatrix matrix = SkMatrix::Concat(
-            SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm());
+            SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->localToDevice());
         const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y);
         sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
         SkImageFilter_Base::Context ctx(matrix, clipBounds, cache.get(), fBitmap.colorType(),
@@ -649,7 +650,8 @@
     }
 
     if (paint->getMaskFilter()) {
-        paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithMatrix(this->ctm()));
+        paint.writable()->setMaskFilter(
+                paint->getMaskFilter()->makeWithMatrix(this->localToDevice()));
     }
 
     if (!clipImage) {
@@ -666,7 +668,7 @@
         return;
     }
 
-    const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix);
+    const SkMatrix totalMatrix = SkMatrix::Concat(this->localToDevice(), clipMatrix);
     SkRect clipBounds;
     totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds()));
     const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y);
@@ -708,7 +710,7 @@
         shaderMatrix = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y());
     }
 
-    SkAutoDeviceCTMRestore adctmr(this, maskMatrix);
+    SkAutoDeviceTransformRestore adr(this, maskMatrix);
     paint.writable()->setShader(srcImage->makeShader(&shaderMatrix));
     this->drawImageRect(mask.get(), nullptr,
                         SkRect::MakeXYWH(maskBounds.x(), maskBounds.y(),
@@ -756,15 +758,15 @@
 }
 
 void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
-    fRCStack.clipRect(this->ctm(), rect, op, aa);
+    fRCStack.clipRect(this->localToDevice(), rect, op, aa);
 }
 
 void SkBitmapDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
-    fRCStack.clipRRect(this->ctm(), rrect, op, aa);
+    fRCStack.clipRRect(this->localToDevice(), rrect, op, aa);
 }
 
 void SkBitmapDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
-    fRCStack.clipPath(this->ctm(), path, op, aa);
+    fRCStack.clipPath(this->localToDevice(), path, op, aa);
 }
 
 void SkBitmapDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) {
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index b31ee31..90335e87 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1028,11 +1028,11 @@
 
         // Manually setting the device's CTM requires accounting for the device's origin.
         // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
-        // before filtering the backdrop device, and if SkAutoDeviceCTMRestore had a way to accept
+        // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
         // a global CTM instead of a device CTM.
         SkMatrix dstCTM = toRoot;
         dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
-        SkAutoDeviceCTMRestore acr(dst, dstCTM);
+        SkAutoDeviceTransformRestore adr(dst, dstCTM);
 
         // And because devices don't have a special-image draw function that supports arbitrary
         // matrices, we are abusing the asImage() functionality here...
@@ -2434,7 +2434,7 @@
         const SkPaint& pnt = draw.paint();
         if (special) {
             SkPoint pt;
-            iter.fDevice->ctm().mapXY(x, y, &pt);
+            iter.fDevice->localToDevice().mapXY(x, y, &pt);
             iter.fDevice->drawSpecial(special.get(),
                                       SkScalarRoundToInt(pt.fX),
                                       SkScalarRoundToInt(pt.fY), pnt,
@@ -2512,7 +2512,7 @@
         const SkPaint& pnt = draw.paint();
         if (special) {
             SkPoint pt;
-            iter.fDevice->ctm().mapXY(x, y, &pt);
+            iter.fDevice->localToDevice().mapXY(x, y, &pt);
             iter.fDevice->drawSpecial(special.get(),
                                       SkScalarRoundToInt(pt.fX),
                                       SkScalarRoundToInt(pt.fY), pnt,
@@ -3003,7 +3003,7 @@
 }
 
 const SkMatrix& SkCanvas::LayerIter::matrix() const {
-    return fImpl->fDevice->ctm();
+    return fImpl->fDevice->localToDevice();
 }
 
 const SkPaint& SkCanvas::LayerIter::paint() const {
diff --git a/src/core/SkClipStackDevice.cpp b/src/core/SkClipStackDevice.cpp
index 72bfd1b..6734756 100644
--- a/src/core/SkClipStackDevice.cpp
+++ b/src/core/SkClipStackDevice.cpp
@@ -28,15 +28,15 @@
 }
 
 void SkClipStackDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
-    fClipStack.clipRect(rect, this->ctm(), op, aa);
+    fClipStack.clipRect(rect, this->localToDevice(), op, aa);
 }
 
 void SkClipStackDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
-    fClipStack.clipRRect(rrect, this->ctm(), op, aa);
+    fClipStack.clipRRect(rrect, this->localToDevice(), op, aa);
 }
 
 void SkClipStackDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
-    fClipStack.clipPath(path, this->ctm(), op, aa);
+    fClipStack.clipPath(path, this->localToDevice(), op, aa);
 }
 
 void SkClipStackDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) {
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 094f273..b1d64b7 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -37,19 +37,19 @@
     , fSurfaceProps(surfaceProps)
 {
     fOrigin = {0, 0};
-    fCTM.reset();
+    fLocalToDevice.reset();
 }
 
 void SkBaseDevice::setOrigin(const SkMatrix& globalCTM, int x, int y) {
     fOrigin.set(x, y);
-    fCTM = globalCTM;
-    fCTM.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+    fLocalToDevice = globalCTM;
+    fLocalToDevice.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
 }
 
 void SkBaseDevice::setGlobalCTM(const SkMatrix& ctm) {
-    fCTM = ctm;
+    fLocalToDevice = ctm;
     if (fOrigin.fX | fOrigin.fY) {
-        fCTM.postTranslate(-SkIntToScalar(fOrigin.fX), -SkIntToScalar(fOrigin.fY));
+        fLocalToDevice.postTranslate(-SkIntToScalar(fOrigin.fX), -SkIntToScalar(fOrigin.fY));
     }
 }
 
@@ -83,12 +83,12 @@
 }
 
 void SkBaseDevice::drawRegion(const SkRegion& region, const SkPaint& paint) {
-    const SkMatrix& ctm = this->ctm();
-    bool isNonTranslate = ctm.getType() & ~(SkMatrix::kTranslate_Mask);
+    const SkMatrix& localToDevice = this->localToDevice();
+    bool isNonTranslate = localToDevice.getType() & ~(SkMatrix::kTranslate_Mask);
     bool complexPaint = paint.getStyle() != SkPaint::kFill_Style || paint.getMaskFilter() ||
                         paint.getPathEffect();
-    bool antiAlias = paint.isAntiAlias() && (!is_int(ctm.getTranslateX()) ||
-                                             !is_int(ctm.getTranslateY()));
+    bool antiAlias = paint.isAntiAlias() && (!is_int(localToDevice.getTranslateX()) ||
+                                             !is_int(localToDevice.getTranslateY()));
     if (isNonTranslate || complexPaint || antiAlias) {
         SkPath path;
         region.getBoundaryPath(&path);
@@ -125,7 +125,7 @@
 
 void SkBaseDevice::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
                              const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) {
-    SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &this->ctm());
+    SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &this->localToDevice());
     auto vertices = SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height(),
                                                this->imageInfo().colorSpace());
     if (vertices) {
@@ -270,7 +270,7 @@
     SkASSERT(!paint.getPathEffect());
 
     SkPaint entryPaint = paint;
-    const SkMatrix baseCTM = this->ctm();
+    const SkMatrix baseLocalToDevice = this->localToDevice();
     int clipIndex = 0;
     for (int i = 0; i < count; ++i) {
         // TODO: Handle per-edge AA. Right now this mirrors the SkiaRenderer component of Chrome
@@ -283,8 +283,8 @@
         SkASSERT(images[i].fMatrixIndex < 0 || preViewMatrices);
         if (images[i].fMatrixIndex >= 0) {
             this->save();
-            this->setGlobalCTM(SkMatrix::Concat(
-                    baseCTM, preViewMatrices[images[i].fMatrixIndex]));
+            this->setLocalToDevice(SkMatrix::Concat(
+                    baseLocalToDevice, preViewMatrices[images[i].fMatrixIndex]));
             needsRestore = true;
         }
 
@@ -303,7 +303,7 @@
         this->drawImageRect(images[i].fImage.get(), &images[i].fSrcRect, images[i].fDstRect,
                             entryPaint, constraint);
         if (needsRestore) {
-            this->restore(baseCTM);
+            this->restoreLocal(baseLocalToDevice);
         }
     }
 }
@@ -366,8 +366,8 @@
 void SkBaseDevice::drawGlyphRunRSXform(const SkFont& font, const SkGlyphID glyphs[],
                                        const SkRSXform xform[], int count, SkPoint origin,
                                        const SkPaint& paint) {
-    const SkMatrix originalCTM = this->ctm();
-    if (!originalCTM.isFinite() || !SkScalarIsFinite(font.getSize()) ||
+    const SkMatrix originalLocalToDevice = this->localToDevice();
+    if (!originalLocalToDevice.isFinite() || !SkScalarIsFinite(font.getSize()) ||
         !SkScalarIsFinite(font.getScaleX()) ||
         !SkScalarIsFinite(font.getSkewX())) {
         return;
@@ -387,8 +387,8 @@
         glyphID = glyphs[i];
         // now "glyphRun" is pointing at the current glyphID
 
-        SkMatrix ctm;
-        ctm.setRSXform(xform[i]).postTranslate(origin.fX, origin.fY);
+        SkMatrix glyphToDevice;
+        glyphToDevice.setRSXform(xform[i]).postTranslate(origin.fX, origin.fY);
 
         // We want to rotate each glyph by the rsxform, but we don't want to rotate "space"
         // (i.e. the shader that cares about the ctm) so we have to undo our little ctm trick
@@ -397,19 +397,19 @@
         auto shader = transformingPaint.getShader();
         if (shader) {
             SkMatrix inverse;
-            if (ctm.invert(&inverse)) {
+            if (glyphToDevice.invert(&inverse)) {
                 transformingPaint.setShader(shader->makeWithLocalMatrix(inverse));
             } else {
                 transformingPaint.setShader(nullptr);  // can't handle this xform
             }
         }
 
-        ctm.setConcat(originalCTM, ctm);
-        this->setCTM(ctm);
+        glyphToDevice.postConcat(originalLocalToDevice);
+        this->setLocalToDevice(glyphToDevice);
 
         this->drawGlyphRunList(SkGlyphRunList{glyphRun, transformingPaint});
     }
-    this->setCTM(originalCTM);
+    this->setLocalToDevice(originalLocalToDevice);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index ce2629f..c03e9eb 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -103,6 +103,10 @@
         this->onRestore();
         this->setGlobalCTM(ctm);
     }
+    void restoreLocal(const SkMatrix& localToDevice) {
+        this->onRestore();
+        this->setLocalToDevice(localToDevice);
+    }
     void clipRect(const SkRect& rect, SkClipOp op, bool aa) {
         this->onClipRect(rect, op, aa);
     }
@@ -120,9 +124,9 @@
     }
     bool clipIsWideOpen() const;
 
-    const SkMatrix& ctm() const { return fCTM; }
-    void setCTM(const SkMatrix& ctm) {
-        fCTM = ctm;
+    const SkMatrix& localToDevice() const { return fLocalToDevice; }
+    void setLocalToDevice(const SkMatrix& localToDevice) {
+        fLocalToDevice = localToDevice;
     }
     void setGlobalCTM(const SkMatrix& ctm);
     virtual void validateDevBounds(const SkIRect&) {}
@@ -372,7 +376,7 @@
     SkIPoint             fOrigin;
     const SkImageInfo    fInfo;
     const SkSurfaceProps fSurfaceProps;
-    SkMatrix             fCTM;
+    SkMatrix             fLocalToDevice;
 
     typedef SkRefCnt INHERITED;
 };
@@ -432,21 +436,21 @@
     typedef SkBaseDevice INHERITED;
 };
 
-class SkAutoDeviceCTMRestore : SkNoncopyable {
+class SkAutoDeviceTransformRestore : SkNoncopyable {
 public:
-    SkAutoDeviceCTMRestore(SkBaseDevice* device, const SkMatrix& ctm)
+    SkAutoDeviceTransformRestore(SkBaseDevice* device, const SkMatrix& localToDevice)
         : fDevice(device)
-        , fPrevCTM(device->ctm())
+        , fPrevLocalToDevice(device->localToDevice())
     {
-        fDevice->setCTM(ctm);
+        fDevice->setLocalToDevice(localToDevice);
     }
-    ~SkAutoDeviceCTMRestore() {
-        fDevice->setCTM(fPrevCTM);
+    ~SkAutoDeviceTransformRestore() {
+        fDevice->setLocalToDevice(fPrevLocalToDevice);
     }
 
 private:
     SkBaseDevice*   fDevice;
-    const SkMatrix  fPrevCTM;
+    const SkMatrix  fPrevLocalToDevice;
 };
 
 #endif
diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp
index 021a476..2e0bb3e 100644
--- a/src/core/SkRemoteGlyphCache.cpp
+++ b/src/core/SkRemoteGlyphCache.cpp
@@ -333,7 +333,7 @@
         GrTextContext::SanitizeOptions(&options);
 
         fPainter.processGlyphRunList(glyphRunList,
-                                     this->ctm(),
+                                     this->localToDevice(),
                                      this->surfaceProps(),
                                      fDFTSupport,
                                      options,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 77b9963..22b2238 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -162,7 +162,7 @@
     SkASSERT(srcImg->isTextureBacked());
     SkASSERT(filter);
 
-    SkMatrix matrix = this->ctm();
+    SkMatrix matrix = this->localToDevice();
     matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
     const SkIRect clipBounds = this->devClipBounds().makeOffset(-left, -top);
     sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
@@ -265,12 +265,12 @@
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPaint", fContext.get());
 
     GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint, this->ctm(),
-                          &grPaint)) {
+    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
+                          this->localToDevice(), &grPaint)) {
         return;
     }
 
-    fRenderTargetContext->drawPaint(this->clip(), std::move(grPaint), this->ctm());
+    fRenderTargetContext->drawPaint(this->clip(), std::move(grPaint), this->localToDevice());
 }
 
 static inline GrPrimitiveType point_mode_to_primitive_type(SkCanvas::PointMode mode) {
@@ -298,7 +298,7 @@
         GrStyle style(paint, SkPaint::kStroke_Style);
         GrPaint grPaint;
         if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
-                              this->ctm(), &grPaint)) {
+                              this->localToDevice(), &grPaint)) {
             return;
         }
         SkPath path;
@@ -306,21 +306,21 @@
         path.moveTo(pts[0]);
         path.lineTo(pts[1]);
         fRenderTargetContext->drawPath(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
-                                       this->ctm(), path, style);
+                                       this->localToDevice(), path, style);
         return;
     }
 
     SkScalar scales[2];
-    bool isHairline = (0 == width) || (1 == width && this->ctm().getMinMaxScales(scales) &&
-                                       SkScalarNearlyEqual(scales[0], 1.f) &&
-                                       SkScalarNearlyEqual(scales[1], 1.f));
+    bool isHairline = (0 == width) ||
+                       (1 == width && this->localToDevice().getMinMaxScales(scales) &&
+                        SkScalarNearlyEqual(scales[0], 1.f) && SkScalarNearlyEqual(scales[1], 1.f));
     // we only handle non-antialiased hairlines and paints without path effects or mask filters,
     // else we let the SkDraw call our drawPath()
     if (!isHairline || paint.getPathEffect() || paint.getMaskFilter() || paint.isAntiAlias()) {
         SkRasterClip rc(this->devClipBounds());
         SkDraw draw;
         draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(this->width(), this->height()), nullptr, 0);
-        draw.fMatrix = &this->ctm();
+        draw.fMatrix = &this->localToDevice();
         draw.fRC = &rc;
         draw.drawPoints(mode, count, pts, paint, this);
         return;
@@ -328,7 +328,7 @@
 
     GrPrimitiveType primitiveType = point_mode_to_primitive_type(mode);
 
-    const SkMatrix* viewMatrix = &this->ctm();
+    const SkMatrix* viewMatrix = &this->localToDevice();
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     // This offsetting in device space matches the expectations of the Android framework for non-AA
     // points and lines.
@@ -368,18 +368,18 @@
         GrShape shape(rect, style);
 
         GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
-                                             this->clip(), paint, this->ctm(), shape);
+                                             this->clip(), paint, this->localToDevice(), shape);
         return;
     }
 
     GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint, this->ctm(),
-                          &grPaint)) {
+    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
+                          this->localToDevice(), &grPaint)) {
         return;
     }
 
     fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
-                                   this->ctm(), rect, &style);
+                                   this->localToDevice(), rect, &style);
 }
 
 void SkGpuDevice::drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
@@ -401,11 +401,11 @@
     if (clip) {
         // Use fillQuadWithEdgeAA
         fRenderTargetContext->fillQuadWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA,
-                                                 this->ctm(), clip, nullptr);
+                                                 this->localToDevice(), clip, nullptr);
     } else {
         // Use fillRectWithEdgeAA to preserve mathematical properties of dst being rectangular
         fRenderTargetContext->fillRectWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA,
-                                                 this->ctm(), rect);
+                                                 this->localToDevice(), rect);
     }
 }
 
@@ -429,20 +429,20 @@
         GrShape shape(rrect, style);
 
         GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
-                                             this->clip(), paint, this->ctm(), shape);
+                                             this->clip(), paint, this->localToDevice(), shape);
         return;
     }
 
     SkASSERT(!style.pathEffect());
 
     GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint, this->ctm(),
-                          &grPaint)) {
+    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
+                          this->localToDevice(), &grPaint)) {
         return;
     }
 
     fRenderTargetContext->drawRRect(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
-                                    this->ctm(), rrect, style);
+                                    this->localToDevice(), rrect, style);
 }
 
 
@@ -462,12 +462,13 @@
     if (stroke.isFillStyle() && !paint.getMaskFilter() && !paint.getPathEffect()) {
         GrPaint grPaint;
         if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
-                              this->ctm(), &grPaint)) {
+                              this->localToDevice(), &grPaint)) {
             return;
         }
 
         fRenderTargetContext->drawDRRect(this->clip(), std::move(grPaint),
-                                         GrAA(paint.isAntiAlias()), this->ctm(), outer, inner);
+                                         GrAA(paint.isAntiAlias()), this->localToDevice(),
+                                         outer, inner);
         return;
     }
 
@@ -482,7 +483,7 @@
     GrShape shape(path, paint);
 
     GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
-                                         paint, this->ctm(), shape);
+                                         paint, this->localToDevice(), shape);
 }
 
 
@@ -497,13 +498,13 @@
     }
 
     GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint, this->ctm(),
-                          &grPaint)) {
+    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
+                          this->localToDevice(), &grPaint)) {
         return;
     }
 
     fRenderTargetContext->drawRegion(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
-                                     this->ctm(), region, GrStyle(paint));
+                                     this->localToDevice(), region, GrStyle(paint));
 }
 
 void SkGpuDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
@@ -517,13 +518,13 @@
     }
 
     GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint, this->ctm(),
-                          &grPaint)) {
+    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
+                          this->localToDevice(), &grPaint)) {
         return;
     }
 
     fRenderTargetContext->drawOval(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
-                                   this->ctm(), oval, GrStyle(paint));
+                                   this->localToDevice(), oval, GrStyle(paint));
 }
 
 void SkGpuDevice::drawArc(const SkRect& oval, SkScalar startAngle,
@@ -535,13 +536,13 @@
         return;
     }
     GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint, this->ctm(),
-                          &grPaint)) {
+    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
+                          this->localToDevice(), &grPaint)) {
         return;
     }
 
     fRenderTargetContext->drawArc(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
-                                  this->ctm(), oval, startAngle, sweepAngle, useCenter,
+                                  this->localToDevice(), oval, startAngle, sweepAngle, useCenter,
                                   GrStyle(paint));
 }
 
@@ -588,7 +589,7 @@
 
     SkMatrix local = m;
 
-    m.postConcat(this->ctm());
+    m.postConcat(this->localToDevice());
 
     GrPaint grPaint;
     if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), newPaint, m,
@@ -606,9 +607,9 @@
         SkPoint points[2];
         if (SkPaint::kStroke_Style == paint.getStyle() && paint.getStrokeWidth() > 0 &&
             !paint.getMaskFilter() && SkPaint::kRound_Cap != paint.getStrokeCap() &&
-            this->ctm().preservesRightAngles() && origSrcPath.isLine(points)) {
+            this->localToDevice().preservesRightAngles() && origSrcPath.isLine(points)) {
             // Path-based stroking looks better for thin rects
-            SkScalar strokeWidth = this->ctm().getMaxScale() * paint.getStrokeWidth();
+            SkScalar strokeWidth = this->localToDevice().getMaxScale() * paint.getStrokeWidth();
             if (strokeWidth >= 1.0f) {
                 // Round capping support is currently disabled b.c. it would require a RRect
                 // GrDrawOp that takes a localMatrix.
@@ -622,11 +623,11 @@
     if (!paint.getMaskFilter()) {
         GrPaint grPaint;
         if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), paint,
-                              this->ctm(), &grPaint)) {
+                              this->localToDevice(), &grPaint)) {
             return;
         }
         fRenderTargetContext->drawPath(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
-                                       this->ctm(), origSrcPath, GrStyle(paint));
+                                       this->localToDevice(), origSrcPath, GrStyle(paint));
         return;
     }
 
@@ -634,7 +635,7 @@
     GrShape shape(origSrcPath, paint);
 
     GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
-                                         paint, this->ctm(), shape);
+                                         paint, this->localToDevice(), shape);
 }
 
 static const int kBmpSmallTileSize = 1 << 10;
@@ -1028,7 +1029,7 @@
         return;
     }
 
-    SkMatrix ctm = this->ctm();
+    SkMatrix ctm = this->localToDevice();
     ctm.postTranslate(-SkIntToScalar(left), -SkIntToScalar(top));
 
     SkPaint tmpUnfiltered(paint);
@@ -1163,7 +1164,7 @@
         GrSamplerState sampleState;
         bool doBicubic;
         GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
-                bitmap.width(), bitmap.height(), paint.getFilterQuality(), this->ctm(),
+                bitmap.width(), bitmap.height(), paint.getFilterQuality(), this->localToDevice(),
                 srcToDstMatrix, fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
 
         int tileFilterPad;
@@ -1178,16 +1179,16 @@
         sampleState.setFilterMode(textureFilterMode);
 
         int maxTileSizeForFilter = this->caps()->maxTileSize() - 2 * tileFilterPad;
-        if (this->shouldTileImageID(bitmap.getGenerationID(), bitmap.getSubset(), this->ctm(),
-                                    srcToDstMatrix, sampleState, src, maxTileSizeForFilter,
-                                    &tileSize, &clippedSrcRect)) {
-            this->drawTiledBitmap(bitmap, this->ctm(), srcToDstMatrix, *src, clippedSrcRect,
+        if (this->shouldTileImageID(bitmap.getGenerationID(), bitmap.getSubset(),
+                                    this->localToDevice(), srcToDstMatrix, sampleState, src,
+                                    maxTileSizeForFilter, &tileSize, &clippedSrcRect)) {
+            this->drawTiledBitmap(bitmap, this->localToDevice(), srcToDstMatrix, *src, clippedSrcRect,
                                   sampleState, paint, constraint, tileSize, doBicubic);
             return;
         }
     }
     GrBitmapTextureMaker maker(fContext.get(), bitmap);
-    this->drawTextureProducer(&maker, src, dst, constraint, this->ctm(), paint, true);
+    this->drawTextureProducer(&maker, src, dst, constraint, this->localToDevice(), paint, true);
 }
 
 sk_sp<SkSpecialImage> SkGpuDevice::makeSpecial(const SkBitmap& bitmap) {
@@ -1369,7 +1370,7 @@
     auto csxf = GrColorSpaceXform::Make(producer->colorSpace(), producer->alphaType(),
                                         dstColorSpace,          kPremul_SkAlphaType);
 
-    fRenderTargetContext->drawImageLattice(this->clip(), std::move(grPaint), this->ctm(),
+    fRenderTargetContext->drawImageLattice(this->clip(), std::move(grPaint), this->localToDevice(),
                                            std::move(proxy), producer->colorType(), std::move(csxf),
                                            filter, std::move(iter), dst);
 }
@@ -1490,7 +1491,7 @@
     GrPrimitiveType primitiveType = GrPrimitiveType::kLines;
     fRenderTargetContext->drawVertices(this->clip(),
                                        std::move(grPaint),
-                                       this->ctm(),
+                                       this->localToDevice(),
                                        builder.detach(),
                                        bones,
                                        boneCount,
@@ -1513,11 +1514,11 @@
                                 paint);
         return;
     }
-    if (!init_vertices_paint(fContext.get(), fRenderTargetContext->colorInfo(), paint, this->ctm(),
-                             mode, hasTexs, hasColors, &grPaint)) {
+    if (!init_vertices_paint(fContext.get(), fRenderTargetContext->colorInfo(), paint,
+                             this->localToDevice(), mode, hasTexs, hasColors, &grPaint)) {
         return;
     }
-    fRenderTargetContext->drawVertices(this->clip(), std::move(grPaint), this->ctm(),
+    fRenderTargetContext->drawVertices(this->clip(), std::move(grPaint), this->localToDevice(),
                                        sk_ref_sp(const_cast<SkVertices*>(vertices)),
                                        bones, boneCount);
 }
@@ -1529,7 +1530,7 @@
     ASSERT_SINGLE_OWNER
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawShadow", fContext.get());
 
-    if (!fRenderTargetContext->drawFastShadow(this->clip(), this->ctm(), path, rec)) {
+    if (!fRenderTargetContext->drawFastShadow(this->clip(), this->localToDevice(), path, rec)) {
         // failed to find an accelerated case
         this->INHERITED::drawShadow(path, rec);
     }
@@ -1549,18 +1550,18 @@
     GrPaint grPaint;
     if (colors) {
         if (!SkPaintToGrPaintWithXfermode(this->context(), fRenderTargetContext->colorInfo(), p,
-                                          this->ctm(), (SkBlendMode)mode, &grPaint)) {
+                                          this->localToDevice(), (SkBlendMode)mode, &grPaint)) {
             return;
         }
     } else {
-        if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), p, this->ctm(),
-                              &grPaint)) {
+        if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorInfo(), p,
+                              this->localToDevice(), &grPaint)) {
             return;
         }
     }
 
     fRenderTargetContext->drawAtlas(
-            this->clip(), std::move(grPaint), this->ctm(), count, xform, texRect, colors);
+            this->clip(), std::move(grPaint), this->localToDevice(), count, xform, texRect, colors);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1570,12 +1571,11 @@
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawGlyphRunList", fContext.get());
 
     // Check for valid input
-    const SkMatrix& ctm = this->ctm();
-    if (!ctm.isFinite() || !glyphRunList.allFontsFinite()) {
+    if (!this->localToDevice().isFinite() || !glyphRunList.allFontsFinite()) {
         return;
     }
 
-    fRenderTargetContext->drawGlyphRunList(this->clip(), ctm, glyphRunList);
+    fRenderTargetContext->drawGlyphRunList(this->clip(), this->localToDevice(), glyphRunList);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 20c4976..809ac10 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -376,7 +376,7 @@
     bool attemptDrawTexture = !useDecal; // rtc->drawTexture() only clamps
 
     // Get final CTM matrix
-    SkMatrix ctm = this->ctm();
+    SkMatrix ctm = this->localToDevice();
     if (preViewMatrix) {
         ctm.preConcat(*preViewMatrix);
     }
@@ -497,8 +497,8 @@
                     set[base].fImage->colorSpace(), set[base].fImage->alphaType(),
                     fRenderTargetContext->colorInfo().colorSpace(), kPremul_SkAlphaType);
             fRenderTargetContext->drawTextureSet(this->clip(), textures.get() + base, n,
-                                                 filter, mode, GrAA::kYes, constraint, this->ctm(),
-                                                 std::move(textureXform));
+                                                 filter, mode, GrAA::kYes, constraint,
+                                                 this->localToDevice(), std::move(textureXform));
         }
     };
     int dstClipIndex = 0;
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 2ac998f..d82df1c 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -225,7 +225,7 @@
             fDevice->setUpContentEntry(clipStack, matrix, paint, textScale, &fDstFormXObject);
     }
     ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, SkScalar textScale = 0)
-        : ScopedContentEntry(dev, &dev->cs(), dev->ctm(), paint, textScale) {}
+        : ScopedContentEntry(dev, &dev->cs(), dev->localToDevice(), paint, textScale) {}
 
     ~ScopedContentEntry() {
         if (fContentStream) {
@@ -321,7 +321,7 @@
             return;
         }
         if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) {
-            SkPoint p = deviceOffset + this->ctm().mapXY(rect.x(), rect.y());
+            SkPoint p = deviceOffset + this->localToDevice().mapXY(rect.x(), rect.y());
             pageXform.mapPoints(&p, 1);
             auto pg = fDocument->currentPage();
             fDocument->fNamedDestinations.push_back(SkPDFNamedDestination{sk_ref_sp(value), p, pg});
@@ -330,7 +330,7 @@
     }
     // Convert to path to handle non-90-degree rotations.
     SkPath path = to_path(rect);
-    path.transform(this->ctm(), &path);
+    path.transform(this->localToDevice(), &path);
     SkPath clip;
     (void)this->cs().asPath(&clip);
     Op(clip, path, kIntersect_SkPathOp, &path);
@@ -351,7 +351,7 @@
 
 void SkPDFDevice::drawPaint(const SkPaint& srcPaint) {
     SkMatrix inverse;
-    if (!this->ctm().invert(&inverse)) {
+    if (!this->localToDevice().invert(&inverse)) {
         return;
     }
     SkRect bbox = this->cs().bounds(this->bounds());
@@ -388,7 +388,7 @@
     // of multiple calls to setUpContentEntry it causes.
     if (paint->getPathEffect()) {
         draw_points(mode, count, points, *paint,
-                    this->devClipBounds(), this->ctm(), this);
+                    this->devClipBounds(), this->localToDevice(), this);
         return;
     }
 
@@ -449,23 +449,23 @@
 void SkPDFDevice::drawRect(const SkRect& rect, const SkPaint& paint) {
     SkRect r = rect;
     r.sort();
-    this->internalDrawPath(this->cs(), this->ctm(), to_path(r), paint, true);
+    this->internalDrawPath(this->cs(), this->localToDevice(), to_path(r), paint, true);
 }
 
 void SkPDFDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
     SkPath path;
     path.addRRect(rrect);
-    this->internalDrawPath(this->cs(), this->ctm(), path, paint, true);
+    this->internalDrawPath(this->cs(), this->localToDevice(), path, paint, true);
 }
 
 void SkPDFDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
     SkPath path;
     path.addOval(oval);
-    this->internalDrawPath(this->cs(), this->ctm(), path, paint, true);
+    this->internalDrawPath(this->cs(), this->localToDevice(), path, paint, true);
 }
 
 void SkPDFDevice::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
-    this->internalDrawPath(this->cs(), this->ctm(), path, paint, pathIsMutable);
+    this->internalDrawPath(this->cs(), this->localToDevice(), path, paint, pathIsMutable);
 }
 
 void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
@@ -613,7 +613,7 @@
                                 SkCanvas::SrcRectConstraint) {
     SkASSERT(image);
     this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))),
-                                src, dst, paint, this->ctm());
+                                src, dst, paint, this->localToDevice());
 }
 
 void SkPDFDevice::drawBitmapRect(const SkBitmap& bm,
@@ -622,7 +622,7 @@
                                  const SkPaint& paint,
                                  SkCanvas::SrcRectConstraint) {
     SkASSERT(!bm.drawsNothing());
-    this->internalDrawImageRect(SkKeyedImage(bm), src, dst, paint, this->ctm());
+    this->internalDrawImageRect(SkKeyedImage(bm), src, dst, paint, this->localToDevice());
 }
 
 void SkPDFDevice::drawSprite(const SkBitmap& bm, int x, int y, const SkPaint& paint) {
@@ -751,7 +751,7 @@
                       }
                       rec->fPos += 1; // move to the next glyph's position
                   }, &rec);
-    this->internalDrawPath(this->cs(), this->ctm(), path, runPaint, true);
+    this->internalDrawPath(this->cs(), this->localToDevice(), path, runPaint, true);
 
     SkFont transparentFont = glyphRun.font();
     transparentFont.setEmbolden(false); // Stop Recursion
@@ -760,11 +760,9 @@
     SkPaint transparent;
     transparent.setColor(SK_ColorTRANSPARENT);
 
-    if (this->ctm().hasPerspective()) {
-        SkMatrix prevCTM = this->ctm();
-        this->setCTM(SkMatrix::I());
+    if (this->localToDevice().hasPerspective()) {
+        SkAutoDeviceTransformRestore adr(this, SkMatrix::I());
         this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent);
-        this->setCTM(prevCTM);
     } else {
         this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent);
     }
@@ -800,7 +798,7 @@
     if (runPaint.getPathEffect()
         || runPaint.getMaskFilter()
         || glyphRunFont.isEmbolden()
-        || this->ctm().hasPerspective()
+        || this->localToDevice().hasPerspective()
         || SkPaint::kFill_Style != runPaint.getStyle()) {
         // Stroked Text doesn't work well with Type3 fonts.
         this->drawGlyphRunAsPath(glyphRun, offset, runPaint);
@@ -916,7 +914,7 @@
             // Do a glyph-by-glyph bounds-reject if positions are absolute.
             SkRect glyphBounds = get_glyph_bounds_device_space(
                     glyphs[index], textScaleX, textScaleY,
-                    xy + offset, this->ctm());
+                    xy + offset, this->localToDevice());
             if (glyphBounds.isEmpty()) {
                 if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
                     continue;
@@ -1081,7 +1079,7 @@
             // To be consistent with the raster output, hairline strokes
             // are rendered as non-inverted.
             modifiedPath.toggleInverseFillType();
-            this->internalDrawPath(this->cs(), this->ctm(), modifiedPath, paint, true);
+            this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, paint, true);
             return true;
         }
     }
@@ -1089,7 +1087,7 @@
     // Get bounds of clip in current transform space
     // (clip bounds are given in device space).
     SkMatrix transformInverse;
-    SkMatrix totalMatrix = this->ctm();
+    SkMatrix totalMatrix = this->localToDevice();
 
     if (!totalMatrix.invert(&transformInverse)) {
         return false;
@@ -1106,7 +1104,7 @@
         return false;
     }
 
-    this->internalDrawPath(this->cs(), this->ctm(), modifiedPath, noInversePaint, true);
+    this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, noInversePaint, true);
     return true;
 }
 
@@ -1578,7 +1576,7 @@
     if (paint->getMaskFilter()) {
         paint.writable()->setShader(imageSubset.image()->makeShader(&transform));
         SkPath path = to_path(dst); // handles non-integral clipping.
-        this->internalDrawPath(this->cs(), this->ctm(), path, *paint, true);
+        this->internalDrawPath(this->cs(), this->localToDevice(), path, *paint, true);
         return;
     }
     transform.postConcat(ctm);
@@ -1725,7 +1723,7 @@
     SkImageFilter* filter = paint.getImageFilter();
     if (filter) {
         SkIPoint offset = SkIPoint::Make(0, 0);
-        SkMatrix matrix = this->ctm();
+        SkMatrix matrix = this->localToDevice();
         matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
         const SkIRect clipBounds =
             this->cs().bounds(this->bounds()).roundOut().makeOffset(-x, -y);
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index 85e0b1b..7ee26f7 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -231,7 +231,7 @@
     const SkClipStack*  fClipStack;
 
     MxCp(const SkMatrix* mx, const SkClipStack* cs) : fMatrix(mx), fClipStack(cs) {}
-    MxCp(SkSVGDevice* device) : fMatrix(&device->ctm()), fClipStack(&device->cs()) {}
+    MxCp(SkSVGDevice* device) : fMatrix(&device->localToDevice()), fClipStack(&device->cs()) {}
 };
 
 class SkSVGDevice::AutoElement : ::SkNoncopyable {
@@ -764,7 +764,7 @@
     if (!strcmp(SkAnnotationKeys::URL_Key(), key) ||
         !strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
         this->cs().save();
-        this->cs().clipRect(rect, this->ctm(), kIntersect_SkClipOp, true);
+        this->cs().clipRect(rect, this->localToDevice(), kIntersect_SkClipOp, true);
         SkRect transformedRect = this->cs().bounds(this->getGlobalBounds());
         this->cs().restore();
         if (transformedRect.isEmpty()) {
@@ -910,14 +910,14 @@
     SkClipStack::AutoRestore ar(cs, false);
     if (srcOrNull && *srcOrNull != SkRect::Make(bm.bounds())) {
         cs->save();
-        cs->clipRect(dst, this->ctm(), kIntersect_SkClipOp, paint.isAntiAlias());
+        cs->clipRect(dst, this->localToDevice(), kIntersect_SkClipOp, paint.isAntiAlias());
     }
 
     SkMatrix adjustedMatrix;
     adjustedMatrix.setRectToRect(srcOrNull ? *srcOrNull : SkRect::Make(bm.bounds()),
                                  dst,
                                  SkMatrix::kFill_ScaleToFit);
-    adjustedMatrix.postConcat(this->ctm());
+    adjustedMatrix.postConcat(this->localToDevice());
 
     drawBitmapCommon(MxCp(&adjustedMatrix, cs), bm, paint);
 }
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index 1b47af5..f40854c 100644
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -571,10 +571,11 @@
             // For perspective shadows we've already computed the shadow in world space,
             // and we can't translate it without changing it. Otherwise we concat the
             // change in translation from the cached version.
-            SkAutoDeviceCTMRestore adr(
-                this,
-                hasPerspective ? SkMatrix::I()
-                               : SkMatrix::Concat(this->ctm(), SkMatrix::MakeTrans(tx, ty)));
+            SkAutoDeviceTransformRestore adr(
+                    this,
+                    hasPerspective ? SkMatrix::I()
+                                   : SkMatrix::Concat(this->localToDevice(),
+                                                      SkMatrix::MakeTrans(tx, ty)));
             this->drawVertices(vertices, nullptr, 0, mode, paint);
         }
     };
@@ -583,8 +584,8 @@
         return;
     }
 
-    SkMatrix viewMatrix = this->ctm();
-    SkAutoDeviceCTMRestore adr(this, SkMatrix::I());
+    SkMatrix viewMatrix = this->localToDevice();
+    SkAutoDeviceTransformRestore adr(this, SkMatrix::I());
 
     ShadowedPath shadowedPath(&path, &viewMatrix);
 
@@ -752,7 +753,7 @@
                                                                  &shadowMatrix, &radius)) {
                     return;
                 }
-                SkAutoDeviceCTMRestore adr(this, shadowMatrix);
+                SkAutoDeviceTransformRestore adr(this, shadowMatrix);
 
                 SkPaint paint;
                 paint.setColor(rec.fSpotColor);
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index cebf60d..7a4ff99 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -1179,7 +1179,7 @@
     }
 
     //Path the rect if we can't optimize it.
-    if (rect_must_be_pathed(paint, this->ctm())) {
+    if (rect_must_be_pathed(paint, this->localToDevice())) {
         SkPath tmp;
         tmp.addRect(r);
         tmp.setFillType(SkPath::kWinding_FillType);
@@ -1204,13 +1204,13 @@
     //Set the brushes.
     BOOL fill = FALSE;
     BOOL stroke = FALSE;
-    HRV(this->shadePath(shadedPath.get(), paint, this->ctm(), &fill, &stroke));
+    HRV(this->shadePath(shadedPath.get(), paint, this->localToDevice(), &fill, &stroke));
 
     bool xpsTransformsPath = true;
     //Transform the geometry.
     if (transformRect && xpsTransformsPath) {
         SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
-        HRV(this->createXpsTransform(this->ctm(), &xpsTransform));
+        HRV(this->createXpsTransform(this->localToDevice(), &xpsTransform));
         if (xpsTransform.get()) {
             HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
                  "Could not set transform for rect.");
@@ -1229,7 +1229,7 @@
             { r.fRight, r.fTop },
         };
         if (!xpsTransformsPath && transformRect) {
-            this->ctm().mapPoints(points, SK_ARRAY_COUNT(points));
+            this->localToDevice().mapPoints(points, SK_ARRAY_COUNT(points));
         }
         HRV(this->createXpsQuad(points, stroke, fill, &rectFigure));
     }
@@ -1495,7 +1495,7 @@
                                  || paint->getStyle() != SkPaint::kFill_Style;
 
     //Apply pre-path matrix [Platonic-path -> Skeletal-path].
-    SkMatrix matrix = this->ctm();
+    SkMatrix matrix = this->localToDevice();
     SkPath* skeletalPath = const_cast<SkPath*>(&platonicPath);
 
     //Apply path effect [Skeletal-path -> Fillable-path].
@@ -1545,7 +1545,7 @@
     BOOL stroke;
     HRV(this->shadePath(shadedPath.get(),
                         *paint,
-                        this->ctm(),
+                        this->localToDevice(),
                         &fill,
                         &stroke));
 
@@ -1898,7 +1898,8 @@
         }
 
         TypefaceUse* typeface;
-        if (FAILED(CreateTypefaceUse(font, &typeface)) || text_must_be_pathed(paint, this->ctm())) {
+        if (FAILED(CreateTypefaceUse(font, &typeface)) ||
+            text_must_be_pathed(paint, this->localToDevice())) {
             SkPath path;
             //TODO: make this work, Draw currently does not handle as well.
             //paint.getTextPath(text, byteLength, x, y, &path);
@@ -1937,7 +1938,7 @@
                       &origin,
                       SkScalarToFLOAT(font.getSize()),
                       XPS_STYLE_SIMULATION_NONE,
-                      this->ctm(),
+                      this->localToDevice(),
                       paint));
     }
 }
diff --git a/tests/LayerDrawLooperTest.cpp b/tests/LayerDrawLooperTest.cpp
index c79ec99..05044a0 100644
--- a/tests/LayerDrawLooperTest.cpp
+++ b/tests/LayerDrawLooperTest.cpp
@@ -32,7 +32,7 @@
     }
 
     void drawRect(const SkRect& r, const SkPaint& paint) override {
-        fLastMatrix = this->ctm();
+        fLastMatrix = this->localToDevice();
         this->INHERITED::drawRect(r, paint);
     }