Add a central skiagm::GM::DrawFailureMessage for error messages

Bug: skia:8731
Change-Id: If73216bd427a1ce773fa41044a45c1bbd7ea08e9
Reviewed-on: https://skia-review.googlesource.com/c/189124
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/gm/3dgm.cpp b/gm/3dgm.cpp
index 8c9b8a8..9184a76 100644
--- a/gm/3dgm.cpp
+++ b/gm/3dgm.cpp
@@ -103,6 +103,7 @@
 
     void onDraw(SkCanvas* canvas) override {
         if (!fAnim) {
+            DrawFailureMessage(canvas, "No animation.");
             return;
         }
         SkMatrix44  camera,
diff --git a/gm/animatedGif.cpp b/gm/animatedGif.cpp
index 34df9de..7074fa8 100644
--- a/gm/animatedGif.cpp
+++ b/gm/animatedGif.cpp
@@ -21,18 +21,6 @@
 
 DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
 
-namespace {
-    void error(SkCanvas* canvas, const SkString& errorText) {
-        constexpr SkScalar kOffset = 5.0f;
-        canvas->drawColor(SK_ColorRED);
-        SkPaint paint;
-        SkFont font;
-        SkRect bounds;
-        font.measureText(errorText.c_str(), errorText.size(), kUTF8_SkTextEncoding, &bounds);
-        canvas->drawString(errorText, kOffset, bounds.height() + kOffset, font, paint);
-    }
-}
-
 class AnimatedGifGM : public skiagm::GM {
 private:
     std::unique_ptr<SkCodec>        fCodec;
@@ -115,7 +103,6 @@
 
         fCodec = SkCodec::MakeFromStream(std::move(stream));
         if (!fCodec) {
-            SkDebugf("Could create codec from %s", FLAGS_animatedGif[0]);
             return false;
         }
 
@@ -127,8 +114,7 @@
 
     void onDraw(SkCanvas* canvas) override {
         if (!this->initCodec()) {
-            SkString errorText = SkStringPrintf("Nothing to draw; %s", FLAGS_animatedGif[0]);
-            error(canvas, errorText);
+            DrawFailureMessage(canvas, "Could not create codec from %s", FLAGS_animatedGif[0]);
             return;
         }
 
diff --git a/gm/atlastext.cpp b/gm/atlastext.cpp
index 9fc7d6c..a7e95b4 100644
--- a/gm/atlastext.cpp
+++ b/gm/atlastext.cpp
@@ -88,7 +88,7 @@
 
     void onDraw(SkCanvas* canvas) override {
         if (!fRenderer || !fTarget || !fTarget->handle()) {
-            canvas->clear(SK_ColorRED);
+            DrawFailureMessage(canvas, "No renderer and/or target.");
             return;
         }
         fRenderer->clearTarget(fTarget->handle(), 0xFF808080);
diff --git a/gm/bitmapimage.cpp b/gm/bitmapimage.cpp
index 82f9626..2dc692c 100644
--- a/gm/bitmapimage.cpp
+++ b/gm/bitmapimage.cpp
@@ -31,7 +31,8 @@
         const char* path = "images/mandrill_512_q075.jpg";
         sk_sp<SkImage> image = GetResourceAsImage(path);
         if (!image) {
-            SkDebugf("Failure: Is the resource path set properly?");
+            DrawFailureMessage(canvas, "Couldn't load images/mandrill_512_q075.jpg. "
+                                       "Did you forget to set the resource path?");
             return;
         }
 
diff --git a/gm/blurimagevmask.cpp b/gm/blurimagevmask.cpp
index c2dd516..0056a05 100644
--- a/gm/blurimagevmask.cpp
+++ b/gm/blurimagevmask.cpp
@@ -55,6 +55,8 @@
 DEF_SIMPLE_GM(blur_image, canvas, 500, 500) {
     auto image = GetResourceAsImage("images/mandrill_128.png");
     if (!image) {
+        skiagm::GM::DrawFailureMessage(canvas, "Could not load mandrill_128.png. "
+                                               "Did you forget to set the resourcePath?");
         return;
     }
 
diff --git a/gm/copyTo4444.cpp b/gm/copyTo4444.cpp
index bdc2fe6..46b034f 100644
--- a/gm/copyTo4444.cpp
+++ b/gm/copyTo4444.cpp
@@ -33,8 +33,8 @@
     virtual void onDraw(SkCanvas* canvas) {
         SkBitmap bm, bm4444;
         if (!GetResourceAsBitmap("images/dog.jpg", &bm)) {
-            SkDebugf("Could not decode the file. Did you forget to set the "
-                     "resourcePath?\n");
+            DrawFailureMessage(canvas, "Could not decode the file. "
+                                       "Did you forget to set the resourcePath?");
             return;
         }
         canvas->drawBitmap(bm, 0, 0);
diff --git a/gm/crbug_691386.cpp b/gm/crbug_691386.cpp
index b08c315..76572b7 100644
--- a/gm/crbug_691386.cpp
+++ b/gm/crbug_691386.cpp
@@ -11,6 +11,7 @@
 DEF_SIMPLE_GM(crbug_691386, canvas, 256, 256) {
     SkPath path;
     if (!SkParsePath::FromSVGString("M -1 0 A 1 1 0 0 0 1 0 Z", &path)) {
+        skiagm::GM::DrawFailureMessage(canvas, "Failed to parse path.");
         return;
     }
     SkPaint p;
diff --git a/gm/crosscontextimage.cpp b/gm/crosscontextimage.cpp
index 5dfefbd..265331a 100644
--- a/gm/crosscontextimage.cpp
+++ b/gm/crosscontextimage.cpp
@@ -20,6 +20,8 @@
 
     sk_sp<SkData> encodedData = GetResourceAsData("images/mandrill_512.png");
     if (!encodedData) {
+        skiagm::GM::DrawFailureMessage(canvas, "Could not load mandrill_512.png. "
+                                               "Did you forget to set the resourcePath?");
         return;
     }
 
diff --git a/gm/discard.cpp b/gm/discard.cpp
index 7a1e9d9..d0c17a8 100644
--- a/gm/discard.cpp
+++ b/gm/discard.cpp
@@ -45,6 +45,7 @@
         SkImageInfo info = SkImageInfo::MakeN32Premul(size);
         auto surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
         if (nullptr == surface) {
+            DrawFailureMessage(canvas, "Could not create render target.");
             return;
         }
 
diff --git a/gm/encode-alpha-jpeg.cpp b/gm/encode-alpha-jpeg.cpp
index e2eeac6..f716c77 100644
--- a/gm/encode-alpha-jpeg.cpp
+++ b/gm/encode-alpha-jpeg.cpp
@@ -45,6 +45,8 @@
     void onDraw(SkCanvas* canvas) override {
         sk_sp<SkImage> srcImg = GetResourceAsImage("images/rainbow-gradient.png");
         if (!srcImg) {
+            DrawFailureMessage(canvas, "Could not load images/rainbow-gradient.png. "
+                                       "Did you forget to set the resourcePath?");
             return;
         }
         fStorage.reset(srcImg->width() * srcImg->height() *
diff --git a/gm/encode-platform.cpp b/gm/encode-platform.cpp
index eb35a4a..c9ea8ce 100644
--- a/gm/encode-platform.cpp
+++ b/gm/encode-platform.cpp
@@ -85,10 +85,14 @@
         SkBitmap opaqueBm, premulBm, unpremulBm;
 
         if (!GetResourceAsBitmap("images/mandrill_256.png", &opaqueBm)) {
+            DrawFailureMessage(canvas, "Could not load images/mandrill_256.png.png. "
+                                       "Did you forget to set the resourcePath?");
             return;
         }
         SkBitmap tmp;
         if (!GetResourceAsBitmap("images/yellow_rose.png", &tmp)) {
+            DrawFailureMessage(canvas, "Could not load images/yellow_rose.png. "
+                                       "Did you forget to set the resourcePath?");
             return;
         }
         tmp.extractSubset(&premulBm, SkIRect::MakeWH(256, 256));
diff --git a/gm/etc1.cpp b/gm/etc1.cpp
index f8432db..d9895d8 100644
--- a/gm/etc1.cpp
+++ b/gm/etc1.cpp
@@ -72,6 +72,7 @@
 
         GrContext* context = canvas->getGrContext();
         if (!context || context->abandoned()) {
+            DrawFailureMessage(canvas, "GrContext unavailable or abandoned.");
             return;
         }
 
diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp
index f16af88..2249b51 100644
--- a/gm/fontmgr.cpp
+++ b/gm/fontmgr.cpp
@@ -190,6 +190,7 @@
             }
         }
         if (nullptr == fset.get()) {
+            DrawFailureMessage(canvas, "No SkFontStyleSet");
             return;
         }
 
diff --git a/gm/fontscalerdistortable.cpp b/gm/fontscalerdistortable.cpp
index b060a0a..e112f75 100644
--- a/gm/fontscalerdistortable.cpp
+++ b/gm/fontscalerdistortable.cpp
@@ -41,6 +41,7 @@
         sk_sp<SkTypeface> distortable(MakeResourceAsTypeface("fonts/Distortable.ttf"));
 
         if (!distortableStream) {
+            DrawFailureMessage(canvas, "No distortableStream");
             return;
         }
         const char* text = "abc";
diff --git a/gm/fwidth_squircle.cpp b/gm/fwidth_squircle.cpp
index eb8ffa0..eb5438d 100644
--- a/gm/fwidth_squircle.cpp
+++ b/gm/fwidth_squircle.cpp
@@ -175,8 +175,7 @@
 
     if (!ctx->contextPriv().caps()->shaderCaps()->shaderDerivativeSupport()) {
         SkFont font(sk_tool_utils::create_portable_typeface(), 15);
-        SkTextUtils::DrawString(canvas, "Shader derivatives not supported.", 150,
-                                150 - 8, font, SkPaint(), SkTextUtils::kCenter_Align);
+        DrawFailureMessage(canvas, "Shader derivatives not supported.");
         return;
     }
 
diff --git a/gm/gm.cpp b/gm/gm.cpp
index 0dbcf5a..25ab29a 100644
--- a/gm/gm.cpp
+++ b/gm/gm.cpp
@@ -92,6 +92,24 @@
     return;
 }
 
+void GM::DrawFailureMessage(SkCanvas* canvas, const char format[], ...)  {
+    SkString failureMsg;
+
+    va_list argp;
+    va_start(argp, format);
+    failureMsg.appendVAList(format, argp);
+    va_end(argp);
+
+    constexpr SkScalar kOffset = 5.0f;
+    canvas->drawColor(SkColorSetRGB(200,0,0));
+    SkFont font;
+    SkRect bounds;
+    font.measureText(failureMsg.c_str(), failureMsg.size(), kUTF8_SkTextEncoding, &bounds);
+    SkPaint textPaint;
+    textPaint.setColor(SK_ColorWHITE);
+    canvas->drawString(failureMsg, kOffset, bounds.height() + kOffset, font, textPaint);
+}
+
 // need to explicitly declare this, or we get some weird infinite loop llist
 template GMRegistry* GMRegistry::gHead;
 
diff --git a/gm/gm.h b/gm/gm.h
index 35fc9d2..5e8d8f6 100644
--- a/gm/gm.h
+++ b/gm/gm.h
@@ -99,6 +99,8 @@
         /** draws a standard message that the GM is only intended to be used with the GPU.*/
         static void DrawGpuOnlyMessage(SkCanvas*);
 
+        static void DrawFailureMessage(SkCanvas*, const char[], ...) SK_PRINTF_LIKE(2, 3);
+
     protected:
         virtual void onOnceBeforeDraw() {}
         virtual void onDraw(SkCanvas*) = 0;
diff --git a/gm/image.cpp b/gm/image.cpp
index f1afa94..743fa22 100644
--- a/gm/image.cpp
+++ b/gm/image.cpp
@@ -407,6 +407,7 @@
     auto surf = sk_tool_utils::makeSurface(canvas, info, nullptr);
     auto img = make_lazy_image(surf.get());
     if (!img) {
+        skiagm::GM::DrawFailureMessage(canvas, "Failed to make lazy image.");
         return;
     }
 
diff --git a/gm/imagealphathreshold.cpp b/gm/imagealphathreshold.cpp
index 4bdf6eb..9e66fef 100644
--- a/gm/imagealphathreshold.cpp
+++ b/gm/imagealphathreshold.cpp
@@ -133,6 +133,7 @@
         sk_sp<SkSurface> surface(make_color_matching_surface(canvas, WIDTH, HEIGHT,
                                                              kPremul_SkAlphaType));
         if (!surface) {
+            DrawFailureMessage(canvas, "make_color_matching_surface failed");
             return;
         }
 
diff --git a/gm/makecolorspace.cpp b/gm/makecolorspace.cpp
index aa28efd..39afc69 100644
--- a/gm/makecolorspace.cpp
+++ b/gm/makecolorspace.cpp
@@ -57,6 +57,8 @@
         sk_sp<SkImage> opaqueImage = GetResourceAsImage("images/mandrill_128.png");
         sk_sp<SkImage> premulImage = GetResourceAsImage("images/color_wheel.png");
         if (!opaqueImage || !premulImage) {
+            DrawFailureMessage(canvas, "Failed to load images. "
+                                       "Did you forget to set the resourcePath?");
             return;
         }
         canvas->drawImage(opaqueImage, 0.0f, 0.0f);
diff --git a/gm/pdf_never_embed.cpp b/gm/pdf_never_embed.cpp
index dd69ea7..914bd79 100644
--- a/gm/pdf_never_embed.cpp
+++ b/gm/pdf_never_embed.cpp
@@ -29,6 +29,8 @@
 
     SkFont font(MakeResourceAsTypeface("fonts/Roboto2-Regular_NoEmbed.ttf"), 60);
     if (!font.getTypefaceOrDefault()) {
+        skiagm::GM::DrawFailureMessage(canvas, "Could not load fonts/Roboto2-Regular_NoEmbed.ttf. "
+                                               "Did you forget to set the resourcePath?");
         return;
     }
 
diff --git a/gm/rectangletexture.cpp b/gm/rectangletexture.cpp
index 857fecb..c82b61d 100644
--- a/gm/rectangletexture.cpp
+++ b/gm/rectangletexture.cpp
@@ -145,9 +145,7 @@
         };
         SkASSERT(SkToBool(rectImgs[0]) == SkToBool(rectImgs[1]));
         if (!rectImgs[0]) {
-            SkPaint paint;
-            SkFont font;
-            canvas->drawString("Could not create rectangle texture image.", 10, 100, font, paint);
+            DrawFailureMessage(canvas, "Could not create rectangle texture image.");
             return;
         }
 
diff --git a/gm/repeated_bitmap.cpp b/gm/repeated_bitmap.cpp
index 09f30fb..6f23ea2 100644
--- a/gm/repeated_bitmap.cpp
+++ b/gm/repeated_bitmap.cpp
@@ -14,6 +14,7 @@
     sk_tool_utils::draw_checkerboard(canvas, SkColorSetRGB(156, 154, 156),
                                      SK_ColorWHITE, 12);
     if (!image) {
+        skiagm::GM::DrawFailureMessage(canvas, "No image. Did you forget to set the resourcePath?");
         return;
     }
     SkRect rect = SkRect::MakeLTRB(-68.0f, -68.0f, 68.0f, 68.0f);
diff --git a/gm/shadermaskfilter.cpp b/gm/shadermaskfilter.cpp
index fedab48..cd3af70 100644
--- a/gm/shadermaskfilter.cpp
+++ b/gm/shadermaskfilter.cpp
@@ -61,6 +61,8 @@
     auto image = GetResourceAsImage("images/mandrill_128.png");
     auto mask = GetResourceAsImage("images/color_wheel.png");
     if (!image || !mask) {
+        skiagm::GM::DrawFailureMessage(canvas, "Could not load images. "
+                                               "Did you forget to set the resourcePath?");
         return;
     }
     auto blurmf = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5);
diff --git a/gm/shapes_as_paths.cpp b/gm/shapes_as_paths.cpp
index 4ad760d..8305715 100644
--- a/gm/shapes_as_paths.cpp
+++ b/gm/shapes_as_paths.cpp
@@ -34,6 +34,7 @@
     pmapA.alloc(info);
     pmapB.alloc(info);
     if (!imgA->readPixels(pmapA, 0, 0) || !imgB->readPixels(pmapB, 0, 0)) {
+        skiagm::GM::DrawFailureMessage(canvas, "Failed to read pixels.");
         return;
     }
 
diff --git a/gm/simple_magnification.cpp b/gm/simple_magnification.cpp
index d772ed9..6b70a43 100644
--- a/gm/simple_magnification.cpp
+++ b/gm/simple_magnification.cpp
@@ -105,6 +105,8 @@
         sk_sp<SkImage> bottomLImg = make_image(context, kImgSize, kBottomLeft_GrSurfaceOrigin);
         sk_sp<SkImage> topLImg = make_image(context, kImgSize, kTopLeft_GrSurfaceOrigin);
         if (!bottomLImg || !topLImg) {
+            DrawFailureMessage(canvas, "Could not load images. "
+                                       "Did you forget to set the resourcePath?");
             return;
         }
 
diff --git a/gm/subsetshader.cpp b/gm/subsetshader.cpp
index 4657aa5..545788b 100644
--- a/gm/subsetshader.cpp
+++ b/gm/subsetshader.cpp
@@ -15,6 +15,8 @@
 
     SkBitmap source;
     if (!GetResourceAsBitmap("images/color_wheel.png", &source)) {
+        skiagm::GM::DrawFailureMessage(canvas, "Could not load images/color_wheel.png. "
+                                               "Did you forget to set the resourcePath?");
         return;
     }
     SkIRect left = SkIRect::MakeWH(source.width()/2, source.height());
diff --git a/gm/textblobrandomfont.cpp b/gm/textblobrandomfont.cpp
index 7257849..e0f1cf5 100644
--- a/gm/textblobrandomfont.cpp
+++ b/gm/textblobrandomfont.cpp
@@ -110,7 +110,7 @@
         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
         auto surface(sk_tool_utils::makeSurface(canvas, info, &props));
         if (!surface) {
-            canvas->drawString("This test requires a surface", 10, 100, SkFont(), SkPaint());
+            DrawFailureMessage(canvas, "This test requires a surface");
             return;
         }
 
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index 99680f1..05d0611 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -101,6 +101,7 @@
             SkBitmap copy;
             SkImageInfo info = as_IB(fImage)->onImageInfo().makeColorType(kN32_SkColorType);
             if (!copy.tryAllocPixels(info) || !fImage->readPixels(copy.pixmap(), 0, 0)) {
+                DrawFailureMessage(canvas, "Failed to read pixels.");
                 return;
             }
             proxy = proxyProvider->createMipMapProxyFromBitmap(copy);
@@ -109,6 +110,7 @@
                 fImage, kNone_GrSurfaceFlags, 1, SkBudgeted::kYes, SkBackingFit::kExact);
         }
         if (!proxy) {
+            DrawFailureMessage(canvas, "Failed to create proxy.");
             return;
         }
 
diff --git a/gm/windowrectangles.cpp b/gm/windowrectangles.cpp
index dc12d1d..9362e68 100644
--- a/gm/windowrectangles.cpp
+++ b/gm/windowrectangles.cpp
@@ -125,7 +125,6 @@
     void visualizeAlphaMask(GrContext*, GrRenderTargetContext*, const GrReducedClip&, GrPaint&&);
     void visualizeStencilMask(GrContext*, GrRenderTargetContext*, const GrReducedClip&, GrPaint&&);
     void stencilCheckerboard(GrRenderTargetContext*, bool flip);
-    void fail(SkCanvas*);
 };
 
 /**
@@ -176,7 +175,7 @@
     GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
 
     if (!ctx || !rtc || rtc->priv().maxWindowRectangles() < kNumWindows) {
-        this->fail(canvas);
+        DrawFailureMessage(canvas, "Requires GPU with %i window rectangles", kNumWindows);
         return;
     }
 
@@ -265,20 +264,6 @@
     }
 }
 
-void WindowRectanglesMaskGM::fail(SkCanvas* canvas) {
-    SkFont font(sk_tool_utils::create_portable_typeface(), 20);
-
-    SkString errorMsg;
-    errorMsg.printf("Requires GPU with %i window rectangles", kNumWindows);
-
-    canvas->clipRect(SkRect::Make(kCoverRect));
-    canvas->clear(SK_ColorWHITE);
-
-    SkTextUtils::DrawString(canvas, errorMsg.c_str(), SkIntToScalar((kCoverRect.left() + kCoverRect.right())/2),
-                     SkIntToScalar((kCoverRect.top() + kCoverRect.bottom())/2 - 10),
-                            font, SkPaint(), SkTextUtils::kCenter_Align);
-}
-
 DEF_GM( return new WindowRectanglesMaskGM(); )
 
 }
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index 18c7b77..6a16aca 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -92,6 +92,7 @@
             proxies[i] = proxyProvider->createTextureProxy(fImage[i], kNone_GrSurfaceFlags, 1,
                                                            SkBudgeted::kYes, SkBackingFit::kExact);
             if (!proxies[i]) {
+                DrawFailureMessage(canvas, "Failed to create proxy");
                 return;
             }
         }
@@ -219,6 +220,7 @@
             proxies[i] = proxyProvider->createTextureProxy(fImage[i], kNone_GrSurfaceFlags, 1,
                                                            SkBudgeted::kYes, SkBackingFit::kExact);
             if (!proxies[i]) {
+                DrawFailureMessage(canvas, "Failed to create proxy");
                 return;
             }
         }
diff --git a/modules/skottie/gm/SkottieGM.cpp b/modules/skottie/gm/SkottieGM.cpp
index c8a4a27..09908b8 100644
--- a/modules/skottie/gm/SkottieGM.cpp
+++ b/modules/skottie/gm/SkottieGM.cpp
@@ -63,6 +63,7 @@
 
     void onDraw(SkCanvas* canvas) override {
         if (!fAnimation) {
+            DrawFailureMessage(canvas, "No animation");
             return;
         }
 
@@ -114,6 +115,7 @@
 
     void onDraw(SkCanvas* canvas) override {
         if (!fAnimation) {
+            DrawFailureMessage(canvas, "No animation");
             return;
         }
 
@@ -185,6 +187,7 @@
 
     void onDraw(SkCanvas* canvas) override {
         if (!fAnimation) {
+            DrawFailureMessage(canvas, "No animation");
             return;
         }