text: /*
   * Copyright 2012 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "SkMatrix.h"
  #include "SkPointPriv.h"
  #include "SkRRect.h"
  #include "Test.h"
  
  static void test_tricky_radii(skiatest::Reporter* reporter) {
      {
          // crbug.com/458522
          SkRRect rr;
          const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
          const SkScalar rad = 12814;
          const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
          rr.setRectRadii(bounds, vec);
      }
  
      {
          // crbug.com//463920
          SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
          SkVector radii[4] = {
              { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
          };
          SkRRect rr;
          rr.setRectRadii(r, radii);
  
          REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY +
                                    (double) rr.radii(SkRRect::kLowerRight_Corner).fY <=
                                    rr.height());
      }
  }
  
  static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
      SkRRect rr;
      const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
      const SkScalar rad = 40;
      rr.setRectXY(bounds, rad, rad);
  
      SkRRect other;
      SkMatrix matrix;
      matrix.setScale(0, 1);
      rr.transform(matrix, &other);
      REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType());
  }
  
  // Test that all the SkRRect entry points correctly handle un-sorted and
  // zero-sized input rects
  static void test_empty(skiatest::Reporter* reporter) {
      static const SkRect oooRects[] = {  // out of order
          { 100, 0, 0, 100 },  // ooo horizontal
          { 0, 100, 100, 0 },  // ooo vertical
          { 100, 100, 0, 0 },  // ooo both
      };
  
      static const SkRect emptyRects[] = {
          { 100, 100, 100, 200 }, // empty horizontal
          { 100, 100, 200, 100 }, // empty vertical
          { 100, 100, 100, 100 }, // empty both
          { 0, 0, 0, 0 }          // setEmpty-empty
      };
  
      static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } };
  
      SkRRect r;
  
      for (size_t i = 0; i < SK_ARRAY_COUNT(oooRects); ++i) {
          r.setRect(oooRects[i]);
          REPORTER_ASSERT(reporter, !r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
  
          r.setOval(oooRects[i]);
          REPORTER_ASSERT(reporter, !r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
  
          r.setRectXY(oooRects[i], 1, 2);
          REPORTER_ASSERT(reporter, !r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
  
          r.setNinePatch(oooRects[i], 0, 1, 2, 3);
          REPORTER_ASSERT(reporter, !r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
  
          r.setRectRadii(oooRects[i], radii);
          REPORTER_ASSERT(reporter, !r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
      }
  
      for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) {
          r.setRect(emptyRects[i]);
          REPORTER_ASSERT(reporter, r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
  
          r.setOval(emptyRects[i]);
          REPORTER_ASSERT(reporter, r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
  
          r.setRectXY(emptyRects[i], 1, 2);
          REPORTER_ASSERT(reporter, r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
  
          r.setNinePatch(emptyRects[i], 0, 1, 2, 3);
          REPORTER_ASSERT(reporter, r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
  
          r.setRectRadii(emptyRects[i], radii);
          REPORTER_ASSERT(reporter, r.isEmpty());
          REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
      }
  
      r.setRect({SK_ScalarNaN, 10, 10, 20});
      REPORTER_ASSERT(reporter, r == SkRRect::MakeEmpty());
      r.setRect({0, 10, 10, SK_ScalarInfinity});
      REPORTER_ASSERT(reporter, r == SkRRect::MakeEmpty());
  }
  
  static const SkScalar kWidth = 100.0f;
  static const SkScalar kHeight = 100.0f;
  
  static void test_inset(skiatest::Reporter* reporter) {
      SkRRect rr, rr2;
      SkRect r = { 0, 0, 100, 100 };
  
      rr.setRect(r);
      rr.inset(-20, -20, &rr2);
      REPORTER_ASSERT(reporter, rr2.isRect());
  
      rr.inset(20, 20, &rr2);
      REPORTER_ASSERT(reporter, rr2.isRect());
  
      rr.inset(r.width()/2, r.height()/2, &rr2);
      REPORTER_ASSERT(reporter, rr2.isEmpty());
  
      rr.setRectXY(r, 20, 20);
      rr.inset(19, 19, &rr2);
      REPORTER_ASSERT(reporter, rr2.isSimple());
      rr.inset(20, 20, &rr2);
      REPORTER_ASSERT(reporter, rr2.isRect());
  }
  
  
  static void test_9patch_rrect(skiatest::Reporter* reporter,
                                const SkRect& rect,
                                SkScalar l, SkScalar t, SkScalar r, SkScalar b,
                                bool checkRadii) {
      SkRRect rr;
      rr.setNinePatch(rect, l, t, r, b);
  
      REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type());
      REPORTER_ASSERT(reporter, rr.rect() == rect);
  
      if (checkRadii) {
          // This test doesn't hold if the radii will be rescaled by SkRRect
          SkRect ninePatchRadii = { l, t, r, b };
          SkPoint rquad[4];
          ninePatchRadii.toQuad(rquad);
          for (int i = 0; i < 4; ++i) {
              REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i));
          }
      }
      SkRRect rr2; // construct the same RR using the most general set function
      SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
      rr2.setRectRadii(rect, radii);
      REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType());
  }
  
  // Test out the basic API entry points
  static void test_round_rect_basic(skiatest::Reporter* reporter) {
      // Test out initialization methods
      SkPoint zeroPt = { 0, 0 };
      SkRRect empty;
  
      empty.setEmpty();
  
      REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
      REPORTER_ASSERT(reporter, empty.rect().isEmpty());
  
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
      }
  
      //----
      SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
  
      SkRRect rr1;
      rr1.setRect(rect);
  
      REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
      REPORTER_ASSERT(reporter, rr1.rect() == rect);
  
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
      }
      SkRRect rr1_2; // construct the same RR using the most general set function
      SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
      rr1_2.setRectRadii(rect, rr1_2_radii);
      REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
      SkRRect rr1_3;  // construct the same RR using the nine patch set function
      rr1_3.setNinePatch(rect, 0, 0, 0, 0);
      REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
  
      //----
      SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
      SkRRect rr2;
      rr2.setOval(rect);
  
      REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
      REPORTER_ASSERT(reporter, rr2.rect() == rect);
  
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter,
                          SkPointPriv::EqualsWithinTolerance(rr2.radii((SkRRect::Corner) i),
                          halfPoint));
      }
      SkRRect rr2_2;  // construct the same RR using the most general set function
      SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
                                  { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
      rr2_2.setRectRadii(rect, rr2_2_radii);
      REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
      SkRRect rr2_3;  // construct the same RR using the nine patch set function
      rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
      REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
  
      //----
      SkPoint p = { 5, 5 };
      SkRRect rr3;
      rr3.setRectXY(rect, p.fX, p.fY);
  
      REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
      REPORTER_ASSERT(reporter, rr3.rect() == rect);
  
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
      }
      SkRRect rr3_2; // construct the same RR using the most general set function
      SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
      rr3_2.setRectRadii(rect, rr3_2_radii);
      REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
      SkRRect rr3_3;  // construct the same RR using the nine patch set function
      rr3_3.setNinePatch(rect, 5, 5, 5, 5);
      REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
  
      //----
      test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
  
      {
          // Test out the rrect from skia:3466
          SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
  
          test_9patch_rrect(reporter,
                            rect2,
                            0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
                            false);
      }
  
      //----
      SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
  
      SkRRect rr5;
      rr5.setRectRadii(rect, radii2);
  
      REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
      REPORTER_ASSERT(reporter, rr5.rect() == rect);
  
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
      }
  
      // Test out == & !=
      REPORTER_ASSERT(reporter, empty != rr3);
      REPORTER_ASSERT(reporter, rr3 != rr5);
  }
  
  // Test out the cases when the RR degenerates to a rect
  static void test_round_rect_rects(skiatest::Reporter* reporter) {
      SkRect r;
  
      //----
      SkRRect empty;
  
      empty.setEmpty();
  
      REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
      r = empty.rect();
      REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
  
      //----
      SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
      SkRRect rr1;
      rr1.setRectXY(rect, 0, 0);
  
      REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
      r = rr1.rect();
      REPORTER_ASSERT(reporter, rect == r);
  
      //----
      SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
  
      SkRRect rr2;
      rr2.setRectRadii(rect, radii);
  
      REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
      r = rr2.rect();
      REPORTER_ASSERT(reporter, rect == r);
  
      //----
      SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
  
      SkRRect rr3;
      rr3.setRectRadii(rect, radii2);
      REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
  }
  
  // Test out the cases when the RR degenerates to an oval
  static void test_round_rect_ovals(skiatest::Reporter* reporter) {
      //----
      SkRect oval;
      SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
      SkRRect rr1;
      rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
  
      REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
      oval = rr1.rect();
      REPORTER_ASSERT(reporter, oval == rect);
  }
  
  // Test out the non-degenerate RR cases
  static void test_round_rect_general(skiatest::Reporter* reporter) {
      //----
      SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
      SkRRect rr1;
      rr1.setRectXY(rect, 20, 20);
  
      REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
  
      //----
      SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
  
      SkRRect rr2;
      rr2.setRectRadii(rect, radii);
  
      REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
  }
  
  // Test out questionable-parameter handling
  static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
  
      // When the radii exceed the base rect they are proportionally scaled down
      // to fit
      SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
      SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
  
      SkRRect rr1;
      rr1.setRectRadii(rect, radii);
  
      REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
  
      const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
  
      REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
      REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
  
      // Negative radii should be capped at zero
      SkRRect rr2;
      rr2.setRectXY(rect, -10, -20);
  
      REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
  
      const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
  
      REPORTER_ASSERT(reporter, 0.0f == p2.fX);
      REPORTER_ASSERT(reporter, 0.0f == p2.fY);
  }
  
  // Move a small box from the start position by (stepX, stepY) 'numSteps' times
  // testing for containment in 'rr' at each step.
  static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
                             SkScalar initX, int stepX, SkScalar initY, int stepY,
                             int numSteps, const bool* contains) {
      SkScalar x = initX, y = initY;
      for (int i = 0; i < numSteps; ++i) {
          SkRect test = SkRect::MakeXYWH(x, y,
                                         stepX ? SkIntToScalar(stepX) : SK_Scalar1,
                                         stepY ? SkIntToScalar(stepY) : SK_Scalar1);
          test.sort();
  
          REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
  
          x += stepX;
          y += stepY;
      }
  }
  
  // Exercise the RR's contains rect method
  static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
  
      static const int kNumRRects = 4;
      static const SkVector gRadii[kNumRRects][4] = {
          { {  0,  0 }, {  0,  0 }, {  0,  0 }, {  0,  0 } },  // rect
          { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } },  // circle
          { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } },  // simple
          { {  0,  0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } }   // complex
      };
  
      SkRRect rrects[kNumRRects];
      for (int i = 0; i < kNumRRects; ++i) {
          rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
      }
  
      // First test easy outs - boxes that are obviously out on
      // each corner and edge
      static const SkRect easyOuts[] = {
          { -5, -5,  5,  5 }, // NW
          { 15, -5, 20,  5 }, // N
          { 35, -5, 45,  5 }, // NE
          { 35, 15, 45, 20 }, // E
          { 35, 45, 35, 45 }, // SE
          { 15, 35, 20, 45 }, // S
          { -5, 35,  5, 45 }, // SW
          { -5, 15,  5, 20 }  // W
      };
  
      for (int i = 0; i < kNumRRects; ++i) {
          for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) {
              REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
          }
      }
  
      // Now test non-trivial containment. For each compass
      // point walk a 1x1 rect in from the edge  of the bounding
      // rect
      static const int kNumSteps = 15;
      bool answers[kNumRRects][8][kNumSteps] = {
          // all the test rects are inside the degenerate rrect
          {
              // rect
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
          },
          // for the circle we expect 6 blocks to be out on the
          // corners (then the rest in) and only the first block
          // out on the vertical and horizontal axes (then
          // the rest in)
          {
              // circle
              { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
          },
          // for the simple round rect we expect 3 out on
          // the corners (then the rest in) and no blocks out
          // on the vertical and horizontal axes
          {
              // simple RR
              { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
          },
          // for the complex case the answer is different for each direction
          {
              // complex RR
              // all in for NW (rect) corner (same as rect case)
              { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              // only first block out for N (same as circle case)
              { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              // first 6 blocks out for NE (same as circle case)
              { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              // only first block out for E (same as circle case)
              { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              // first 3 blocks out for SE (same as simple case)
              { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              // first two blocks out for S
              { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
              // first 9 blocks out for SW
              { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
              // first two blocks out for W (same as S)
              { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
           }
      };
  
      for (int i = 0; i < kNumRRects; ++i) {
          test_direction(reporter, rrects[i],     0,  1,     0,  1, kNumSteps, answers[i][0]); // NW
          test_direction(reporter, rrects[i], 19.5f,  0,     0,  1, kNumSteps, answers[i][1]); // N
          test_direction(reporter, rrects[i],    40, -1,     0,  1, kNumSteps, answers[i][2]); // NE
          test_direction(reporter, rrects[i],    40, -1, 19.5f,  0, kNumSteps, answers[i][3]); // E
          test_direction(reporter, rrects[i],    40, -1,    40, -1, kNumSteps, answers[i][4]); // SE
          test_direction(reporter, rrects[i], 19.5f,  0,    40, -1, kNumSteps, answers[i][5]); // S
          test_direction(reporter, rrects[i],     0,  1,    40, -1, kNumSteps, answers[i][6]); // SW
          test_direction(reporter, rrects[i],     0,  1, 19.5f,  0, kNumSteps, answers[i][7]); // W
      }
  }
  
  // Called for a matrix that should cause SkRRect::transform to fail.
  static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
                                       const SkMatrix& matrix) {
      // The test depends on the fact that the original is not empty.
      SkASSERT(!orig.isEmpty());
      SkRRect dst;
      dst.setEmpty();
  
      const SkRRect copyOfDst = dst;
      const SkRRect copyOfOrig = orig;
      bool success = orig.transform(matrix, &dst);
      // This transform should fail.
      REPORTER_ASSERT(reporter, !success);
      // Since the transform failed, dst should be unchanged.
      REPORTER_ASSERT(reporter, copyOfDst == dst);
      // original should not be modified.
      REPORTER_ASSERT(reporter, copyOfOrig == orig);
      REPORTER_ASSERT(reporter, orig != dst);
  }
  
  #define GET_RADII                                                       \
      const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner);    \
      const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner);   \
      const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner);   \
      const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner);    \
      const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner);      \
      const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner);     \
      const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner);     \
      const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
  
  // Called to test various transforms on a single SkRRect.
  static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
      SkRRect dst;
      dst.setEmpty();
  
      // The identity matrix will duplicate the rrect.
      bool success = orig.transform(SkMatrix::I(), &dst);
      REPORTER_ASSERT(reporter, success);
      REPORTER_ASSERT(reporter, orig == dst);
  
      // Skew and Perspective make transform fail.
      SkMatrix matrix;
      matrix.reset();
      matrix.setSkewX(SkIntToScalar(2));
      assert_transform_failure(reporter, orig, matrix);
  
      matrix.reset();
      matrix.setSkewY(SkIntToScalar(3));
      assert_transform_failure(reporter, orig, matrix);
  
      matrix.reset();
      matrix.setPerspX(4);
      assert_transform_failure(reporter, orig, matrix);
  
      matrix.reset();
      matrix.setPerspY(5);
      assert_transform_failure(reporter, orig, matrix);
  
      // Rotation fails.
      matrix.reset();
      matrix.setRotate(SkIntToScalar(90));
      assert_transform_failure(reporter, orig, matrix);
      matrix.setRotate(SkIntToScalar(37));
      assert_transform_failure(reporter, orig, matrix);
  
      // Translate will keep the rect moved, but otherwise the same.
      matrix.reset();
      SkScalar translateX = SkIntToScalar(32);
      SkScalar translateY = SkIntToScalar(15);
      matrix.setTranslateX(translateX);
      matrix.setTranslateY(translateY);
      dst.setEmpty();
      success = orig.transform(matrix, &dst);
      REPORTER_ASSERT(reporter, success);
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter,
                  orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
      }
      REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
      REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
      REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
      REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
  
      // Keeping the translation, but adding skew will make transform fail.
      matrix.setSkewY(SkIntToScalar(7));
      assert_transform_failure(reporter, orig, matrix);
  
      // Scaling in -x will flip the round rect horizontally.
      matrix.reset();
      matrix.setScaleX(SkIntToScalar(-1));
      dst.setEmpty();
      success = orig.transform(matrix, &dst);
      REPORTER_ASSERT(reporter, success);
      {
          GET_RADII;
          // Radii have swapped in x.
          REPORTER_ASSERT(reporter, origUL == dstUR);
          REPORTER_ASSERT(reporter, origUR == dstUL);
          REPORTER_ASSERT(reporter, origLR == dstLL);
          REPORTER_ASSERT(reporter, origLL == dstLR);
      }
      // Width and height remain the same.
      REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
      REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
      // Right and left have swapped (sort of)
      REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
      // Top has stayed the same.
      REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
  
      // Keeping the scale, but adding a persp will make transform fail.
      matrix.setPerspX(7);
      assert_transform_failure(reporter, orig, matrix);
  
      // Scaling in -y will flip the round rect vertically.
      matrix.reset();
      matrix.setScaleY(SkIntToScalar(-1));
      dst.setEmpty();
      success = orig.transform(matrix, &dst);
      REPORTER_ASSERT(reporter, success);
      {
          GET_RADII;
          // Radii have swapped in y.
          REPORTER_ASSERT(reporter, origUL == dstLL);
          REPORTER_ASSERT(reporter, origUR == dstLR);
          REPORTER_ASSERT(reporter, origLR == dstUR);
          REPORTER_ASSERT(reporter, origLL == dstUL);
      }
      // Width and height remain the same.
      REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
      REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
      // Top and bottom have swapped (sort of)
      REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
      // Left has stayed the same.
      REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
  
      // Scaling in -x and -y will swap in both directions.
      matrix.reset();
      matrix.setScaleY(SkIntToScalar(-1));
      matrix.setScaleX(SkIntToScalar(-1));
      dst.setEmpty();
      success = orig.transform(matrix, &dst);
      REPORTER_ASSERT(reporter, success);
      {
          GET_RADII;
          REPORTER_ASSERT(reporter, origUL == dstLR);
          REPORTER_ASSERT(reporter, origUR == dstLL);
          REPORTER_ASSERT(reporter, origLR == dstUL);
          REPORTER_ASSERT(reporter, origLL == dstUR);
      }
      // Width and height remain the same.
      REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
      REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
      REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
      REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
  
      // Scale in both directions.
      SkScalar xScale = SkIntToScalar(3);
      SkScalar yScale = 3.2f;
      matrix.reset();
      matrix.setScaleX(xScale);
      matrix.setScaleY(yScale);
      dst.setEmpty();
      success = orig.transform(matrix, &dst);
      REPORTER_ASSERT(reporter, success);
      // Radii are scaled.
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
                                      orig.radii((SkRRect::Corner) i).fX * xScale));
          REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
                                      orig.radii((SkRRect::Corner) i).fY * yScale));
      }
      REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
                                                    orig.rect().width() * xScale));
      REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
                                                    orig.rect().height() * yScale));
      REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
                                                    orig.rect().left() * xScale));
      REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
                                                    orig.rect().top() * yScale));
  }
  
  static void test_round_rect_transform(skiatest::Reporter* reporter) {
      SkRRect rrect;
      {
          SkRect r = { 0, 0, kWidth, kHeight };
          rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
          test_transform_helper(reporter, rrect);
      }
      {
          SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
                       SkIntToScalar(27), SkIntToScalar(34) };
          SkVector radii[4] = { { 0, SkIntToScalar(1) },
                                { SkIntToScalar(2), SkIntToScalar(3) },
                                { SkIntToScalar(4), SkIntToScalar(5) },
                                { SkIntToScalar(6), SkIntToScalar(7) } };
          rrect.setRectRadii(r, radii);
          test_transform_helper(reporter, rrect);
      }
  }
  
  // Test out the case where an oval already off in space is translated/scaled
  // further off into space - yielding numerical issues when the rect & radii
  // are transformed separatly
  // BUG=skia:2696
  static void test_issue_2696(skiatest::Reporter* reporter) {
      SkRRect rrect;
      SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
      rrect.setOval(r);
  
      SkMatrix xform;
      xform.setAll(2.44f,  0.0f, 485411.7f,
                   0.0f,  2.44f,   -438.7f,
                   0.0f,   0.0f,      1.0f);
      SkRRect dst;
  
      bool success = rrect.transform(xform, &dst);
      REPORTER_ASSERT(reporter, success);
  
      SkScalar halfWidth = SkScalarHalf(dst.width());
      SkScalar halfHeight = SkScalarHalf(dst.height());
  
      for (int i = 0; i < 4; ++i) {
          REPORTER_ASSERT(reporter,
                          SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
          REPORTER_ASSERT(reporter,
                          SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
      }
  }
  
  void test_read_rrect(skiatest::Reporter* reporter, const SkRRect& rrect, bool shouldEqualSrc) {
      // It would be cleaner to call rrect.writeToMemory into a buffer. However, writeToMemory asserts
      // that the rrect is valid and our caller may have fiddled with the internals of rrect to make
      // it invalid.
      const void* buffer = reinterpret_cast<const void*>(&rrect);
      SkRRect deserialized;
      size_t size = deserialized.readFromMemory(buffer, sizeof(SkRRect));
      REPORTER_ASSERT(reporter, size == SkRRect::kSizeInMemory);
      REPORTER_ASSERT(reporter, deserialized.isValid());
      if (shouldEqualSrc) {
         REPORTER_ASSERT(reporter, rrect == deserialized);
      }
  }
  
  static void test_read(skiatest::Reporter* reporter) {
      static const SkRect kRect = {10.f, 10.f, 20.f, 20.f};
      static const SkRect kNaNRect = {10.f, 10.f, 20.f, SK_ScalarNaN};
      static const SkRect kInfRect = {10.f, 10.f, SK_ScalarInfinity, 20.f};
      SkRRect rrect;
  
      test_read_rrect(reporter, SkRRect::MakeEmpty(), true);
      test_read_rrect(reporter, SkRRect::MakeRect(kRect), true);
      // These get coerced to empty.
      test_read_rrect(reporter, SkRRect::MakeRect(kInfRect), true);
      test_read_rrect(reporter, SkRRect::MakeRect(kNaNRect), true);
  
      rrect.setRect(kRect);
      SkRect* innerRect = reinterpret_cast<SkRect*>(&rrect);
      SkASSERT(*innerRect == kRect);
      *innerRect = kInfRect;
      test_read_rrect(reporter, rrect, false);
      *innerRect = kNaNRect;
      test_read_rrect(reporter, rrect, false);
  
      test_read_rrect(reporter, SkRRect::MakeOval(kRect), true);
      test_read_rrect(reporter, SkRRect::MakeOval(kInfRect), true);
      test_read_rrect(reporter, SkRRect::MakeOval(kNaNRect), true);
      rrect.setOval(kRect);
      *innerRect = kInfRect;
      test_read_rrect(reporter, rrect, false);
      *innerRect = kNaNRect;
      test_read_rrect(reporter, rrect, false);
  
      test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 5.f), true);
      // rrect should scale down the radii to make this legal
      test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 400.f), true);
  
      static const SkVector kRadii[4] = {{0.5f, 1.f}, {1.5f, 2.f}, {2.5f, 3.f}, {3.5f, 4.f}};
      rrect.setRectRadii(kRect, kRadii);
      test_read_rrect(reporter, rrect, true);
      SkScalar* innerRadius = reinterpret_cast<SkScalar*>(&rrect) + 6;
      SkASSERT(*innerRadius == 1.5f);
      *innerRadius = 400.f;
      test_read_rrect(reporter, rrect, false);
      *innerRadius = SK_ScalarInfinity;
      test_read_rrect(reporter, rrect, false);
      *innerRadius = SK_ScalarNaN;
      test_read_rrect(reporter, rrect, false);
      *innerRadius = -10.f;
      test_read_rrect(reporter, rrect, false);
  }
  
  DEF_TEST(RoundRect, reporter) {
      test_round_rect_basic(reporter);
      test_round_rect_rects(reporter);
      test_round_rect_ovals(reporter);
      test_round_rect_general(reporter);
      test_round_rect_iffy_parameters(reporter);
      test_inset(reporter);
      test_round_rect_contains_rect(reporter);
      test_round_rect_transform(reporter);
      test_issue_2696(reporter);
      test_tricky_radii(reporter);
      test_empty_crbug_458524(reporter);
      test_empty(reporter);
      test_read(reporter);
  }
  