text: /*
   * Copyright 2016 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "Fuzz.h"
  #include "SkCanvas.h"
  #include "SkGradientShader.h"
  #include "SkSurface.h"
  #include "SkTLazy.h"
  
  #include <algorithm>
  
  const int MAX_COUNT = 400;
  
  bool makeMatrix(Fuzz* fuzz, SkMatrix* m) {
      SkScalar scaleX, skewX, transX, skewY, scaleY, transY, persp0, persp1, persp2;
      if (!fuzz->next<SkScalar>(&scaleX) ||
              !fuzz->next<SkScalar>(&skewX)  ||
              !fuzz->next<SkScalar>(&transX) ||
              !fuzz->next<SkScalar>(&skewY)  ||
              !fuzz->next<SkScalar>(&scaleY) ||
              !fuzz->next<SkScalar>(&transY) ||
              !fuzz->next<SkScalar>(&persp0) ||
              !fuzz->next<SkScalar>(&persp1) ||
              !fuzz->next<SkScalar>(&persp2)) {
          return false;
      }
      m->setAll(scaleX, skewX, transX, skewY, scaleY, transY, persp0, persp1, persp2);
      return true;
  }
  
  bool initGradientParams(Fuzz* fuzz, uint32_t* count, SkColor** colors, SkScalar** pos,
      SkShader::TileMode* mode) {
      if (fuzz->remaining() < sizeof(uint32_t)) {
          return false;
      }
      uint32_t t_count;
      SkColor* t_colors;
      SkScalar* t_pos;
  
      t_count = fuzz->nextRangeU(0, MAX_COUNT);
      if (t_count == 1) {
          t_count = 2;
      }
  
      if (fuzz->remaining() < (1 + t_count * (sizeof(SkColor) + sizeof(SkScalar)))) {
          return false;
      }
      t_colors = new SkColor[t_count];
      t_pos = new SkScalar[t_count];
      for (uint32_t i = 0; i < t_count; i++) {
          fuzz->next<SkColor>(&t_colors[i]);
          fuzz->next<SkScalar>(&t_pos[i]);
      }
  
      if (t_count == 0) {
          *count = 0;
          *colors = NULL;
          *pos = NULL;
      } else {
          std::sort(t_pos, t_pos + t_count);
          t_pos[0] = 0;
          t_pos[t_count - 1] = 1;
          *count = t_count;
          *colors = t_colors;
          *pos = t_pos;
      }
  
      *mode = static_cast<SkShader::TileMode>(fuzz->nextRangeU(0, 3));
      return true;
  }
  
  void fuzzLinearGradient(Fuzz* fuzz) {
          SkScalar a, b, c, d;
          bool useLocalMatrix, useGlobalMatrix;
          if (!fuzz->next<SkScalar>(&a)          ||
                  !fuzz->next<SkScalar>(&b)          ||
                  !fuzz->next<SkScalar>(&c)          ||
                  !fuzz->next<SkScalar>(&d)          ||
                  !fuzz->next<bool>(&useLocalMatrix) ||
                  !fuzz->next<bool>(&useGlobalMatrix)) {
              return;
          }
          SkPoint pts[2] = {SkPoint::Make(a,b), SkPoint::Make(c, d)};
  
          uint32_t count;
          SkColor* colors;
          SkScalar* pos;
          SkShader::TileMode mode;
          if (!initGradientParams(fuzz, &count, &colors, &pos, &mode)) {
              return;
          }
  
          SkPaint p;
          uint32_t flags;
          if (!fuzz->next(&flags)) {
              return;
          }
  
          SkTLazy<SkMatrix> localMatrix;
          if (useLocalMatrix && !makeMatrix(fuzz, localMatrix.init())) {
              return;
          }
          p.setShader(SkGradientShader::MakeLinear(pts, colors, pos, count, mode,
              flags, localMatrix.getMaybeNull()));
  
          sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
          if (useGlobalMatrix) {
              SkMatrix gm;
              if (!makeMatrix(fuzz, &gm)) {
                  return;
              }
              SkCanvas* c = surface->getCanvas();
              c->setMatrix(gm);
              c->drawPaint(p);
          } else {
              surface->getCanvas()->drawPaint(p);
          }
  }
  
  void fuzzRadialGradient(Fuzz* fuzz) {
          SkScalar a, b, radius;
          bool useLocalMatrix, useGlobalMatrix;
          if (!fuzz->next<SkScalar>(&a)          ||
                  !fuzz->next<SkScalar>(&b)          ||
                  !fuzz->next<SkScalar>(&radius)     ||
                  !fuzz->next<bool>(&useLocalMatrix) ||
                  !fuzz->next<bool>(&useGlobalMatrix)) {
              return;
          }
          SkPoint center = SkPoint::Make(a,b);
  
          uint32_t count;
          SkColor* colors;
          SkScalar* pos;
          SkShader::TileMode mode;
          if (!initGradientParams(fuzz, &count, &colors, &pos, &mode)) {
              return;
          }
  
          SkPaint p;
          uint32_t flags;
          if (!fuzz->next(&flags)) {
              return;
          }
  
          SkTLazy<SkMatrix> localMatrix;
          if (useLocalMatrix && !makeMatrix(fuzz, localMatrix.init())) {
              return;
          }
          p.setShader(SkGradientShader::MakeRadial(center, radius, colors, pos,
              count, mode, flags, localMatrix.getMaybeNull()));
  
  
          sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
          if (useGlobalMatrix) {
              SkMatrix gm;
              if (!makeMatrix(fuzz, &gm)) {
                  return;
              }
              SkCanvas* c = surface->getCanvas();
              c->setMatrix(gm);
              c->drawPaint(p);
          } else {
              surface->getCanvas()->drawPaint(p);
          }
  }
  
  void fuzzTwoPointConicalGradient(Fuzz* fuzz) {
          SkScalar a, b, startRadius, c, d, endRadius;
          bool useLocalMatrix, useGlobalMatrix;
          if (!fuzz->next<SkScalar>(&a)           ||
                  !fuzz->next<SkScalar>(&b)           ||
                  !fuzz->next<SkScalar>(&startRadius) ||
                  !fuzz->next<SkScalar>(&c)           ||
                  !fuzz->next<SkScalar>(&d)           ||
                  !fuzz->next<SkScalar>(&endRadius)   ||
                  !fuzz->next<bool>(&useLocalMatrix)  ||
                  !fuzz->next<bool>(&useGlobalMatrix)) {
              return;
          }
          SkPoint start = SkPoint::Make(a, b);
          SkPoint end = SkPoint::Make(c, d);
  
          uint32_t count;
          SkColor* colors;
          SkScalar* pos;
          SkShader::TileMode mode;
          if (!initGradientParams(fuzz, &count, &colors, &pos, &mode)) {
              return;
          }
  
          SkPaint p;
          uint32_t flags;
          if (!fuzz->next(&flags)) {
              return;
          }
  
          SkTLazy<SkMatrix> localMatrix;
          if (useLocalMatrix && !makeMatrix(fuzz, localMatrix.init())) {
              return;
          }
          p.setShader(SkGradientShader::MakeTwoPointConical(start, startRadius, end,
              endRadius, colors, pos, count, mode, flags, localMatrix.getMaybeNull()));
  
          sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
          if (useGlobalMatrix) {
              SkMatrix gm;
              if (!makeMatrix(fuzz, &gm)) {
                  return;
              }
              SkCanvas* c = surface->getCanvas();
              c->setMatrix(gm);
              c->drawPaint(p);
          } else {
              surface->getCanvas()->drawPaint(p);
          }
  }
  
  void fuzzSweepGradient(Fuzz* fuzz) {
          SkScalar cx, cy;
          bool useLocalMatrix, useGlobalMatrix;
          if (!fuzz->next<SkScalar>(&cx)         ||
                  !fuzz->next<SkScalar>(&cy)         ||
                  !fuzz->next<bool>(&useLocalMatrix) ||
                  !fuzz->next<bool>(&useGlobalMatrix)) {
              return;
          }
  
          uint32_t count;
          SkColor* colors;
          SkScalar* pos;
          SkShader::TileMode mode;
          if (!initGradientParams(fuzz, &count, &colors, &pos, &mode)) {
              return;
          }
  
          SkPaint p;
          if (useLocalMatrix) {
              SkMatrix m;
              if (!makeMatrix(fuzz, &m)) {
                  return;
              }
              uint32_t flags;
              if (!fuzz->next(&flags)) {
                  return;
              }
              p.setShader(SkGradientShader::MakeSweep(cx, cy, colors, pos, count, flags, &m));
          } else {
              p.setShader(SkGradientShader::MakeSweep(cx, cy, colors, pos, count));
          }
  
  
          sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
          if (useGlobalMatrix) {
              SkMatrix gm;
              if (!makeMatrix(fuzz, &gm)) {
                  return;
              }
              SkCanvas* c = surface->getCanvas();
              c->setMatrix(gm);
              c->drawPaint(p);
          } else {
              surface->getCanvas()->drawPaint(p);
          }
  }
  
  DEF_FUZZ(Gradients, fuzz) {
      uint8_t i;
      if (!fuzz->next<uint8_t>(&i)) {
          return;
      }
  
      switch(i) {
          case 0:
              SkDebugf("LinearGradient\n");
              fuzzLinearGradient(fuzz);
              return;
          case 1:
              SkDebugf("RadialGradient\n");
              fuzzRadialGradient(fuzz);
              return;
          case 2:
              SkDebugf("TwoPointConicalGradient\n");
              fuzzTwoPointConicalGradient(fuzz);
              return;
      }
      SkDebugf("SweepGradient\n");
      fuzzSweepGradient(fuzz);
      return;
  }
  