Factor code to rotate a canvas about a point.

SkMatrix::scale and ::rotate take a point around which to scale or rotate.
Canvas lacks these helpers, so the code to rotate a canvas around a
point has been duplicated many times. Factor all of these
implementations into SkCanvas::rotate.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2142033002

Review-Url: https://codereview.chromium.org/2142033002
diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp
index 812f8c3..f756d4c 100644
--- a/bench/BitmapBench.cpp
+++ b/bench/BitmapBench.cpp
@@ -239,10 +239,7 @@
         if (fFlags & kRotate_Flag) {
             const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
             const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
-
-            canvas->translate(x, y);
-            canvas->rotate(SkIntToScalar(35));
-            canvas->translate(-x, -y);
+            canvas->rotate(SkIntToScalar(35), x, y);
         }
         INHERITED::onDraw(loops, canvas);
     }
diff --git a/gm/dftext.cpp b/gm/dftext.cpp
index 42d650e..8ab2df7 100644
--- a/gm/dftext.cpp
+++ b/gm/dftext.cpp
@@ -33,14 +33,6 @@
         return SkISize::Make(1024, 768);
     }
 
-    static void rotate_about(SkCanvas* canvas,
-        SkScalar degrees,
-        SkScalar px, SkScalar py) {
-        canvas->translate(px, py);
-        canvas->rotate(degrees);
-        canvas->translate(-px, -py);
-    }
-
     virtual void onDraw(SkCanvas* inputCanvas) override {
         SkScalar textSizes[] = { 9.0f, 9.0f*2.0f, 9.0f*5.0f, 9.0f*2.0f*5.0f };
         SkScalar scales[] = { 2.0f*5.0f, 5.0f, 2.0f, 1.0f };
@@ -95,7 +87,7 @@
 
             SkAutoCanvasRestore acr(canvas, true);
             canvas->translate(SkIntToScalar(10 + i * 200), -80);
-            rotate_about(canvas, SkIntToScalar(i * 5), rotX, rotY);
+            canvas->rotate(SkIntToScalar(i * 5), rotX, rotY);
             for (int ps = 6; ps <= 32; ps += 3) {
                 paint.setTextSize(SkIntToScalar(ps));
                 canvas->drawText(text, textLen, rotX, rotY, paint);
diff --git a/gm/fontscaler.cpp b/gm/fontscaler.cpp
index 69cf346..cc9815c 100644
--- a/gm/fontscaler.cpp
+++ b/gm/fontscaler.cpp
@@ -15,9 +15,6 @@
         this->setBGColor(0xFFFFFFFF);
     }
 
-    virtual ~FontScalerGM() {
-    }
-
 protected:
 
     SkString onShortName() override {
@@ -30,14 +27,6 @@
         return SkISize::Make(1450, 750);
     }
 
-    static void rotate_about(SkCanvas* canvas,
-                             SkScalar degrees,
-                             SkScalar px, SkScalar py) {
-        canvas->translate(px, py);
-        canvas->rotate(degrees);
-        canvas->translate(-px, -py);
-    }
-
     void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
 
@@ -59,7 +48,7 @@
                 SkAutoCanvasRestore acr(canvas, true);
                 canvas->translate(SkIntToScalar(50 + i * 230),
                                   SkIntToScalar(20));
-                rotate_about(canvas, SkIntToScalar(i * 5), x, y * 10);
+                canvas->rotate(SkIntToScalar(i * 5), x, y * 10);
 
                 {
                     SkPaint p;
diff --git a/gm/fontscalerdistortable.cpp b/gm/fontscalerdistortable.cpp
index b4f2ca3..be6a7eb 100644
--- a/gm/fontscalerdistortable.cpp
+++ b/gm/fontscalerdistortable.cpp
@@ -19,8 +19,6 @@
         this->setBGColor(0xFFFFFFFF);
     }
 
-    virtual ~FontScalerDistortableGM() { }
-
 protected:
 
     SkString onShortName() override {
@@ -31,12 +29,6 @@
         return SkISize::Make(550, 700);
     }
 
-    static void rotate_about(SkCanvas* canvas, SkScalar degrees, SkScalar px, SkScalar py) {
-        canvas->translate(px, py);
-        canvas->rotate(degrees);
-        canvas->translate(-px, -py);
-    }
-
     void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -63,7 +55,7 @@
 
                 SkAutoCanvasRestore acr(canvas, true);
                 canvas->translate(SkIntToScalar(30 + i * 100), SkIntToScalar(20));
-                rotate_about(canvas, SkIntToScalar(i * 5), x, y * 10);
+                canvas->rotate(SkIntToScalar(i * 5), x, y * 10);
 
                 {
                     SkPaint p;
diff --git a/gm/imagefilterstransformed.cpp b/gm/imagefilterstransformed.cpp
index 7ceff00..0906eb3 100644
--- a/gm/imagefilterstransformed.cpp
+++ b/gm/imagefilterstransformed.cpp
@@ -140,18 +140,14 @@
 
         canvas->translate(150, 0);
         canvas->save();
-            canvas->translate(100, 100);
-            canvas->rotate(30);
-            canvas->translate(-100, -100);
+            canvas->rotate(30, 100, 100);
             canvas->drawRect(r, paint);
         canvas->restore();
 
         paint.setAntiAlias(true);
         canvas->translate(150, 0);
         canvas->save();
-            canvas->translate(100, 100);
-            canvas->rotate(30);
-            canvas->translate(-100, -100);
+            canvas->rotate(30, 100, 100);
             canvas->drawRect(r, paint);
         canvas->restore();
 
diff --git a/gm/strokes.cpp b/gm/strokes.cpp
index 61f5a8a..afbe5ee 100644
--- a/gm/strokes.cpp
+++ b/gm/strokes.cpp
@@ -290,12 +290,6 @@
         return SkISize::Make(W, H*2);
     }
 
-    static void rotate(SkScalar angle, SkScalar px, SkScalar py, SkCanvas* canvas) {
-        SkMatrix matrix;
-        matrix.setRotate(angle, px, py);
-        canvas->concat(matrix);
-    }
-
     void onDraw(SkCanvas* canvas) override {
         canvas->drawColor(SK_ColorWHITE);
 
@@ -316,7 +310,7 @@
             for (int i = 0; i < N/2; i++) {
                 SkRect r;
                 rnd_rect(&r, &paint, rand);
-                rotate(SkIntToScalar(15), SW/2, SH/2, canvas);
+                canvas->rotate(SkIntToScalar(15), SW/2, SH/2);
                 canvas->drawPath(fPath, paint);
             }
         }
diff --git a/gm/typeface.cpp b/gm/typeface.cpp
index 34420db..d579a15 100644
--- a/gm/typeface.cpp
+++ b/gm/typeface.cpp
@@ -154,20 +154,9 @@
     typedef skiagm::GM INHERITED;
 };
 
