GM: replace boilerplate with macros

I have verified locally that nothing draws differently.

Motivation:
*   SK_SIMPLE_GM makes it easier to write a GM.
*   Reducing 1100 lines of code makes maintenance easier.
*   Simple GMs are easy to convert to Fiddles.

Review URL: https://codereview.chromium.org/1333553002
diff --git a/gm/astcbitmap.cpp b/gm/astcbitmap.cpp
index bc5e1e9..d3191e25 100644
--- a/gm/astcbitmap.cpp
+++ b/gm/astcbitmap.cpp
@@ -42,26 +42,12 @@
     return kASTCFilenames[idx];
 }
 
-namespace skiagm {
+namespace {
+const int kGMDimension = 600;
+const int kBitmapDimension = kGMDimension / 4;
+}  // namespace
 
-/**
- *  Test decoding an image from an ASTC file and then from compressed ASTC data.
- */
-class ASTCBitmapGM : public GM {
-public:
-    ASTCBitmapGM() { }
-    virtual ~ASTCBitmapGM() { }
-
-protected:
-    SkString onShortName() override {
-        return SkString("astcbitmap");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(kGMDimension, kGMDimension);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(astcbitmap, canvas, kGMDimension, kGMDimension) {
         for (int j = 0; j < 4; ++j) {
             for (int i = 0; i < 4; ++i) {
                 SkString filename = GetResourcePath(get_astc_filename(j*4+i));
@@ -86,17 +72,4 @@
                 canvas->drawBitmap(bm, bmX, bmY);
             }
         }
-    }
-
-private:
-    static const int kGMDimension = 600;
-    static const int kBitmapDimension = kGMDimension/4;
-
-    typedef GM INHERITED;
-};
-
-}  // namespace skiagm
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new skiagm::ASTCBitmapGM;)
+}
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index ff75f74..1886a4a2 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -122,7 +122,7 @@
     void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (nullptr == rt) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
         GrContext* context = rt->getContext();
@@ -270,7 +270,7 @@
     void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (nullptr == rt) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
         GrContext* context = rt->getContext();
@@ -513,7 +513,7 @@
     void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (nullptr == rt) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
         GrContext* context = rt->getContext();
diff --git a/gm/bigmatrix.cpp b/gm/bigmatrix.cpp
index c7c3b0d..2d2b7d9 100644
--- a/gm/bigmatrix.cpp
+++ b/gm/bigmatrix.cpp
@@ -11,24 +11,8 @@
 #include "SkPath.h"
 #include "SkShader.h"
 
-namespace skiagm {
-
-class BigMatrixGM : public GM {
-public:
-    BigMatrixGM() {
-        this->setBGColor(sk_tool_utils::color_to_565(0xFF66AA99));
-    }
-
-protected:
-    virtual SkString onShortName() {
-        return SkString("bigmatrix");
-    }
-
-    virtual SkISize onISize() {
-        return SkISize::Make(50, 50);
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
+DEF_SIMPLE_GM_BG(bigmatrix, canvas, 50, 50,
+                 sk_tool_utils::color_to_565(0xFF66AA99)) {
         SkMatrix m;
         m.reset();
         m.setRotate(33 * SK_Scalar1);
@@ -82,15 +66,4 @@
         rect.setLTRB(pt.fX - small, pt.fY - small,
                      pt.fX + small, pt.fY + small);
         canvas->drawRect(rect, paint);
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new BigMatrixGM; }
-static GMRegistry reg(MyFactory);
-
 }
diff --git a/gm/bigrrectaaeffect.cpp b/gm/bigrrectaaeffect.cpp
index 3facc8c..bc0b56a 100644
--- a/gm/bigrrectaaeffect.cpp
+++ b/gm/bigrrectaaeffect.cpp
@@ -34,7 +34,7 @@
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         GrContext* context = rt ? rt->getContext() : nullptr;
         if (!context) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
 
diff --git a/gm/bitmaprecttest.cpp b/gm/bitmaprecttest.cpp
index 741cc9d..b83279b 100644
--- a/gm/bitmaprecttest.cpp
+++ b/gm/bitmaprecttest.cpp
@@ -34,7 +34,7 @@
 //  twice. The fix resulted in (a) not taking the fast-path, but (b) drawing
 //  the image correctly.
 //
-static void test_bitmaprect(SkCanvas* canvas) {
+DEF_SIMPLE_GM(bitmaprecttest, canvas, 320, 240) {
     SkBitmap bm;
     make_bm(&bm);
 
@@ -49,30 +49,3 @@
     canvas->scale(-1, 1);
     canvas->drawBitmap(bm, -310, 45, nullptr);
 }
-
-class BitmapRectTestGM : public skiagm::GM {
-public:
-    BitmapRectTestGM() {
-
-    }
-
-protected:
-    SkString onShortName() override {
-        return SkString("bitmaprecttest");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(320, 240);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
-        test_bitmaprect(canvas);
-    }
-
-private:
-    typedef skiagm::GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM( return new BitmapRectTestGM; )
diff --git a/gm/blurrect.cpp b/gm/blurrect.cpp
index 6b73540..4f15815 100644
--- a/gm/blurrect.cpp
+++ b/gm/blurrect.cpp
@@ -169,31 +169,11 @@
     typedef GM INHERITED;
 };
 
+DEF_SIMPLE_GM(blurrect_gallery, canvas, 1200, 1024) {
+        const int fGMWidth = 1200;
+        const int fPadding = 10;
+        const int fMargin = 100;
 
-class BlurRectDirectGM : public skiagm::GM {
-    SkString  fName;
-    int fGMWidth, fGMHeight;
-    int fPadding, fMargin;
-public:
-    BlurRectDirectGM(const char name[])
-        : fName(name),
-          fGMWidth(1200),
-          fGMHeight(1024),
-          fPadding(10),
-          fMargin(100)
-    {
-    }
-
-protected:
-    virtual SkString onShortName() {
-        return fName;
-    }
-
-    virtual SkISize onISize() {
-        return SkISize::Make(fGMWidth, fGMHeight);
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
         const int widths[] = {25, 5, 5, 100, 150, 25};
         const int heights[] = {100, 100, 5, 25, 150, 25};
         const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle};
@@ -244,177 +224,8 @@
                 }
             }
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-class BlurRectCompareGM : public skiagm::GM {
-    SkString  fName;
-    unsigned int fRectWidth, fRectHeight;
-    SkScalar fRadius;
-    SkBlurStyle fStyle;
-public:
-    BlurRectCompareGM(const char name[], unsigned int rectWidth,
-                      unsigned int rectHeight, float radius,
-                      SkBlurStyle style)
-        : fName(name)
-        , fRectWidth(rectWidth)
-        , fRectHeight(rectHeight)
-        , fRadius(radius)
-        , fStyle(style) {
-    }
-    int width() const {
-        return fRectWidth;
-    }
-    int height() const {
-        return fRectHeight;
-    }
-    SkScalar radius() const {
-        return fRadius;
-    }
-    SkBlurStyle style() const {
-        return fStyle;
-    }
-
-protected:
-    virtual SkString onShortName() {
-        return fName;
-    }
-
-    virtual SkISize onISize() {
-        return SkISize::Make(640, 480);
-    }
-
-    virtual bool makeMask(SkMask *m, const SkRect&) = 0;
-
-    virtual void onDraw(SkCanvas* canvas) {
-        SkRect r;
-        r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
-
-        SkISize canvas_size = canvas->getDeviceSize();
-        int center_x = (canvas_size.fWidth - (int)(r.width()))/2;
-        int center_y = (canvas_size.fHeight - (int)(r.height()))/2;
-
-        SkMask mask;
-
-        if (!this->makeMask(&mask, r)) {
-            SkPaint paint;
-            r.offset( SkIntToScalar(center_x), SkIntToScalar(center_y) );
-            canvas->drawRect(r,paint);
-            return;
-        }
-        SkAutoMaskFreeImage amfi(mask.fImage);
-
-        SkBitmap bm;
-        bm.installMaskPixels(mask);
-
-        center_x = (canvas_size.fWidth - mask.fBounds.width())/2;
-        center_y = (canvas_size.fHeight - mask.fBounds.height())/2;
-
-        canvas->drawBitmap(bm, SkIntToScalar(center_x), SkIntToScalar(center_y), nullptr);
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-class BlurRectFastGM: public BlurRectCompareGM {
-public:
-    BlurRectFastGM(const char name[], unsigned int rectWidth,
-                   unsigned int rectHeight, float blurRadius,
-                   SkBlurStyle style) :
-        INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
-        }
-
-protected:
-    bool makeMask(SkMask *m, const SkRect& r) override {
-        return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()),
-                                    m, r, this->style());
-    }
-private:
-    typedef BlurRectCompareGM INHERITED;
-};
-
-class BlurRectSlowGM: public BlurRectCompareGM {
-public:
-    BlurRectSlowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
-                   float blurRadius, SkBlurStyle style)
-        : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
-        }
-
-protected:
-    bool makeMask(SkMask *m, const SkRect& r) override {
-        SkMask src;
-        r.roundOut(&src.fBounds);
-        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
-        src.fFormat = SkMask::kA8_Format;
-        src.fRowBytes = src.fBounds.width();
-        src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
-        SkAutoMaskFreeImage amfi(src.fImage);
-
-        memset(src.fImage, 0xff, src.computeTotalImageSize());
-
-        return SkBlurMask::BoxBlur(m, src,
-                                   SkBlurMask::ConvertRadiusToSigma(this->radius()),
-                                   this->style(), this->getQuality());
-    }
-
-    virtual SkBlurQuality getQuality() {
-        return kHigh_SkBlurQuality;
-    }
-private:
-    typedef BlurRectCompareGM INHERITED;
-};
-
-class BlurRectSlowLowGM: public BlurRectSlowGM {
-public:
-    BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
-                      float blurRadius, SkBlurStyle style)
-        : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
-        }
-
-protected:
-    SkBlurQuality getQuality() override {
-        return kLow_SkBlurQuality;
-    }
-private:
-    typedef BlurRectSlowGM INHERITED;
-};
-
-class BlurRectGroundTruthGM: public BlurRectCompareGM {
-public:
-    BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
-                          float blurRadius, SkBlurStyle style)
-        : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
-        }
-
-protected:
-    bool makeMask(SkMask *m, const SkRect& r) override {
-        SkMask src;
-        r.roundOut(&src.fBounds);
-        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
-        src.fFormat = SkMask::kA8_Format;
-        src.fRowBytes = src.fBounds.width();
-        src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
-        SkAutoMaskFreeImage amfi(src.fImage);
-
-        memset(src.fImage, 0xff, src.computeTotalImageSize());
-
-        return SkBlurMask::BlurGroundTruth(SkBlurMask::ConvertRadiusToSigma(this->radius()),
-                                           m, src, this->style());
-    }
-
-    virtual SkBlurQuality getQuality() {
-        return kHigh_SkBlurQuality;
-    }
-private:
-    typedef BlurRectCompareGM INHERITED;
-};
-
+}
 
 //////////////////////////////////////////////////////////////////////////////
 
 DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
