Pixel zoomer in viewer

Made it a separate ImGui window (rather than part of the debug window).
Bring it up with 'z'. Draggable/resizable. Variable zoom scale. Enjoy.

BUG=skia:

Change-Id: I949ab398126c892c8d353aaebcc8403765f42841
Reviewed-on: https://skia-review.googlesource.com/8357
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index e935563..1d5e033 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -173,6 +173,8 @@
     , fRefresh(false)
     , fShowImGuiDebugWindow(false)
     , fShowImGuiTestWindow(false)
+    , fShowZoomWindow(false)
+    , fLastImage(nullptr)
     , fBackendType(sk_app::Window::kNativeGL_BackendType)
     , fColorType(kN32_SkColorType)
     , fColorSpace(nullptr)
@@ -225,6 +227,10 @@
         this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow;
         fWindow->inval();
     });
+    fCommands.addCommand('z', "GUI", "Toggle zoom window", [this]() {
+        this->fShowZoomWindow = !this->fShowZoomWindow;
+        fWindow->inval();
+    });
     fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
         this->fDisplayStats = !this->fDisplayStats;
         fWindow->inval();
@@ -351,7 +357,14 @@
     io.Fonts->GetTexDataAsAlpha8(&pixels, &w, &h);
     SkImageInfo info = SkImageInfo::MakeA8(w, h);
     SkPixmap pmap(info, pixels, info.minRowBytes());
-    fImGuiFontImage = SkImage::MakeFromRaster(pmap, nullptr, nullptr);
+    SkMatrix localMatrix = SkMatrix::MakeScale(1.0f / w, 1.0f / h);
+    auto fontImage = SkImage::MakeFromRaster(pmap, nullptr, nullptr);
+    auto fontShader = fontImage->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
+                                            &localMatrix);
+    fImGuiFontPaint.setShader(fontShader);
+    fImGuiFontPaint.setColor(SK_ColorWHITE);
+    fImGuiFontPaint.setFilterQuality(kLow_SkFilterQuality);
+    io.Fonts->TexID = &fImGuiFontPaint;
 
     fWindow->show();
 }
@@ -559,10 +572,11 @@
 
     // By default, we render directly into the window's surface/canvas
     SkCanvas* slideCanvas = canvas;
+    fLastImage.reset();
 
-    // ... but if we're in F16, or the gamut isn't sRGB, we need to render offscreen
+    // If we're in F16, or the gamut isn't sRGB, or we're zooming, we need to render offscreen
     sk_sp<SkSurface> offscreenSurface = nullptr;
-    if (kRGBA_F16_SkColorType == fColorType ||
+    if (kRGBA_F16_SkColorType == fColorType || fShowZoomWindow ||
         (fColorSpace && fColorSpace != SkColorSpace::MakeSRGB())) {
         SkImageInfo info = SkImageInfo::Make(fWindow->width(), fWindow->height(), fColorType,
                                              kPremul_SkAlphaType, fColorSpace);
@@ -586,12 +600,12 @@
 
     // If we rendered offscreen, snap an image and push the results to the window's canvas
     if (offscreenSurface) {
-        auto slideImage = offscreenSurface->makeImageSnapshot();
+        fLastImage = offscreenSurface->makeImageSnapshot();
 
         // Tag the image with the sRGB gamut, so no further color space conversion happens
         sk_sp<SkColorSpace> cs = (kRGBA_F16_SkColorType == fColorType)
             ? SkColorSpace::MakeSRGBLinear() : SkColorSpace::MakeSRGB();
-        auto retaggedImage = SkImageMakeRasterCopyAndAssignColorSpace(slideImage.get(), cs.get());
+        auto retaggedImage = SkImageMakeRasterCopyAndAssignColorSpace(fLastImage.get(), cs.get());
         canvas->drawImage(retaggedImage, 0, 0);
     }
 
@@ -745,6 +759,30 @@
         ImGui::End();
     }
 
+    SkPaint zoomImagePaint;
+    if (fShowZoomWindow && fLastImage) {
+        if (ImGui::Begin("Zoom", &fShowZoomWindow, ImVec2(200, 200))) {
+            static int zoomFactor = 4;
+            ImGui::SliderInt("Scale", &zoomFactor, 1, 16);
+
+            zoomImagePaint.setShader(fLastImage->makeShader(SkShader::kClamp_TileMode,
+                                                            SkShader::kClamp_TileMode));
+            zoomImagePaint.setColor(SK_ColorWHITE);
+
+            // Zoom by shrinking the corner UVs towards the mouse cursor
+            ImVec2 mousePos = ImGui::GetMousePos();
+            ImVec2 avail = ImGui::GetContentRegionAvail();
+
+            ImVec2 zoomHalfExtents = ImVec2((avail.x * 0.5f) / zoomFactor,
+                                            (avail.y * 0.5f) / zoomFactor);
+            ImGui::Image(&zoomImagePaint, avail,
+                         ImVec2(mousePos.x - zoomHalfExtents.x, mousePos.y - zoomHalfExtents.y),
+                         ImVec2(mousePos.x + zoomHalfExtents.x, mousePos.y + zoomHalfExtents.y));
+        }
+
+        ImGui::End();
+    }
+
     // This causes ImGui to rebuild vertex/index data based on all immediate-mode commands
     // (widgets, etc...) that have been issued
     ImGui::Render();
@@ -754,14 +792,6 @@
     SkTDArray<SkPoint> pos;
     SkTDArray<SkPoint> uv;
     SkTDArray<SkColor> color;
-    SkPaint imguiPaint;
-    imguiPaint.setColor(SK_ColorWHITE);
-    SkMatrix localMatrix = SkMatrix::MakeScale(1.0f / fImGuiFontImage->width(),
-                                               1.0f / fImGuiFontImage->height());
-    imguiPaint.setShader(fImGuiFontImage->makeShader(SkShader::kClamp_TileMode,
-                                                     SkShader::kClamp_TileMode,
-                                                     &localMatrix));
-    imguiPaint.setFilterQuality(kLow_SkFilterQuality);
 
     for (int i = 0; i < drawData->CmdListsCount; ++i) {
         const ImDrawList* drawList = drawData->CmdLists[i];
@@ -787,13 +817,16 @@
             if (drawCmd->UserCallback) {
                 drawCmd->UserCallback(drawList, drawCmd);
             } else {
+                SkPaint* paint = static_cast<SkPaint*>(drawCmd->TextureId);
+                SkASSERT(paint);
+
                 canvas->save();
                 canvas->clipRect(SkRect::MakeLTRB(drawCmd->ClipRect.x, drawCmd->ClipRect.y,
                                                   drawCmd->ClipRect.z, drawCmd->ClipRect.w));
                 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, drawList->VtxBuffer.size(),
                                      pos.begin(), uv.begin(), color.begin(),
                                      drawList->IdxBuffer.begin() + indexOffset, drawCmd->ElemCount,
-                                     imguiPaint);
+                                     *paint);
                 indexOffset += drawCmd->ElemCount;
                 canvas->restore();
             }
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index 9499c6c..74d3f06 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -60,10 +60,13 @@
     bool                   fDisplayStats;
     bool                   fRefresh; // whether to continuously refresh for measuring render time
 
-    sk_sp<SkImage>         fImGuiFontImage;
+    SkPaint                fImGuiFontPaint;
     bool                   fShowImGuiDebugWindow;
     bool                   fShowImGuiTestWindow;
 
+    bool                   fShowZoomWindow;
+    sk_sp<SkImage>         fLastImage;
+
     sk_app::Window::BackendType fBackendType;
 
     // Color properties for slide rendering