add /img/n/m endpoint to skiaserve

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1755563003

Review URL: https://codereview.chromium.org/1755563003
diff --git a/include/private/GrAuditTrail.h b/include/private/GrAuditTrail.h
index f9b0091..4d2d99a 100644
--- a/include/private/GrAuditTrail.h
+++ b/include/private/GrAuditTrail.h
@@ -119,6 +119,7 @@
     };
 
     void getBoundsByClientID(SkTArray<BatchInfo>* outInfo, int clientID);
+    void getBoundsByBatchListID(BatchInfo* outInfo, int batchListID);
 
     void fullReset();
 
@@ -147,6 +148,8 @@
     };
     typedef SkTArray<SkAutoTDelete<BatchNode>, true> BatchList;
 
+    void copyOutFromBatchList(BatchInfo* outBatchInfo, int batchListID);
+
     template <typename T>
     static void JsonifyTArray(SkString* json, const char* name, const T& array,
                               bool addComma);
diff --git a/src/gpu/GrAuditTrail.cpp b/src/gpu/GrAuditTrail.cpp
index cf5c510..752074d 100644
--- a/src/gpu/GrAuditTrail.cpp
+++ b/src/gpu/GrAuditTrail.cpp
@@ -71,6 +71,19 @@
     fBatchList.emplace_back(batchNode);
 }
 
+void GrAuditTrail::copyOutFromBatchList(BatchInfo* outBatchInfo, int batchListID) {
+    SkASSERT(batchListID < fBatchList.count());
+    const BatchNode* bn = fBatchList[batchListID];
+    outBatchInfo->fBounds = bn->fBounds;
+    outBatchInfo->fRenderTargetUniqueID = bn->fRenderTargetUniqueID;
+    for (int j = 0; j < bn->fChildren.count(); j++) {
+        BatchInfo::Batch& outBatch = outBatchInfo->fBatches.push_back();
+        const Batch* currentBatch = bn->fChildren[j];
+        outBatch.fBounds = currentBatch->fBounds;
+        outBatch.fClientID = currentBatch->fClientID;
+    }
+}
+
 void GrAuditTrail::getBoundsByClientID(SkTArray<BatchInfo>* outInfo, int clientID) {
     Batches** batchesLookup = fClientIDLookup.find(clientID);
     if (batchesLookup) {
@@ -87,24 +100,19 @@
             if (kGrAuditTrailInvalidID == currentBatchListID ||
                 batch->fBatchListID != currentBatchListID) {
                 BatchInfo& outBatchInfo = outInfo->push_back();
-                currentBatchListID = batch->fBatchListID;
                 
                 // copy out all of the batches so the client can display them even if
                 // they have a different clientID
-                const BatchNode* bn = fBatchList[currentBatchListID];
-                outBatchInfo.fBounds = bn->fBounds;
-                outBatchInfo.fRenderTargetUniqueID = bn->fRenderTargetUniqueID;
-                for (int j = 0; j < bn->fChildren.count(); j++) {
-                    BatchInfo::Batch& outBatch = outBatchInfo.fBatches.push_back();
-                    const Batch* currentBatch = bn->fChildren[j];
-                    outBatch.fBounds = currentBatch->fBounds;
-                    outBatch.fClientID = currentBatch->fClientID;
-                }
+                this->copyOutFromBatchList(&outBatchInfo, batch->fBatchListID);
             }
         }
     }
 }
 
+void GrAuditTrail::getBoundsByBatchListID(BatchInfo* outInfo, int batchListID) {
+    this->copyOutFromBatchList(outInfo, batchListID);
+}
+
 void GrAuditTrail::fullReset() {
     SkASSERT(fEnabled);
     fBatchList.reset();
diff --git a/tools/debugger/SkDebugCanvas.cpp b/tools/debugger/SkDebugCanvas.cpp
index dc529a4..a58db14 100644
--- a/tools/debugger/SkDebugCanvas.cpp
+++ b/tools/debugger/SkDebugCanvas.cpp
@@ -196,7 +196,7 @@
 
 }
 