-DEF_GM(return new BlurRectDirectGM("blurrect_gallery");)
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index d52a656..8f2137e 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -10,23 +10,7 @@
 #include "SkBlurMaskFilter.h"
 #include "SkPath.h"
 
-class BlursGM : public skiagm::GM {
-public:
-    BlursGM() {
-        this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("blurs");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(700, 500);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG(blurs, canvas, 700, 500, sk_tool_utils::color_to_565(0xFFDDDDDD)) {
         SkBlurStyle NONE = SkBlurStyle(-999);
         static const struct {
             SkBlurStyle fStyle;
@@ -85,12 +69,7 @@
             flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
             canvas->translate(SkIntToScalar(350), SkIntToScalar(0));
         }
-    }
-
-private:
-    typedef skiagm::GM INHERITED;
-};
-DEF_GM( return new BlursGM; )
+}
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -100,17 +79,7 @@
 // in particular, we want to notice that the 2nd rect draws slightly differently, since it
 // is translated a fractional amount.
 //
-class Blur2RectsGM : public skiagm::GM {
-public:
-    SkString onShortName() override {
-        return SkString("blur2rects");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(700, 500);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(blur2rects, canvas, 700, 500) {
         SkPaint paint;
 
         paint.setMaskFilter(SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
@@ -128,21 +97,9 @@
         SkScalar dx = SkScalarRoundToScalar(path.getBounds().width()) + 14 + 0.25f;
         canvas->translate(dx, 0);
         canvas->drawPath(path, paint);
-    }
-};
-DEF_GM( return new Blur2RectsGM; )
+}
 
-class Blur2RectsNonNinePatchGM : public skiagm::GM {
-public:
-    SkString onShortName() override {
-        return SkString("blur2rectsnonninepatch");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(700, 500);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(blur2rectsnonninepatch, canvas, 700, 500) {
         SkPaint paint;
         paint.setMaskFilter(SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
                                                      4.3f))->unref();
@@ -162,6 +119,4 @@
         canvas->translate(-dx, 0);
         canvas->translate(-30, -150);
         canvas->drawPath(path, paint);
-    }
-};
-DEF_GM( return new Blur2RectsNonNinePatchGM; )
+}
diff --git a/gm/clipdrawdraw.cpp b/gm/clipdrawdraw.cpp
index aa635d9..1aecb9f 100644
--- a/gm/clipdrawdraw.cpp
+++ b/gm/clipdrawdraw.cpp
@@ -7,8 +7,6 @@
 
 #include "gm.h"
 
-namespace skiagm {
-
 // This GM exercises the use case found in crbug.com/423834.
 // The following pattern:
 //    save();
@@ -18,16 +16,7 @@
 //
 //    drawRect(rect, noAA);
 // can leave 1 pixel wide remnants of the first rect.
-class ClipDrawDrawGM : public GM {
-public:
-    ClipDrawDrawGM() { this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); }
-
-protected:
-    SkString onShortName() override { return SkString("clipdrawdraw"); }
-
-    SkISize onISize() override { return SkISize::Make(512, 512); }
-
-    static void Draw(SkCanvas* canvas, const SkRect& rect) {
+static void Draw(SkCanvas* canvas, const SkRect& rect) {
         SkPaint p;
         p.setAntiAlias(false);
 
@@ -44,9 +33,10 @@
             p.setColor(SK_ColorWHITE);
             canvas->drawRect(rect, p);
         canvas->restore();
-    }
+}
 
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG(clipdrawdraw, canvas, 512, 512,
+                 sk_tool_utils::color_to_565(0xFFCCCCCC)) {
         // Vertical remnant
         const SkRect rect1 = SkRect::MakeLTRB(136.5f, 137.5f, 338.5f, 293.5f);
 
@@ -56,13 +46,4 @@
 
         Draw(canvas, rect1);
         Draw(canvas, rect2);
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new ClipDrawDrawGM;)
 }
diff --git a/gm/colorfilterimagefilter.cpp b/gm/colorfilterimagefilter.cpp
index 4f5a768..fb5b0d4 100644
--- a/gm/colorfilterimagefilter.cpp
+++ b/gm/colorfilterimagefilter.cpp
@@ -49,29 +49,19 @@
     return SkColorFilterImageFilter::Create(filter, input);
 }
 