-static void rotate_about(SkCanvas* canvas,
-                         SkScalar degrees,
-                         SkScalar px, SkScalar py) {
-    canvas->translate(px, py);
-    canvas->rotate(degrees);
-    canvas->translate(-px, -py);
-}
-
 class TypefaceRenderingGM : public skiagm::GM {
     sk_sp<SkTypeface> fFace;
 
-public:
-    TypefaceRenderingGM() { }
-
 protected:
     void onOnceBeforeDraw() override {
         fFace = MakeResourceAsTypeface("/fonts/hintgasp.ttf");
@@ -256,8 +245,8 @@
                         for (const bool& rotateABit : rotateABitTypes) {
                             SkAutoCanvasRestore acr(canvas, true);
                             if (rotateABit) {
-                                rotate_about(canvas, 2, x + subpixel.offset.x(),
-                                                        y + subpixel.offset.y());
+                                canvas->rotate(2, x + subpixel.offset.x(),
+                                                  y + subpixel.offset.y());
                             }
                             canvas->drawText("A", 1, x + subpixel.offset.x(),
                                                      y + subpixel.offset.y(), paint);
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 337ce72..bba045c 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -419,11 +419,18 @@
     */
     void scale(SkScalar sx, SkScalar sy);
 
-    /** Preconcat the current matrix with the specified rotation.
+    /** Preconcat the current matrix with the specified rotation about the origin.
         @param degrees  The amount to rotate, in degrees
     */
     void rotate(SkScalar degrees);
 
+    /** Preconcat the current matrix with the specified rotation about a given point.
+        @param degrees  The amount to rotate, in degrees
+        @param px  The x coordinate of the point to rotate about.
+        @param py  The y coordinate of the point to rotate about.
+    */
+    void rotate(SkScalar degrees, SkScalar px, SkScalar py);
+
     /** Preconcat the current matrix with the specified skew.
         @param sx   The amount to skew in X
         @param sy   The amount to skew in Y
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index ffa507b..7afaa96 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -1372,9 +1372,7 @@
     if (fRotate) {
         SkScalar cx = this->width() / 2;
         SkScalar cy = this->height() / 2;
-        canvas->translate(cx, cy);
-        canvas->rotate(gAnimTimer.scaled(10));
-        canvas->translate(-cx, -cy);
+        canvas->rotate(gAnimTimer.scaled(10), cx, cy);
     }
 
     if (fPerspAnim) {
diff --git a/samplecode/SampleCircle.cpp b/samplecode/SampleCircle.cpp
index ff88ac4..f4e6cc9 100644
--- a/samplecode/SampleCircle.cpp
+++ b/samplecode/SampleCircle.cpp
@@ -87,17 +87,10 @@
         path->close();
     }
 
-    static void rotate(SkCanvas* canvas, SkScalar angle, SkScalar px, SkScalar py) {
-        canvas->translate(-px, -py);
-        canvas->rotate(angle);
-        canvas->translate(px, py);
-    }
-
     virtual void onDrawContent(SkCanvas* canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
-//        canvas->drawCircle(250, 250, 220, paint);
         SkMatrix matrix;
         matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
         matrix.postTranslate(SkIntToScalar(200), SkIntToScalar(200));
diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp
index 99b33f0..a1b751b 100644
--- a/samplecode/SampleDither.cpp
+++ b/samplecode/SampleDither.cpp
@@ -37,10 +37,7 @@
                                             colors, nullptr, SK_ARRAY_COUNT(colors)));
 
     SkAutoCanvasRestore acr(c, true);
-
-    c->translate(r.centerX(), r.centerY());
-    c->rotate(angle);
-    c->translate(-r.centerX(), -r.centerY());
+    c->rotate(angle, r.centerX(), r.centerY());
 
     SkRect bounds = r;
     r.inset(p.getStrokeWidth(), p.getStrokeWidth());
diff --git a/samplecode/SampleFontScalerTest.cpp b/samplecode/SampleFontScalerTest.cpp
index bd93097..2b4a46d 100644
--- a/samplecode/SampleFontScalerTest.cpp
+++ b/samplecode/SampleFontScalerTest.cpp
@@ -44,7 +44,6 @@
             fFaces[i] = SkTypeface::MakeFromName(
                 gFaces[i].fName, SkFontStyle::FromOldStyle(gFaces[i].fStyle));
         }
-//        this->setBGColor(0xFFDDDDDD);
     }
 
 protected:
@@ -57,12 +56,6 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    static void rotate_about(SkCanvas* canvas, SkScalar degrees, SkScalar px, SkScalar py) {
-        canvas->translate(px, py);
-        canvas->rotate(degrees);
-        canvas->translate(-px, -py);
-    }
-
     virtual void onDrawContent(SkCanvas* canvas) {
         SkPaint paint;
 
@@ -100,7 +93,7 @@
                 SkAutoCanvasRestore acr(canvas, true);
                 canvas->translate(SkIntToScalar(50 + i * 230),
                                   SkIntToScalar(20));
-                rotate_about(canvas, SkIntToScalar(i * 5), x, y * 10);
+                canvas->rotate(SkIntToScalar(i * 5), x, y * 10);
 
                 {
                     SkPaint p;
diff --git a/samplecode/SampleHT.cpp b/samplecode/SampleHT.cpp
index d0cd3a5..50ea287 100644
--- a/samplecode/SampleHT.cpp
+++ b/samplecode/SampleHT.cpp
@@ -95,9 +95,7 @@
             fColor = floats_to_color(values);
 
             canvas->save();
-            canvas->translate(fR.centerX(), fR.centerY());
-            canvas->rotate(values[4]);
-            canvas->translate(-fR.centerX(), -fR.centerY());
+            canvas->rotate(values[4], fR.centerX(), fR.centerY());
 
             switch (res) {
                 case SkInterpolator::kFreezeEnd_Result:
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 20e7457..2aa3f5e 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1503,6 +1503,12 @@
     this->concat(m);
 }
 
+void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
+    SkMatrix m;
+    m.setRotate(degrees, px, py);
+    this->concat(m);
+}
+
 void SkCanvas::skew(SkScalar sx, SkScalar sy) {
     SkMatrix m;
     m.setSkew(sx, sy);