1/*
2 * Copyright 2015 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 "gm/gm.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkColor.h"
11#include "include/core/SkImage.h"
12#include "include/core/SkImageInfo.h"
13#include "include/core/SkMatrix.h"
14#include "include/core/SkPaint.h"
15#include "include/core/SkPoint.h"
16#include "include/core/SkRect.h"
17#include "include/core/SkRefCnt.h"
18#include "include/core/SkScalar.h"
19#include "include/core/SkShader.h"
20#include "include/core/SkSize.h"
21#include "include/core/SkString.h"
22#include "include/core/SkSurface.h"
23#include "include/core/SkTileMode.h"
24#include "include/core/SkTypes.h"
25#include "include/effects/SkGradientShader.h"
26#include "include/utils/SkRandom.h"
27#include "src/core/SkMathPriv.h"
28
29static sk_sp<SkImage> makebm(int w, int h) {
30    SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
31    auto surface(SkSurface::MakeRaster(info));
32    SkCanvas* canvas = surface->getCanvas();
33
34    const SkScalar wScalar = SkIntToScalar(w);
35    const SkScalar hScalar = SkIntToScalar(h);
36
37    const SkPoint     pt = { wScalar / 2, hScalar / 2 };
38
39    const SkScalar    radius = 4 * std::max(wScalar, hScalar);
40
41    constexpr SkColor     colors[] = { SK_ColorRED, SK_ColorYELLOW,
42                                          SK_ColorGREEN, SK_ColorMAGENTA,
43                                          SK_ColorBLUE, SK_ColorCYAN,
44                                          SK_ColorRED};
45
46    constexpr SkScalar    pos[] = {0,
47                                      SK_Scalar1 / 6,
48                                      2 * SK_Scalar1 / 6,
49                                      3 * SK_Scalar1 / 6,
50                                      4 * SK_Scalar1 / 6,
51                                      5 * SK_Scalar1 / 6,
52                                      SK_Scalar1};
53
54    SkASSERT(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos));
55    SkPaint     paint;
56    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
57    SkMatrix mat = SkMatrix::I();
58    for (int i = 0; i < 4; ++i) {
59        paint.setShader(SkGradientShader::MakeRadial(
60                        pt, radius,
61                        colors, pos,
62                        SK_ARRAY_COUNT(colors),
63                        SkTileMode::kRepeat,
64                        0, &mat));
65        canvas->drawRect(rect, paint);
66        rect.inset(wScalar / 8, hScalar / 8);
67        mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
68    }
69    return surface->makeImageSnapshot();
70}
71
72constexpr int gSize = 1024;
73constexpr int gSurfaceSize = 2048;
74
75// This GM calls drawImageRect several times using the same texture. This is intended to exercise
76// combining GrDrawOps during these calls.
77class DrawMiniBitmapRectGM : public skiagm::GM {
78public:
79    DrawMiniBitmapRectGM(bool antiAlias) : fAA(antiAlias) {
80        fName.set("drawminibitmaprect");
81        if (fAA) {
82            fName.appendf("_aa");
83        }
84    }
85
86protected:
87    SkString onShortName() override { return fName; }
88
89    SkISize onISize() override { return SkISize::Make(gSize, gSize); }
90
91    void onDraw(SkCanvas* canvas) override {
92        if (nullptr == fImage) {
93            fImage = makebm(gSurfaceSize, gSurfaceSize);
94        }
95
96        const SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)};
97        const int kMaxSrcRectSize = 1 << (SkNextLog2(gSurfaceSize) + 2);
98
99        constexpr int kPadX = 30;
100        constexpr int kPadY = 40;
101
102        int rowCount = 0;
103        canvas->translate(SkIntToScalar(kPadX), SkIntToScalar(kPadY));
104        canvas->save();
105        SkRandom random;
106
107        SkPaint paint;
108        paint.setAntiAlias(fAA);
109        for (int w = 1; w <= kMaxSrcRectSize; w *= 3) {
110            for (int h = 1; h <= kMaxSrcRectSize; h *= 3) {
111
112                const SkIRect srcRect =
113                        SkIRect::MakeXYWH((gSurfaceSize - w) / 2, (gSurfaceSize - h) / 2, w, h);
114                canvas->save();
115                switch (random.nextU() % 3) {
116                    case 0:
117                        canvas->rotate(random.nextF() * 10.f);
118                        break;
119                    case 1:
120                        canvas->rotate(-random.nextF() * 10.f);
121                        break;
122                    case 2:
123                        // rect stays rect
124                        break;
125                }
126                canvas->drawImageRect(fImage.get(), SkRect::Make(srcRect), dstRect,
127                                      SkSamplingOptions(), &paint,
128                                      SkCanvas::kFast_SrcRectConstraint);
129                canvas->restore();
130
131                canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0);
132                ++rowCount;
133                if ((dstRect.width() + 2 * kPadX) * rowCount > gSize) {
134                    canvas->restore();
135                    canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY);
136                    canvas->save();
137                    rowCount = 0;
138                }
139            }
140        }
141        canvas->restore();
142    }
143
144private:
145    bool            fAA;
146    sk_sp<SkImage>  fImage;
147    SkString        fName;
148
149    using INHERITED = skiagm::GM;
150};
151
152DEF_GM( return new DrawMiniBitmapRectGM(true); )
153DEF_GM( return new DrawMiniBitmapRectGM(false); )
154