1/*
2 * Copyright 2011 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.h"
9#include "sk_tool_utils.h"
10#include "SkBlurMask.h"
11#include "SkBlurMaskFilter.h"
12#include "SkColorPriv.h"
13#include "SkGradientShader.h"
14#include "SkImage.h"
15#include "SkImage_Base.h"
16#include "SkMathPriv.h"
17#include "SkShader.h"
18#include "SkSurface.h"
19
20
21static SkBitmap make_chessbm(int w, int h) {
22    SkBitmap bm;
23    bm.allocN32Pixels(w, h);
24
25    for (int y = 0; y < bm.height(); y++) {
26        uint32_t* p = bm.getAddr32(0, y);
27        for (int x = 0; x < bm.width(); x++) {
28            p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
29        }
30    }
31    bm.unlockPixels();
32    return bm;
33}
34
35// Creates a bitmap and a matching image.
36static sk_sp<SkImage> makebm(SkCanvas* origCanvas, SkBitmap* resultBM, int w, int h) {
37    SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
38
39    auto surface(origCanvas->makeSurface(info));
40    if (nullptr == surface) {
41        // picture canvas will return null, so fall-back to raster
42        surface = SkSurface::MakeRaster(info);
43    }
44
45    SkCanvas* canvas = surface->getCanvas();
46
47    canvas->clear(SK_ColorTRANSPARENT);
48
49    SkScalar wScalar = SkIntToScalar(w);
50    SkScalar hScalar = SkIntToScalar(h);
51
52    SkPoint     pt = { wScalar / 2, hScalar / 2 };
53
54    SkScalar    radius = 4 * SkMaxScalar(wScalar, hScalar);
55
56    SkColor     colors[] = { SK_ColorRED, SK_ColorYELLOW,
57                             SK_ColorGREEN, SK_ColorMAGENTA,
58                             SK_ColorBLUE, SK_ColorCYAN,
59                             SK_ColorRED};
60
61    SkScalar    pos[] = {0,
62                         SK_Scalar1 / 6,
63                         2 * SK_Scalar1 / 6,
64                         3 * SK_Scalar1 / 6,
65                         4 * SK_Scalar1 / 6,
66                         5 * SK_Scalar1 / 6,
67                         SK_Scalar1};
68
69    SkPaint     paint;
70    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
71    SkMatrix mat = SkMatrix::I();
72    for (int i = 0; i < 4; ++i) {
73        paint.setShader(SkGradientShader::MakeRadial(
74                        pt, radius,
75                        colors, pos,
76                        SK_ARRAY_COUNT(colors),
77                        SkShader::kRepeat_TileMode,
78                        0, &mat));
79        canvas->drawRect(rect, paint);
80        rect.inset(wScalar / 8, hScalar / 8);
81        mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
82    }
83
84    auto image = surface->makeImageSnapshot();
85
86    SkBitmap tempBM;
87
88    image->asLegacyBitmap(&tempBM, SkImage::kRO_LegacyBitmapMode);
89
90    // Let backends know we won't change this, so they don't have to deep copy it defensively.
91    tempBM.setImmutable();
92    *resultBM = tempBM;
93
94    return image;
95}
96
97static void bitmapproc(SkCanvas* canvas, SkImage*, const SkBitmap& bm, const SkIRect& srcR,
98                       const SkRect& dstR, const SkPaint* paint) {
99    canvas->drawBitmapRect(bm, srcR, dstR, paint);
100}
101
102static void bitmapsubsetproc(SkCanvas* canvas, SkImage*, const SkBitmap& bm, const SkIRect& srcR,
103                             const SkRect& dstR, const SkPaint* paint) {
104    if (!bm.bounds().contains(srcR)) {
105        bitmapproc(canvas, nullptr, bm, srcR, dstR, paint);
106        return;
107    }
108
109    SkBitmap subset;
110    if (bm.extractSubset(&subset, srcR)) {
111        canvas->drawBitmapRect(subset, dstR, paint);
112    }
113}
114
115static void imageproc(SkCanvas* canvas, SkImage* image, const SkBitmap&, const SkIRect& srcR,
116                      const SkRect& dstR, const SkPaint* paint) {
117    canvas->drawImageRect(image, srcR, dstR, paint);
118}
119
120static void imagesubsetproc(SkCanvas* canvas, SkImage* image, const SkBitmap& bm,
121                            const SkIRect& srcR, const SkRect& dstR, const SkPaint* paint) {
122    if (!image->bounds().contains(srcR)) {
123        imageproc(canvas, image, bm, srcR, dstR, paint);
124        return;
125    }
126
127    if (sk_sp<SkImage> subset = image->makeSubset(srcR)) {
128        canvas->drawImageRect(subset, dstR, paint);
129    }
130}
131
132typedef void DrawRectRectProc(SkCanvas*, SkImage*, const SkBitmap&, const SkIRect&, const SkRect&,
133                              const SkPaint*);
134
135constexpr int gSize = 1024;
136constexpr int gBmpSize = 2048;
137
138class DrawBitmapRectGM : public skiagm::GM {
139public:
140    DrawBitmapRectGM(DrawRectRectProc proc, const char suffix[]) : fProc(proc) {
141        fName.set("drawbitmaprect");
142        if (suffix) {
143            fName.append(suffix);
144        }
145    }
146
147    DrawRectRectProc*   fProc;
148    SkBitmap            fLargeBitmap;
149    sk_sp<SkImage>      fImage;
150    SkString            fName;
151
152protected:
153    SkString onShortName() override { return fName; }
154
155    SkISize onISize() override { return SkISize::Make(gSize, gSize); }
156
157    void setupImage(SkCanvas* canvas) {
158        fImage = makebm(canvas, &fLargeBitmap, gBmpSize, gBmpSize);
159    }
160
161    void onDraw(SkCanvas* canvas) override {
162        if (!fImage) {
163            this->setupImage(canvas);
164        }
165
166        SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)};
167        const int kMaxSrcRectSize = 1 << (SkNextLog2(gBmpSize) + 2);
168
169        const int kPadX = 30;
170        const int kPadY = 40;
171        SkPaint paint;
172        paint.setAlpha(0x20);
173        canvas->drawImageRect(fImage, SkRect::MakeIWH(gSize, gSize), &paint);
174        canvas->translate(SK_Scalar1 * kPadX / 2,
175                          SK_Scalar1 * kPadY / 2);
176        SkPaint blackPaint;
177        SkScalar titleHeight = SK_Scalar1 * 24;
178        blackPaint.setColor(SK_ColorBLACK);
179        blackPaint.setTextSize(titleHeight);
180        blackPaint.setAntiAlias(true);
181        sk_tool_utils::set_portable_typeface(&blackPaint);
182        SkString title;
183        title.printf("Bitmap size: %d x %d", gBmpSize, gBmpSize);
184        canvas->drawText(title.c_str(), title.size(), 0,
185                         titleHeight, blackPaint);
186
187        canvas->translate(0, SK_Scalar1 * kPadY / 2  + titleHeight);
188        int rowCount = 0;
189        canvas->save();
190        for (int w = 1; w <= kMaxSrcRectSize; w *= 4) {
191            for (int h = 1; h <= kMaxSrcRectSize; h *= 4) {
192
193                SkIRect srcRect = SkIRect::MakeXYWH((gBmpSize - w) / 2, (gBmpSize - h) / 2, w, h);
194                fProc(canvas, fImage.get(), fLargeBitmap, srcRect, dstRect, nullptr);
195
196                SkString label;
197                label.appendf("%d x %d", w, h);
198                blackPaint.setAntiAlias(true);
199                blackPaint.setStyle(SkPaint::kFill_Style);
200                blackPaint.setTextSize(SK_Scalar1 * 10);
201                SkScalar baseline = dstRect.height() +
202                                    blackPaint.getTextSize() + SK_Scalar1 * 3;
203                canvas->drawText(label.c_str(), label.size(),
204                                    0, baseline,
205                                    blackPaint);
206                blackPaint.setStyle(SkPaint::kStroke_Style);
207                blackPaint.setStrokeWidth(SK_Scalar1);
208                blackPaint.setAntiAlias(false);
209                canvas->drawRect(dstRect, blackPaint);
210
211                canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0);
212                ++rowCount;
213                if ((dstRect.width() + kPadX) * rowCount > gSize) {
214                    canvas->restore();
215                    canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY);
216                    canvas->save();
217                    rowCount = 0;
218                }
219            }
220        }
221
222        {
223            // test the following code path:
224            // SkGpuDevice::drawPath() -> SkGpuDevice::drawWithMaskFilter()
225            SkIRect srcRect;
226            SkPaint paint;
227            SkBitmap bm;
228
229            bm = make_chessbm(5, 5);
230            paint.setFilterQuality(kLow_SkFilterQuality);
231
232            srcRect.setXYWH(1, 1, 3, 3);
233            paint.setMaskFilter(SkBlurMaskFilter::Make(
234                kNormal_SkBlurStyle,
235                SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)),
236                SkBlurMaskFilter::kHighQuality_BlurFlag));
237
238            sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm));
239            fProc(canvas, image.get(), bm, srcRect, dstRect, &paint);
240        }
241    }
242
243private:
244    typedef skiagm::GM INHERITED;
245};
246
247DEF_GM( return new DrawBitmapRectGM(bitmapproc      , nullptr); )
248DEF_GM( return new DrawBitmapRectGM(bitmapsubsetproc, "-subset"); )
249DEF_GM( return new DrawBitmapRectGM(imageproc       , "-imagerect"); )
250DEF_GM( return new DrawBitmapRectGM(imagesubsetproc , "-imagerect-subset"); )
251