Expose camera matrix in SkCanvas

3 new getters:
- localToWorld
- localToCamera
- localToDevice (same as total-matrix)

The current tracking minimizes overhead, by using a computed inverse to
produce the localToWorld/Camera. This can be change as needed in the
future (more precision, but more memory/overhead), but for now is
sufficient to try out the new APIs.

Change-Id: I85440318f36dca935124b782e110fe9c0152ae7a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/264648
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index c43ad4d..cc46277 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -736,10 +736,8 @@
 int SkCanvas::saveCamera(const SkMatrix44& projection, const SkMatrix44& camera) {
     // TODO: add a virtual for this, and update clients (e.g. chrome)
     int n = this->save();
-    this->concat(projection);
-    // TODO: remember this point in the matrix stack, so we can communicate it to shaders
-    //       that want to perform lighting.
-    this->concat(camera);
+    this->concat(projection * camera);
+    fCameraStack.push_back(CameraRec(fMCRec, camera));
     return n;
 }
 
@@ -1269,6 +1267,10 @@
     // move this out before we do the actual restore
     auto backImage = std::move(fMCRec->fBackImage);
 
+    if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
+        fCameraStack.pop_back();
+    }
+
     // now do the normal restore()
     fMCRec->~MCRec();       // balanced in save()
     fMCStack.pop_back();
@@ -1435,7 +1437,7 @@
 void SkCanvas::translate(SkScalar dx, SkScalar dy) {
     if (dx || dy) {
         this->checkForDeferredSave();
-        fMCRec->fMatrix.preTranslate(dx,dy);
+        fMCRec->fMatrix.preTranslate(dx, dy);
 
         // Translate shouldn't affect the is-scale-translateness of the matrix.
         // However, if either is non-finite, we might still complicate the matrix type,
@@ -1494,6 +1496,7 @@
 
     this->checkForDeferredSave();
     fMCRec->fMatrix.preConcat(matrix);
+
     fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
 
     FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
@@ -1504,7 +1507,7 @@
 void SkCanvas::concat44(const SkScalar m[16]) {
     this->checkForDeferredSave();
 
-    fMCRec->fMatrix.preConcat44(m);
+    fMCRec->fMatrix.preConcat16(m);
 
     fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
 
@@ -1786,7 +1789,7 @@
 
     SkMatrix inverse;
     // if we can't invert the CTM, we can't return local clip bounds
-    if (!fMCRec->fMatrix.invert(&inverse)) {
+    if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
         return SkRect::MakeEmpty();
     }
 
@@ -1803,14 +1806,44 @@
     return fMCRec->fRasterClip.getBounds();
 }
 
+///////////////////////////////////////////////////////////////////////
+
+SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
+    : fMCRec(owner)
+    , fCamera(camera)
+{
+    // assumes the mcrec has already been concatenated with the camera
+    if (!owner->fMatrix.invert(&fInvPostCamera)) {
+        fInvPostCamera.setIdentity();
+    }
+}
+
 SkMatrix SkCanvas::getTotalMatrix() const {
     return fMCRec->fMatrix;
 }
 
-SkM44 SkCanvas::getTotalM44() const {
+SkM44 SkCanvas::getLocalToDevice() const {
     return fMCRec->fMatrix;
 }
 
+SkM44 SkCanvas::getLocalToWorld() const {
+    if (fCameraStack.empty()) {
+        return this->getLocalToDevice();
+    } else {
+        const auto& top = fCameraStack.back();
+        return top.fInvPostCamera * this->getLocalToDevice();
+    }
+}
+
+SkM44 SkCanvas::getLocalToCamera() const {
+    if (fCameraStack.empty()) {
+        return this->getLocalToDevice();
+    } else {
+        const auto& top = fCameraStack.back();
+        return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
+    }
+}
+
 GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
     SkBaseDevice* dev = this->getTopDevice();
     return dev ? dev->accessRenderTargetContext() : nullptr;