-void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
+void SkDebugCanvas::drawTo(SkCanvas* canvas, int index, int m) {
     SkASSERT(!fCommandVector.isEmpty());
     SkASSERT(index < fCommandVector.count());
 
@@ -228,7 +228,7 @@
 #if SK_SUPPORT_GPU
     GrAuditTrail* at = nullptr;
     GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
-    if (rt && fDrawGpuBatchBounds) {
+    if (rt && (fDrawGpuBatchBounds || m != -1)) {
         GrContext* ctx = rt->getContext();
         if (ctx) {
             at = ctx->getAuditTrail();
@@ -344,7 +344,12 @@
         // get the bounding boxes to draw
         GrAuditTrail::AutoEnable ae(at);
         SkTArray<GrAuditTrail::BatchInfo> childrenBounds;
-        at->getBoundsByClientID(&childrenBounds, index);
+        if (m == -1) {
+            at->getBoundsByClientID(&childrenBounds, index);
+        } else {
+            // the client wants us to draw the mth batch
+            at->getBoundsByBatchListID(&childrenBounds.push_back(), m);
+        }
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setStrokeWidth(1);
diff --git a/tools/debugger/SkDebugCanvas.h b/tools/debugger/SkDebugCanvas.h
index d81d471..7379698 100644
--- a/tools/debugger/SkDebugCanvas.h
+++ b/tools/debugger/SkDebugCanvas.h
@@ -68,8 +68,9 @@
         Executes the draw calls up to the specified index.
         @param canvas  The canvas being drawn to
         @param index  The index of the final command being executed
+        @param m an optional Mth gpu batch to highlight, or -1
      */
-    void drawTo(SkCanvas* canvas, int index);
+    void drawTo(SkCanvas* canvas, int index, int m = -1);
 
     /**
         Returns the most recently calculated transformation matrix
diff --git a/tools/skiaserve/Request.cpp b/tools/skiaserve/Request.cpp
index 92e18fd..8ffcc36 100644
--- a/tools/skiaserve/Request.cpp
+++ b/tools/skiaserve/Request.cpp
@@ -93,13 +93,13 @@
     return target;
 }
 
-void Request::drawToCanvas(int n) {
+void Request::drawToCanvas(int n, int m) {
     SkCanvas* target = this->getCanvas();
-    fDebugCanvas->drawTo(target, n);
+    fDebugCanvas->drawTo(target, n, m);
 }
 
-SkData* Request::drawToPng(int n) {
-    this->drawToCanvas(n);
+SkData* Request::drawToPng(int n, int m) {
+    this->drawToCanvas(n, m);
     return writeCanvasToPng(this->getCanvas());
 }
 
diff --git a/tools/skiaserve/Request.h b/tools/skiaserve/Request.h
index 60a59a7..0b974e7 100644
--- a/tools/skiaserve/Request.h
+++ b/tools/skiaserve/Request.h
@@ -29,7 +29,8 @@
 struct Request {
     Request(SkString rootUrl); 
 
-    SkData* drawToPng(int n);
+    // draws to skia draw op N, highlighting the Mth batch(-1 means no highlight)
+    SkData* drawToPng(int n, int m = -1);
     SkCanvas* getCanvas();
     SkBitmap* getBitmapFromCanvas(SkCanvas* canvas);
     bool enableGPU(bool enable);
@@ -57,7 +58,7 @@
     
 private:
     SkData* writeCanvasToPng(SkCanvas* canvas);
-    void drawToCanvas(int n);
+    void drawToCanvas(int n, int m = -1);
     SkSurface* createCPUSurface();
     SkSurface* createGPUSurface();
     GrAuditTrail* getAuditTrail(SkCanvas*);
diff --git a/tools/skiaserve/urlhandlers/ImgHandler.cpp b/tools/skiaserve/urlhandlers/ImgHandler.cpp
index 3e390fe..a0e58bc 100644
--- a/tools/skiaserve/urlhandlers/ImgHandler.cpp
+++ b/tools/skiaserve/urlhandlers/ImgHandler.cpp
@@ -25,19 +25,22 @@
     SkTArray<SkString> commands;
     SkStrSplit(url, "/", &commands);
 
-    if (!request->hasPicture() || commands.count() > 2) {
+    if (!request->hasPicture() || commands.count() > 3) {
         return MHD_NO;
     }
 
-    int n;
+    int n, m = -1;
     // /img or /img/N
     if (commands.count() == 1) {
         n = request->fDebugCanvas->getSize() - 1;
+    } else if (commands.count() == 2) {
+        sscanf(commands[1].c_str(), "%d", &n);
     } else {
         sscanf(commands[1].c_str(), "%d", &n);
+        sscanf(commands[2].c_str(), "%d", &m);
     }
 
-    SkAutoTUnref<SkData> data(request->drawToPng(n));
+    SkAutoTUnref<SkData> data(request->drawToPng(n, m));
     return SendData(connection, data, "image/png");
 }