Add toggle for 'real' vs. 'fake' perspective in Viewer

Real perspective draws content with perspective applied. Fake
perspective rasterizes in ortho, then stretches the image by the
perspective matrix.

Change-Id: I738cd379f9a58b965469ef8a57fb2dfd597fda10
Reviewed-on: https://skia-review.googlesource.com/125442
Commit-Queue: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
Auto-Submit: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 5d8b431..90c4dfc 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -179,7 +179,7 @@
     , fZoomLevel(0.0f)
     , fRotation(0.0f)
     , fGestureDevice(GestureDevice::kNone)
-    , fPerspective(false)
+    , fPerspectiveMode(kPerspective_Off)
     , fTileCnt(0)
     , fThreadCnt(0)
 {
@@ -489,6 +489,18 @@
         this->updateTitle();
         fWindow->inval();
     });
+    fCommands.addCommand('p', "Transform", "Toggle Perspective Mode", [this]() {
+        fPerspectiveMode = (kPerspective_Real == fPerspectiveMode) ? kPerspective_Fake
+                                                                   : kPerspective_Real;
+        this->updateTitle();
+        fWindow->inval();
+    });
+    fCommands.addCommand('P', "Transform", "Toggle Perspective", [this]() {
+        fPerspectiveMode = (kPerspective_Off == fPerspectiveMode) ? kPerspective_Real
+                                                                  : kPerspective_Off;
+        this->updateTitle();
+        fWindow->inval();
+    });
 
     // set up slides
     this->initSlides();
@@ -788,6 +800,12 @@
         title.appendf(" [Path renderer: %s]", gPathRendererNames[pr].c_str());
     }
 
+    if (kPerspective_Real == fPerspectiveMode) {
+        title.append(" Perpsective (Real)");
+    } else if (kPerspective_Fake == fPerspectiveMode) {
+        title.append(" Perspective (Fake)");
+    }
+
     fWindow->setTitle(title.c_str());
 }
 
@@ -878,6 +896,20 @@
     fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
 }
 
+SkMatrix Viewer::computePerspectiveMatrix() {
+    SkScalar w = fWindow->width(), h = fWindow->height();
+    SkPoint orthoPts[4] = { { 0, 0 }, { w, 0 }, { 0, h }, { w, h } };
+    SkPoint perspPts[4] = {
+        { fPerspectivePoints[0].fX * w, fPerspectivePoints[0].fY * h },
+        { fPerspectivePoints[1].fX * w, fPerspectivePoints[1].fY * h },
+        { fPerspectivePoints[2].fX * w, fPerspectivePoints[2].fY * h },
+        { fPerspectivePoints[3].fX * w, fPerspectivePoints[3].fY * h }
+    };
+    SkMatrix m;
+    m.setPolyToPoly(orthoPts, perspPts, 4);
+    return m;
+}
+
 SkMatrix Viewer::computePreTouchMatrix() {
     SkMatrix m = fDefaultMatrix;
     SkScalar zoomScale = (fZoomLevel < 0) ? SK_Scalar1 / (SK_Scalar1 - fZoomLevel)
@@ -887,17 +919,8 @@
     const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
     m.preRotate(fRotation, slideSize.width() * 0.5f, slideSize.height() * 0.5f);
 
-    if (fPerspective) {
-        SkScalar w = fWindow->width(), h = fWindow->height();
-        SkPoint orthoPts[4] = { { 0, 0 }, { w, 0 }, { 0, h }, { w, h } };
-        SkPoint perspPts[4] = {
-            { fPerspectivePoints[0].fX * w, fPerspectivePoints[0].fY * h },
-            { fPerspectivePoints[1].fX * w, fPerspectivePoints[1].fY * h },
-            { fPerspectivePoints[2].fX * w, fPerspectivePoints[2].fY * h },
-            { fPerspectivePoints[3].fX * w, fPerspectivePoints[3].fY * h }
-        };
-        SkMatrix persp;
-        persp.setPolyToPoly(orthoPts, perspPts, 4);
+    if (kPerspective_Real == fPerspectiveMode) {
+        SkMatrix persp = this->computePerspectiveMatrix();
         m.postConcat(persp);
     }
 
@@ -1026,10 +1049,11 @@
 
     // If we're in F16, or we're zooming, or we're in color correct 8888 and the gamut isn't sRGB,
     // we need to render offscreen. We also need to render offscreen if we're in any raster mode,
-    // because the window surface is actually GL.
+    // because the window surface is actually GL, or we're doing fake perspective.
     sk_sp<SkSurface> offscreenSurface = nullptr;
     std::unique_ptr<SkCanvas> threadedCanvas;
     if (Window::kRaster_BackendType == fBackendType ||
+        kPerspective_Fake == fPerspectiveMode ||
         ColorMode::kColorManagedLinearF16 == fColorMode ||
         fShowZoomWindow ||
         (ColorMode::kColorManagedSRGB8888 == fColorMode &&
@@ -1093,7 +1117,14 @@
         auto retaggedImage = SkImageMakeRasterCopyAndAssignColorSpace(fLastImage.get(), srgb.get());
         SkPaint paint;
         paint.setBlendMode(SkBlendMode::kSrc);
+        int prePerspectiveCount = canvas->save();
+        if (kPerspective_Fake == fPerspectiveMode) {
+            paint.setFilterQuality(kHigh_SkFilterQuality);
+            canvas->clear(SK_ColorWHITE);
+            canvas->concat(this->computePerspectiveMatrix());
+        }
         canvas->drawImage(retaggedImage, 0, 0, &paint);
+        canvas->restoreToCount(prePerspectiveCount);
     }
 }
 
@@ -1436,8 +1467,11 @@
                     this->preTouchMatrixChanged();
                     paramsChanged = true;
                 }
-                if (ImGui::Checkbox("Perspective", &fPerspective)) {
+                int perspectiveMode = static_cast<int>(fPerspectiveMode);
+                if (ImGui::Combo("Perspective", &perspectiveMode, "Off\0Real\0Fake\0\0")) {
+                    fPerspectiveMode = static_cast<PerspectiveMode>(perspectiveMode);
                     this->preTouchMatrixChanged();
+                    this->updateTitle();
                 }
                 if (ImGui_DragQuad(fPerspectivePoints)) {
                     this->preTouchMatrixChanged();
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index be35a88..d843946 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -102,6 +102,7 @@
     void changeZoomLevel(float delta);
     void preTouchMatrixChanged();
     SkMatrix computePreTouchMatrix();
+    SkMatrix computePerspectiveMatrix();
     SkMatrix computeMatrix();
     SkPoint mapEvent(float x, float y);
 
@@ -158,7 +159,12 @@
     // identity unless the window initially scales the content to fit the screen.
     SkMatrix               fDefaultMatrix;
 
-    bool                   fPerspective;
+    enum PerspectiveMode {
+        kPerspective_Off,
+        kPerspective_Real,
+        kPerspective_Fake,
+    };
+    PerspectiveMode        fPerspectiveMode;
     SkPoint                fPerspectivePoints[4];
 
     SkTArray<std::function<void(void)>> fDeferredActions;