| /* | 
 |  * 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 "SkCommonFlags.h" | 
 | #include "SkGradientShader.h" | 
 | #include "SkSurface.h" | 
 | #include "SkTLazy.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <vector> | 
 |  | 
 | const int MAX_COUNT = 400; | 
 |  | 
 | void makeMatrix(Fuzz* fuzz, SkMatrix* m) { | 
 |     SkScalar mat[9]; | 
 |     fuzz->nextN(mat, 9); | 
 |     m->set9(mat); | 
 | } | 
 |  | 
 | void initGradientParams(Fuzz* fuzz, std::vector<SkColor>* colors, | 
 |                         std::vector<SkScalar>* pos, SkShader::TileMode* mode) { | 
 |     int count; | 
 |     fuzz->nextRange(&count, 0, MAX_COUNT); | 
 |  | 
 |     // Use a uint8_t to conserve bytes.  This makes our "fuzzed bytes footprint" | 
 |     // smaller, which leads to more efficient fuzzing. | 
 |     uint8_t m; | 
 |     fuzz->nextRange(&m, 0, 2); | 
 |     *mode = static_cast<SkShader::TileMode>(m); | 
 |  | 
 |     colors->clear(); | 
 |     pos   ->clear(); | 
 |     for (int i = 0; i < count; i++) { | 
 |         SkColor c; | 
 |         SkScalar s; | 
 |         fuzz->next(&c, &s); | 
 |         colors->push_back(c); | 
 |         pos   ->push_back(s); | 
 |     } | 
 |     if (count) { | 
 |         std::sort(pos->begin(), pos->end()); | 
 |         // The order matters.  If count == 1, we want pos == 0. | 
 |         (*pos)[count - 1] = 1; | 
 |         (*pos)[0]         = 0; | 
 |     } | 
 | } | 
 |  | 
 | static void logOptionalMatrix(const char* label, const SkMatrix* m) { | 
 |     if (!m) { | 
 |         return; | 
 |     } | 
 |  | 
 |     SkDEBUGF(("  %s: [ ", label)); | 
 |     for (int i = 0; i < 9; ++i) { | 
 |         SkDEBUGF(("%.9g ", m->get(i))); | 
 |     } | 
 |     SkDEBUGF(("]\n")); | 
 | } | 
 |  | 
 | static void logLinearGradient(const SkPoint pts[2], | 
 |                               const std::vector<SkColor>& colors, | 
 |                               const std::vector<SkScalar> pos, | 
 |                               SkShader::TileMode mode, | 
 |                               uint32_t flags, | 
 |                               const SkMatrix* localMatrix, | 
 |                               const SkMatrix* globalMatrix) { | 
 |     if (!FLAGS_verbose) { | 
 |         return; | 
 |     } | 
 |  | 
 |     SkDebugf("--- fuzzLinearGradient ---\n"); | 
 |     SkDebugf("  pts:\t\t[ (%.9g %.9g) (%.9g %.9g) ]\n", | 
 |              pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y()); | 
 |     SkDebugf("  colors:\t[ "); | 
 |     for (auto color : colors) { | 
 |         SkDebugf("0x%x ", color); | 
 |     } | 
 |  | 
 |     SkDebugf("]\n  pos:\t\t"); | 
 |     if (pos.empty()) { | 
 |         SkDebugf("nullptr"); | 
 |     } else { | 
 |         SkDebugf("[ "); | 
 |         for (auto p : pos) { | 
 |             SkDebugf("%f ", p); | 
 |         } | 
 |     } | 
 |     SkDebugf("]\n"); | 
 |  | 
 |     static const char* gModeName[] = { | 
 |         "kClamp_TileMode", "kRepeat_TileMode", "kMirror_TileMode" | 
 |     }; | 
 |     SkASSERT(mode < SK_ARRAY_COUNT(gModeName)); | 
 |     SkDebugf("  mode:\t\t%s\n", gModeName[mode]); | 
 |     SkDebugf("  flags:\t0x%x\n", flags); | 
 |     logOptionalMatrix("local matrix", localMatrix); | 
 |     logOptionalMatrix("global matrix", globalMatrix); | 
 | } | 
 |  | 
 | void fuzzLinearGradient(Fuzz* fuzz) { | 
 |     SkPoint pts[2]; | 
 |     fuzz->next(&pts[0].fX, &pts[0].fY, &pts[1].fX, &pts[1].fY); | 
 |     bool useLocalMatrix, useGlobalMatrix; | 
 |     fuzz->next(&useLocalMatrix, &useGlobalMatrix); | 
 |  | 
 |     std::vector<SkColor> colors; | 
 |     std::vector<SkScalar> pos; | 
 |     SkShader::TileMode mode; | 
 |     initGradientParams(fuzz, &colors, &pos, &mode); | 
 |  | 
 |     SkPaint p; | 
 |     uint32_t flags; | 
 |     fuzz->next(&flags); | 
 |  | 
 |     SkTLazy<SkMatrix> localMatrix; | 
 |     if (useLocalMatrix) { | 
 |         makeMatrix(fuzz, localMatrix.init()); | 
 |     } | 
 |     p.setShader(SkGradientShader::MakeLinear(pts, colors.data(), pos.data(), | 
 |         colors.size(), mode, flags, localMatrix.getMaybeNull())); | 
 |  | 
 |     sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50)); | 
 |     if (useGlobalMatrix) { | 
 |         SkMatrix gm; | 
 |         makeMatrix(fuzz, &gm); | 
 |         logLinearGradient(pts, colors, pos, mode, flags, localMatrix.getMaybeNull(), &gm); | 
 |         SkCanvas* c = surface->getCanvas(); | 
 |         c->setMatrix(gm); | 
 |         c->drawPaint(p); | 
 |     } else { | 
 |         logLinearGradient(pts, colors, pos, mode, flags, localMatrix.getMaybeNull(), nullptr); | 
 |         surface->getCanvas()->drawPaint(p); | 
 |     } | 
 | } | 
 |  | 
 | void fuzzRadialGradient(Fuzz* fuzz) { | 
 |     SkPoint center; | 
 |     fuzz->next(¢er.fX, ¢er.fY); | 
 |     SkScalar radius; | 
 |     bool useLocalMatrix, useGlobalMatrix; | 
 |     fuzz->next(&radius, &useLocalMatrix, &useGlobalMatrix); | 
 |  | 
 |  | 
 |     std::vector<SkColor> colors; | 
 |     std::vector<SkScalar> pos; | 
 |     SkShader::TileMode mode; | 
 |     initGradientParams(fuzz, &colors, &pos, &mode); | 
 |  | 
 |     SkPaint p; | 
 |     uint32_t flags; | 
 |     fuzz->next(&flags); | 
 |  | 
 |     SkTLazy<SkMatrix> localMatrix; | 
 |     if (useLocalMatrix) { | 
 |         makeMatrix(fuzz, localMatrix.init()); | 
 |     } | 
 |     p.setShader(SkGradientShader::MakeRadial(center, radius, colors.data(), | 
 |         pos.data(), colors.size(), mode, flags, localMatrix.getMaybeNull())); | 
 |  | 
 |  | 
 |     sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50)); | 
 |     if (useGlobalMatrix) { | 
 |         SkMatrix gm; | 
 |         makeMatrix(fuzz, &gm); | 
 |         SkCanvas* c = surface->getCanvas(); | 
 |         c->setMatrix(gm); | 
 |         c->drawPaint(p); | 
 |     } else { | 
 |         surface->getCanvas()->drawPaint(p); | 
 |     } | 
 | } | 
 |  | 
 | void fuzzTwoPointConicalGradient(Fuzz* fuzz) { | 
 |     SkPoint start; | 
 |     fuzz->next(&start.fX, &start.fY); | 
 |     SkPoint end; | 
 |     fuzz->next(&end.fX, &end.fY); | 
 |     SkScalar startRadius, endRadius; | 
 |     bool useLocalMatrix, useGlobalMatrix; | 
 |     fuzz->next(&startRadius, &endRadius, &useLocalMatrix, &useGlobalMatrix); | 
 |  | 
 |     std::vector<SkColor> colors; | 
 |     std::vector<SkScalar> pos; | 
 |     SkShader::TileMode mode; | 
 |     initGradientParams(fuzz, &colors, &pos, &mode); | 
 |  | 
 |     SkPaint p; | 
 |     uint32_t flags; | 
 |     fuzz->next(&flags); | 
 |  | 
 |     SkTLazy<SkMatrix> localMatrix; | 
 |     if (useLocalMatrix) { | 
 |         makeMatrix(fuzz, localMatrix.init()); | 
 |     } | 
 |     p.setShader(SkGradientShader::MakeTwoPointConical(start, startRadius, | 
 |         end, endRadius, colors.data(), pos.data(), colors.size(), mode, | 
 |         flags, localMatrix.getMaybeNull())); | 
 |  | 
 |     sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50)); | 
 |     if (useGlobalMatrix) { | 
 |         SkMatrix gm; | 
 |         makeMatrix(fuzz, &gm); | 
 |         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; | 
 |     fuzz->next(&cx, &cy, &useLocalMatrix, &useGlobalMatrix); | 
 |  | 
 |     std::vector<SkColor> colors; | 
 |     std::vector<SkScalar> pos; | 
 |     SkShader::TileMode mode; | 
 |     initGradientParams(fuzz, &colors, &pos, &mode); | 
 |  | 
 |     SkPaint p; | 
 |     if (useLocalMatrix) { | 
 |         SkMatrix m; | 
 |         makeMatrix(fuzz, &m); | 
 |         uint32_t flags; | 
 |         fuzz->next(&flags); | 
 |  | 
 |         p.setShader(SkGradientShader::MakeSweep(cx, cy, colors.data(), | 
 |             pos.data(), colors.size(), flags, &m)); | 
 |     } else { | 
 |         p.setShader(SkGradientShader::MakeSweep(cx, cy, colors.data(), | 
 |             pos.data(), colors.size())); | 
 |     } | 
 |  | 
 |     sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50)); | 
 |     if (useGlobalMatrix) { | 
 |         SkMatrix gm; | 
 |         makeMatrix(fuzz, &gm); | 
 |         SkCanvas* c = surface->getCanvas(); | 
 |         c->setMatrix(gm); | 
 |         c->drawPaint(p); | 
 |     } else { | 
 |         surface->getCanvas()->drawPaint(p); | 
 |     } | 
 | } | 
 |  | 
 | DEF_FUZZ(Gradients, fuzz) { | 
 |     uint8_t i; | 
 |     fuzz->next(&i); | 
 |  | 
 |     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; | 
 | } |