-class ColorFilterImageFilterGM : public skiagm::GM {
-public:
-    ColorFilterImageFilterGM () {}
-
-protected:
-
-    virtual SkString onShortName() {
-        return SkString("colorfilterimagefilter");
-    }
-
-    void drawClippedRect(SkCanvas* canvas, const SkRect& r, const SkPaint& paint, float outset = 0.0f) {
+static void drawClippedRect(SkCanvas* canvas,
+                            const SkRect& r,
+                            const SkPaint& paint,
+                            float outset = 0.0f) {
         canvas->save();
         SkRect clip(r);
         clip.outset(outset, outset);
         canvas->clipRect(clip);
         canvas->drawRect(r, paint);
         canvas->restore();
-    }
+}
 
-    virtual SkISize onISize() { return SkISize::Make(400, 100); }
-
-    virtual void onDraw(SkCanvas* canvas) {
-
+DEF_SIMPLE_GM(colorfilterimagefilter, canvas, 400, 100){
         SkRect r = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -126,13 +116,4 @@
             drawClippedRect(canvas, r, paint, 5);
             canvas->translate(FILTER_WIDTH + MARGIN, 0);
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static skiagm::GM* MyFactory(void*) { return new ColorFilterImageFilterGM; }
-static skiagm::GMRegistry reg(MyFactory);
+}
diff --git a/gm/constcolorprocessor.cpp b/gm/constcolorprocessor.cpp
index 3d2e586..23400e3 100644
--- a/gm/constcolorprocessor.cpp
+++ b/gm/constcolorprocessor.cpp
@@ -51,7 +51,7 @@
         }
         GrContext* context = rt->getContext();
         if (nullptr == context) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
 
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index b6a05cc..c014e6a 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -152,7 +152,7 @@
         using namespace GrDefaultGeoProcFactory;
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (nullptr == rt) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
         GrContext* context = rt->getContext();
diff --git a/gm/dashcubics.cpp b/gm/dashcubics.cpp
index ecbe415..1773303 100644
--- a/gm/dashcubics.cpp
+++ b/gm/dashcubics.cpp
@@ -14,22 +14,8 @@
 /*
  *  Inspired by http://code.google.com/p/chromium/issues/detail?id=112145
  */
-
-class DashCubicsGM : public skiagm::GM {
-public:
-    DashCubicsGM() {}
-
-protected:
-
-    virtual SkString onShortName() {
-        return SkString("dashcubics");
-    }
-
-    virtual SkISize onISize() {
-        return SkISize::Make(860, 700);
-    }
-
-    void flower(SkCanvas* canvas, const SkPath& path, SkScalar intervals[2], SkPaint::Join join) {
+static void flower(SkCanvas* canvas, const SkPath& path,
+                   SkScalar intervals[2], SkPaint::Join join) {
         SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 0);
 
         SkPaint paint;
@@ -48,9 +34,9 @@
         paint.setPathEffect(nullptr);
         paint.setStrokeWidth(0);
         canvas->drawPath(path, paint);
-    }
+}
 
-    virtual void onDraw(SkCanvas* canvas) {
+DEF_SIMPLE_GM(dashcubics, canvas, 860, 700) {
         SkPath path;
         const char* d = "M 337,98 C 250,141 250,212 250,212 C 250,212 250,212 250,212"
         "C 250,212 250,212 250,212 C 250,212 250,141 163,98 C 156,195 217,231 217,231"
@@ -73,13 +59,4 @@
                 canvas->restore();
             }
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static skiagm::GM* MyFactory(void*) { return new DashCubicsGM; }
-static skiagm::GMRegistry reg(MyFactory);
+}
diff --git a/gm/dcshader.cpp b/gm/dcshader.cpp
index 976e253..56265f1 100644
--- a/gm/dcshader.cpp
+++ b/gm/dcshader.cpp
@@ -231,7 +231,7 @@
         // This GM exists to test a specific feature of the GPU backend. It does not work with the
         // sw rasterizer, tile modes, etc.
         if (nullptr == canvas->getGrContext()) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
         SkPaint paint;
diff --git a/gm/fatpathfill.cpp b/gm/fatpathfill.cpp
index be5fbc4..d76cd46 100644
--- a/gm/fatpathfill.cpp
+++ b/gm/fatpathfill.cpp
@@ -46,21 +46,9 @@
     draw_pixel_centers(canvas);
 }
 
-class FatPathFillGM : public skiagm::GM {
-public:
-    FatPathFillGM() {}
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("fatpathfill");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(SMALL_W * ZOOM, SMALL_H * ZOOM * REPEAT_LOOP);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(fatpathfill, canvas,
+              SMALL_W * ZOOM,
+              SMALL_H * ZOOM * REPEAT_LOOP) {
         SkAutoTUnref<SkSurface> surface(new_surface(SMALL_W, SMALL_H));
 
         canvas->scale(ZOOM, ZOOM);
@@ -78,12 +66,4 @@
 
             canvas->translate(0, SMALL_H);
         }
-    }
-
-private:
-    typedef skiagm::GM INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new FatPathFillGM;)
+}
diff --git a/gm/getpostextpath.cpp b/gm/getpostextpath.cpp
index faace24..dcd983b 100644
--- a/gm/getpostextpath.cpp
+++ b/gm/getpostextpath.cpp
@@ -12,27 +12,14 @@
 #include "SkRandom.h"
 #include "SkTemplates.h"
 
-class GetPosTextPathGM : public skiagm::GM {
-public:
-    GetPosTextPathGM() {}
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("getpostextpath");
-    }
-
-    SkISize onISize() override { return SkISize::Make(480, 780); }
-
-    static void strokePath(SkCanvas* canvas, const SkPath& path) {
+static void strokePath(SkCanvas* canvas, const SkPath& path) {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setColor(SK_ColorRED);
         paint.setStyle(SkPaint::kStroke_Style);
         canvas->drawPath(path, paint);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+}
+DEF_SIMPLE_GM(getpostextpath, canvas, 480, 780) {
         // explicitly add spaces, to test a prev. bug
         const char* text = "Ham bur ge fons";
         int len = SkToInt(strlen(text));
@@ -67,10 +54,4 @@
         canvas->drawPosText(text, len, &pos[0], paint);
         paint.getPosTextPath(text, len, &pos[0], &path);
         strokePath(canvas, path);
-    }
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static skiagm::GM* F(void*) { return new GetPosTextPathGM; }
-static skiagm::GMRegistry gR(F);
+}
diff --git a/gm/glyph_pos.cpp b/gm/glyph_pos.cpp
index 15229b8..ed61c76 100644
--- a/gm/glyph_pos.cpp
+++ b/gm/glyph_pos.cpp
@@ -17,60 +17,36 @@
 static const SkScalar kTextHeight = 14.0f;
 static const char kText[] = "Proportional Hamburgefons #% fi";
 
