1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "Fuzz.h"
9#include "SkCanvas.h"
10#include "SkCommonFlags.h"
11#include "SkGradientShader.h"
12#include "SkSurface.h"
13#include "SkTLazy.h"
14
15#include <algorithm>
16#include <vector>
17
18const int MAX_COUNT = 400;
19
20void makeMatrix(Fuzz* fuzz, SkMatrix* m) {
21    SkScalar mat[9];
22    fuzz->nextN(mat, 9);
23    m->set9(mat);
24}
25
26void initGradientParams(Fuzz* fuzz, std::vector<SkColor>* colors,
27                        std::vector<SkScalar>* pos, SkShader::TileMode* mode) {
28    int count;
29    fuzz->nextRange(&count, 0, MAX_COUNT);
30
31    // Use a uint8_t to conserve bytes.  This makes our "fuzzed bytes footprint"
32    // smaller, which leads to more efficient fuzzing.
33    uint8_t m;
34    fuzz->nextRange(&m, 0, 2);
35    *mode = static_cast<SkShader::TileMode>(m);
36
37    colors->clear();
38    pos   ->clear();
39    for (int i = 0; i < count; i++) {
40        SkColor c;
41        SkScalar s;
42        fuzz->next(&c, &s);
43        colors->push_back(c);
44        pos   ->push_back(s);
45    }
46    if (count) {
47        std::sort(pos->begin(), pos->end());
48        // The order matters.  If count == 1, we want pos == 0.
49        (*pos)[count - 1] = 1;
50        (*pos)[0]         = 0;
51    }
52}
53
54static void logOptionalMatrix(const char* label, const SkMatrix* m) {
55    if (!m) {
56        return;
57    }
58
59    SkDebugf("  %s: [ ", label);
60    for (int i = 0; i < 9; ++i) {
61        SkDebugf("%.9g ", m->get(i));
62    }
63    SkDebugf("]\n");
64}
65
66static void logLinearGradient(const SkPoint pts[2],
67                              const std::vector<SkColor>& colors,
68                              const std::vector<SkScalar> pos,
69                              SkShader::TileMode mode,
70                              uint32_t flags,
71                              const SkMatrix* localMatrix,
72                              const SkMatrix* globalMatrix) {
73    if (!FLAGS_verbose) {
74        return;
75    }
76
77    SkDebugf("--- fuzzLinearGradient ---\n");
78    SkDebugf("  pts:\t\t[ (%.9g %.9g) (%.9g %.9g) ]\n",
79             pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
80    SkDebugf("  colors:\t[ ");
81    for (auto color : colors) {
82        SkDebugf("0x%x ", color);
83    }
84
85    SkDebugf("]\n  pos:\t\t");
86    if (pos.empty()) {
87        SkDebugf("nullptr");
88    } else {
89        SkDebugf("[ ");
90        for (auto p : pos) {
91            SkDebugf("%f ", p);
92        }
93    }
94    SkDebugf("]\n");
95
96    static const char* gModeName[] = {
97        "kClamp_TileMode", "kRepeat_TileMode", "kMirror_TileMode"
98    };
99    SkASSERT(mode < SK_ARRAY_COUNT(gModeName));
100    SkDebugf("  mode:\t\t%s\n", gModeName[mode]);
101    SkDebugf("  flags:\t0x%x\n", flags);
102    logOptionalMatrix("local matrix", localMatrix);
103    logOptionalMatrix("global matrix", globalMatrix);
104}
105
106void fuzzLinearGradient(Fuzz* fuzz) {
107    SkPoint pts[2];
108    fuzz->next(&pts[0].fX, &pts[0].fY, &pts[1].fX, &pts[1].fY);
109    bool useLocalMatrix, useGlobalMatrix;
110    fuzz->next(&useLocalMatrix, &useGlobalMatrix);
111
112    std::vector<SkColor> colors;
113    std::vector<SkScalar> pos;
114    SkShader::TileMode mode;
115    initGradientParams(fuzz, &colors, &pos, &mode);
116
117    SkPaint p;
118    uint32_t flags;
119    fuzz->next(&flags);
120
121    SkTLazy<SkMatrix> localMatrix;
122    if (useLocalMatrix) {
123        makeMatrix(fuzz, localMatrix.init());
124    }
125    p.setShader(SkGradientShader::MakeLinear(pts, colors.data(), pos.data(),
126        colors.size(), mode, flags, localMatrix.getMaybeNull()));
127
128    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
129    if (useGlobalMatrix) {
130        SkMatrix gm;
131        makeMatrix(fuzz, &gm);
132        logLinearGradient(pts, colors, pos, mode, flags, localMatrix.getMaybeNull(), &gm);
133        SkCanvas* c = surface->getCanvas();
134        c->setMatrix(gm);
135        c->drawPaint(p);
136    } else {
137        logLinearGradient(pts, colors, pos, mode, flags, localMatrix.getMaybeNull(), nullptr);
138        surface->getCanvas()->drawPaint(p);
139    }
140}
141
142void fuzzRadialGradient(Fuzz* fuzz) {
143    SkPoint center;
144    fuzz->next(&center.fX, &center.fY);
145    SkScalar radius;
146    bool useLocalMatrix, useGlobalMatrix;
147    fuzz->next(&radius, &useLocalMatrix, &useGlobalMatrix);
148
149
150    std::vector<SkColor> colors;
151    std::vector<SkScalar> pos;
152    SkShader::TileMode mode;
153    initGradientParams(fuzz, &colors, &pos, &mode);
154
155    SkPaint p;
156    uint32_t flags;
157    fuzz->next(&flags);
158
159    SkTLazy<SkMatrix> localMatrix;
160    if (useLocalMatrix) {
161        makeMatrix(fuzz, localMatrix.init());
162    }
163    p.setShader(SkGradientShader::MakeRadial(center, radius, colors.data(),
164        pos.data(), colors.size(), mode, flags, localMatrix.getMaybeNull()));
165
166
167    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
168    if (useGlobalMatrix) {
169        SkMatrix gm;
170        makeMatrix(fuzz, &gm);
171        SkCanvas* c = surface->getCanvas();
172        c->setMatrix(gm);
173        c->drawPaint(p);
174    } else {
175        surface->getCanvas()->drawPaint(p);
176    }
177}
178
179void fuzzTwoPointConicalGradient(Fuzz* fuzz) {
180    SkPoint start;
181    fuzz->next(&start.fX, &start.fY);
182    SkPoint end;
183    fuzz->next(&end.fX, &end.fY);
184    SkScalar startRadius, endRadius;
185    bool useLocalMatrix, useGlobalMatrix;
186    fuzz->next(&startRadius, &endRadius, &useLocalMatrix, &useGlobalMatrix);
187
188    std::vector<SkColor> colors;
189    std::vector<SkScalar> pos;
190    SkShader::TileMode mode;
191    initGradientParams(fuzz, &colors, &pos, &mode);
192
193    SkPaint p;
194    uint32_t flags;
195    fuzz->next(&flags);
196
197    SkTLazy<SkMatrix> localMatrix;
198    if (useLocalMatrix) {
199        makeMatrix(fuzz, localMatrix.init());
200    }
201    p.setShader(SkGradientShader::MakeTwoPointConical(start, startRadius,
202        end, endRadius, colors.data(), pos.data(), colors.size(), mode,
203        flags, localMatrix.getMaybeNull()));
204
205    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
206    if (useGlobalMatrix) {
207        SkMatrix gm;
208        makeMatrix(fuzz, &gm);
209        SkCanvas* c = surface->getCanvas();
210        c->setMatrix(gm);
211        c->drawPaint(p);
212    } else {
213        surface->getCanvas()->drawPaint(p);
214    }
215}
216
217void fuzzSweepGradient(Fuzz* fuzz) {
218    SkScalar cx, cy;
219    bool useLocalMatrix, useGlobalMatrix;
220    fuzz->next(&cx, &cy, &useLocalMatrix, &useGlobalMatrix);
221
222    std::vector<SkColor> colors;
223    std::vector<SkScalar> pos;
224    SkShader::TileMode mode;
225    initGradientParams(fuzz, &colors, &pos, &mode);
226
227    SkPaint p;
228    if (useLocalMatrix) {
229        SkMatrix m;
230        makeMatrix(fuzz, &m);
231        uint32_t flags;
232        fuzz->next(&flags);
233
234        p.setShader(SkGradientShader::MakeSweep(cx, cy, colors.data(),
235            pos.data(), colors.size(), flags, &m));
236    } else {
237        p.setShader(SkGradientShader::MakeSweep(cx, cy, colors.data(),
238            pos.data(), colors.size()));
239    }
240
241    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
242    if (useGlobalMatrix) {
243        SkMatrix gm;
244        makeMatrix(fuzz, &gm);
245        SkCanvas* c = surface->getCanvas();
246        c->setMatrix(gm);
247        c->drawPaint(p);
248    } else {
249        surface->getCanvas()->drawPaint(p);
250    }
251}
252
253DEF_FUZZ(Gradients, fuzz) {
254    uint8_t i;
255    fuzz->next(&i);
256
257    switch(i) {
258        case 0:
259            SkDebugf("LinearGradient\n");
260            fuzzLinearGradient(fuzz);
261            return;
262        case 1:
263            SkDebugf("RadialGradient\n");
264            fuzzRadialGradient(fuzz);
265            return;
266        case 2:
267            SkDebugf("TwoPointConicalGradient\n");
268            fuzzTwoPointConicalGradient(fuzz);
269            return;
270    }
271    SkDebugf("SweepGradient\n");
272    fuzzSweepGradient(fuzz);
273    return;
274}
275