1/*
2 * Copyright 2013 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 "SkCanvas.h"
9#include "SkColor.h"
10#include "SkGradientShader.h"
11#include "SkMatrix.h"
12#include "SkPaint.h"
13#include "SkPoint.h"
14#include "SkRect.h"
15#include "SkRefCnt.h"
16#include "SkScalar.h"
17#include "SkSize.h"
18#include "SkString.h"
19
20#include "gm.h"
21
22static const SkColor gColors[] = {
23    SK_ColorRED, SK_ColorYELLOW
24};
25
26// These annoying defines are necessary, because the only other alternative
27// is to use SkIntToScalar(...) everywhere.
28static const SkScalar sZero = 0;
29static const SkScalar sHalf = SK_ScalarHalf;
30static const SkScalar sOne = SK_Scalar1;
31
32// These arrays define the gradient stop points
33// as x1, y1, x2, y2 per gradient to draw.
34static const SkPoint linearPts[][2] = {
35    {{sZero, sZero}, {sOne,  sZero}},
36    {{sZero, sZero}, {sZero, sOne}},
37    {{sOne,  sZero}, {sZero, sZero}},
38    {{sZero, sOne},  {sZero, sZero}},
39
40    {{sZero, sZero}, {sOne,  sOne}},
41    {{sOne,  sOne},  {sZero, sZero}},
42    {{sOne,  sZero}, {sZero, sOne}},
43    {{sZero, sOne},  {sOne,  sZero}}
44};
45
46static const SkPoint radialPts[][2] = {
47    {{sZero, sHalf}, {sOne,  sHalf}},
48    {{sHalf, sZero}, {sHalf, sOne}},
49    {{sOne,  sHalf}, {sZero, sHalf}},
50    {{sHalf, sOne},  {sHalf, sZero}},
51
52    {{sZero, sZero}, {sOne,  sOne}},
53    {{sOne,  sOne},  {sZero, sZero}},
54    {{sOne,  sZero}, {sZero, sOne}},
55    {{sZero, sOne},  {sOne,  sZero}}
56};
57
58// These define the pixels allocated to each gradient image.
59static const SkScalar TESTGRID_X = SkIntToScalar(200);
60static const SkScalar TESTGRID_Y = SkIntToScalar(200);
61
62static const int IMAGES_X = 4;             // number of images per row
63
64static SkShader* make_linear_gradient(const SkPoint pts[2], const SkMatrix& localMatrix) {
65    return SkGradientShader::CreateLinear(pts, gColors, NULL, SK_ARRAY_COUNT(gColors),
66                                          SkShader::kClamp_TileMode, 0, &localMatrix);
67}
68
69static SkShader* make_radial_gradient(const SkPoint pts[2], const SkMatrix& localMatrix) {
70    SkPoint center;
71    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
72               SkScalarAve(pts[0].fY, pts[1].fY));
73    float radius = (center - pts[0]).length();
74    return SkGradientShader::CreateRadial(center, radius, gColors, NULL, SK_ARRAY_COUNT(gColors),
75                                          SkShader::kClamp_TileMode, 0, &localMatrix);
76}
77
78static void draw_gradients(SkCanvas* canvas,
79                           SkShader* (*makeShader)(const SkPoint[2], const SkMatrix&),
80                           const SkPoint ptsArray[][2], int numImages) {
81    // Use some nice prime numbers for the rectangle and matrix with
82    // different scaling along the x and y axes (which is the bug this
83    // test addresses, where incorrect order of operations mixed up the axes)
84    SkRect rectGrad = {
85        SkIntToScalar(43),  SkIntToScalar(61),
86        SkIntToScalar(181), SkIntToScalar(167) };
87    SkMatrix shaderMat;
88    shaderMat.setScale(rectGrad.width(), rectGrad.height());
89    shaderMat.postTranslate(rectGrad.left(), rectGrad.top());
90
91    canvas->save();
92    for (int i = 0; i < numImages; i++) {
93        // Advance line downwards if necessary.
94        if (i % IMAGES_X == 0 && i != 0) {
95            canvas->restore();
96            canvas->translate(0, TESTGRID_Y);
97            canvas->save();
98        }
99
100        // Setup shader and draw.
101        SkAutoTUnref<SkShader> shader(makeShader(*ptsArray, shaderMat));
102
103        SkPaint paint;
104        paint.setShader(shader);
105        canvas->drawRect(rectGrad, paint);
106
107        // Advance to next position.
108        canvas->translate(TESTGRID_X, 0);
109        ptsArray++;
110    }
111    canvas->restore();
112}
113
114namespace skiagm {
115
116class GradientMatrixGM : public GM {
117public:
118    GradientMatrixGM() {
119        this->setBGColor(0xFFDDDDDD);
120    }
121
122protected:
123    virtual uint32_t onGetFlags() const SK_OVERRIDE {
124        return kSkipTiled_Flag;
125    }
126
127    SkString onShortName() SK_OVERRIDE {
128        return SkString("gradient_matrix");
129    }
130
131    virtual SkISize onISize() SK_OVERRIDE {
132        return SkISize::Make(800, 800);
133    }
134
135    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
136        draw_gradients(canvas, &make_linear_gradient,
137                      linearPts, SK_ARRAY_COUNT(linearPts));
138
139        canvas->translate(0, TESTGRID_Y);
140
141        draw_gradients(canvas, &make_radial_gradient,
142                      radialPts, SK_ARRAY_COUNT(radialPts));
143    }
144
145private:
146    typedef GM INHERITED;
147};
148
149DEF_GM( return new GradientMatrixGM; )
150}
151