-namespace skiagm {
+static void drawTestCase(SkCanvas* canvas,
+                         SkScalar textScale,
+                         SkScalar strokeWidth,
+                         SkPaint::Style strokeStyle);
 
-class GlyphPosGM : public GM {
-public:
-    GlyphPosGM(SkScalar strokeWidth, SkPaint::Style strokeStyle)
-        : fStrokeWidth(strokeWidth)
-        , fStrokeStyle(strokeStyle) {
-        }
-
-protected:
-
-    SkString onShortName() override {
-        SkString str("glyph_pos");
-        if (fStrokeWidth == 0.0f) {
-            str.append("_h"); // h == Hairline.
-        } else {
-            str.append("_n"); // n == Normal.
-        }
-        if (fStrokeStyle == SkPaint::kStroke_Style) {
-            str.append("_s");
-        } else if (fStrokeStyle == SkPaint::kFill_Style) {
-            str.append("_f");
-        } else {
-            str.append("_b"); // b == Both.
-        }
-        return str;
-    }
-
-    SkISize onISize() override { return SkISize::Make(800, 600); }
-
-    void onDraw(SkCanvas* canvas) override {
-
+static void draw_gm(SkCanvas* canvas,
+                    SkScalar strokeWidth,
+                    SkPaint::Style strokeStyle) {
         // There's a black pixel at 40, 40 for reference.
         canvas->drawPoint(40.0f, 40.0f, SK_ColorBLACK);
 
         // Two reference images.
         canvas->translate(50.0f, 50.0f);
-        drawTestCase(canvas, 1.0f);
+        drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
 
         canvas->translate(0.0f, 50.0f);
-        drawTestCase(canvas, 3.0f);
+        drawTestCase(canvas, 3.0f, strokeWidth, strokeStyle);
 
         // Uniform scaling test.
         canvas->translate(0.0f, 100.0f);
         canvas->save();
         canvas->scale(3.0f, 3.0f);
-        drawTestCase(canvas, 1.0f);
+        drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
         canvas->restore();
 
         // Non-uniform scaling test.
         canvas->translate(0.0f, 100.0f);
         canvas->save();
         canvas->scale(3.0f, 6.0f);
-        drawTestCase(canvas, 1.0f);
+        drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
         canvas->restore();
 
         // Skew test.
@@ -82,7 +58,7 @@
         skew.setSkewX(8.0f / 25.0f);
         skew.setSkewY(2.0f / 25.0f);
         canvas->concat(skew);
-        drawTestCase(canvas, 1.0f);
+        drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
         canvas->restore();
 
         // Perspective test.
@@ -94,23 +70,26 @@
         perspective.setSkewX(8.0f / 25.0f);
         perspective.setSkewY(2.0f / 25.0f);
 
-
         canvas->concat(perspective);
-        drawTestCase(canvas, 1.0f);
+        drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
         canvas->restore();
-    }
+}
 
-    void drawTestCase(SkCanvas* canvas, SkScalar textScale) {
+static void drawTestCase(SkCanvas* canvas,
+                         SkScalar textScale,
+                         SkScalar strokeWidth,
+                         SkPaint::Style strokeStyle) {
         SkPaint paint;
         paint.setColor(SK_ColorBLACK);
         paint.setAntiAlias(true);
         paint.setTextSize(kTextHeight * textScale);
         sk_tool_utils::set_portable_typeface(&paint);
-        paint.setStrokeWidth(fStrokeWidth);
-        paint.setStyle(fStrokeStyle);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStyle(strokeStyle);
 
-        // This demonstrates that we can not measure the text if there's a device transform. The
-        // canvas total matrix will end up being a device transform.
+        // This demonstrates that we can not measure the text if
+        // there's a device transform. The canvas total matrix will
+        // end up being a device transform.
         bool drawRef = !(canvas->getTotalMatrix().getType() &
                          ~(SkMatrix::kIdentity_Mask | SkMatrix::kTranslate_Mask));
 
@@ -132,8 +111,8 @@
 
         // Black text is the testcase, eg. the text.
         paint.setColor(SK_ColorBLACK);
-        paint.setStrokeWidth(fStrokeWidth);
-        paint.setStyle(fStrokeStyle);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStyle(strokeStyle);
         canvas->drawText(kText, sizeof(kText) - 1, 0.0f, 0.0f, paint);
 
         if (drawRef) {
@@ -151,42 +130,23 @@
                 w += widths[i];
             }
         }
-    }
-
-private:
-    SkScalar fStrokeWidth;
-    SkPaint::Style fStrokeStyle;
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* GlyphPosHairlineStrokeAndFillFactory(void*) {
-    return new GlyphPosGM(0.0f, SkPaint::kStrokeAndFill_Style);
-}
-static GM* GlyphPosStrokeAndFillFactory(void*) {
-    return new GlyphPosGM(1.2f, SkPaint::kStrokeAndFill_Style);
-}
-static GM* GlyphPosHairlineStrokeFactory(void*) {
-    return new GlyphPosGM(0.0f, SkPaint::kStroke_Style);
-}
-static GM* GlyphPosStrokeFactory(void*) {
-    return new GlyphPosGM(1.2f, SkPaint::kStroke_Style);
-}
-static GM* GlyphPosHairlineFillFactory(void*) {
-    return new GlyphPosGM(0.0f, SkPaint::kFill_Style);
-}
-static GM* GlyphPosFillFactory(void*) {
-    return new GlyphPosGM(1.2f, SkPaint::kFill_Style);
 }
 
-static GMRegistry reg1(GlyphPosHairlineStrokeAndFillFactory);
-static GMRegistry reg2(GlyphPosStrokeAndFillFactory);
-static GMRegistry reg3(GlyphPosHairlineStrokeFactory);
-static GMRegistry reg4(GlyphPosStrokeFactory);
-static GMRegistry reg5(GlyphPosHairlineFillFactory);
-static GMRegistry reg6(GlyphPosFillFactory);
-
-
+DEF_SIMPLE_GM(glyph_pos_h_b, c, 800, 600) {
+    draw_gm(c, 0.0f, SkPaint::kStrokeAndFill_Style);
+}
+DEF_SIMPLE_GM(glyph_pos_n_b, c, 800, 600) {
+    draw_gm(c, 1.2f, SkPaint::kStrokeAndFill_Style);
+}
+DEF_SIMPLE_GM(glyph_pos_h_s, c, 800, 600) {
+    draw_gm(c, 0.0f, SkPaint::kStroke_Style);
+}
+DEF_SIMPLE_GM(glyph_pos_n_s, c, 800, 600) {
+    draw_gm(c, 1.2f, SkPaint::kStroke_Style);
+}
+DEF_SIMPLE_GM(glyph_pos_h_f, c, 800, 600) {
+    draw_gm(c, 0.0f, SkPaint::kFill_Style);
+}
+DEF_SIMPLE_GM(glyph_pos_n_f, c, 800, 600) {
+    draw_gm(c, 1.2f, SkPaint::kFill_Style);
 }
diff --git a/gm/glyph_pos_align.cpp b/gm/glyph_pos_align.cpp
index 9add494..e80955e 100644
--- a/gm/glyph_pos_align.cpp
+++ b/gm/glyph_pos_align.cpp
@@ -17,20 +17,9 @@
 static const SkScalar kTextHeight = 64.0f;
 static const int kMaxStringLength = 12;
 
-namespace skiagm {
+static void drawTestCase(SkCanvas*, const char*, SkScalar, const SkPaint&);
 
-class GlyphPosAlignGM : public GM {
-protected:
-
-    SkString onShortName() override {
-        return SkString("glyph_pos_align");
-    }
-
-    SkISize onISize() override { return SkISize::Make(kWidth, kHeight); }
-
-    void onDraw(SkCanvas* canvas) override {
-        canvas->clear(SK_ColorBLACK);
-
+DEF_SIMPLE_GM_BG(glyph_pos_align, canvas, kWidth, kHeight, SK_ColorBLACK) {
         SkPaint paint;
         paint.setTextSize(kTextHeight);
         paint.setFakeBoldText(true);
@@ -50,9 +39,9 @@
 
         paint.setTextAlign(SkPaint::kLeft_Align);
         drawTestCase(canvas, "Left Align", 7 * kTextHeight, paint);
-    }
+}
 
-    void drawTestCase(SkCanvas* canvas, const char* text, SkScalar y, const SkPaint& paint) {
+void drawTestCase(SkCanvas* canvas, const char* text, SkScalar y, const SkPaint& paint) {
         SkScalar widths[kMaxStringLength];
         SkScalar posX[kMaxStringLength];
         SkPoint pos[kMaxStringLength];
@@ -78,19 +67,4 @@
 
         canvas->drawPosTextH(text, length, posX, y, paint);
         canvas->drawPosText(text, length, pos, paint);
-    }
-
-private:
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* GlyphPosAlignFactory(void*) {
-    return new GlyphPosAlignGM();
-}
-
-static GMRegistry reg(GlyphPosAlignFactory);
-
 }
diff --git a/gm/gm.cpp b/gm/gm.cpp
index 575ffc1..dc882fd 100644
--- a/gm/gm.cpp
+++ b/gm/gm.cpp
@@ -70,7 +70,7 @@
     canvas->drawRect(r, paint);
 }
 
-void GM::drawGpuOnlyMessage(SkCanvas* canvas) {
+void GM::DrawGpuOnlyMessage(SkCanvas* canvas) {
     SkBitmap bmp;
     bmp.allocN32Pixels(128, 64);
     SkCanvas bmpCanvas(bmp);
diff --git a/gm/gm.h b/gm/gm.h
index 7765dca..e00a650 100644
--- a/gm/gm.h
+++ b/gm/gm.h
@@ -23,11 +23,21 @@
     static skiagm::GM*          SK_MACRO_APPEND_LINE(F_)(void*) { code; } \
     static skiagm::GMRegistry   SK_MACRO_APPEND_LINE(R_)(SK_MACRO_APPEND_LINE(F_));
 
-// See colorwheel.cpp for example usage.
-#define DEF_SIMPLE_GM(NAME, CANVAS, W, H)                                           \
-    static void SK_MACRO_CONCAT(NAME, _GM)(SkCanvas * CANVAS);                      \
-    DEF_GM(return new skiagm::SimpleGM(SkString(#NAME), SK_MACRO_CONCAT(NAME, _GM), \
-                                       SkISize::Make(W, H));)                       \
+// a Simple GM is a rendering test that does not store state between
+// rendering calls or make use of the onOnceBeforeDraw() virtual; it
+// consists of:
+//   *   A single void(*)(SkCanvas*) function.
+//   *   A name.
+//   *   Prefered width and height.
+//   *   Optionally, a background color (default is white).
+#define DEF_SIMPLE_GM(NAME, CANVAS, W, H) \
+    DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, SK_ColorWHITE, SkString(#NAME))
+#define DEF_SIMPLE_GM_BG(NAME, CANVAS, W, H, BGCOLOR)\
+    DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, BGCOLOR, SkString(#NAME))
+#define DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, BGCOLOR, NAME_STR)         \
+    static void SK_MACRO_CONCAT(NAME, _GM)(SkCanvas * CANVAS);               \
+    DEF_GM(return new skiagm::SimpleGM(NAME_STR, SK_MACRO_CONCAT(NAME, _GM), \
+                                       SkISize::Make(W, H), BGCOLOR);)       \
     void SK_MACRO_CONCAT(NAME, _GM)(SkCanvas * CANVAS)
 
 namespace skiagm {
@@ -93,9 +103,10 @@
 
         virtual void modifyGrContextOptions(GrContextOptions* options) {}
 
-    protected:
         /** draws a standard message that the GM is only intended to be used with the GPU.*/
-        void drawGpuOnlyMessage(SkCanvas*);
+        static void DrawGpuOnlyMessage(SkCanvas*);
+
+    protected:
         virtual void onOnceBeforeDraw() {}
         virtual void onDraw(SkCanvas*) = 0;
         virtual void onDrawBackground(SkCanvas*);
@@ -120,8 +131,13 @@
     public:
         SimpleGM(const SkString& name,
                  void (*drawProc)(SkCanvas*),
-                 const SkISize& size)
-            : fName(name), fDrawProc(drawProc), fSize(size) {}
+                 const SkISize& size,
+                 SkColor backgroundColor)
+            : fName(name), fDrawProc(drawProc), fSize(size) {
+            if (backgroundColor != SK_ColorWHITE) {
+                this->setBGColor(backgroundColor);
+            }
+        }
     protected:
         void onDraw(SkCanvas* canvas) override;
         SkISize onISize() override;
diff --git a/gm/gradient_matrix.cpp b/gm/gradient_matrix.cpp
index 7722759..8f665a1 100644
--- a/gm/gradient_matrix.cpp
+++ b/gm/gradient_matrix.cpp
@@ -111,25 +111,8 @@
     canvas->restore();
 }
 
-namespace skiagm {
-
-class GradientMatrixGM : public GM {
-public:
-    GradientMatrixGM() {
-        this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("gradient_matrix");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(800, 800);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG(gradient_matrix, canvas, 800, 800,
+                 sk_tool_utils::color_to_565(0xFFDDDDDD)) {
         draw_gradients(canvas, &make_linear_gradient,
                       linearPts, SK_ARRAY_COUNT(linearPts));
 
@@ -137,11 +120,4 @@
 
         draw_gradients(canvas, &make_radial_gradient,
                       radialPts, SK_ARRAY_COUNT(radialPts));
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-DEF_GM( return new GradientMatrixGM; )
 }
diff --git a/gm/hittestpath.cpp b/gm/hittestpath.cpp
index b5f01cd..d0fe1aa 100644
--- a/gm/hittestpath.cpp
+++ b/gm/hittestpath.cpp
@@ -30,19 +30,7 @@
     }
 }
 
-class HitTestPathGM : public skiagm::GM {
-public:
-    HitTestPathGM () {}
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("hittestpath");
-    }
-
-    SkISize onISize() override { return SkISize::Make(700, 460); }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(hittestpath, canvas, 700, 460) {
         SkPath path;
         SkRandom rand;
 
@@ -70,13 +58,4 @@
         path.setFillType(SkPath::kWinding_FillType);
 
         test_hittest(canvas, path);
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static skiagm::GM* MyFactory(void*) { return new HitTestPathGM; }
-static skiagm::GMRegistry reg(MyFactory);
+}
diff --git a/gm/imageblur.cpp b/gm/imageblur.cpp
index f6ac098..575f02c 100644
--- a/gm/imageblur.cpp
+++ b/gm/imageblur.cpp
@@ -12,27 +12,7 @@
 #define WIDTH 500
 #define HEIGHT 500
 
-namespace skiagm {
-
-class ImageBlurGM : public GM {
-public:
-    ImageBlurGM(SkScalar sigmaX, SkScalar sigmaY, const char* suffix)
-        : fSigmaX(sigmaX), fSigmaY(sigmaY) {
-        this->setBGColor(0xFF000000);
-        fName.printf("imageblur%s", suffix);
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return fName;
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(WIDTH, HEIGHT);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+void imageblurgm_draw(SkScalar fSigmaX, SkScalar fSigmaY, SkCanvas* canvas) {
         SkPaint paint;
         paint.setImageFilter(SkBlurImageFilter::Create(fSigmaX, fSigmaY))->unref();
         canvas->saveLayer(nullptr, &paint);
@@ -51,22 +31,10 @@
                              SkIntToScalar(y), textPaint);
         }
         canvas->restore();
-    }
-
-private:
-    SkScalar fSigmaX;
-    SkScalar fSigmaY;
-    SkString fName;
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory1(void*) { return new ImageBlurGM(24.0f, 0.0f, ""); }
-static GMRegistry reg1(MyFactory1);
-
-static GM* MyFactory2(void*) { return new ImageBlurGM(80.0f, 80.0f, "_large"); }
-static GMRegistry reg2(MyFactory2);
-
+}
+DEF_SIMPLE_GM_BG(imageblur,       canvas, WIDTH, HEIGHT, SK_ColorBLACK) {
+    imageblurgm_draw(24.0f, 0.0f, canvas);
+}
+DEF_SIMPLE_GM_BG(imageblur_large, canvas, WIDTH, HEIGHT, SK_ColorBLACK) {
+    imageblurgm_draw(80.0f, 80.0f, canvas);
 }
diff --git a/gm/imagefilters.cpp b/gm/imagefilters.cpp
index e28bf52..8b4deca 100644
--- a/gm/imagefilters.cpp
+++ b/gm/imagefilters.cpp
@@ -16,15 +16,7 @@
  *
  *  see skbug.com/3741
  */
-class ImageFilterXfermodeTestGM : public skiagm::GM {
-protected:
-    SkString onShortName() override {
-        return SkString("imagefilters_xfermodes");
-    }
-
-    SkISize onISize() override { return SkISize::Make(480, 480); }
-
-    void doDraw(SkCanvas* canvas, SkXfermode::Mode mode, SkImageFilter* imf) {
+static void do_draw(SkCanvas* canvas, SkXfermode::Mode mode, SkImageFilter* imf) {
         SkAutoCanvasRestore acr(canvas, true);
         canvas->clipRect(SkRect::MakeWH(220, 220));
         
@@ -47,9 +39,9 @@
         paint.setImageFilter(imf);
         paint.setXfermodeMode(mode);
         canvas->drawOval(r1, paint);
-    }
+}
 
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(imagefilters_xfermodes, canvas, 480, 480) {
         canvas->translate(10, 10);
 
         // just need an imagefilter to trigger the code-path (which creates a tmp layer)
@@ -62,17 +54,11 @@
         
         for (size_t i = 0; i < SK_ARRAY_COUNT(modes); ++i) {
             canvas->save();
-            this->doDraw(canvas, modes[i], nullptr);
+            do_draw(canvas, modes[i], nullptr);
             canvas->translate(240, 0);
-            this->doDraw(canvas, modes[i], imf);
+            do_draw(canvas, modes[i], imf);
             canvas->restore();
             
             canvas->translate(0, 240);
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-DEF_GM( return new ImageFilterXfermodeTestGM; )
-
+}
diff --git a/gm/imagefromyuvtextures.cpp b/gm/imagefromyuvtextures.cpp
index faa3eef..8c8ab24 100644
--- a/gm/imagefromyuvtextures.cpp
+++ b/gm/imagefromyuvtextures.cpp
@@ -127,7 +127,7 @@
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         GrContext* context;
         if (!rt || !(context = rt->getContext())) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
 
diff --git a/gm/imagemagnifier.cpp b/gm/imagemagnifier.cpp
index 2bf2226..7363981 100644
--- a/gm/imagemagnifier.cpp
+++ b/gm/imagemagnifier.cpp
@@ -12,25 +12,7 @@
 #define WIDTH 500
 #define HEIGHT 500
 
-namespace skiagm {
-
-class ImageMagnifierGM : public GM {
-public:
-    ImageMagnifierGM() {
-        this->setBGColor(0xFF000000);
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("imagemagnifier");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(WIDTH, HEIGHT);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG(imagemagnifier, canvas, WIDTH, HEIGHT, SK_ColorBLACK) {
         SkPaint filterPaint;
         filterPaint.setImageFilter(
             SkMagnifierImageFilter::Create(
@@ -53,15 +35,4 @@
                              SkIntToScalar(y), paint);
         }
         canvas->restore();
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new ImageMagnifierGM; }
-static GMRegistry reg(MyFactory);
-
 }
diff --git a/gm/imageresizetiled.cpp b/gm/imageresizetiled.cpp
index 1da2bce..d925298 100644
--- a/gm/imageresizetiled.cpp
+++ b/gm/imageresizetiled.cpp
@@ -14,24 +14,7 @@
 
 #define RESIZE_FACTOR SkIntToScalar(2)
 
-namespace skiagm {
-
-class ImageResizeTiledGM : public GM {
-public:
-    ImageResizeTiledGM() {
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("imageresizetiled");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(WIDTH, HEIGHT);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(imageresizetiled, canvas, WIDTH, HEIGHT) {
         SkPaint paint;
         SkMatrix matrix;
         matrix.setScale(RESIZE_FACTOR, RESIZE_FACTOR);
@@ -68,14 +51,4 @@
                 canvas->restore();
             }
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new ImageResizeTiledGM(); )
-
 }
diff --git a/gm/inversepaths.cpp b/gm/inversepaths.cpp
index ae54bee..92ea051 100644
--- a/gm/inversepaths.cpp
+++ b/gm/inversepaths.cpp
@@ -9,8 +9,6 @@
 #include "SkCanvas.h"
 #include "SkPath.h"
 
-namespace skiagm {
-
 static SkPath generate_square(SkScalar cx, SkScalar cy, SkScalar w) {
     SkRect rect = SkRect::MakeXYWH(cx - w / 2, cy - w / 2, w, w);
     SkPath path;
@@ -38,6 +36,7 @@
     return path;
 }
 
+namespace {
 SkPaint::Style styles[] = {
         SkPaint::kStroke_Style,
         SkPaint::kStrokeAndFill_Style,
@@ -62,24 +61,9 @@
 const SkScalar slideWidth = 90, slideHeight = 90;
 const SkScalar slideBoundary = 5;
 
+}  // namespace
 
-class InversePathsGM : public GM {
-public:
-    InversePathsGM() {
-
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("inverse_paths");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(800, 900);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(inverse_paths, canvas, 800, 900) {
         SkScalar cx = slideWidth / 2 + slideBoundary;
         SkScalar cy = slideHeight / 2 + slideBoundary;
         SkScalar dx = slideWidth + 2 * slideBoundary;
@@ -137,11 +121,4 @@
                 canvas->translate(0, dy);
             }
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-DEF_GM( return new InversePathsGM; )
 }
diff --git a/gm/largeglyphblur.cpp b/gm/largeglyphblur.cpp
index b74f902..a4c1123 100644
--- a/gm/largeglyphblur.cpp
+++ b/gm/largeglyphblur.cpp
@@ -14,21 +14,7 @@
 
 // This test ensures that glyphs whose point size is less than the SkGlyphCache's maxmium, but
 // who have a large blur, are still handled correctly
-namespace skiagm {
-class LargeGlyphBlur : public GM {
-public:
-    LargeGlyphBlur() {}
-
-protected:
-    SkString onShortName() override {
-        return SkString("largeglyphblur");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(kWidth, kHeight);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(largeglyphblur, canvas, 1920, 600) {
         const char text[] = "Hamburgefons";
 
         SkPaint paint;
@@ -54,16 +40,4 @@
         size_t len = strlen(text);
         canvas->drawText(text, len, 10, 500, blurPaint);
         canvas->drawText(text, len, 10, 500, paint);
-    }
-
-private:
-    static const int kWidth = 1920;
-    static const int kHeight = 600;
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new LargeGlyphBlur;)
 }
diff --git a/gm/lerpmode.cpp b/gm/lerpmode.cpp
index bcf17c3..ea30b6b 100644
--- a/gm/lerpmode.cpp
+++ b/gm/lerpmode.cpp
@@ -34,30 +34,9 @@
     canvas->restore();
 }
 
-class LerpXfermodeGM : public skiagm::GM {
-public:
-    LerpXfermodeGM() {}
-
-protected:
-    SkString onShortName() override {
-        return SkString("lerpmode");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(240, 120);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(lerpmode, canvas, 240, 120) {
         show_circlelayers(canvas, nullptr);
         canvas->translate(150, 0);
         SkAutoTUnref<SkXfermode> mode(SkLerpXfermode::Create(0.5f));
         show_circlelayers(canvas, mode.get());
-    }
-
-private:
-    typedef skiagm::GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new LerpXfermodeGM;)
+}
diff --git a/gm/linepaths.cpp b/gm/linepaths.cpp
index 0c99fa7..51df788 100644
--- a/gm/linepaths.cpp
+++ b/gm/linepaths.cpp
@@ -11,24 +11,10 @@
 #include "SkPaint.h"
 #include "SkRandom.h"
 
-namespace skiagm {
-
-class LinePathGM : public GM {
-public:
-    LinePathGM() {}
-
-protected:
-
-    SkString onShortName() {
-        return SkString("linepath");
-    }
-
-    SkISize onISize() { return SkISize::Make(1240, 390); }
-
-    void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
-                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
-                  SkPaint::Style style, SkPath::FillType fill,
-                  SkScalar strokeWidth) {
+static void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
+                     const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
+                     SkPaint::Style style, SkPath::FillType fill,
+                     SkScalar strokeWidth) {
         path.setFillType(fill);
         SkPaint paint;
         paint.setStrokeCap(cap);
@@ -40,9 +26,9 @@
         canvas->clipRect(clip);
         canvas->drawPath(path, paint);
         canvas->restore();
-    }
+}
 
-    virtual void onDraw(SkCanvas* canvas) {
+static void draw(SkCanvas* canvas, bool doClose) {
         struct FillAndName {
             SkPath::FillType fFill;
             const char*      fName;
@@ -79,15 +65,23 @@
         PathAndName path;
         path.fPath.moveTo(25*SK_Scalar1, 15*SK_Scalar1);
         path.fPath.lineTo(75*SK_Scalar1, 15*SK_Scalar1);
-        path.fName = "moveTo-line";
+        if (doClose) {
+            path.fPath.close();
+            path.fName = "moveTo-line-close";
+        } else {
+            path.fName = "moveTo-line";
+        }
 
         SkPaint titlePaint;
         titlePaint.setColor(SK_ColorBLACK);
         titlePaint.setAntiAlias(true);
         sk_tool_utils::set_portable_typeface(&titlePaint);
         titlePaint.setTextSize(15 * SK_Scalar1);
-        const char title[] = "Line Drawn Into Rectangle Clips With "
-                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        const char titleNoClose[] = "Line Drawn Into Rectangle Clips With "
+            "Indicated Style, Fill and Linecaps, with stroke width 10";
+        const char titleClose[] = "Line Closed Drawn Into Rectangle Clips With "
+            "Indicated Style, Fill and Linecaps, with stroke width 10";
+        const char* title = doClose ? titleClose : titleNoClose;
         canvas->drawText(title, strlen(title),
                             20 * SK_Scalar1,
                             20 * SK_Scalar1,
@@ -114,7 +108,7 @@
                     }
 
                     SkColor color = sk_tool_utils::color_to_565(0xff007000);
-                    this->drawPath(path.fPath, canvas, color, rect,
+                    drawPath(path.fPath, canvas, color, rect,
                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                     gFills[fill].fFill, SK_Scalar1*10);
 
@@ -149,160 +143,10 @@
         }
         canvas->restore();
         canvas->restore();
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-class LineClosePathGM : public GM {
-public:
-    LineClosePathGM() {}
-
-protected:
-    SkString onShortName() {
-        return SkString("lineclosepath");
-    }
-
-    SkISize onISize() { return SkISize::Make(1240, 390); }
-
-    void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
-                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
-                  SkPaint::Style style, SkPath::FillType fill,
-                  SkScalar strokeWidth) {
-        path.setFillType(fill);
-        SkPaint paint;
-        paint.setStrokeCap(cap);
-        paint.setStrokeWidth(strokeWidth);
-        paint.setStrokeJoin(join);
-        paint.setColor(color);
-        paint.setStyle(style);
-        canvas->save();
-        canvas->clipRect(clip);
-        canvas->drawPath(path, paint);
-        canvas->restore();
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        struct FillAndName {
-            SkPath::FillType fFill;
-            const char*      fName;
-        };
-        static const FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
-        };
-        struct StyleAndName {
-            SkPaint::Style fStyle;
-            const char*    fName;
-        };
-        static const StyleAndName gStyles[] = {
-            {SkPaint::kFill_Style, "Fill"},
-            {SkPaint::kStroke_Style, "Stroke"},
-            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
-        };
-        struct CapAndName {
-            SkPaint::Cap  fCap;
-            SkPaint::Join fJoin;
-            const char*   fName;
-        };
-        static const CapAndName gCaps[] = {
-            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
-            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
-            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
-        };
-        struct PathAndName {
-            SkPath      fPath;
-            const char* fName;
-        };
-        PathAndName path;
-        path.fPath.moveTo(25*SK_Scalar1, 15*SK_Scalar1);
-        path.fPath.lineTo(75*SK_Scalar1, 15*SK_Scalar1);
-        path.fPath.close();
-        path.fName = "moveTo-line-close";
-
-        SkPaint titlePaint;
-        titlePaint.setColor(SK_ColorBLACK);
-        titlePaint.setAntiAlias(true);
-        sk_tool_utils::set_portable_typeface(&titlePaint);
-        titlePaint.setTextSize(15 * SK_Scalar1);
-        const char title[] = "Line Closed Drawn Into Rectangle Clips With "
-                             "Indicated Style, Fill and Linecaps, with stroke width 10";
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
-
-        SkRandom rand;
-        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
-        canvas->save();
-        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
-        canvas->save();
-        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
-            if (0 < cap) {
-                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
-            }
-            canvas->save();
-            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
-                if (0 < fill) {
-                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
-                }
-                canvas->save();
-                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
-                    if (0 < style) {
-                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
-                    }
-
-                    SkColor color = sk_tool_utils::color_to_565(0xff007000);
-                    this->drawPath(path.fPath, canvas, color, rect,
-                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
-                                    gFills[fill].fFill, SK_Scalar1*10);
-
-                    SkPaint rectPaint;
-                    rectPaint.setColor(SK_ColorBLACK);
-                    rectPaint.setStyle(SkPaint::kStroke_Style);
-                    rectPaint.setStrokeWidth(-1);
-                    rectPaint.setAntiAlias(true);
-                    canvas->drawRect(rect, rectPaint);
-
-                    SkPaint labelPaint;
-                    labelPaint.setColor(color);
-                    labelPaint.setAntiAlias(true);
-                    sk_tool_utils::set_portable_typeface(&labelPaint);
-                    labelPaint.setTextSize(10 * SK_Scalar1);
-                    canvas->drawText(gStyles[style].fName,
-                                        strlen(gStyles[style].fName),
-                                        0, rect.height() + 12 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gFills[fill].fName,
-                                        strlen(gFills[fill].fName),
-                                        0, rect.height() + 24 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gCaps[cap].fName,
-                                        strlen(gCaps[cap].fName),
-                                        0, rect.height() + 36 * SK_Scalar1,
-                                        labelPaint);
-                }
-                canvas->restore();
-            }
-            canvas->restore();
-        }
-        canvas->restore();
-        canvas->restore();
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* LinePathFactory(void*) { return new LinePathGM; }
-static GMRegistry regLinePath(LinePathFactory);
-
-static GM* LineClosePathFactory(void*) { return new LineClosePathGM; }
-static GMRegistry regLineClosePath(LineClosePathFactory);
-
+}
+DEF_SIMPLE_GM(linepath, canvas, 1240, 390) {
+    draw(canvas, false);
+}
+DEF_SIMPLE_GM(lineclosepath, canvas, 1240, 390) {
+    draw(canvas, true);
 }
diff --git a/gm/matriximagefilter.cpp b/gm/matriximagefilter.cpp
index b4242a7..3b4c0f6 100644
--- a/gm/matriximagefilter.cpp
+++ b/gm/matriximagefilter.cpp
@@ -9,21 +9,8 @@
 #include "SkColor.h"
 #include "SkImageFilter.h"
 
-namespace skiagm {
-
-class MatrixImageFilterGM : public GM {
-public:
-    MatrixImageFilterGM() {
-        this->setBGColor(0x00000000);
-    }
-
-protected:
-    virtual SkString onShortName() {
-        return SkString("matriximagefilter");
-    }
-
-    void draw(SkCanvas* canvas, const SkRect& rect, const SkBitmap& bitmap,
-              const SkMatrix& matrix, SkFilterQuality filter) {
+static void draw(SkCanvas* canvas, const SkRect& rect, const SkBitmap& bitmap,
+                 const SkMatrix& matrix, SkFilterQuality filter) {
         SkAutoTUnref<SkImageFilter> imageFilter(
             SkImageFilter::CreateMatrixFilter(matrix, filter));
         SkPaint paint;
@@ -31,13 +18,9 @@
         canvas->saveLayer(&rect, &paint);
         canvas->drawBitmap(bitmap, 0, 0);
         canvas->restore();
-    }
+}
 
-    virtual SkISize onISize() {
-        return SkISize::Make(420, 100);
-    }
-
-    void make_checkerboard(SkBitmap* bitmap) {
+static void make_checkerboard(SkBitmap* bitmap) {
         bitmap->allocN32Pixels(64, 64);
         SkCanvas canvas(*bitmap);
         SkPaint darkPaint;
@@ -55,10 +38,9 @@
                 canvas.restore();
             }
         }
-    }
+}
 
-    virtual void onDraw(SkCanvas* canvas) {
-        canvas->clear(SK_ColorBLACK);
+DEF_SIMPLE_GM_BG(matriximagefilter, canvas, 420, 100, SK_ColorBLACK) {
         SkMatrix matrix;
         SkScalar margin = SkIntToScalar(10);
         matrix.setSkew(SkDoubleToScalar(0.5), SkDoubleToScalar(0.2));
@@ -81,15 +63,4 @@
         canvas->translate(srcRect.width() + margin, 0);
         draw(canvas, srcRect, checkerboard, matrix, kHigh_SkFilterQuality);
 #endif
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new MatrixImageFilterGM; }
-static GMRegistry reg(MyFactory);
-
 }
diff --git a/gm/mipmap.cpp b/gm/mipmap.cpp
index 4eb5c2a..b0b0cc4 100644
--- a/gm/mipmap.cpp
+++ b/gm/mipmap.cpp
@@ -28,7 +28,7 @@
     return surface->newImageSnapshot();
 }
 
-static void test_mip(SkCanvas* canvas) {
+DEF_SIMPLE_GM(mipmap, canvas, 400, 200) {
     SkAutoTUnref<SkImage> img(make_image());//SkImage::NewFromEncoded(data));
 
     SkPaint paint;
@@ -48,21 +48,3 @@
     canvas->drawImage(img, 20, 20, nullptr);
 }
 
-class MipMapGM : public skiagm::GM {
-public:
-    MipMapGM() {}
-
-protected:
-    SkString onShortName() override { return SkString("mipmap"); }
-
-    SkISize onISize() override { return SkISize::Make(400, 200); }
-
-    void onDraw(SkCanvas* canvas) override {
-        test_mip(canvas);
-    }
-
-private:
-    typedef skiagm::GM INHERITED;
-};
-DEF_GM( return new MipMapGM; )
-
diff --git a/gm/patch.cpp b/gm/patch.cpp
index c3e4300..d9f6d57 100644
--- a/gm/patch.cpp
+++ b/gm/patch.cpp
@@ -64,28 +64,7 @@
     canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, right + 1, paint);
 }
 
-namespace skiagm {
-/**
- * This GM draws a cubics coons patch using the specialized call SkCanvas::drawPatch.
- */
-class SkPatchGM : public GM {
-    
-public:
-    SkPatchGM() {
-        this->setBGColor(0xFFFFFFFF);
-    }
-
-protected:
-    SkString onShortName() override {
-        return SkString("patch_primitive");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(800, 800);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
-
+DEF_SIMPLE_GM(patch_primitive, canvas, 800, 800) {
         SkPaint paint;
         
         // The order of the colors and points is clockwise starting at upper-left corner.
@@ -148,11 +127,4 @@
             }
         }
         canvas->restore();
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-DEF_GM(return new SkPatchGM;)
 }
diff --git a/gm/pathreverse.cpp b/gm/pathreverse.cpp
index 4487f9a..3350765 100644
--- a/gm/pathreverse.cpp
+++ b/gm/pathreverse.cpp
@@ -68,25 +68,8 @@
     canvas->restore();
 }
 
-namespace skiagm {
-
-class PathReverseGM : public GM {
-public:
-    PathReverseGM() {
-
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("path-reverse");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(640, 480);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG_NAME(pathreverse, canvas, 640, 480, SK_ColorWHITE,
+                      SkString("path-reverse")) {
         SkRect r = { 10, 10, 100, 60 };
 
         SkPath path;
@@ -108,15 +91,4 @@
         path = hiragino_maru_goth_pro_e();
         canvas->translate(0, 100);
         test_rev(canvas, path);
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new PathReverseGM; }
-static GMRegistry reg(MyFactory);
-
 }
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index c4736c7..279f4a6 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -63,7 +63,7 @@
         context = rt ? rt->getContext() : nullptr;
 #endif
         if (kEffect_Type == fType && nullptr == context) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
 
diff --git a/gm/shadertext2.cpp b/gm/shadertext2.cpp
index 8ceb5e4..74f01a0 100644
--- a/gm/shadertext2.cpp
+++ b/gm/shadertext2.cpp
@@ -9,8 +9,6 @@
 #include "SkGradientShader.h"
 #include "SkPath.h"
 
-namespace skiagm {
-
 static void makebm(SkBitmap* bm, int w, int h) {
     bm->allocN32Pixels(w, h);
     bm->eraseColor(SK_ColorTRANSPARENT);
@@ -41,21 +39,8 @@
     const char* fLabel;
 };
 
-class ShaderText2GM : public GM {
-public:
-    ShaderText2GM() {
-        this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("shadertext2");
-    }
-
-    SkISize onISize() override { return SkISize::Make(1800, 900); }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG(shadertext2, canvas, 1800, 900,
+                 sk_tool_utils::color_to_565(0xFFDDDDDD)) {
         static const char kText[] = "SKIA";
         static const int kTextLen = SK_ARRAY_COUNT(kText) - 1;
         static const int kPointSize = 55;
@@ -198,14 +183,4 @@
                 canvas->drawText(kStrokeLabel, strlen(kStrokeLabel), strokeX, y, labelPaint);
             }
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new ShaderText2GM; }
-static GMRegistry reg(MyFactory);
 }
diff --git a/gm/skbug1719.cpp b/gm/skbug1719.cpp
index 0a75d35..23c0c5f 100644
--- a/gm/skbug1719.cpp
+++ b/gm/skbug1719.cpp
@@ -10,8 +10,6 @@
 #include "SkColorFilter.h"
 #include "SkPath.h"
 
-namespace skiagm {
-
 /**
  * This test exercises bug 1719. An anti-aliased blurred path is rendered through a soft clip. On
  * the GPU a scratch texture was used to hold the original path mask as well as the blurred path
@@ -20,26 +18,8 @@
  *
  * The correct image should look like a thin stroked round rect.
  */
-class SkBug1719GM : public GM {
-public:
-    SkBug1719GM() {}
-
-protected:
-    SkString onShortName() override {
-        return SkString("skbug1719");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(300, 100);
-    }
-
-    void onDrawBackground(SkCanvas* canvas) override {
-        SkPaint bgPaint;
-        bgPaint.setColor(sk_tool_utils::color_to_565(0xFF303030));
-        canvas->drawPaint(bgPaint);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG(skbug1719, canvas, 300, 100,
+                 sk_tool_utils::color_to_565(0xFF303030)) {
         canvas->translate(SkIntToScalar(-800), SkIntToScalar(-650));
 
         // The data is lifted from an SKP that exhibited the bug.
@@ -89,15 +69,4 @@
 
         canvas->clipPath(clipPath, SkRegion::kIntersect_Op, true);
         canvas->drawPath(drawPath, paint);
-    }
-
-private:
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new SkBug1719GM;)
-
 }
diff --git a/gm/smallarc.cpp b/gm/smallarc.cpp
index 6c0df8d..25755ac 100644
--- a/gm/smallarc.cpp
+++ b/gm/smallarc.cpp
@@ -8,27 +8,10 @@
 #include "gm.h"
 #include "SkPath.h"
 
-namespace skiagm {
-
 // this draws a small arc scaled up
 // see https://code.google.com/p/chromium/issues/detail?id=102411
 // and https://code.google.com/p/skia/issues/detail?id=2769
-class SmallArcGM : public GM {
-public:
-    SmallArcGM() {
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("smallarc");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(762, 762);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(smallarc, canvas, 762, 762) {
         SkPaint p;
         p.setColor(SK_ColorRED);
         p.setAntiAlias(true);
@@ -42,13 +25,4 @@
         canvas->translate(-400, -400);
         canvas->scale(8, 8);
         canvas->drawPath(path, p);
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new SmallArcGM;)
 }
diff --git a/gm/strokefill.cpp b/gm/strokefill.cpp
index e8878b5..4f6e6d2 100644
--- a/gm/strokefill.cpp
+++ b/gm/strokefill.cpp
@@ -11,8 +11,6 @@
 #include "SkTextFormatParams.h"
 #include "SkTypeface.h"
 
-namespace skiagm {
-
 /* Generated on a Mac with:
  * paint.setTypeface(SkTypeface::CreateByName("Papyrus"));
  * paint.getTextPath("H", 1, 100, 80, &textPath);
@@ -228,31 +226,15 @@
     return path;
 }
 
-class StrokeFillGM : public GM {
-public:
-    StrokeFillGM() {
-
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("stroke-fill");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(640, 480);
-    }
-
-    static void show_bold(SkCanvas* canvas, const void* text, int len,
-                          SkScalar x, SkScalar y, const SkPaint& paint) {
+static void show_bold(SkCanvas* canvas, const void* text, int len,
+                      SkScalar x, SkScalar y, const SkPaint& paint) {
         SkPaint p(paint);
         canvas->drawText(text, len, x, y, p);
         p.setFakeBoldText(true);
         canvas->drawText(text, len, x, y + SkIntToScalar(120), p);
-    }
-    
-    static void path_bold(SkCanvas* canvas, const SkPath& path, const SkPaint& paint) {
+}
+
+static void path_bold(SkCanvas* canvas, const SkPath& path, const SkPaint& paint) {
         SkPaint p(paint);
         canvas->drawPath(path, p);
         p.setStyle(SkPaint::kStrokeAndFill_Style);
@@ -265,9 +247,10 @@
         canvas->translate(0, 120);
         canvas->drawPath(path, p);
         canvas->restore();
-    }
-    
-    void onDraw(SkCanvas* canvas) override {
+}
+
+DEF_SIMPLE_GM_BG_NAME(strokefill, canvas, 640, 480, SK_ColorWHITE,
+                      SkString("stroke-fill")) {
         SkScalar x = SkIntToScalar(100);
         SkScalar y = SkIntToScalar(88);
 
@@ -347,13 +330,4 @@
         SkASSERT(SkPathPriv::CheapIsFirstDirection(path4, SkPathPriv::kCW_FirstDirection));
         path4.moveTo(0, 0); // test for crbug.com/247770
         canvas->drawPath(path4, paint);
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new StrokeFillGM;)
 }
diff --git a/gm/stroketext.cpp b/gm/stroketext.cpp
index 75ce164..d0a9882 100644
--- a/gm/stroketext.cpp
+++ b/gm/stroketext.cpp
@@ -59,28 +59,14 @@
     draw_text_stroked(canvas, p, 10);
 }
 
-class StrokeTextGM : public skiagm::GM {
-    // Skia has a threshold above which it draws text via paths instead of using scalercontext
-    // and caching the glyph. This GM wants to ensure that we draw stroking correctly on both
-    // sides of this threshold.
+namespace {
     enum {
         kBelowThreshold_TextSize = 255,
         kAboveThreshold_TextSize = 257
     };
-public:
-    StrokeTextGM() {}
+}
 
-protected:
-
-    SkString onShortName() override {
-        return SkString("stroketext");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(1200, 480);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(stroketext, canvas, 1200, 480) {
         if (true) { test_nulldev(canvas); }
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -92,10 +78,4 @@
         canvas->translate(600, 0);
         paint.setTextSize(kAboveThreshold_TextSize);
         draw_text_set(canvas, paint);
-    }
-
-private:
-    typedef skiagm::GM INHERITED;
-};
-
-DEF_GM(return new StrokeTextGM;)
+}
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index 1a807c7..e36e94d 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -17,26 +17,9 @@
 #include "effects/GrPorterDuffXferProcessor.h"
 #include "effects/GrSimpleTextureEffect.h"
 
-namespace skiagm {
-
 static const int S = 200;
 
-class TexDataGM : public GM {
-public:
-    TexDataGM() {
-        this->setBGColor(0xff000000);
-    }
-
-protected:
-    SkString onShortName() override {
-        return SkString("texdata");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(2*S, 2*S);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM_BG(texdata, canvas, 2 * S, 2 * S, SK_ColorBLACK) {
         GrRenderTarget* target = canvas->internal_private_accessTopLayerRenderTarget();
         GrContext* ctx = canvas->getGrContext();
         SkAutoTUnref<GrDrawContext> drawContext(ctx ? ctx->drawContext() : nullptr);
@@ -130,19 +113,7 @@
                 drawContext->drawRect(target, clip, paint, vm, SkRect::MakeWH(2*S, 2*S));
             }
         } else {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
         }
-    }
-
-private:
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new TexDataGM; }
-static GMRegistry reg(MyFactory);
-
 }
-
 #endif
diff --git a/gm/textblobrandomfont.cpp b/gm/textblobrandomfont.cpp
index 862d83b..f21380e 100644
--- a/gm/textblobrandomfont.cpp
+++ b/gm/textblobrandomfont.cpp
@@ -94,7 +94,7 @@
     void onDraw(SkCanvas* canvas) override {
         // This GM exists to test a specific feature of the GPU backend.
         if (nullptr == canvas->getGrContext()) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
 
diff --git a/gm/textblobuseaftergpufree.cpp b/gm/textblobuseaftergpufree.cpp
index 91681b8..61df533 100644
--- a/gm/textblobuseaftergpufree.cpp
+++ b/gm/textblobuseaftergpufree.cpp
@@ -32,7 +32,7 @@
     void onDraw(SkCanvas* canvas) override {
         // This GM exists to test a specific feature of the GPU backend.
         if (nullptr == canvas->getGrContext()) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
 
diff --git a/gm/texteffects.cpp b/gm/texteffects.cpp
index 902bed3..53395f0 100644
--- a/gm/texteffects.cpp
+++ b/gm/texteffects.cpp
@@ -170,20 +170,7 @@
     paint->setColor(SK_ColorBLUE);
 }
 
-class TextEffectsGM : public skiagm::GM {
-public:
-    TextEffectsGM() {}
-
-protected:
-    SkString onShortName() override {
-        return SkString("texteffects");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(460, 680);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
+DEF_SIMPLE_GM(texteffects, canvas, 460, 680) {
         canvas->save();
 
         SkPaint     paint;
@@ -208,13 +195,4 @@
         }
 
         canvas->restore();
-    }
-
-private:
-    typedef skiagm::GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static skiagm::GM* MyFactory(void*) { return new TextEffectsGM; }
-static skiagm::GMRegistry reg(MyFactory);
+}
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index facfb7a..24e09db 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -75,7 +75,7 @@
         }
         GrContext* context = rt->getContext();
         if (nullptr == context) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }
 
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index 4fd23ef..b639ba3 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -73,7 +73,7 @@
         }
         GrContext* context = rt->getContext();
         if (nullptr == context) {
-            this->drawGpuOnlyMessage(canvas);
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
             return;
         }