Fix CFX_Matrix.RotateAt()

The translations were in the opposite order they should be.
First the specified (x, y) coord needs to be translated to the
origin, then rotation should be applied, then the translation should
be reversed. To translate (x, y) to the origin, the initial
translation should be Tx = -x, Ty = -y.

Bug: pdfium:987
Change-Id: I7f873c6a20858bf7fd03e5fdb49020b196b44a1d
Reviewed-on: https://pdfium-review.googlesource.com/23210
Commit-Queue: Henrique Nakashima <hnakashima@chromium.org>
Reviewed-by: Nicolás Peña Moreno <npm@chromium.org>
Reviewed-by: Shirleen Lou <xlou@chromium.org>
diff --git a/core/fxcrt/fx_coordinates.cpp b/core/fxcrt/fx_coordinates.cpp
index ac13a32..69fedb5 100644
--- a/core/fxcrt/fx_coordinates.cpp
+++ b/core/fxcrt/fx_coordinates.cpp
@@ -271,10 +271,10 @@
                  bPrepended);
 }
 
-void CFX_Matrix::RotateAt(float fRadian, float dx, float dy, bool bPrepended) {
-  Translate(dx, dy, bPrepended);
+void CFX_Matrix::RotateAt(float fRadian, float x, float y, bool bPrepended) {
+  Translate(-x, -y, bPrepended);
   Rotate(fRadian, bPrepended);
-  Translate(-dx, -dy, bPrepended);
+  Translate(x, y, bPrepended);
 }
 
 void CFX_Matrix::Shear(float fAlphaRadian, float fBetaRadian, bool bPrepended) {
diff --git a/core/fxcrt/fx_coordinates.h b/core/fxcrt/fx_coordinates.h
index 69d16d1..3d09652 100644
--- a/core/fxcrt/fx_coordinates.h
+++ b/core/fxcrt/fx_coordinates.h
@@ -637,6 +637,8 @@
 
   void Scale(float sx, float sy, bool bPrepended = false);
   void Rotate(float fRadian, bool bPrepended = false);
+
+  // Rotates counterclockwise around the (x, y) point.
   void RotateAt(float fRadian, float x, float y, bool bPrepended = false);
 
   void Shear(float fAlphaRadian, float fBetaRadian, bool bPrepended = false);
diff --git a/core/fxcrt/fx_coordinates_unittest.cpp b/core/fxcrt/fx_coordinates_unittest.cpp
index 3368a40..6fec10e 100644
--- a/core/fxcrt/fx_coordinates_unittest.cpp
+++ b/core/fxcrt/fx_coordinates_unittest.cpp
@@ -289,3 +289,67 @@
   EXPECT_FLOAT_EQ(expected.x, result.x);
   EXPECT_FLOAT_EQ(expected.y, result.y);
 }
+
+TEST(CFX_Matrix, RotateAt) {
+  CFX_Matrix m;
+  m.RotateAt(FX_PI, 10, 20);
+
+  // 180 degree rotation
+  CFX_PointF p(27, 19);
+  CFX_PointF new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(-7, new_p.x);
+  EXPECT_FLOAT_EQ(21, new_p.y);
+
+  p = CFX_PointF(10, 20);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(10, new_p.x);
+  EXPECT_FLOAT_EQ(20, new_p.y);
+
+  p = CFX_PointF(0, 0);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(20, new_p.x);
+  EXPECT_FLOAT_EQ(40, new_p.y);
+
+  // 90 degree rotation
+  m.SetIdentity();
+  m.RotateAt(FX_PI / 2, 10, 20);
+
+  p = CFX_PointF(6, 17);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(13, new_p.x);
+  EXPECT_FLOAT_EQ(16, new_p.y);
+
+  p = CFX_PointF(10, 20);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(10, new_p.x);
+  EXPECT_FLOAT_EQ(20, new_p.y);
+
+  p = CFX_PointF(0, 0);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(30, new_p.x);
+  EXPECT_FLOAT_EQ(10, new_p.y);
+
+  // 60 degree rotation
+  m.SetIdentity();
+  m.RotateAt(FX_PI / 3, 10, 20);
+
+  p = CFX_PointF(20, 20);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(15, new_p.x);
+  EXPECT_FLOAT_EQ(28.660254f, new_p.y);
+
+  p = CFX_PointF(10, 20);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(10, new_p.x);
+  EXPECT_FLOAT_EQ(20, new_p.y);
+
+  p = CFX_PointF(0, 0);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(22.320509f, new_p.x);
+  EXPECT_FLOAT_EQ(1.3397465f, new_p.y);
+
+  p = CFX_PointF(10, -80);
+  new_p = m.Transform(p);
+  EXPECT_FLOAT_EQ(96.602540f, new_p.x);
+  EXPECT_FLOAT_EQ(-30, new_p.y);
+}